wk/0000755000176200001440000000000014531546132010677 5ustar liggesuserswk/NAMESPACE0000644000176200001440000003173314515070354012125 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("$",wk_rcrd) S3method("$<-",wk_grd) S3method("[",wk_grd) S3method("[",wk_rcrd) S3method("[",wk_vctr) S3method("[<-",wk_crc) S3method("[<-",wk_rct) S3method("[<-",wk_wkb) S3method("[<-",wk_wkt) S3method("[<-",wk_xy) S3method("[[",wk_rcrd) S3method("[[",wk_vctr) S3method("[[<-",wk_grd) S3method("[[<-",wk_rcrd) S3method("[[<-",wk_vctr) S3method("names<-",wk_rcrd) S3method(as.character,wk_rcrd) S3method(as.character,wk_wkb) S3method(as.character,wk_wkt) S3method(as.data.frame,wk_rcrd) S3method(as.data.frame,wk_vctr) S3method(as.matrix,wk_rcrd) S3method(as.matrix,wk_trans_affine) S3method(as.raster,wk_grd_rct) S3method(as_crc,data.frame) S3method(as_crc,matrix) S3method(as_crc,wk_crc) S3method(as_grd_rct,wk_grd_rct) S3method(as_grd_rct,wk_grd_xy) S3method(as_grd_xy,wk_grd_rct) S3method(as_grd_xy,wk_grd_xy) S3method(as_rct,bbox) S3method(as_rct,data.frame) S3method(as_rct,matrix) S3method(as_rct,wk_grd_rct) S3method(as_rct,wk_grd_xy) S3method(as_rct,wk_rct) S3method(as_wk_trans,wk_trans) S3method(as_wkb,WKB) S3method(as_wkb,blob) S3method(as_wkb,character) S3method(as_wkb,default) S3method(as_wkb,sf) S3method(as_wkb,sfc) S3method(as_wkb,sfg) S3method(as_wkb,wk_wkb) S3method(as_wkt,character) S3method(as_wkt,default) S3method(as_wkt,sf) S3method(as_wkt,wk_wkt) S3method(as_xy,data.frame) S3method(as_xy,default) S3method(as_xy,matrix) S3method(as_xy,sf) S3method(as_xy,sfc) S3method(as_xy,wk_grd_rct) S3method(as_xy,wk_grd_xy) S3method(as_xy,wk_xy) S3method(c,wk_rcrd) S3method(c,wk_vctr) S3method(dim,wk_grd) S3method(format,wk_crc) S3method(format,wk_crs_inherit) S3method(format,wk_grd) S3method(format,wk_rcrd) S3method(format,wk_rct) S3method(format,wk_trans_affine) S3method(format,wk_wkb) S3method(format,wk_wkt) S3method(format,wk_xy) S3method(format,wk_xym) S3method(format,wk_xyz) S3method(format,wk_xyzm) S3method(grd_cell,wk_grd_rct) S3method(grd_cell,wk_grd_xy) S3method(grd_cell_range,default) S3method(grd_cell_rct,wk_grd_rct) S3method(grd_cell_rct,wk_grd_xy) S3method(grd_cell_xy,wk_grd_rct) S3method(grd_cell_xy,wk_grd_xy) S3method(grd_crop,wk_grd_rct) S3method(grd_crop,wk_grd_xy) S3method(grd_extend,wk_grd_rct) S3method(grd_extend,wk_grd_xy) S3method(grd_subset,wk_grd_rct) S3method(grd_subset,wk_grd_xy) S3method(grd_summary,wk_grd_rct) S3method(grd_summary,wk_grd_xy) S3method(grd_tile,wk_grd_rct) S3method(grd_tile,wk_grd_xy) S3method(is.na,wk_rcrd) S3method(is.na,wk_wkb) S3method(is.na,wk_xy) S3method(length,wk_rcrd) S3method(names,wk_rcrd) S3method(plot,wk_crc) S3method(plot,wk_grd_rct) S3method(plot,wk_grd_xy) S3method(plot,wk_rct) S3method(plot,wk_wkb) S3method(plot,wk_wkt) S3method(plot,wk_xy) S3method(print,wk_crs_inherit) S3method(print,wk_grd) S3method(print,wk_handler) S3method(print,wk_rcrd) S3method(print,wk_trans_affine) S3method(print,wk_vctr) S3method(rep,wk_rcrd) S3method(rep,wk_vctr) S3method(rep_len,wk_rcrd) S3method(rep_len,wk_vctr) S3method(str,wk_rcrd) S3method(str,wk_vctr) S3method(vec_cast.wk_crc,default) S3method(vec_cast.wk_rct,default) S3method(vec_cast.wk_wkb,default) S3method(vec_cast.wk_wkb,wk_crc) S3method(vec_cast.wk_wkb,wk_rct) S3method(vec_cast.wk_wkb,wk_wkb) S3method(vec_cast.wk_wkb,wk_wkt) S3method(vec_cast.wk_wkb,wk_xy) S3method(vec_cast.wk_wkb,wk_xym) S3method(vec_cast.wk_wkb,wk_xyz) S3method(vec_cast.wk_wkb,wk_xyzm) S3method(vec_cast.wk_wkt,default) S3method(vec_cast.wk_wkt,wk_crc) S3method(vec_cast.wk_wkt,wk_rct) S3method(vec_cast.wk_wkt,wk_wkb) S3method(vec_cast.wk_wkt,wk_wkt) S3method(vec_cast.wk_wkt,wk_xy) S3method(vec_cast.wk_wkt,wk_xym) S3method(vec_cast.wk_wkt,wk_xyz) S3method(vec_cast.wk_wkt,wk_xyzm) S3method(vec_cast.wk_xy,default) S3method(vec_cast.wk_xy,wk_wkb) S3method(vec_cast.wk_xy,wk_wkt) S3method(vec_cast.wk_xy,wk_xy) S3method(vec_cast.wk_xy,wk_xym) S3method(vec_cast.wk_xy,wk_xyz) S3method(vec_cast.wk_xy,wk_xyzm) S3method(vec_cast.wk_xym,default) S3method(vec_cast.wk_xym,wk_wkb) S3method(vec_cast.wk_xym,wk_wkt) S3method(vec_cast.wk_xym,wk_xy) S3method(vec_cast.wk_xym,wk_xym) S3method(vec_cast.wk_xym,wk_xyz) S3method(vec_cast.wk_xym,wk_xyzm) S3method(vec_cast.wk_xyz,default) S3method(vec_cast.wk_xyz,wk_wkb) S3method(vec_cast.wk_xyz,wk_wkt) S3method(vec_cast.wk_xyz,wk_xy) S3method(vec_cast.wk_xyz,wk_xym) S3method(vec_cast.wk_xyz,wk_xyz) S3method(vec_cast.wk_xyz,wk_xyzm) S3method(vec_cast.wk_xyzm,default) S3method(vec_cast.wk_xyzm,wk_wkb) S3method(vec_cast.wk_xyzm,wk_wkt) S3method(vec_cast.wk_xyzm,wk_xy) S3method(vec_cast.wk_xyzm,wk_xym) S3method(vec_cast.wk_xyzm,wk_xyz) S3method(vec_cast.wk_xyzm,wk_xyzm) S3method(vec_ptype2.wk_crc,wk_crc) S3method(vec_ptype2.wk_crc,wk_wkb) S3method(vec_ptype2.wk_crc,wk_wkt) S3method(vec_ptype2.wk_crc,wk_xy) S3method(vec_ptype2.wk_crc,wk_xym) S3method(vec_ptype2.wk_crc,wk_xyz) S3method(vec_ptype2.wk_crc,wk_xyzm) S3method(vec_ptype2.wk_rct,wk_crc) S3method(vec_ptype2.wk_rct,wk_rct) S3method(vec_ptype2.wk_rct,wk_wkb) S3method(vec_ptype2.wk_rct,wk_wkt) S3method(vec_ptype2.wk_rct,wk_xy) S3method(vec_ptype2.wk_rct,wk_xym) S3method(vec_ptype2.wk_rct,wk_xyz) S3method(vec_ptype2.wk_rct,wk_xyzm) S3method(vec_ptype2.wk_wkb,default) S3method(vec_ptype2.wk_wkb,wk_crc) S3method(vec_ptype2.wk_wkb,wk_rct) S3method(vec_ptype2.wk_wkb,wk_wkb) S3method(vec_ptype2.wk_wkb,wk_wkt) S3method(vec_ptype2.wk_wkb,wk_xy) S3method(vec_ptype2.wk_wkb,wk_xym) S3method(vec_ptype2.wk_wkb,wk_xyz) S3method(vec_ptype2.wk_wkb,wk_xyzm) S3method(vec_ptype2.wk_wkt,default) S3method(vec_ptype2.wk_wkt,wk_crc) S3method(vec_ptype2.wk_wkt,wk_rct) S3method(vec_ptype2.wk_wkt,wk_wkb) S3method(vec_ptype2.wk_wkt,wk_wkt) S3method(vec_ptype2.wk_wkt,wk_xy) S3method(vec_ptype2.wk_wkt,wk_xym) S3method(vec_ptype2.wk_wkt,wk_xyz) S3method(vec_ptype2.wk_wkt,wk_xyzm) S3method(vec_ptype2.wk_xy,wk_crc) S3method(vec_ptype2.wk_xy,wk_rct) S3method(vec_ptype2.wk_xy,wk_wkb) S3method(vec_ptype2.wk_xy,wk_wkt) S3method(vec_ptype2.wk_xy,wk_xy) S3method(vec_ptype2.wk_xy,wk_xym) S3method(vec_ptype2.wk_xy,wk_xyz) S3method(vec_ptype2.wk_xy,wk_xyzm) S3method(vec_ptype2.wk_xym,wk_crc) S3method(vec_ptype2.wk_xym,wk_rct) S3method(vec_ptype2.wk_xym,wk_wkb) S3method(vec_ptype2.wk_xym,wk_wkt) S3method(vec_ptype2.wk_xym,wk_xy) S3method(vec_ptype2.wk_xym,wk_xym) S3method(vec_ptype2.wk_xym,wk_xyz) S3method(vec_ptype2.wk_xym,wk_xyzm) S3method(vec_ptype2.wk_xyz,wk_crc) S3method(vec_ptype2.wk_xyz,wk_rct) S3method(vec_ptype2.wk_xyz,wk_wkb) S3method(vec_ptype2.wk_xyz,wk_wkt) S3method(vec_ptype2.wk_xyz,wk_xy) S3method(vec_ptype2.wk_xyz,wk_xym) S3method(vec_ptype2.wk_xyz,wk_xyz) S3method(vec_ptype2.wk_xyz,wk_xyzm) S3method(vec_ptype2.wk_xyzm,wk_crc) S3method(vec_ptype2.wk_xyzm,wk_rct) S3method(vec_ptype2.wk_xyzm,wk_wkb) S3method(vec_ptype2.wk_xyzm,wk_wkt) S3method(vec_ptype2.wk_xyzm,wk_xy) S3method(vec_ptype2.wk_xyzm,wk_xym) S3method(vec_ptype2.wk_xyzm,wk_xyz) S3method(vec_ptype2.wk_xyzm,wk_xyzm) S3method(wk_bbox,default) S3method(wk_bbox,wk_grd) S3method(wk_coords,default) S3method(wk_coords,wk_xy) S3method(wk_count,default) S3method(wk_crs,data.frame) S3method(wk_crs,sf) S3method(wk_crs,sfc) S3method(wk_crs,sfg) S3method(wk_crs,wk_grd) S3method(wk_crs,wk_rcrd) S3method(wk_crs,wk_vctr) S3method(wk_crs_equal_generic,crs) S3method(wk_crs_equal_generic,default) S3method(wk_crs_equal_generic,double) S3method(wk_crs_equal_generic,integer) S3method(wk_crs_proj_definition,"NULL") S3method(wk_crs_proj_definition,character) S3method(wk_crs_proj_definition,crs) S3method(wk_crs_proj_definition,double) S3method(wk_crs_proj_definition,integer) S3method(wk_crs_proj_definition,wk_crs_inherit) S3method(wk_crs_projjson,crs) S3method(wk_crs_projjson,default) S3method(wk_envelope,default) S3method(wk_envelope,wk_crc) S3method(wk_envelope,wk_rct) S3method(wk_envelope,wk_xy) S3method(wk_handle,bbox) S3method(wk_handle,data.frame) S3method(wk_handle,sf) S3method(wk_handle,sfc) S3method(wk_handle,sfg) S3method(wk_handle,wk_crc) S3method(wk_handle,wk_grd_rct) S3method(wk_handle,wk_grd_xy) S3method(wk_handle,wk_rct) S3method(wk_handle,wk_wkb) S3method(wk_handle,wk_wkt) S3method(wk_handle,wk_xy) S3method(wk_handle_slice,data.frame) S3method(wk_handle_slice,default) S3method(wk_is_geodesic,data.frame) S3method(wk_is_geodesic,default) S3method(wk_is_geodesic,wk_wkb) S3method(wk_is_geodesic,wk_wkt) S3method(wk_meta,default) S3method(wk_plot,default) S3method(wk_restore,data.frame) S3method(wk_restore,default) S3method(wk_restore,sf) S3method(wk_restore,tbl_df) S3method(wk_set_crs,data.frame) S3method(wk_set_crs,sf) S3method(wk_set_crs,sfc) S3method(wk_set_crs,wk_grd) S3method(wk_set_crs,wk_rcrd) S3method(wk_set_crs,wk_vctr) S3method(wk_set_geodesic,data.frame) S3method(wk_set_geodesic,default) S3method(wk_set_geodesic,wk_wkb) S3method(wk_set_geodesic,wk_wkt) S3method(wk_trans_inverse,wk_trans_affine) S3method(wk_translate,data.frame) S3method(wk_translate,default) S3method(wk_translate,sf) S3method(wk_translate,sfc) S3method(wk_translate,tbl_df) S3method(wk_vector_meta,default) S3method(wk_writer,data.frame) S3method(wk_writer,default) S3method(wk_writer,sf) S3method(wk_writer,sfc) S3method(wk_writer,wk_wkb) S3method(wk_writer,wk_wkt) S3method(wk_writer,wk_xy) export("wk_coords<-") export("wk_crs<-") export("wk_is_geodesic<-") export(as_crc) export(as_grd_rct) export(as_grd_xy) export(as_rct) export(as_wk_handler) export(as_wk_trans) export(as_wkb) export(as_wkt) export(as_xy) export(crc) export(crc_center) export(crc_r) export(crc_x) export(crc_y) export(grd) export(grd_cell) export(grd_cell_range) export(grd_cell_rct) export(grd_cell_xy) export(grd_crop) export(grd_data_extract) export(grd_data_subset) export(grd_extend) export(grd_extract) export(grd_extract_nearest) export(grd_rct) export(grd_snap_next) export(grd_snap_previous) export(grd_subset) export(grd_summary) export(grd_tile) export(grd_tile_summary) export(grd_tile_template) export(grd_xy) export(handle_wkt_without_vector_size) export(is_handleable) export(is_wk_handler) export(is_wk_wkb) export(is_wk_wkt) export(new_wk_crc) export(new_wk_grd) export(new_wk_handler) export(new_wk_rct) export(new_wk_trans) export(new_wk_wkb) export(new_wk_wkt) export(new_wk_xy) export(new_wk_xym) export(new_wk_xyz) export(new_wk_xyzm) export(parse_wkb) export(parse_wkt) export(rct) export(rct_contains) export(rct_height) export(rct_intersection) export(rct_intersects) export(rct_width) export(rct_xmax) export(rct_xmin) export(rct_ymax) export(rct_ymin) export(sfc_writer) export(validate_wk_wkb) export(validate_wk_wkt) export(validate_wk_xy) export(validate_wk_xym) export(validate_wk_xyz) export(validate_wk_xyzm) export(vec_cast.wk_crc) export(vec_cast.wk_rct) export(vec_cast.wk_wkb) export(vec_cast.wk_wkt) export(vec_cast.wk_xy) export(vec_cast.wk_xym) export(vec_cast.wk_xyz) export(vec_cast.wk_xyzm) export(vec_ptype2.wk_crc) export(vec_ptype2.wk_rct) export(vec_ptype2.wk_wkb) export(vec_ptype2.wk_wkt) export(vec_ptype2.wk_xy) export(vec_ptype2.wk_xym) export(vec_ptype2.wk_xyz) export(vec_ptype2.wk_xyzm) export(wk_affine_compose) export(wk_affine_fit) export(wk_affine_identity) export(wk_affine_invert) export(wk_affine_rescale) export(wk_affine_rotate) export(wk_affine_scale) export(wk_affine_translate) export(wk_bbox) export(wk_bbox_handler) export(wk_chunk_strategy_coordinates) export(wk_chunk_strategy_feature) export(wk_chunk_strategy_single) export(wk_clockwise) export(wk_collection) export(wk_collection_filter) export(wk_coords) export(wk_count) export(wk_count_handler) export(wk_counterclockwise) export(wk_crs) export(wk_crs_auto) export(wk_crs_auto_value) export(wk_crs_equal) export(wk_crs_equal_generic) export(wk_crs_inherit) export(wk_crs_longlat) export(wk_crs_output) export(wk_crs_proj_definition) export(wk_crs_projjson) export(wk_debug) export(wk_debug_filter) export(wk_drop_m) export(wk_drop_z) export(wk_envelope) export(wk_envelope_handler) export(wk_example) export(wk_flatten) export(wk_flatten_filter) export(wk_format) export(wk_geodesic_inherit) export(wk_geometry_type) export(wk_geometry_type_label) export(wk_handle) export(wk_handle_slice) export(wk_identity) export(wk_identity_filter) export(wk_is_geodesic) export(wk_is_geodesic_output) export(wk_linestring) export(wk_linestring_filter) export(wk_meta) export(wk_meta_handler) export(wk_orient) export(wk_orient_filter) export(wk_platform_endian) export(wk_plot) export(wk_polygon) export(wk_polygon_filter) export(wk_problems) export(wk_problems_handler) export(wk_restore) export(wk_set_crs) export(wk_set_geodesic) export(wk_set_m) export(wk_set_z) export(wk_trans_affine) export(wk_trans_explicit) export(wk_trans_inverse) export(wk_trans_set) export(wk_transform) export(wk_transform_filter) export(wk_translate) export(wk_vector_meta) export(wk_vector_meta_handler) export(wk_vertex_filter) export(wk_vertices) export(wk_void) export(wk_void_handler) export(wk_writer) export(wkb) export(wkb_to_hex) export(wkb_translate_wkb) export(wkb_translate_wkt) export(wkb_writer) export(wkt) export(wkt_format_handler) export(wkt_translate_wkb) export(wkt_translate_wkt) export(wkt_writer) export(xy) export(xy_dims) export(xy_m) export(xy_writer) export(xy_x) export(xy_y) export(xy_z) export(xym) export(xyz) export(xyzm) importFrom(grDevices,as.raster) importFrom(graphics,plot) useDynLib(wk, .registration = TRUE) wk/LICENSE0000644000176200001440000000005614106220314011672 0ustar liggesusersYEAR: 2020 COPYRIGHT HOLDER: Dewey Dunnington wk/README.md0000644000176200001440000001014014531512043012144 0ustar liggesusers # wk [![R build status](https://github.com/paleolimbot/wk/workflows/R-CMD-check/badge.svg)](https://github.com/paleolimbot/wk/actions) [![Codecov test coverage](https://codecov.io/gh/paleolimbot/wk/branch/master/graph/badge.svg)](https://app.codecov.io/gh/paleolimbot/wk?branch=master) The goal of wk is to provide lightweight R, C, and C++ infrastructure for a distributed ecosystem of packages that operate on collections of coordinates. First, wk provides vector classes for points, circles, rectangles, well-known text (WKT), and well-known binary (WKB). Second, wk provides a C API and set of S3 generics for event-based iteration over vectors of geometries. ## Installation You can install the released version of wk from [CRAN](https://cran.r-project.org/) with: ``` r install.packages("wk") ``` You can install the development version from [GitHub](https://github.com/) with: ``` r # install.packages("remotes") remotes::install_github("paleolimbot/wk") ``` If you can load the package, you’re good to go! ``` r library(wk) ``` ## Vector classes Use `wkt()` to mark a character vector as containing well-known text, or `wkb()` to mark a vector as well-known binary. Use `xy()`, `xyz()`, `xym()`, and `xyzm()` to create vectors of points, and `rct()` to create vectors of rectangles. These classes have full [vctrs](https://vctrs.r-lib.org) support and `plot()`/`format()` methods to make them as frictionless as possible working in R and RStudio. ``` r wkt("POINT (30 10)") #> #> [1] POINT (30 10) as_wkb(wkt("POINT (30 10)")) #> #> [1] xy(1, 2) #> #> [1] (1 2) rct(1, 2, 3, 4) #> #> [1] [1 2 3 4] crc(0, 0, 1) #> #> [1] [0 0, r = 1] ``` ## Generics The wk package is made up of readers, handlers, and filters. Readers parse the various formats supported by the wk package, handlers calculate values based on information from the readers (e.g., translating a vector of geometries into another format), and filters transform information from the readers (e.g., transforming coordinates) on the fly. The `wk_handle()` and `wk_translate()` generics power operations for many geometry vector formats without having to explicitly support each one. ## C API The distributed nature of the wk framework is powered by a [~100-line header](https://github.com/paleolimbot/wk/blob/master/inst/include/wk-v1.h) describing the types of information that parsers typically encounter when reading geometries and the order in which that information is typically organized. Detailed information is available in the [C and C++ API article](https://paleolimbot.github.io/wk/articles/articles/programming.html). ``` r wk_debug( as_wkt("LINESTRING (1 1, 2 2, 3 3)"), wkt_format_handler(max_coords = 2) ) #> initialize (dirty = 0 -> 1) #> vector_start: [1] <0x16d75aac0> => WK_CONTINUE #> feature_start (1): <0x16d75aac0> => WK_CONTINUE #> geometry_start (): LINESTRING[UNKNOWN] <0x16d75a950> => WK_CONTINUE #> coord (1): <0x16d75a950> (1.000000 1.000000) => WK_CONTINUE #> coord (2): <0x16d75a950> (2.000000 2.000000) => WK_ABORT_FEATURE #> vector_end: <0x16d75aac0> #> deinitialize #> [1] "LINESTRING (1 1, 2 2..." ``` ## sf support The wk package implements a reader and writer for sfc objects so you can use them wherever you’d use an `xy()`, `rct()`, `crc()`, `wkb()`, or `wkt()`: ``` r wk_debug( sf::st_sfc(sf::st_linestring(rbind(c(1, 1), c(2, 2), c(3, 3)))), wkt_format_handler(max_coords = 2) ) #> initialize (dirty = 0 -> 1) #> vector_start: LINESTRING B[1] <0x16d75dac8> => WK_CONTINUE #> feature_start (1): <0x16d75dac8> => WK_CONTINUE #> geometry_start (): LINESTRING[3] <0x16d75da10> => WK_CONTINUE #> coord (1): <0x16d75da10> (1.000000 1.000000) => WK_CONTINUE #> coord (2): <0x16d75da10> (2.000000 2.000000) => WK_ABORT_FEATURE #> vector_end: <0x16d75dac8> #> deinitialize #> [1] "LINESTRING (1 1, 2 2..." ``` ## Lightweight The wk package has zero dependencies and compiles in ~10 seconds. wk/data/0000755000176200001440000000000014510374414011607 5ustar liggesuserswk/data/wk_example_wkt.rda0000644000176200001440000006070414313206670015326 0ustar liggesusersBZh91AY&SY+|L7&RxP9b}ij| l4P= P$*P)EPEځT*HPIT*@HHP$ @QJB)E$ ( "(A P$`@*@UIJH ( A( R  ` %)"@DI$H$PqA@@((pN$(H "TJSC$@($RRDIO;b͂hJ D #))4# 4 smL R"]-9ۇŒӬk+l0FbjRQYTKӓao-OP.r#w^Lsej1y=o<NVѳ!l3nJUV ұaXDx](F; 5[l3 I c{G ݮkf6à ҍ'=s#[5YzmOq6X=@ZDɔv,P&u͍OٯJmo0|u 6:Aw{^f\ᙏ4s4 ^5o]o x}I! AEl3H*jϣՊPҦjƠS(m[Vb+ lSeV5@Qe2efQV5mY%նVl͕l)V)fQjVA`[ejmS2ljSe, m6V[5m lͩV2FژجjUWߗ_dVGŜEp_ˋi֗/JC 7Hs&j6,j1ݴXu6/):\aq\G7fm+4XwmjܨM#m4qfkyc.2X]]UFif,qJ.,"q՗QVN-Ve/tik;eU--8Y#.VUeWNv8_(묮k\0#+"2.X.gXcث#V]+ ehizZBʽֳ9]Y\Qe8WkYadfU-*]YC(6^ő+%u)40»]+L%]L:q iz1W]Z4]mW]hʢc60*Ƙe+)VG]]"X4,+m귆ZDB0!ƖZe\0FE1.Bs ^Ul UJBY{ETYګIvV"2kcc.Ͳ0,0mUЃ c(3*dͶKmif#ed5KUhצYZ!.ivZm WXqi !Ƶeb5єu dk 4hFigruL Fъ068kk奎"̬ήUu,nK4&[*]]X5(cfaf .#lLN4RSm]Va6ʔ6mVUZ_ )̬Mia)dՕdu6KFafVW2Y0_ej0#+L22EY4ieQVAwiGt.d IYnScxp(Ld('x0u:V42m%e YuonNkx-7Iu%YeucIU{2Yqm[fp"T.˭4<g"w bՙ^fTueeulQUluuj;JSaje0UY0%i.}K%W,[aN0Ι6G3ʽm{+\ZͲqgYVkq+:7f۽Yq8i+hq՜w*iYl+dvS^1RVYz9[nNȻ8*N&je+{u;U|rI]]Ycdu$aWdU]]fa{'wV,-2,XB6ewiƙ26 u֬e2Rm:Ù˲ޮfVllIb MtCN(&xyXiEaWR㕒ˡ<'Jw t)ђ,X"iesKo UaaBiUwue:Ӯ8#ݚYz :ʌ斥m(p,8եKzqw[v,:mjƛM2Yj,FWY4ᛚM\xWC 3Pn0`ueuEmںZ6fvo۬4ͮ^UӦz %f[aեRkV8iVm+!"u9Y0ٵuz:j͘2Ye]uc4,"* CZuz*DV2Y/Lk/8՟4CI]940- md}|HᖛXӊVt]Qk0<.Fah%uj xZ$Imf5HBQ )$#xeje6%"2̵$m;MiL.ˎDB%J ص%9,2%g ji"RTbܻ%JR[t#̵Ga룇(iӭ=ĥ9z7.kFu\6"#'J"$B HJe:Eq#UPFÆY]vWMWO=U4g5G*RFiQ2eyupU2z,ҶڶQ(aW^ac&N: U9YŞ8F\yM SGui:t}jM-5EvHe]UmRF$z{/vۗDB&$n쬮2:m1aw;[iמ <˨!,%m[F/%TDmk87Xy%%Y*ExiʉH$NEikJ'NsdqƝk˗6Wr%D$iMB!*%l+>y+>]$>;^-iN[|}Qӝ>]KNynʬZ"%V׮+˵[w笲IdDJkH",DUX;vOtV*R+%Նҙ]vXYb]9+iWafDUꫲ!H6#kkʬˏVRL6yf4{ak 峫wIr%K,Gקj;#<ᇋ1[q^"SryƟ-i>0z=z:VIZVyǛM^4RT '+JggivVtKD"ZYm×YSIn_bDB"*JI. 0lW^-^B&]iO,if̸WV"\fmk$oiӷ \wo%:ӌW)ZXˆIckO1,R`iOĕV=$'.֭mTJ^YZY"v+uvxW$J]y %"SxYڬ;2oUI[~ݕqʕQ%44X{7O,m#",%+zT*4uƽL2,]RK%kUrD*BcY[M#+8 yRF\0cR벇]vQ3Y]T!$6\FȲmQjī2bqvHQeXˬ<%QnVDH"nn=zIem)ww:Lݺ|fUq=ԄDr!q+EJ,'uԭHkGpT$.ѷV:ˍׇ6j9WSiƞuFez+50˰|"Q!"+ONnæZEkXzx|qɳ0JpƗ8XBv:|qtM4VY4J)|x-2]xXbӎ*R"TDD+VY[#us^fi:񆎱YVQW7"_vw-0%EI󖶜x=&t52ŦkN9#hN^}j+M6ѮUGQʴÆҬmηZۭ}qDD3WZ6Qdǁ(8FZYC!4D1M0m Q҉) cbPM)MJ!AŘKD)Pz\y˷כn]>źW/ 11^o>[H^PS×!(YruHdR*5gعqB+o#DrQ׷GY˃<*e,L8%qdՅr.betӵMH%wgG.qi/m+uJ:GNK/:ч\3YkEbT(Ii֐kd*z{$:v%%"jB&U%(4Y%CnY1^T2ݞ 5\xцJ Hdg6i YjdTmx|S-2qhה"B%R#&Y9JEJmڈ*4V"+Yem"4qeagk"1kjI]w-kݫ:YVilүQMӜ0 ^Gmr='I-gԉ$ZNOooG Ii5U"$:y"ulZDBW6ˇUڨKV]q%T׍LZAyWew*"%IuN-T .NyDDHi,բ<֫Ojδ,2ĩ\=:9[h$kn_^ƘtN{e*4%FdaJvYlMu\[9JYoM0uȉU QQۏuӋiӋ4iǫѦͮ٧-*"# R8y:5U qVomR$"4IKD[EHDR-i$ZIK)qVM45J*'팷LSi商0 ,4X9֖r{B=!vZ^%|dΫ׫ "YDqmf)J2ci"Ӫ7*u4SЕg'>=8om5&IhP)$eT6ïUi۞dU4uܡ!>12ϪQejONO-dY9xx]wZ\GQrWk;rs-!)"Qp6su 1 Q* oN]97X[Fm0 2<ۮ4ZB%4\"ͣFQ\<>rM ē%YdQYp'I!d)sֈrkBƗ{K4ǛRy:!&Wei:jۆuܮGrvEHYk}r|z;&qʵ]o.L,6"%8iwmf,iܬ%-$֖6ӭdeˌ/]ZS8㴔enTq yKz]U *$I-^'rt%TH3ELzˋVZ*;w\*Di[Ik!pR[I$|zwӲeT2 ޫӤE5MR"ZDD$ISimQa)5D81t%5ω!Bݤ$P"8eA1@D"H(InP|[@cH"#% iz)x$.)&  дYyx>4BEOiZZVVեkeJRmΝ݇*TDDJy|+r~]y^F*uHwYaqv 'V5]mg]n6댺05LLIp6\;=M e,.qf:R'OmYk$ZHܺz|$X$ZDI, J~0̷ק;y}O|F_.ۋ<ѥqU,U0ur"JJBZRK-m>zΦEI#gknQ̒<ˬ4ôߥCz3U,:|.#<̶בҽ%DqwUru&)oݞiFˎyXm:yJ]ĒI(WVW~? R˹HW8vI)"$ҔX˜Ӈ|}=\bYɆEJB%eSfYrͰiyRXY,]V8;WUtMysئz*qqO6pa*;pxὟRIZɥ޻>=KhÌDDq+ZcKH f!"!)$>)E6oTd댴W|ζYhJQfVrQe᪵s%]tF3QuVx!T0VpG)Pl8[{,V*8GF՛7\xH#'WZs$XBZJkOOfuQ[㵚e8+=sn5YQ)+aƘa)**X6 ]^lۮ R$DvY֭\"fnt+&l5 %V$ZJgk- EV*UI%=Ɩw!z+Kc ;tuƝniwU[% qڮDDue֘yev*='+IihYKV,!%OU*A*"2;V;\iU6oމɡdysmVN~ޟ=UyǘW 0ݭ%6\un;["JN:LMr¸rOTTDǙ4p?.Kas|mDDBQ#8 !duW-j]l$KK%=òH&4+8v7utDD"YWuJmYk c+_n8;ez/Os)WzDDmf wuVS4>ߧiRҡMbҥT$DՋe"T%#Y$Zzm%qǫ3R"*Lۺvp-+R,H"-e+V$-,=r:õ< /=sQZԑ*!EN7wj8UǍ|N^m"*8v0pƙCoV2JN2i֌}jYDc}oO:{"i5Y-J֒DDDkθmG<HQFe{nC:骮6a%TJuKkm>\>:xc{$eVTc,V&[2# ߅DDmm+V6:W,.YJi$M)e-&ֳ IjIDU-MdCy[qxL]jj󊔈2i2b*xˊmuj.ykjr(Esŗ^z, +3]Vx[< xMӼ-42ymWhtYtK[FKZX4ζ:6 2u]"B#ew\&V\+a4ewvl*!QS']Xާμ""#ͳK:͍=VGNijGY,"DORk5%{# kH%BA*JZqmSǪJFo6b%Wg0,u"QXw`,q*""RD\;Wvi %ZWfͬrTeȶudDL8]TuIh;[p.sVǒMFWPKל2W k09NW󵌷W )jDٮUZ""!*%*%u0y.LVKX,ǞwO,n.qf^Jl"DUaWl+Ǜ:$w3ij<ӋTHp+O![rr ^HnW7BI(іq=Nehe;rGXu.˷!Ƌ-IXѢ%M$)$ȪҨ-mϖxr-I4$Xyޛ֒H 5mk-KO=l"8ˬ3U}]Qmvi]%Dqg/GmKi: iuWuͺS.yiQ֌,mf<J)f4rJ=[r|ݺr|w-%4"".Fsm,ۏXaB'Upu& ˤ+Yacʕ{fq*:ʼNTpRTZӫӈ8{v\&eXYηRJeM1M8t֝tBQ &JM˝Z% $JU1]TG\7ȨQ$Um qd>j]uѦ,uV`̯YV/U*DpS՗Vo7W̊B%XJNGJ^Y7ֶƞ2*$iYYżˌ-\e5=N-dHn7_+|qح^}NSns/4m֋:'۷}--dFˍvXi^JI)o(۽KJI$vǞޝZYKURܽ$}YWru4r;^WY=Xz7MGJY/Y/kiJ)P]vmu#k`dCξN>ujkm7<>xy.4:Um)45uk0˝v\aU)Xܞ.zE%J}ZMđZFii-WDH ,_<.eC:4,E.uQ zT-۽|OIXaxVu{g8,/O7TU$D{,eB"9Em SFikm}qT*py˾N}YIfj%%hs[Z]uƙꄄDD6N2rKw2ÚYlTcqݩ[sz7\y/3߶g^k&HjEDhEyq +8z`yf!# qaΜPd ̉hЉ(OChǵDEw83EC]*Ӆ9pc_'v`yjTE <[y#ǗG,ZUTk=w_eY^G+Jk֍P,3^B:huDA6Jnw6iKibHJN%TyzS)P*m%aoua+,jR"QzgY`] Tr2*"atnv_-,ݼI"DLzzYX۴*nmLG^ur*]TYWVaz.RĪufZyVnoد!:")"JGYwZEwƣ45]FT"EZɢYZ;zpDIii7G[uYkv۷Ħ*iQ ZzWp۔,ߨe8J=WΗ\]Gii4I]:;UdVI%#8Byw8H:[nyq }m\m" PDIId-zvՅ)TaZXWjY֋kyi׫[Yb$q- OL0LQ1-1LƔL&)0DLa,q(Q&rQ46 & 4C]WXu->qZOCuKc`, >o-xdbЋuUaU֓8ͭ25GIFA<:x_-%8O^i"7GzThbEaZukWEMlJ)?,]m՞?meY"Yi$YIMYUYY~=jyzVbEfc-Eev8v%kktTBⴔmT-,g]%!"*26o8n J8g]^GqlQEIL񳫴na| IiYbZ%YJ$~JUQqr4f#4i|TNZ*EiJ_w.۷}kME[=vǷH.M ӈm9QUTE]q[Yu4\u 0ì-\Zez[uYeIQ Μ`KzTK\J2jѼޯ]a\yQ{yreꝤu)c޶uY$%TT#eڧrn0qZCo͝!"2yڭ*VaaS$$QHJ\V]0,hqywKI"Eo\;Yym!*""]<复e+EƘFiQ+Uj8k͜EDRU<"*,Yf`e`ӎhMZn"ED".n8lF,HJ0u6f+HHJY"RJ' Fm{;Zq4m\-L6+."]g .(N%EtD%eUR?n7N ?ON)b62|SJn穧]U)7YU<{vOZDЦJ"M}G94hZiJHU\ayyLܳ4Uug[DJD`9Xm$KO2L<$VZOGi\}Nyu`U^i7M= R1_6&\Y ԃվ`.ϖqGjT"DJ&$ҫPf덹J6gVԇnӎSՃoahZi3JT\O=;v-0,./uՙi׭+XU]S:ˍ=HVɒMI-5rrZ֘-,򵖈#,E1agJxl6 8u/TʑEJUmn]-N<l]N6udYƙFyHϟ\m4&DLJÆՙۈyYi b̰*UZb^:qQrMK]RfNJʰիrw.^%dL$["iJ*Ǐ.mV7s%\yeqH,ɪMW/{S;a:B"VodYiQ"i+$I%jL\vgī]*WqQszuKb7;nV"+MevM.GI%aw7RtFfz{O7[<|z mmJ-^YVqmxJSXqcQv|yvHO~dìDgGg'g45(n/M0=yG)+8-Eg^Mޭ:"cw6q޳"ihuqs«'eJǖQR'l!BYş25[mf_R,6kV*]vKےSL̨-I+{DH)FՎ*:R|vo&˷:ȑ9Yki"YZas,ph`8گZifXˣU^D+u;=r{8MjiM$Id T%ۧҋ%j'XmՂ=JNU΢T͘\0ڧvUug {Vq]DDJBUAZʃm \{i[iQ"ƙu_j0Wat9άN+uZ-ˇGērvaeaMM[WUwt8UYddju]sbU\Ïi÷XeYE$9ܹO7ՑiT#Mu"$]^GnVH.Ylxz×icUf)%:iv̫v{>ZڥURKK%J|xM-G=>86:m%"%qz\6Jŝ"!*0㚺< Vmjq\*J)*Q4xtǷ$MiZDֲۄpiafK#k2jޭQ"V_ #2ujxLRYdn:'Hf=RmXۅݘ*K-ZsytHjq^{V0D#azrJ0u^IaSNYyK~a"ebh YoJ{7H!0A-8YtlaZz:\3~b:L?5oMg3" y@# 'wp92;頙a~w%RM5B! PR牮Tsc>L[:G5NGѽ֛{ V&6V8nt}{;=]M[z?[ʿOC"ˇ{}^ns{nƧv-kr&ztyW`DCCH<$R@(݂% FA $zy0zlN3@A:xk7^e_"W*UX5wz`)Uy@ J Հ!邜|$4y*G7osa&mͣ ]IN2FY8.gqv*R`(˨ع ;("X*YTejbtQY^XCPn`auU`FZTlHL78l %+)#l:AŅ cB<<@ B!#7/QDTǷZdp>U!0UwTϲ'_uG}AsJH\C4 e"@}wzW`[; ERHιĺ֝Ջ*Lv & xXD(HW{,9B VZ#,Lb"Z!BSboBo y^vrˀ@4sE"+{ (* QWlPX>*>@ ̯'Nװ&LEAtHv9¹htNW;s]n O{UKڞToxut $}L- GrR:tUJ޹W79hn͸@otzaEy:9BQnQ6+{{9 =ppg`wmWRw-aYwk`{u9u9kyieFoݸ]rW{KN;w.`g/wx;s3c&!+l\^A50/ lmH[;=P v &HvaCvofJW{MgPC8Imgwi{fİ3{^ ׵QTK(ҁ+[T(*F60J)IR]Dvi $QPPuAECAJJS`8xgx{BH4 ГShFLOBOL?MSzCMhDSOS54zQ2=LjhiƍGcIIeOUJD&jz  ADhS6iC#@ 4dѦ! 4h @ 4C@"iF1 44RR&4ħ=ODShCG=A$h0)F4hѓj4yT[?cU5j=??O{C`REDҥ,!&d[* ?ҨGDO?W[$I$}7}_<9Qw3Ŷ_v{$ZOd@fHmrߖ@mm'7w3}$4n$[׃;uLE u#I1I1y߯yt?msY$@[9$O_I7yޖRI$w7malI%m--s3 mC2{fff8-hmm}]I {ܜwsHI7rwI$mcNv}֛8flg{6nmI"dޱs $@;@I AmI$I$wm<{w ( $n$@m= dEL I$ o{jFffffgw37 8j3~sFff`[m $D8` $@m;ߙ$E-_}mII@'ws-w!lm 7r:cKimۗu,M7wQFyϾα/ݶrbEM[|[ $}{ffgI$[ymI&m$Km$ݾd-w{d$ms30Pm.emmU7jnw}Lɼk36;wws1ϟ8("ANeTG5sn+ח,o~'s;gL{]u{{{(%[m{d 3$$黠f%ݢmmmmm{mݷm-m,ٟImm $.弽{ϗm۶mom~r#{/=-on[ymmܶmvNsnۖ[mym-[o۶~~ml=s &{Żwwt^{www|5yN/<'.K3nyl7+az vws${|}O$7żmmd}9n_ww{j_m۶m巜s{mt틹m[ۻSq1RỵRn%E.y$qE_nTMWwvl֒Nommmy$'Ken ݑ;[6{Mݶmmmʶmm_9romϤdmmmm@|vm[mvyW5w?γw-? /m-@3-9g6@9;{gl^MFƚ]i9t׏UOg6XsJ25s;OfZTV֝9MoW1NGG*z6ֻ]g9霴mg~V$K1]nl=k>+`vm\;]'gM ae噚zhU|*sR^UUUUUUZUUUUUUUZUUUUUUUZUUUW䗋UUUiUUUVffffffE*$I$I$I$-UUUZUUUUUUUZUUUUUUUZUUUVmcR#nѹrӹ ]lpq8jˣ׊_v$ғ3~8ϣg*HJťm~݁2/3k*a%nb~^q4id6QoY/'n97gg68I7/6g0󥹴dx|q=yP*b<?UH2dRzgq^қ͸{uF{40Z"&DI6.#A0`iB"# $@_K)8iDNL99«T;;HK)kh6l6lm-f6֘ٳZmmacV#m6ٶٻ1JA"\Ԁ5E ȀZ b (\sXXW}[jZs_l.Un#c: _eyPH^:qsT:_ak}}x8V/q >:ܬXKyZk12zPR@[dbpعsskpCeaJPQhϤ @膼3hdF~UUUdI& n,I %m^Xd$3Z4|+NIX ?Es#q\/A0`݉a T$J`fw M Ѹd N`c CFX $ c@<MC].e_xS㌒%sQie،TN7LȅjIbh0s̡p E5$O8'ܐ E.Kejh4uh0QЖZV @ate%x Vj¦aPOţX+AK %`C(TՅa-)Y3F7hyQnΌ1icLV&qbEn%U$րRTy"TF# ʹ9|@+w5s02cn%8kP ;^Mg{ƹc=Z_fh>b[U1nњwZ:`x :Mmr2upcܮ\SRېI 8+\.q|@~yXR&B_..\vF<̇wnhq֕f:QMR$\%UiUUUQUUUUiUUUU}҅|G䢒j^yd5gl).Z}'4"M9x9'z-`띙h{7L9Px~8I1w. $3Ѩӣ=eYy7nz{K7=NJнl zsJvyvYpKS퐵`IAcǃ-ab5`ksBY 1ͰA,LnDڵ$n["e+"Wd;ed3\n۔׭Cu NsemRayw UDl1-0х YgO+[@ EntLy/,3)ne~껍Yӹ_u+ɺ( 9ixZk۔: wJ\J[&]2Ě[M\tvMb gttdݒ zo%8 %j3ы\CNQZ s~,MP $R\avk74Vag)>]G~(ŃЕ.sXe@1!!) Z/.t?@fk6{ookPby8-WafȗZz7>DjpY;.EMݴ*UI̹݁glUro6ϭ@VF1! &E1$HQKJMJFCɫNzCm*UZ jcѐudH!u0^ `T38[!ly G|U^k)vb.Ǒjp>u|`.ZаgdtWC3auِL &,^&2 'vS^+$!V*#x}y[ZU{Δ')۸hu/ɫ$2 C*Kkc(W\ -hq2vb}FwNʍvn3Ǒz1mffc*4VjɖͶK48,H-^(OY>J܇(H*Aŭf'1dCV PUUUUUUUUUUUUUUZߏt~D>Dwd(˫.DCvίsݏCj1#BHz <'!&[0ZyY3ƋTRUQ^ҭ2DJH~) Pc q% ]+S&"""""""&B7qww ?9 _E>|G`^BrJ~XQb?vS"`I H?{|?DDDDD{y xU!i\c¯XȃT!s~|:vRፎ8z~|r吾""""uc]mm""""""}Po~/|[l @T>I9cS0 C~Q3S19PrNi9 lXƶ k~t#FZ#:G'NVٳkmS6e-Δ~Zճ Ҟ:TN6;m;iz7c|7p4a9UQ|/)Ģ*҈{h qE j b\)h699qvPUDjҪG}╓k[ں[,IMS>=>NZDxKN#_"Y2H:VZ6pjypZthOmS}c:H7g&}ϳux4޻{H>Rͤ>[Vٲ-*fok{l̾Kv fefT5mkƳmfcpֶfKX5M2dmk3mQ1MelV2j4l-,Ͷ5mhNy/Y=tS]_ 3=)*Ҩ†dbOD|Ҋtسm6hT2TI$ kY-X8WV.] J_{}9.1SӐ@{ԝsmGTl 86cjf6VɸMV(J#Q HDOBak6ZSՎYpaqLtc4iӲf6BP^C= lXTmXVQC-RIi8N+pP# WMS@Uqd´NHLDp+FI̸T`-X[JҭjA3ZmMZ4,fSNT#ȉtNQUZ M WζL#V -UJ)L*.YYp.8rFrirFVUYU4i36d63,UfښJZX}DXDɀy%GT55JrFE&ԅJepfX2cf5S[L[Vl6e0lLjfj͛iX l"mj5fV3QJmœdmdfh)F4ffffH5i6f,ٶm[6+++MXm`dmͬJX ٓV͛m51feUe6)mfՊS#S1͆՘l[54MkeMZXFԭٍjfm ְQQqFSγ98|T\㷪y· =Uȧ/6cr٣Mwpk9OIZb42O_ Hsݩ _! xv#E˺I+`7'\ n,-e_ 7B{x>KKⷷ0B=eK*e/g|Bvn|pr~dn%i{Qs%'a,vi(f6oxŭ7'޾gOɱ/+81<|;x=Ee1wԙތg翅E_ 'QdLĈ?'q׮@ZUd$q/}i~OR]L|񻆜X $LcVѠM*wMUF850p"4 ]+w$`b5p]<:VA%!9&KHp5\j3fzrpĹ,HX$u2D0HNk.eKjǚlF5Pj[UF[ NQ)q oN+39D6ePbˬYJ SUYNƹC1lu6%A h}`4m:C:*4QtU&Zddm̫r8x)hUSIJ 2@[!/ELbp*& 2~h!vÏ.>AГL j,*F.Q +Q>@,nEзL[P\@٢6vcFoz^\sdQ 7_#|p,Q.>=%R*JHېB81,Â6Q"dPAV[õNGtF6 qlDHj(h`` B UQ83°c"0EáŃ,!@@lAv#@auMX}k \EAT.g,)'C#& +Jphn8»8ptkE(<':@#NF dv X#<C`NE` E0 l!XQC n8 R ^%;F-uKAAŊS!CE0mP9DDðltՅIг4 PT.7Ռ`"Š ( n! zqPB N(BQ}@q0z+,e `R]՝au/@cDcopbEއ !X.P.&;Ye.w`4,MUwMY [D]bմs7q<+R$ Y`:6MH`@tHgib<}Fv|WRT*T*@%JY੓Uc~6 ̀}6O&j̦M4[janBtF +76]}37Ж-?O Z(E>**)GWGh Hd$|_gwɧVNִ'M }5T9]׻[;&N=>:k;{vNxyhn2w]&ؠI$EI-nwMz6MMȖ{nۡmݒI$~}}r}}??6_-@-h}]$%B$RtZtT SFɻ6¬p24J̹/f20d\tt vjdu d-othc'L*ތ5)p.<+*Т!4Єė4.k:򻒾̶ekgwo3w>g3ヨȠ2{nf[h}m%K/χ~"#H"ddh,2EIN~Ӷ6ʗ8#u KT)M$BQsYǛ` +q4tv(`B^l(en V%kXY(^5ä@cYΜM Zw*nb **L@ $mPs ET)usnնw[rL;3rN?~?`-q1y,bF~5r0I0,(@ aCDVj"Uw]@ꪅUw][ws^}} $Iww2";;eNeM@@';3333"7vdUQ@xV1yoj((wǽܒ]ٴQG¡s6Ewo{ܒU&o{EUVDFFo3wwwv{fg%ތ{w'wwvt< GcV&0>okFiꪠ;QU9QQEQE@ٺmUDmmU`mSꪍn!ۀ$L@Us-$щ9+BP4*(QEQEY]{ފ4BPQ32MUTkwu@n֛nD6pErꪍnm(MhfJ;rҍhaL0X:i{r~HHޤfy%ɘyum]nwwwi%~yN&jUѕ1/:.;vui{,mS$g{ߞ{==AU@sVU)$e`$teTI$k(%U7lV51*ڣfuL mU3I&UT")$=9H5WT@I$ *7$qs7g8F$spG\8QݔZP PV)0"y=Hݥ{UocY}sSwIwwvK \ kr*y,ѕ:$#*m݂I ^0^`$;xYB齽N% {F͌/PS6RD1zް́)".vӜmI;m틜2@Ռ""2fbI"g$bnZӛeffgwwwwwvxLDG=wsCzniH1x{{95w]۝oA$W9˼-^D26\fIew;B]fI rF E? m$*fr73|o~}$4A@ y~dfM Hy9w{3&$n^I"'9]:m"s:{3zBHMfv^fP&hv) "=ImTlRMZv6BH`S7ӻ˼ "=v*۽BꪴCY`@yngffgocg1,71rު!I3=۪+vYYkZ-E d.=`=߫BЮCr[a7 B o".T wˠdsww@4/oݻBM-h[Vۜo(&wwsw@4.Lūt/"@2  8gR[/w$l-G5{ _gwwwwwwrI__ d{GɂgzDcZPr2I"՘2#V $+D WUIYUB#+>|Eѹ=g`]mEU*9OE-Ї fEPD1\BfD& 0Dim[9m/dyl imy4N;$߾ff@'y$$s8_w|sܟ:$ٙs3I3S[}hK|!E[UCww^%9A3#]G }I /n<$3WT!Nr{l[mN6[Onfff`n.ff4|y|m@ݟ}"nmdI)?~y=~~~~O{vmؐ'WL;3ݛhHY*(:W~|z9UUUUUUUUtwUTԊEUUU*ҬiVFJPCi77cvM A))KU UBSI $AE!D PJԪZ j*J`%BZJ*T1s8QĢ%J"QA(s'wN'FNd'2q'2s':tt̜9sNRz,I $BB!DHQ.(DJ"FCbmr[fYrg"2b!DI $BB!DJ"Q$(I $(mX EP%UD(D9pQģI $B!DBQiEQ(n(I $B!DHQ% QTKmݻv[$]{٭*$URj UBT@Wmu4ݞ{m"|_]̥2 (cO>y *@L`"mm]ٙxS@M8X9В ׻뾤f|X푲}o΁"@$?#z- ,+ *CFaA-کg 5 |*։? {Xr^ȟkoKw!z@d\NKO^ș߾7332333wYmzfL宪ʫnメH7$9mnff }|{)8 +X:4J/ L%D z@ a$ m-3ffagHDQ$Dp"ϙb|ϕUw˻z7<}UMϪngˑ32+2L2=u֥u,_%l{m4J{ $FyWbb-fzLqek 梔^խwvٻI;eQyMߧ\n8ې;$oX$OɈ"@4aX@ڪj>s?<2IVo޿BI1|;ހ$޽a$Sˇ#əw^ yi$W_0o>/ݖW7{~NgK|ym }ϭ'uZ}iLA<5XRyt^"""""""s.#!c#Nl8,_g'zsV-hwFfxP|Us]&Jk<Z9U&=^Sf^O[:.Uʑ2~^f{t  {wwonf%mn̷6T*[߾nL.OI {:mcu;6sv.M22Sh4&A޽ۙl`q_Ӭ6y{޼]+Zu'Rs<>X*0{́.AWG2|d+(566} UՏzfNI%ۙͳfgc2\<3yZy,pdan:c}Mwŏ(IoĈ&. efa |8DDs+vNs(?>Ù,졊u'ePdR7neC1'7+؅IT45CCT PO% R4 nh)M.Hs Sιzm^z& {< 4 TDŦs1ƛ5"ӓY ֿDtU#cs2mm6,;sBm[yk4o|-o7[@mԙrLI$$@{yR${&cQkwwfff}Fm 90[]ض$H9 IKm{2lDK띏[oo{ n\uT+ѳoz&vsN[tvNmj:GUnJ۸ҙBnܕ[~HKȲsﵜRoq܃hnV̻Ӗ-neW}bmy{p mضhgm$IO} };~YXmK&$ e؟^p߬:>Dž2&zGyzDsfP0PF` dTWhJ<烷Nb ֏Z4luC0b- PdB!@x2'HF c2bL50\MU}*N K}wUwfgnuݥ\UhJZU4F>{UY&@{sf`Olnd F`ZNxYY0%6P$m6ܥjiKN~pWH̦+*5y *) U95?;_W8J sUUUS80蠁URTεFvn֕UUUTֵJuYU6(}yhӰ0iZֹ d. s|q336D\T֭k AT+Z"kZӜ:T)IM׭՞#k')=*h71R(R'9mBwvI$I%kgwRI&wms!ۙ I$I%@/]+$I$̆l6I$I}0?S^NI$K2m$I$I$I/Oy]ܒI$"II$I$I"I$ȋ]T׍I$I%$I$I$$T7,~yU5tI$!$$|I#UUUU17Zٝ;BI(&DA&$ `BQ"dSѻٛm۽yd^uUow2AԆr|;nBh{e/zpLsJ#A.ɎBr}8%Tl_8&j^CWEY$Y{^cI$ )$S[/!$DI $@$Ș USTD߾~TKF ) HIH@IS]/O&(b $HUaLFr$I&nI$߬{܌:˕Zd%ܩ%FgɋI$K'U܌㟅%I"I$Ii%DUY+JHI$`~$$"2*-}fnqs3doX{{g}{m/*h'ww$gϼ|I$wv&nNryϹo=I裸( I$nf[h_Izs|x~Џ?G~}|'&rɔ B|UH$I A$IH$1L̫5UUUUUUUUTɢKÜrI$ѭ%$RDI4jdS׭1.UUUq&wc<3$;"tk:Z{9|=¯J$I$X>gw{⪪i{<7qUXg2$&릚rַ7XkkIs˻GHٜ۠h=ڛy;nBhYJQ)R]ڮSg~RfbX[띯k4fr"G5}fVEY"E1Hs* I(PIs3yH$\cy#UU^H(sݭqy_j~˵UUS'w9qoUUULc<#y"yy9{UUUNkFw9ӥԒUYDHQ$(z?߿wwn~YSs1I$}{FLY"C6wvT}(- a`mنnLmsZf&Gی[wނHQ$(UIHT淯^kO;EQ$($(wT\HHQ>y<[5!D!D]E"Ϛ<=| $B!DE̙!DHQ$(vQrIHכ3c3ߞo{‰!DHQ1vQrH߼άa!DHQ$(vQr"s}( $B!DU3uUdsEvTS^ƋY߯Ys浽{v绹vukGOfc13Vffwwvt|ȑdQXH{w㾶ɔI&<<ۮGIE$I&7裸( I$nf[h_n1ǟ~y6}u ~}n7n1߿uUUUR}"USy͝;EUY"w6wUd1l'ryUY"nכq3;qU[UZ浽z91\U7UUy8y};5Ud3<ߙ~EUY"rg7uUUUUChWߟXwuvg$w}vWzkgRFs{[zBYww{ķ>[^;7v_$ַѻ˵U${5sIVI$U;9$$*9{9HI"wYw39yUY$TLI"'W9kI"${Z;$UdE\csd*|D%>>.{mŻɻM'S;>IW-׽ 7*$2Pbc=NIֺm5^׽ۜ{>vcZ߿zǐZUUU1s\IA$($I$U3yZUUUUTεuUUUU.MOK]s5*7UUUURĘ{߲U")}:($U$$UUS֧yk[UUT}|xA&ߪy %we+mY%\$"/)J%/`,0Ň8%:ap o]vֻ"_o/7EU9xC|UUUUsI$I$I/gsrI$I$I$ncu$,TZ;TUVcY6iگUd2_9ܒI$EI$dԒI$Z>uDPo_XQk]V)i%$p133'nrOHniׯ]Wc9k*3338/330.agvt@㛾Nk[RoyqUV䑑fq} t|&e.ݻ7iU}H&qƨ9m @N+\{]T! R,ε"xnycJԏ/w09yvs7T+Epo 9 YCVg4Mݲn-ƤP330sm#8[mEl@ -I/-T/nfY$EmnϾ!h.fdms03 I.;ÿ}5$y7weI߷s{aJg3ys ?{7&DMSNҼKhzg}rؽŜ\\̶wSrffwsm0mm9m̱wi$| wem 3mdI)Nnd+fǥ!jj\Cw)ft(BRbq]ڒ,q?Hnu)MVpB(  ŀ17gIb=n5\-"zca b Aa` 1V@6rvZl %%7hc G Rv3=UMr]7Y!c˗rlメH&@{sf`gvzy9& nwwwwwvx1 //==%5׬zt:hP5rEUY"9S|dUU*wu3uw$n)CyϞy|wk}RĊYϞuy> 2>s0&cKsUV0M:{o{|ټJG Ldnff7O뽟Oo|}}hh( Vz5np HH2*mlLBwh"]b@̻awnw܍߮aUqsP,^K rð=^_r!`gKWJ6x';-IϜo}gNod;?S$;Ij̩uSg"'o4ft0 ۼ*NC=|LL̏@G ;lw7rěflݖ.<{L """""'9rs[#Qs]N8}R;𪪪&jd/;$UUUTsk\⪪"9k\⪪7R;;UUUU9FUU_r*1s{jgϞg%tN܊:g&zR{{VkSSk1"yUUٳ1ow`D{"""""-9q)y}:Li>w#?  UUqׯ^ymQUUnI_NAj@ d~|3Yݯ._r ׭yMUU*gy1UVH5kf*UÍ{wUY"y⾉UwbU\37yUY"~5sn*U1XƯ_!RBJvi]Zjy~f DEqo =SYb8_ס$6<cc?7-4LXj]QeY[9.̇(zXjK,P$L( ĉטo|mɓռ`mA30Zm [hI'9mƠdH$AlHHDDD)D^c pC.bI3˗.;eY4G>A$BEcIOo{;jOsx@Jn 4*I{6@$3RO>U@J瓽UTI}/5URMTLkɛdmKݶsM-kEkEoowwqY=U3S޻ݣֵ)|}zW@HqI7θfkϠ,caq.f\s30dfffw=U$P }s@[`Wh) DmۥWx5eltA){^>Q{-ԙ-yfsyw{~I"nf[h_wx?/o#o΍ĪJK!C/;Ag35RʼnMyoUff0~r l;Mݭ雪}osɼ*"%*ZJǭkZR:v7jvqweI$nmsfY՘&d]+ݣ{4]H'.wm{{ҔRm4C FKř՗Ġj0H+JUd&m16Rp*y.l&wK n57wm~>K3d+jIɺ'zzsyg܄z`UmNsK>c ḱ җ7<$I$UU{N5qUUW"1sUT*9UUUVxwUUUUu#UUUU\c{sws{jwy$I$33gsشA|:NRs./v/{[Zִnmly{ssݛ$?$G\WvD{[kZ֫5*Qk I'ĒI$̖ﺼcz61.du~UUUS9[UUUUnҪ5{ٝ.EI{ꏯ5]: {m m)Rʮƽ."{$n[m[&k7jֽ{ҔRîR2]j0cmqٕT*&l7*0ۿVf\b?tJU$J*|1N"NlҔӫ玷̳g} 40emMDUSõhݕ_^*sV(*[( ,PE\.8XLe0I&7wQj~@նmmڲIw\'dzmh.f4$-I$z̶[g{vI7sCwwwdOBI'{ܽrضmrܶ̒HfDgg8E&9;o{8HO>W~I﷙o-L{QTQ۝oI5Y/jM Y]ڷN%9dR}ٙnt-mmww}bY̒I$L{$I$I$I$I$I$I$I$I$I-h7gD m[m{{m$I$No22+(>-y5T]QUB-`[a2)ëvw#0 xl D9\GYٍ0:ɸ7jjj`lY%P`uэsVsdHɛ^['K\,3p,XT/gJ^Ah4hp3v~ͳモlf3HMs9swɷs;Z@MҀ7w\3- owvx|Ԝl{@~;sz~mRlʻ<<>:?t@Mfc 0 J톬G@֦̙Ak&6QEQEx;ڊ((YQEQExJR)JR)K[*XEfaf%oJ:NzSm㕶w&a3_wG>Y|f-@ss诀Aǿ|'&>uQEQE;']^K(({w5EQJQn5j(Ac{k8ҔQE\cy\RlQEQE\cs]ts((ws j((({("f31ʀ<䛦tնM־ww̾{;cl󻻻#pmZ,3bFt19w8As^ }wpPy2+|)vb ';u*Zօy痻oqn]ҍՌm'X]dywxc]% ^# .E}>>&>{& KRanLȬhinbFp @C眓*ix,@P ݗaة~=̳?T9oHkZґokZpY>3ܝssݒ"""!R;u:rԑUUTqsɔҭ3Zֲg;/jssiy&Rֵ4Iw9iŭh΢kn~dA?WWUZܺ'3o͚q4E]0XM7Saj2.fϾ'F))IwXq|r}:#$2 xb h.͇R9bjoM XFۿ:廷W2ڶЯE쓓Qmm$$wwwg($8lI $VI$Z [fY3HnO}M˙$[h8`己ѻ0ZY[dmڞw-nx-Yd>{va.I&vO7黷}m$%nUUmښzIRS7ْB5WycZvS%꺩:q;WDCI|mYϲ̚s=߾dnf[h_}睉9/>:Ewp\ffEvM``yw|{8P]޸곜Q$&sd6̙1MΫw#dL۽rg9QEQE9u;&EQEQs\L;ڊ (YkZkZ[[OmEYnϲssmm%)@m֚kZuZ~sǮEQEQqwwzQEZ{=Mk[((lΧwgw;٢:R-xDDDDDCĥ~L PBG*sv9{Zӭ( ԤWmUUUU[sFwj($D]{kZUR[aPSl`Pۙ4.6#Kx[v $b ; ϗ9aNRA9͓s73}_?Y?o lEswowglg69, S$KikZu"o[U&ۼJXm؀N݁mz:Efb.ɻJ^M-?V.Ml6G?LZ}Rl11j]:"o^fhI9 aI-Bmݼ"1]y7w$MݒMvJ0#BQ-~[mwwR[fo{E3-߷}>ﻋT$B7w\3- o\.1ǟ{翯q _{߃Efb? }7vI7ny.Yb@os~ <9cC!6s3B!%lff32$C fdH>(UD{ff}[gI3317 =ɹwy>ɝ|FBh}sxˀ76gc,=yׁkΝ$wx@I.$^MݡcXBu}onބۻmyc #2{w~w]sm6xO}kCe!vr{2fcM"""""%,hA/X X: 2lHtbo760Zt޵kXs333 '3.$$f$ww+|  ̿< p<733xB6336ݻBc1elmrr'?$r˟hK#fuWL:EkZ'H00j=)lc{$,)*R9*{s34ﻏ.ߞVfu2f'QgKZX 9rCln_d@~*K-MQy黹uwk3|ouOD.mɆ/mv6b֩Nd {c2ܧoL G2\ﻎe{vZv3wٻvm;ݫ%jN֭io*a%P\=H5.:VgN*Հ?F.(sg>sG"~͞QFslmIw=iQЖ5Ny,9mmm KԒYAk;{[mnf$Hmy~w8,ulJ_܀_ݩJ!,̚^x|;MSX*Yw/8Y魯=ƳB]jfCL^[X&^Ko&(nRo"A1ƪe 9qREmՒIwr2r;>߾UI$ =ms30[`O yyxHf TݭQE_U[{ި(/jUUnyw=E PR̼1eq(3sy Ti) *]Іp$ A4sKXf(UjYټouQEQxQW;B nM,Ë0Ikm7wvno{qs͹Z.;ϞJV@$rKrg1jh\"r{Rx,@ ȥڻ}$`wT \6::A>nD 1Լ##-&r") |gmڲI;Ī'~{ȡUV$&o[M"o7j[{"R#bInD7=睙C}^ֽ-J1Ppg^v{w{1{~=o_?DYrG%bsVZ0w$=7mX|+!'AՐK{zff@(rKmkm{bD$tHӨI˧L.x=ow &ffdJ72$J/1Yx1k1v5 O1si"kVQx3]}Rh89&1Hyg;ִnW׽{8[wvݧG1,κuV4n9mnff }|7;lG@/R::F/5-gr=g GlhWIBESyЎww#wu"ov0^ywwMSyͪ׷=<2sI$9-K[nIWO|⭶ 8}ͳ?>k+體N~x_O͔ſ#1rlťkQp"NٻoF $!ݔbI]r#1wwz#bJSb*m݈LjoZ}jĥycoܛmˑ@ `ziHҔՋr9$*"DdAVi-pntJ---)g6PWDzwx}wX&zww2.3734o8bٹ_N>Ͱ'$zgwitJV峛}N,ƥf&ڝ{$m mMۖ$[m؏ELE90L- H%A3䄎cCc8I0 JcK% y /d̨JFN-PCЙa^~󂻪˺C/&yUfPvc@ŀp?= ×VKЈlw8UC, T[~]mQ=rosr7Ve9FG]XbI$Ҁ7w\3- ^>{'sz|Qy7\O̍ox嶃y޵QEQELsf"wvGRuYIn Invf̻Wi/g{4|?}ݻqŎ[ZWR6إ=.r3=x;g]193jqv JLw8N;"'2VcP)~5j݈4wUEL՚ VZDNХx;g2L ul}Mqū;ݙ}wIn^fpY+5"v^"""""!K uԖƗf7Vk{"734xy<^go;junB>yy137U1+L|K"l#wAv EѫM1XqkzRqvƺ;fͥk[m"""/{֍q'{yƝvgs_ϲV1vL0C(*>\ӳUݕ;t)Oٝ{gngm7^K:^y[Osl'wwwwCD`bf3,fƝǒ![nwp&Z TJmr6eL^-?0-ٺ@ĵe/$fhi#}С~~f}gngm]Qŝ[{3~{:@\;.kǚ+w}+<.̡hN&ncuy`:n@<@I^eeƷmX8ϟǙo2]mO_ݵy6]tJVrPr6EK*ͥPHnT@pNG7U0oWW dFyiV3~frϙ3ff]s3,O$lsϳ{Pb"nXBdh3UݔJFݾ9w֏! Ea̶[wm7$@7GAdD1dHyٶmMb-\mᶞ5Q3s332kۙv񪈈8X mMŒZI&gljF 6ws33-fnffelvsTDS8;s33;.dH;^MdMnff[mw#ݳHibwb)"@3݉U2"J 0iRP̘i Ad"PQz# PiYݘ5aܮy) Yc dig} (/D'H¡QEBkBZ^} _-?as\/--{UBBEz{E'R6#$!'ƦC'͉P&A*:]K~nva'v/I/}IQrIu|yG=He"Ix!a\ja8Nm-Ksk[\`saI*2QUQiViXdkϗʇ,;k9::7H*65m`/ޣsa1eL!=+ o'̔Z*=̼ms6>vr ]] L216uOyګ_ªzGq+` Mc>mpkciZ[)%|~/ؽ{{?tE߼$>|15,JoY!'Pw響o=\@s}+@A ~<ޚ=>5)!k!nP!E%}m^9Δ/0 ~zI$I$;=rijl3L'?%*A  Q=~.ޞ-RTU^ת+sjAeH>1.NU;F(/뀀>_7m_s5٭O|c%=4>LK4jVu-6ѰB4-َܾG`g xk:Kyu)$!uI.sۇ2I$ݲ\^7f~w߿;6iO+O.@d]G 5K|=<3$ysNsV;š;hzɟYOcyjmGuc3 .!{=P4.{M{|j#d<a`G /Pd0=J|KCaAdw㷫;]{"Ȳ,H8{{tSO{ ڽ|UZVAbaj RlkS ha iO&7nxue>y}Id>xU紌O24O"R[~0a/ExZ$W pDžnmor<ޠK!刯 >1ֵUUW$;)BuENc|k<91cTͿ~'ݸՖ#b00I=zp!vfgqgs_N׶x)~~/*@*BPkZMi U͘I!'Ïagio OE<O7iS9^~E )UUUURP%J-2 ?=Mֶg4Qu:d02_Te ~jI5/^z(ʤwzͦiqe:ٕؽ5,&Rs|>õ8>_6kH}ּ.b 0yEbԙp!Tj~|a׊ ]+NyAzwwygNnVXחlr|YpXd/Aqw1qo*a#C}G'1htxs`J}L&H1'm9{c'u>8Ơ qwwwn]lkZֶ/':?w9 {rݿ_1Т$np+]%|$ __uUUI$I&=Gr3K9:tћ8߆##l_u {+i-X pu3Uw`3H" yLZ~pwޛ?SaR=S/Xziԧ+VY63iemSfUL2\.ed??)oҫ~4"WőV*O~w3R/`0MR36a,ѳq3RTXU{}*v#oPW廗 iZ&nlu0r9nE!GD,aSSpC9LU=5[qЉ^}P#v]w嗦XvbFNVgd$7su'?\|EA?z|0ǻww#H$ Vm-M c͓ 9LjL2dL74c}<0S @j*;NN w|t/ S8b֧ [^*^Pj;iB 6[,9|誯U^ydT#UI[7o{P$|-?!>rnD{Њz١I 5Aڥ1.t4D̑ݦ%v33J4B F0I'8:; G~^]I%%b~ٿNğ݊M?~,P YY$I7th=ew[3?:&s$UI/}r3d)¼]4z,ӓ0?="6@ xE!"~׉$ܥh}jn{"FfmL{:6 ͳ#ݏ ͥyD;>oO'ȧO?|g)$<<4~۠ʦuz!S "= GF>n$TZ·ՙd>}!)Gܯ|_Ytd%}(#~|?H?u#Oۈ'S<*'gYo֫2b-l,ZUIkjCۇ6HLҔ Zh6 Bآ>go(zQOgҋ, %[ b/JL.'"9>=cpBf-W79Jb׷WgJ3-1LakkjL*ʽg]R~=Ư/p]} np􏷒ṾyJ)OC( `@Sܞ@ˁ(n0;yco(6/go)Ψ@9rD;2/dЍuyėHP{'%ʃ +W  _)o2s}_$!2JeÆh%u=CHOr~_m^) S=R]|C\JK@!Jx}Ob48NTt}UPT_6gX|E|^@7)כG@vJ_Quԙշ} C.p= qzc;3AA1*Y޻)@ g`?)z[46D0M}HbKVJR\\sCXlr('З5eBH@YJ@4 $NÁқø^#w}|u;ׂO\J0xNu7:]= /=<||0.tZQߣGcFj&4.].Z\utZgbFv"GkJ=vL haQv$$$$ PUUUUUvSfflضflu햞L9&t:8ꕞB^JnԄRiLXh1k$bҥAa4Rř FkK-6ZZjL&T@4UQ ] ]D3.؁;7Sk(IԸžv;1Fv\n26'AunE-6(Mb) s?qLhY M\E?T>`bɮJc6+U|bn+K}&z:9 DYHL.DR6[0ei-98Ռ"ܛBZV LC܁S8ۄ4AVA@(uQFCfw'q{[ޔyUƢo;.6n z4VISPF1AZ)dă&MEFH2`b%[iZBcVe6Ѱa6mFɘ,fٴeZaZ3F1kK2mfk&1, ɔi,DeŚՖm6[6mћ+h,٬-mZmafl6F64mi4ͪTզX̴əmZ٪ɌZlfVlffealmFS6mZfղfYiiiejjeaM0YRa-fdֳ2fFmUZXc&5bffFM2ɘ֤ڻI;LJjllJ@(U ,jRq\wnu!!=zU 8Sm+utAȃjkjaZzr< к]4wD|FzK4)^1~wHbl#çh}r"U^|*_޳z '\B9r~yWʁ]5|_/ zWy6rQϞ>~R:##=&}8@IS, !˾uyY}z"|^|)b؅<`~O d=O_uyKȷ9r4@$ECT{$8GBJxxϿPhCk/v^I"sCܦ8^;,YP}Oh*ozDI4(,R3]6R̎4XR/zҹ@Ӑă~: 꽕%n=?y*H7$&,/4fh $ /}"Lj=1:Q}E^S#}[H>vPG!=^wg@b+ }yz΀W>gzO=vfa^v}~[UykR:j{;{^t 49*?Ԕ\یriNq=(z6Yi\ IA EZ-`%V _3hXPA( ʩF/ r)PCie1f#7 AicU(XFTDtHX|r__u14 bA T@,;_CZZ9l#$q+(r\%Z@:_dX"^GwG{Q[gXl+.EcM=[ZznQ8Fo)`%uZ#/ANvp!dfJIҤU^tnnm-3;[uj7[i)Vx)qœjD;3RC>@*vu0=5/pwx/j}w_~=of}}z-J1tdcܵs==Xl ׸΋ B([ [xyPJE9C1޼ęqx^,kmtKNGRE#Eife53[3ffڦkk %7R^8G}~ϛv;E#O&e$&H=U7(@L>>t<ϺjL%M鐞`S؊!<Z9ZK L_U]DvwB;ƒGr2EF"&@7 Tˠ PEA3SL%V¢G S-c9Q>Ji uxO Ot/%K~FOsB/3:2|t՞`u鯭뫟3gi,_lq-l!`5'l | 8m@XQ2Bt]K)!,)*NEJN|}zk }U" ;V[Cב4aAr,^:7YzA  E"PM0Tu/ W:BD;H6Wl ㋎K@$ QPZүTUYXus8ۈtft"-y ac,BDn7(l!f\ (U Q ^kqD\T3Tډj܎[, rQ7,[R* ,bk01aAij/QDu>^fU;SUaɦ\$P-K:캕ʎ̓,J"t$ΥC`B# 6Tb;uu.G{&=D 0sxZW +iD#n싍{:ŒS-ldgJf0%H4d$HCmD!Rv3AaUu.j\85t܃=$#b)Xv.Nv,ZT@¢ H\3D EUXRVaP7a0, PPSE>wZe+!o1n~soRnyBWÃƏ%αO9Bz!>^>Wzw-u_8S\Oj:)',|3/1|:y>{%>_`wn 87z ٿwxEwCg N@_-]CjK 'ȅ8tvaں P *_{q~s~s+ϧɜH=@z{*4z{:%UqШ_c\]`a=J^ԬfPS2qSI*S ($״?@ Pm$7vu?Ep| "#t{AC}^Way>">pl [dzIz{q>{oy6!8C־q>i㡼ͣ_tk}IڝzuUyN׫E>:$<ja Y ҍ ;AL=wN IT:;kvNAnGPtȐ  Ĉ{ɐ֭9eQ?%^xhB:jG@jQhe]A"4D.sc]~@Ty^ʻI<y'ٷGπ>nFNR /*dVӲ1`)|`S`n&Q >=pcC4xB"A=5Y6Ds?مۍEhCɵD_`ک̊&[=ߎwN~WUhT߁Sokǐw6uvjZ؊&qt];iLkEک*"@ j"Kۧ+c;ˤ0-'"X2nV40,n"|s[: "e:{gTn. ]`nՕT8!>2$cPIoiY VB4` 2 wO#LtvnȧOE=߾E=z*UԐCΐvG{wԼdhh[4I*Ǻ l0I@ڕi8͍66fCd̛f̳XьXfMma3mxy4" M("`oxe1+1KgoTO$zRgUY"s䫢jֶ3k6f[k3mLc-n6jJ4UbZZֵ͵ɡ-5ٛ[mMajʹ+@BЪCT4f5mf͍LThAUEUAZE+m6 14զXɲeXZacfle3cLͱffhf3μaw>/ 7.BOrG|9srYJ h!h#:'5=57zQNvSClHQ~ !1_e7 [~J"{+ڴ';n /X5@">$CXXi<'pGK)e?_@+ty;Z"WۗE/H"S8'6?'QOGtU,"|t{;"܃ƟSt*]AC~_3WRR;% lәM"3Y;n'Ԛ,t $WVKe\={sٹDmAw(5˱9 q@ߚ=Nq(UhR>u3}Vl 4K sА>VjrB6qc.4TeŕU֨ٻvl΅m%[ QɑFq[tsHdo+!πo^PG*"/yUg7OW#>w} mn/Ժum/>{ooOKo=?w?eWS?ϻ?߃>-DOkߣ _o7~k/?1gc?+?#?[6iz~#Cw} Gӳ_1߯?Sk?_??O=?G~~a˳:|lnF_fL1k,ގ~<]?J߸_}s_o_/Ч?_}W4$A56Gr%W+"I.G QhTN)kUU 484\UNT$IdҸ??dS$Mb@Q暱[?f؜H~ & 'LsT ei/P>9&388A1w1_[I$mTFHQ -I$I ]As4ve]&,z"U5pE-/UުQM@wyv%GXS*CL*4#aj1,b""< g[n.Tכc[ѳOtu9j;}X۞U;CXǶ)fâ,}c}׷ECW_dl_NmrcņOvRk'qJ9"y`u(T;6䕧 ܪ8NVs:^4H۬}}xs,2wUUQ\10hƍ,EUc97*RΨ69Z #!֨ɚUF2I&ȪeVPq**!61Z!YH[H]1 TAmTZQTQE(ƀJ5ʿ8ݑ.8GAs1hhC8(b*tpHd"2 nlې& 7}~=ZNr۳@PJHuBK/hggj: n0ݝRwJNs)#$͝՜x!0@[p0 RB@(AA(J$T,`I!$ *  % KUFRmg`'iZ\:\(!oJo񲭸A(+$k%re[ 3J(@ !P -}ku$W R "km$1po\h' Q%(1U':#]`|zkTcZYYM4@'..K hv.6ܨ5*Ra"YH:BMg藼9Lժp!]*DUEJ"T:jE%L@r$D"RJ.6Tf2 J"BEID)*95XJ(*B,tcwtv)iF!DmFAb4h J ԍ цm4ba0F@h04 `F d0ha"I~i{TH4h2 4hA5bSChdA'Te=CM44=& A hh zz"hd b1h4SODdl :?l $D1 '>1X\(ЏOeA8K.-<8N3IU u΅9E@mUWm galq$"` Jf66-ȕFL$ђCi+[-+WkvܵS_#"&w ~L4tjlOEWR0o^{hpOcη㝷O^Rhwsl t2gd7ec *ۃ:&mt3#vf&ECgR$6Zј4K>0 )%3L6]vSDv9z0_m3<@Mrs,2̷nnWiIE(<,]+5ƺX:#h$[1j K'יw+Cm۪ʟ]Tmu< wP@gCMK6o51?f"ĒKҨv,4g2 Uۭ]w͸K% LE}c,gp1@7|xw`"jem3jI&i-b+Z䔙)4,qs%[)iHvogި9*s}uQ`N;>M6 CGlMHx zxH%=gӺ`vڒ*f%ݚo"efn,bQKZ+ee,UQ aL,D!SAMDL*V%bX}OLiE55ee+[.w=j }=uT+#@ NJc``"YJm0W_wE}0,㽔sxBNu7;mάqӻ8ukϘ%**Q,~wXQYY\=#2,RYȗ'Lv vQ_<($RCƛ^DHI= xYNl+21De49Æbڽ(,mjUUmJF[UfQiFm%Edk,(zN$,?7ꭑ)nr)))))))))))*JJJJJJJJJU*J"gl/ڽYwsR&A$nDQD2yY'@ۻ$`ǬcZִLc'@t&&5ekZs馚xK,QׯszȄ"S.Wgp8p8F۝7GA$s'.gaL ^izM'0d>G3(WP I3Q?$L(Ho'I$ e[^Ins8i&E\iA675n֖!kD;{{?^f/&7߿p>˿{}_Ct~@VcV&eLɐĖ)Ӄ/}WbMcai8(7|kX_[@NU tgH@zSW.W=79?">}FTKVjx_[Iا_$D΅p{*=r8N qr <<> "O<^-cgbc1 THIJl٬Zii֖ׅg $҉ Im6[fSl40BP"U0p*'"=PI!'@A*B V,U625%Im&+lJ5ck{ h֢VAM)#EiL*MQ%R[2ĥ6$MVJ̙9LXkTePS`BUT41PRU!MI)VS'9rvݷ ێrj-&3iM5]ڕܛ"F+OFcB/bE%,HXJH$ȄdHH, 5"BI-Rb)PJXD Ĕ1,1P[$D$HbQQJԒʲH XŒ*RIPa$ DL$@"LIJOL""T"CI"JI!H %!DĔ)(LRIJUA#jDRK,"$ZDQTZcH$P)AW6\5m,\DD-}:B,I$$߹$DD}B$ @ $l *I%ZժkVۚa I,̳5, f5SU5PdMҔ $ҚڪZh!RDT!"YA$jٙXRVUAm,S0R[eVlmJ R@[ #f̓iliS($”ҵU$MmkMZmmʕV"%@$DT(JAP(TV (-ERPFQ$~DVh)jȢ-H}[K !$X$?kqOp/'IۼsdX5I?RjW{~W)Bw~0=iI"GmswO$(|lC߶/ru `R`!e"K%'@ߖIDB>G߻e#&p!DE8wX&FHHHWuCٌifY·y -QLAx^\ME'k"f\"1ARXA^\cձYI2JaH TrB.Pt#`كrPw$w6kj[NF9[ldk0Ж(ڪ4Fk%:/( (V (*sjƭiYkkIjbT(@rH,R#q˕0]Sx]TsMyb0 2G}'ޜ⊧#jڒt[D 3$ѐs>"yLƣRlq8HDЉyx.Xx43PqI#8TZSp&U™W 0V0)IE! hihL2dɫ 2 w4KQًkn5%t  Xg)1ASHHZ8Uu 'V$ND4 XpNP9Ǐ  &Xi$2dɓL˴?(xyIFd<"%4wO^PN 'vr_3&S~?ggL&c[YYUekv鼘4pkm[[:<˥;9U߽~4y^I$HI$I&I$I)3ymm6o*x-қVδsyꌇԼu!V.eWm[ 33=,,",G0•2g0ࢠYL-3,o@}.C!j$#Z@dMW@q*,fNȍ5WQf昣02GL-3l K cma)f/-r.R5ZG,nȋ)<` Ko W** vzrYRU5e90m&'Bf".-%QqO3NJ%r_TD3`,v t@I7C0{k#hIUd& qѱ$v[BodF]YnY4'(h8((!rZ4˝8r͌ A?8Aq "2 ~{T;[m1&DQRm ?G챦 $bA"@ !bi .1WxbYR*%$YQ  e6/#pwӆbĖnF7"d yo֌ Ĵ$87jύe]1̒'ecZ6R13M·:P_Ja*- H^%7F 4dYi\ziF*ʓLfh)HA4!` {]-{~t}VP(_}?lgDrQ9'CfQF{y#Zl-/;D)wɶU-29,1- g"/J".ىf5ZѼA[;Aytk)! sPNhh7 6j)b8EJk#p03 Hc'pD…CrYm,&N"aMLfPiCЇN.94P9r gsE]Vr0ӛb'*۰p@)6hHAs!"!N5falkklhJbSiYiAgqƔq%I48pCB8I8Gn6n^en-1ȼ&Bj3HDɘ^{`F YL^A% 2ސǵN5'9Ns._wfڌd)*"E'k)eȓ9HYc #CGQem9/ *% Z2j0s&`)Y |(Ӳ7#"0+ mMk*U.vFveGӅ@v{,R[ 4kryZ1ݬi|QEѷa8LU:86FF0%o\,]to5EQ5;2rL%խ#*hƺE-KQ  t?tD]DH@LLE~&}6Gbȅ봗8rfzB=&LRM͵8oQDPZL%az:%fS85,ldf"acF"f(k3m5c+-H$@Kn%Dh (vL8 NfwM B8zsFM^$u!;` ꌣOI 3,C<3c *n hkmTbA,Q`m1}xnNń iIMVsV4.q82jنg3ZAasEq)%I9"+%}lDb Ul θ4Q M誴!cj4q%MLxV822;O@db(DC|&pV3D& :N8j9B ܷ0Qmw,3"b"1nΩu<t TMoYnobXV=B=m[i +yaYQpHVL G#NA DYF)Q* [ew"E8},N޽I>#|NhIDsoўaڪĎ$3,H$̻Fki)F'94 2Ӂifʁk7 ^)r]N8s6Zqnv Z  Beo$Y(۝Rֵ@E̖HDE HcQ(/$YB ,#7( QeRۡ>|ΕT$#d 0,ufih BK!SjV ${Ƹ!ˣT'NA6CA!C̴Q;*^֒IBpƄ>9봬X5$M-$a:Me泜7y1ӧN:tӧN:w:x:yN<stӧN:tӧN:tӴkUUmnBHU2!ޛ-o\%*e |SC`c#yj|uffƀt:5f4;%zui&AjԹ65ŹZ)ymXuD'*L jD\. }w?BkߴJp43j N:)D]z_o&/L\L e*KqXP1wD34fB(X$PD cÚZʼZL&j6jC(! X"mrզ6 [)[N8xk,SfcӜ9KGOs$דBmrPCs(}i[Tu-JJfXztiֲh8dՇ@W IB@֦ mJ3Er'rZG.Giq q݉*I%8;ڤ[|9́ꎁ]C1IdJ |[oSIZtl eI&è@c2UWIRNi$M n5CCs;_l@t߇M]0Rd'^fdqDҰcC Uq^4vtu,Lb.pNji}k|Hva@( ׈6%U..-m.Ôoގ8I2aӎ7 ~4n!A:hAHIoSZMs$:-.0*iX,UBvppn0 nq[-dQ(M$1|78|xZI$B`0w;e\$OS>=_zٺXsÑSDmݚE1; ócXh8/ ,vV6Ggn*s֓M"<-X0fm(fT sֵΖ0Z!B mKVrhv4n%Uy4„&Ѷf!^ Q[vS\1_3DIfA&xSC@M:ͣߗ^P;y&u׉$n 3pKMgm,M")đUۥsYz^Z  kwiM,[J-Z%]\l*%cUUBHP,MnsDI\@ !EF kק*b).%h)!# H,%vxpQ[d߶il$Myo61yd@J sWfH.W%#;ܼbN n YT[-Km̂V3ȗ[G-zߙCbi6V5Yc*2eQBHoM2k*`,TF͒s|;&ϊf*=d'w~}6IO^UKoW_Awәv򋈙MLuf(IƑ;"m0%X P[ Wn`c)F" X8Cz2]"<)Æ8A~`= 3"| q=M`q{}S$z49I#&7XN o!K+rPtҍcbyN٘N=Kȋ'H|TBDDCĥs$c8SoxN|)y%ׇFޱE,IO>4P|>Șt!mH"HI$D7̺2r|vj0e'ڞ{HJ9Q51aV*KᥓS&f1:tx<N:tx^3Io7f&n+P]4` *!RiaY78I<xs+[ ƻi)ML8ni`h9s:Y&©bG{,\cmaE5BڷR $I jUBC龡׀p%CdECAQ-@sgSFeȈ!]oSAW!@7^c+'^zѱ9n*t3(ƍ76}Q$@A7pEþe"2"5-#!1M:lM=5"i_2F3pqILTɴctl˖6`еP'w zGqѣN7#!Wp);ןrKxh#A%Cg'=-/6e<lTAק/C ej'vݎw96fݤ-7\.{'+dacRURIe)qA)bv4517 vaFb<xx&+j2n])2!t &N-*,ʹ ͐g%sfw (8!Yp62 o Nq;:9ӷnn]!N QJG"oj}wl&@ڭf_RbFMGxltUY!Ee:@ڹZ oa A0XwlY G):5SeKRMVE5FA Yrj,t~/d4$X8'tRȷoq)~=xW~QD$;Xգ4NAA 3  f"g1x><;gD!K7 11A.T!KIE;i3>Sj"gW6$ pG&Irf=-p3pIFqڋ{rvuf肖&4#3kP>9͞(0 f¬IL[,x-%l!$ڋ]E DkQN&(HT+QX'P3KչX;B$NhPDk} :^okL d9 j$T!]w843KB+)t:Poi1]; BaLa.uCQpd#dڎVo}P ;#p5j ~PW*jnx^:sJs#QZ{$[\45;ӽu{wjilmUUc"#,6IL؈MMf `jgx1chyk D!KVsd[Yh.lh/T򪪊)Ȁ̦b ;D9Gi$;`׃ӈ~jfAr#ƶޥJ8iS+~&ivwؑx K`ZJɭv`(B0HCKCe&@zi`JMlvEQCX{}xclI$\+6I.,X|ck]w9[ ,.1ihgCA3Uc9Oǐc^] Wrj6[hl2!*&ʙ u g\jJϧSu븅nVj7L (-lC@D`B܍94,8w1 `ЮmCD7Uf e PAȈ <v[st[YU0$~@[ T䱂c=}I$BQOKK8<@>t#yQ;a*u Eo^&dQ_k99)^!LDėmR ,7ˣMbʴ§|', +,zejijAO=>#7}(ruly$ȬIۿϿ*+c~.7s0yTSsͬmɹ=Nm΃6KmO!J"TTz_>_[2ů"#Aa-ǿf"fه?:(=KOU|c_FT~5Sy̺,S>7jo3i%u](=+5Jk[ikJ]9Aƥ(H$=}pיU^70Z=ШKvWm/^=|r5?} A$4JRK,#` BQ(70F3M AXH`4TnU,?Q"$DDRUUDPmJB^I%VŶسa`nCyu3yo^)o'.YK)RȖRi))))FI&d,J*Rb)bfֵ,TʨۗI$I$I$>~ 1AuBZ%ZZ!U(EJ*Q @$ Z1其ƒIIdJQ&RIdeLUIJUL'IB)d @P*y;2V*+EUIb| B@~qaz"=ωL'$IQLS)cDc1bL`F* USaHZVjBP/5֍^0\0IE Lqw sa2iyA@fĐD.odiۜIL"Pޥ(,VN@AdT$ 0cTmX▫U-dLЪٴɪd5U-p X*FKZ}+ԓF2 buEU$*$h N==~}ݺ||TIw၁0ꇨTI"{SqEtn[klYk*kh/]rz޷.J-+\mn5k\ml%kWj%IRTK%VR,*XbHFVRdڶTLc)0a D~D0&eJRfffYfiI`I&a!Ŭ)!Yn?]2m%zoO^cRҦ22ʒVdbuz?^wyTWuw&U[)RR.Mb6mH#u̐FIRgD3?x 'F,%=d a!HO>),}ln1w)PṂх_4af99*uaJ?^U^M^uR4&А h?RU Ѯ1'I8l!N1B ]ZMo?WEr:v374G A*ʲrmѤZgM54Ar^` ߺz'Gsǻ@{&;ܶRszvO5yDSasUl;_{~.0 _ߟp}|A_OV<1=Z"$ur ^=4>Ydؓ:ĩ#5=cn$ wD8fm2 4@=]}E mH(NmvDӲ(K>?tug[x~}1(IfVc),!>蹈M'܋ڴ`>.=KDJ*-"JX v>}?{+{9}uC)ˮEzWt{?[bmPQ&tw,A'c c|x—Oꘀ=> }{1{=~>Cϩ+=>Khpgǧ4s Noϐv%t\f*=y, 3~׸)==刌T!@PD<謷A)V*xĀ|y=Iu@񲎝}b t'v@=* A~UP=H8!Xv*f|I84\}n8CEg>~n@4Co6r!~;8[?IW`(RcN]:8FZ""*HPT)Z Yڤ{@vxO!qVRUXp.U/EV H/5.>smE/;%K.\ e" 68ˍB'DZhH$m$Rpf[l l GT L1((ܔ-"f*FQ(TldE$Ԡ bDUX/# 4+C8\n`#S#7Kq˙ӧ(+Ne誼 $6@,LYŒl惷~R ,O@E=⊩Ek9zGSuS @u=FP>qEDX@yhn~u|3mؐߕC^D;Uw`-P>:>D|\?H$} 3 ^Pmbn̔x{<}MoӺysg-Q>Ou>!;heP(N??dP{uZ>x:"w*9NA* թ@Tr;>Gn<ߋA, ]#-a m}S@C|O1 jĀ&$~@ AHI=^=;ڐRN%H< hzVj- 8_A}@P W~Bq @l|!{ZmxX8]Q2oKU?ٝ{r!]Q?Iΐ4jȉ-`-zs.6zJ /NnjHRqViiQ \./sGj9TE(E b$CĈ*3` KV3~;!zqɉ#1a; "D rVkYȀ:a<ܩ@ `g8Q8} 5rt쪫{ܹgM@uK/+'\gfL4qKMLspnw@nXTX )}x"glPɂdZj֑UyjdzTOm~u)#D >|onCN}1N{xB"z!7Kk; OMoE뻌=?9O--ߺOw= + h~yGWOf@E_Π=FA$ s"1#1,DM,jڥF-KVʤ"6жmMj}aƯ=]B8}:~l7|O}O$D(HH&!w jkukH|SӠ/srkv$x]!~Wy ;mhQDC0#:fÐ> Y"EF2JfmVm5FJeMI4eX`Uڤ#ej4XhhE[&lJ6dmM-j6ڑm1h(ȄULMXդS#I3,ԙbIDҖm)4M,)R46ebbmmF&KYeTl%S+ckll+)RYe6J5I)&ke*ʒlIZm m-AwIM ٯdq7<! a$K1=}[wl@k{gFq@ H *9˚w2GU÷OFލuD=&uXe_) DFU*Bꔀ QU.q,RλjoZwɳ~f2$f5 ǧˊ6Fkǃy}KnHyBl~eמ`>Ia<t\jnw9>d:sI]ݥqʷ:NzBNPp8Gpfp@V%1a;X@.S1Q04un:"DTKHDK^ Cz[m, q_O,$>.㱮&lCtKjb$MHDEX8'/ޟfM: Aw<91WFEq3YDш $Xˏ RN>\vh & 4f'@R^nrPBC+DȀ<@Ij IRTfffEQ/tl KEc8)6SlJM V %HڭddA m UTux*)xa NIwO&!iʢbCHPZ(#"BP ZJNQ]0*G!6UÔ*0*"3JD 1DxM񱕖0D=ҧәH (;V.8 )¨DR"@""20Jp=bb lXDfd(p:mvt. dT#aK0:J7ֶ#/(5`zWn s,zgE @2iR/c*1,k)%5$DPJVA(1O /ݢO?_[|F7`B.4Y9W;~58-'^mu?Ow w1ll1Wth" =$s?I#'}RAn 8BJ}SgoITyWmA^W {?7z]Pw`?K'_pq;A$SIϽc0k Bm )F{-Z&( 5.!!BG>}HEED,~[=$| 저w "?/H|PD>pP.jPM}=>:;~Qw@|?!"j@DHs"$ -HcLBHH{} CHDLy!񓇒~PTo  T?/66 >(`* D"IPz6}γf\^g\>d>*~'m˴)T;ph@REP 7|SF[0{r y?Gy:${|a@ 'NVMY*%tP$%$HY" yae&7│$D\E+)" f: <0fƛ4Pl؝!#r ,D؀یI m\`zxxan|;xαqȗz'isKBhW@ǯԈAW (֨z@ v8@M6`DkTm,X-VRKlTI!0-B1& zd1]EX@TǣkaQ]1eu8RDO~̵#}1:w?~SRO`- i `پ @>'b w.>W0yO/rF UHt=>ZzC+Gae_z~="}2 NH}okdR *}ON'A#ZyiӨ^( @OUͭ >&}5ꟁ`?CW^=!ɱm]}:}(U0|uwPEНHk1Ovr&@̒mFpil z>'S# F;#vx}͎8^hn~ᦢEƀNH Lb)*zD\L>!q"R8 ƒDr$Lfm;ALdܢ:.~y zs^h mP-xu?WT>`sS|Vcc{^mp5~?3y(Gw}fE5~Sr9luo>/}f&]jzr;m6`l9{۶5'ڝܤ H$JKw9;њ +"BD;#y$$C,$E *{c#$>ER9:~D0t4%g9+7Sw嬯aa|w=fsퟃ'GY9eiYQfjWeLÇaXpÇmj[m)O?c/Ohhw5[Fo9owzsk'̆.Z3] SisMMn;&[[~/ڹsx>Wg[;IߔI$HHI,"%)D`$H@ @ (10aV !#(1((TYH 0$ X ci/OKuI$I N=꼪ڤ[UUI$b7Un*|Uh$TK%b\aba8ƻ#l;2{s qpde<<qsTAˤag#YWE;:֎;<3Qf؛U{S/>RIJz~4˸UI$vvN,K5OBI*A $ ,"@,,PMhl[m.z&x"=N3~][wYZ-tC]F{gKuP97,}̜|)X7_f&~鹚zflwu3Fn,Q `0kz[Qkcj]DڌYYUj'wzwwQ.ߛ[YԹynj^.O9S39 2c|eU]NX5Γ^_h讧:63@Z\Jww9%UTUUU* ڴ] ""';zngI$BBjsZ6KҦ.-JrJTJ*8m 0msp.dUU<;[Y<""xrNmeݰB$LL6l64c56F BqK%/" l:,XJUWƊ4h BQ !cWwWUlNCsTDN6  uuʈV&Prc$'0'dEfZB'y֩K ,T2#59Zݽ&;z,vRSXB!B!B 0`!PB!\q9qH(4ӇEX(q0N,M8Kq`$ۚ33&c9$UwxHiIADJ$C gqK&M"iʩji)!2L!& S2TTJ" m43KMii04ҫ*ӜBAݘiRi7n$&Ldp[hS;!4 ZY42id;"H)L0/(!ez*#lD_o(+$k:&L'Ӫ?~4 i}2wǹ3@\.f_b P:_{o`R|.fABTZEgmX9ŀwv!% ]^>4CLh`  ݂EZ =ݴQѫJk]_g֫ J 0f޹ѶH8np>W{a"2 { ={}Р{faﻧf@t]yT}St};wps:ٖ^[vSZnlᄋ&v:[ivZs|ܺ>׀+-6NR B[q8AM'f6mmc*F"EUUm!սyۭ.s%8m() 4VdS&JSC2°TazƘعJmLh X+ alc1R3)L+!fe)Z66LpLSFÕ,p0^T`$ 0PVڠ60&@5S&WWEw{ԽR]̒ɒZ#ޒ|Ce1VmU]̪;UqV xôoV˚LVGI5K5b|d6Fu\guޘ-0BX`T%xT0 0Br,8K"3Kn %SrĔc2Ɖ‰T YXd2F)EBc7G m[ˌc4366ц4TR6 kc3VUF͛7"dd- xF̖Ra֋[+&JSC3O#W 3=⦞1B}% Y?LOP zf:˝sIx: nǑכޞה1x^rh,3ك%MP_(Y3x(D H麾d$ӷ:n" *2{2{fSfTZKS̒>ED+; GqJ) NKqPPm MpS؞yt+HwJDykJXQt)e\_ M0!VLQˎm=}TWjŧSv/;McFYwYFzSt|D~} #(C+;5I2Mv{e)o|[O5K35n>>%SXE;bE fz1WoX-FKڟkιT 2}"3~:2Nʧs{޹cqUZ{VTufdX1A2L6SV$ֺI( Lڃۿ ND~9yPBCm(PBP|oI{ҕQۄHd@aĒI.Fi%jt_%'׏Xhl~ISMS5Ll[M\Q4l,9d˛r+eS(gǬR7ֶGy _-.gc~n724P`]D\ϻ8-g9>;8}DQcسщJj5),ԨFMM%ZC2-g9ֱe8uj2hNR@M!Pb6Vg>; *|꾬j7ҹuPOvXc B@906;s~:ԣQў{뫠ZmL]y1+V0 H}s:y't-!E˒ m -O׌t8H &n?|smkBM@/߉I 4Y,ąeH,QąwZjԥJ|.]5kI"S]5YmqhD&z,x{ڙgdbNEuu=Ke5TjBBbQmSj+i*ٚZl%meJbZdZ5!IF2icM+e"ICS>^o ٹӸD !Hf`^fĄ1>xOrxNhTU m1Ȥ^=/R%:V!gX" CɔA)]&ʷ[ 0Cqw`6 IY-2*I*J*Z4*RRRRQJze5M)T)J>*JPIJ4nlХR)dRlo7ǖ+̵?nBJww8l=ޫx>xA #yyLM&v;da/y26Xöa XaI $y$ewe(2W݈']$ ӯnHH"s^l ie6 TjcqP:8% K2Eض*#s\ml*ʲI$2\MW5YMYY2f0dYٴ9U\ B+ʸ)t ɯ?}ELLT`V"m6R,RMS)Je"mn[R5TQmbARi.Kc,Uإ#YF`[&K.TI13"yi˛͹0'3\)rkqM˗ &'&frnss[mI+ʔRe*P)ۧ^tf^/s%TP*X"$DLc2B Rڰe2LRX !, d,2T!*"&Zi jRYd1X,F*32DTHK(1;PD ElIM Ԩ^ʉbʘP+E*eƈd!vAĥfjdJF ap ,,P\XU2-!Z5 `FDfHAaru(R]QU_A+ja$%UXB BDDBbm)a-i,l!dX)KAUT$#UMRKjU5MVԭ Zm ,ʚU6RVH,d`X*X*ı,ʬe i,ʩi&TfH $De"C1J)EB*2&uT~ "6 D2}n N?}IP*CZ";U%xU^t~ODu ϰ >؏̀P2UVG"zsQE =aܟr`5,trwD8`ꭵru鷺#揫 Y|FڤP:edPC9(a5OamWUY*%0! %&QY&^׾t| %|R YҌ|Vb:l5޴Q r0:1|7.ݫb/u}5qh &fdlTU fw }+P]LL`*PuiCs5| ꓂dX7\E>@m%A訌Tr(NmXMUjЦbA05L[Mֹ1]b~Y$"[(I&,TV V2 Ŗ(XabVYS2e+J(0 ya ]䑎I&cS-#sI^^I0XQiDMs*O&wOO C'Ψ;vꭧ<̊y) ԰Aw5ӆ IMP=qANj` LqH$(yw0"p,m%zɼ~]ds$Pxj**TL&c M4M8JWH$$pڭye~EY2aT[U2C5t@$D$&eEYbR̉I =d;{ӜiiTbimZPN[j(lcL\g*W33*a=dҦ 4\{`VVٺGľn{@ԫL[Y8Wgq'qO#QqƜiiDF\ަIdrkm՗ynEVE3A@&­ #qi!4h%X\*8:io+ ؁iRPKuZuo$]M[â*ڱ!i͠pL .}rPv:\7"$1}]UT2"|9WXz{P-D?p9rh!Pi4XLKv1qq=pOHL+I/LIj3qcFD.Is R&F2Ycc/+3Ao^W^ΈKYm]i6Nyɫ)R?? UMg?mU`IIE'@N^~aϹ@t=¤ mJYmhjF1cc(g),H6!EncEl6J{QzK֤[&1R.SMS'CMDn@Hߟ|j0ChkE*74vifsn]nVv+pa[c1@Nq]DsTvDSL8MjQ45ȍ׺24Ll\m8l0# zLJT8ECm0jX-85eS5[Tj}ĢfBĕ`VW6?3 $Dwut92r6.m7'viyDeDKNѧXgM8hPwާ# n r,Iu`fywXO V 3j{uW4AS%5ְ!-  pA!a^  k1w^cHbLDq}}\7/fͼIBű@ò^\4 $uEel |TV Cl<Q ,\rZ* gqYh5PpDls= s,[|'w0:ְʓK۾͖ ~$ U d4cxxw L;C)My&<^6L%qvՊټƺ6D*Xj-25ʂfM(:.8}!!Q0!=*<>A!#ڞ>k}i HP<9nBFHE݂["#|9l530h*ȚYij YW11v%d lIIx:\?cxq[sSdNN D0Zzb(K?MOB6UMlA7o[Rק}fh{6h3[o{h$T 37ָUmw[AwQ@L ګʺwNsnoM݉rR"C”ԙc,+)f2KӞW!ˍX qƻuczu %y $I6KF&Z4ɮr'eq4Ll-7D&/mn,Փ#nhnۼm1G`:] :؜xKe aIg#6X2`?,x y fs$Kn;aTMz7p0:+oJ!\;^]u2SSe^`ѫ'l  COgg4^ 'J҉S^81vIP@@ Ē2f )*w).\Ջs%3| j5b0 w>{ 0QBUQT UM& #0ј6>>oڞ!*RfB5{T6KgUdb\67ESfrڮV[ 0fI Mt\0vvگO :yb~޺:%LN9[ d޽кqGHO֌CIoq۵۷EtZxghj5DVOy7CS*2 U>SJ _Xizq2Agy hN#!Іoa@/l9qi(P&N}IXW\z}NpJSvJLpcV>?Z5g)$PL ]!ܘǾ^@~/g@{RClU8q{BJRUbI$I$I$BI$$Ԏ=˺Cxq)21hi.pUuD%lCC;7);2R0wsD@,| Y,"^'`F&BX5{!3q!AOJmiVBg-&cNIxwֆ088q^z0f433&I '&4r$YȠr1=u$l):E-4dK/2ōaB.$BJx>UJ*\)2:w>K@ɥHcϛ_I(I2I HRs$y~x'R(Ѫdگ9 5mYPzD=y05S]Ϊndb$;:!2^Weۏx&[dZŋv.{ζܷdB hhpH k}o^/f6vfڀCzfaffچ`dizfsf ##d5V'sӉNfZ1#:NI1:gJ(;$b:Lb(Me-SBj+ڃE:۹lo8;zNo@'&N<2BpgNG69ƪ'&z 7XwzzUn2G\yFT[Jgf=eKV5%EVYӷxř[t꛷[0_k_!1|&tǞz;{;ybX%5rJ"" å9fzb۱5Y^SxRɵ #@Q!Gx"LgocYYi?DwAy'EI+wKa̚zQ\]M#x9gG0ǡސ6;0Βt$ w;DŽw;rK\ԐJSyWoBxZ|^lZPx5x/Gg%BmcYbo<.G{&a6Ǖu'<;NaB6,ikr< aFx%pWdzq-7XU C:oƞExdG,S;;8kI0`ae p&N30 eC8eD{$qª r)~8gqcD) IunNw{{EgvtJÜTɋVu,P"s™ju[bc#UV[gʼvkZKS3S&fQ%E-OpQ`Ǹ)_<*uYU|5CL KXG^=x_Cɋwqhê^|Q^㚙*N(iTm(  'hI^^d$`mPL;r]mCRG'|%dO.N%LQn 8eLtTB<TYj"\Agvkz/21v2 HRʼ3L-y;SQ9^?Cmfw`r `fC0f5PpSǵiR%fWJL;;L )P4'vW9us:#5:a f/(5Ɜڙ`v3v\mC00Ey7ퟋ1Ab1G\!z2 )2;9kU 42e'^AnN(_VC/Yb Nq%Gɓ$dpN3s.bB$b1+AjGV7zG6M6ExL@d+bR 𫄨CZq}o!$XkuBˏ{ymɘ2$v1<0 YZD!ѽZ&A"ن042@N!aѲbuƏ1猪ȶ$=:e2JtMۭqfpZoiԜ^q[2Tm۳]ߓȮYf܅tu,im[ȽJu( CHl #VI%*f1d̲j6Y%$aX] SC2kt{ij'$=.*Bmb339څO21Lݎ EGK'笡!݃sZ*Nemq Yn ӊƏ[kʂ ) H'0$3 @@=IW5`TNPD!8L[ ?\RtKׂ1VqF[1tK*P*X$'eSa Gd£"TvĨՑ :ȐLzM&lv<[2%̩cl[˳OvH<#<œ.o7wã1{v)!Vv9˭ly1kfI!X,YLƂX,evOI?`;{ÚD~X}ff~E0&L0pJ{ަj]:̧Fuy63 [{A$b*!Џ~޻ı2 Ы_zj I3'd8M[=37,7uiҬ.P008Y͚5(6䄴}iݯi!$M.1(a nwK1 W4@uƲ>_f_[ʾ{gI9 xؘpp1q^r"$xX!1NS)kD2f (;<\ǣ]tpctQǫɔo\M0l77ǺIJbO*&zy{/ 7_J{"vʂ Pd\DcSjΖNVʄbUL$,i5E[@r4<9 s{H-^i5[hu&gO =~}=eh`:n޹{Wv-,;vǏ%17c33h;YUAɄ Cd6 gAǀyȜ''gS;w|Gn$$IiGTkjN|qSN L;781#METOGi9m7bP~I$|g) pPS4j5塚ߝ婙M+]FB 1#D}R!MP|]?$L&Lo=SHMwaa fc<<"z*v&;h=ilô,jA2 /FoE*)P eB)x?OĮr-1QΓAhhF4Ccni{m}[7ܜgqEV|D6FG}&׭{VGyBxz>yVE|J^}d[_ojk+'{5>ZUj1p+ÝnTCn^/n&{W>׿zoD/ϭ܏y39}q{^γ]'[w=#?a}hNo }#졅.G{Mc[{-:m[uip!TƸ7ߠݡ~ 6j'_?rjY7I"z,EL%+~y׿|ǰ˲m+̇Aj^#.~7׷.~jUcwQqku%9JÆkg\W.LPH#.Q?> &N1:!Cߗgɳ;}^ߋ<ߎ9H4 8jk[+^n}oQᎿ~ZBu'3[T1'`Rj4x fq"r'wjĨ'q=~g{$}%֯{j}Q/jXǿ!6|$>UwQ_5y-^ɵ~gշ' y}W_|Ey>밻bZ#ב?鮼MyDdyol?Կ׮~9%d1gW7֟ߞժeN@WT޾7zx]JT1}wݒn~f&S%<^k;{q(aK_aX>0+r`xXd*( eSV&BIJ(Pek)M[iMLV__V]/(CE}fꮂJT`x$Y&fE6$816M򚡶 qSXf"K~ԇGރ$;AA9&? 6+uh-JT(J)RTSD4P "ҥ*R ;^Gz=GEcmjVm۸gv۷[m7 p(|''ǖw<^N<<>[̡6mau= mlKjQLR2)*+%Ib0G|0IIdX@XODRT,.f@kfճ[M-Uz*tEzW$~GcǛ *JQJ)B)R$)JJCjm4Tn6QQMjl>|WVV++U:jxWYW5uYs#ToIS4\WnYlffIZ͛VڦnMjSj'-BY KCG(Y XJ*F$G1"1g9 &2bjŬ̖Z,D2A[M(U%PUJ$*m᫳3:`6ȡ#iJ+0D B={p$+ӯʵkaUU]ٙl]]URwzꪪꪪwzcCKS[c(bX֎oձ&qwuqq1yf}oBBf k3d%lJҫ$"eBȒYC X#~~ XY:Y*T}I+I' [mlt1+nn`j bUnm^UE< 3*^J!k,i]ԺP뮺u]u[Y 0>fA$>~9ېI=i4W.Ž*6I5VH$Z E%*Ƶko7\zytY,K%ZՒJ%K*SEd'2X&D`}ljvXAB55,,ffRi&IR%($hYdi$I$I!$"~$9W2-PNS[WfMQ,-KD|![$&Xul'\V2;x˨i3"4I`3 5@|}PE"*Q_U|wѓ~frxGS_#^뗄 [$}w.vR#I^ݧ(_M/7㏦~ SƏg)f|ct~ubb't6URထ]dƢ ]z߆= o^cj@N"UU|_'BKZ]mm~0l=]5*(9巟LC,鏳 Q#ȕ;%;BA$}ϧ_wyI*_V\(EL&İ`"unwqu%2Ij"[YY*ڂY ({o [e*Q׳RUۭD;Ent5}r}TCo=E{@:U|x,=7ʼ-jqQQXu**QK篻+=N} ;c^?LBԤty̷}7ioɉνB>**7?vXƧZoY~'F&ŝC…'zw|(1TV:Rzx~+9== E?[1 8qI/Rxv%ןl!غsCo,@?;e+gpT\&#BAS<')D3-]SḄ;]bUF=oyU pZe핟~HI$gag%>-%\ySzҭfBmĕEw?[Pt>^8C+ QASV3P'JZbԢ=ӵ]CƧv`z*DLĹ&UP vZOnOY{[{jT/)  6qlRu~ ](PQC`(=3YcW< */u26J̩fH=&?;ؽRuls (8N~ {wYtĬeWKDVAjoW3Rޥ梸! N5[$IJ* q7h f3TN*J*Ɂ IB%YD2YHXɔXIe*XNRSXbѕ6Mk,̴Lٚ6"B64T)Flcl%J4F6Y E, I Q5"Y hT4Y2%l(ȨMʚm6lQbiYM$jEe*(MXڒKfChhQIMdYT͚&hԴXde"TMdSIJf٢ZVɍbحM6V,ٴ16Y@6ichFdM&ɈT4-3(JDPdF-2э%e4A6YI-,kEhQTHeK`BQTQi#EM%"ddY`dLL5cjIfʔiM52&)JSe5JZB J$!bTmŘ1˙E҈aQBsFj Shq% AVRc n׆[gjmuَSs|3=SH|Q4Kj!}ÚM|u- 񍳂51^˸ٌ u|y(+AFG mS/_IzV'x_[ݵv=1[f@fkEfBp宿=&J;pTlW"Z^ * l j#*CZg +s8P=dN}#.%<XkZy*~Ǡs q ꈁE{q5TWv{|41cSK<'s'qߝfp 5ćg}_O=dP IK|rHX۪ކEvd+x!_]c<-@q:j~xY0aπ|&8FSp-Nǻ.s%E;uBOIyLee =ND3 *JVѯ}>֦s*zxb@ E;3QQ_R]"f>6yMMc)el^hcS_ oSj>YN R55^yrG@(Y 3s!ߎӍpD $Kniź2@a;2 (y8m<6y)2;-͚p9(5VYAK$M,sKy aYqѓˌk;lv"Tu|s_Km1f=M]25Ή}ݢƅP")yYܥ"&4DE(3JIN`H9 ~,wYʵJ2b yeDBKRC"V3\44|H>R:no_C#N_YctI/Lomxf͌ZU}u,lm:'-HCm5E>n YrSEd)gyRilwA]k[lʶFNs`D .mzu\s2E5PuБnWAު+$/1%c`^rjR;clG# Ku ̕dE,ǁӺ8W8\jf_7mLgXDCFa}dvWX -,lʈ>L?4be+A +OK%+Ӂ3'%DBmM!Q/[vs@x^n $A_D-/Hz%QYK*Cմ𨒙ФWÞ)#Cdѳ>M`p[ ~b{s:SYDU-zWAAUHe;.4blGN-6l^yew\ 2.;;#o[?~dt8 >`|*/H 8p%&\R؈+Dj@ "}ץi`M>p=NC"^͖^5 V1}mObxa%hffuR]B6dUHR6&*JkedҚƙL3 eY2^PJNDr5|ڰmZn5 mfzD/Yd5>W_7-!xy_0:@P|q:93^2G.pw#!뺇[CZ]}8q!X %ikD*iԦ,i-)4bf̬5[lklڛm*1 e&I&̵ijͱTZVVJ$kZPԅ2diX)23b2f3)Td3jMbhVTmckIVem[ѨKIlLeDڑ!aSRJI)eM3&iEYJŋ^*8AⓄ"kD!]8+!ByP4> 'FW+Yu.v mBq; AurpZJv,̦X0U4iD%W8{1)?i!6Wf&ؤ"/!8Mt654bJ@ɟ5KX7f8/ %,ۯK5Yj ̬s*!FhYe50ˌʹJhR gb̴B+}Nkka w)_pe0lȜ#MJ* %Azw:>a>BWgYH*RXgekG^b-(aF^rVF缝6+.\we*V鎄kD.j!7.V%;j8A-dp4W@,$R`8K'[W:M\s8&㋜-0.k [v$$Qۊ)` Q^TV}Ǯ>77x]w,߻'iP'Dp0zifY\ok/ cwAu e, vjS"U!%;I5$8I1(5D;mW.1479mseCvY/_ʽK@R3ߏ"}1z5QQQ5 hCaTV~!@ ʹD 2KG6RJ[w:vG^3x{ԓUP HvrNοa׭k'{lcKkp;*o8Vc9hR~B|4o_=3\cU#*U5UAPHX"T*I0fH޲ 6̑rsC֫#c 0m-[I:p] l$TC,pH, sjŭvCY I (Dղ,1$q[ $웵cyNg)i # $Z QIA% c nB@ xLmN7+q%Ί7\Q[lLe$5[U kX.f66X8 ykbVy ImAY7#UmRD}|#IIkIn,j`+b-b[ddcL1nاBs#\Qq4Y$I=Pcb1# i(B5XpBsQGnΦiϜ\1w[$lfA)VJ!$YI+ufhhW.Q쪱%V$Yt 8-+ͳ+,)lEܛx &ٴURTs⳵3")*K^th(*暩Іwad`5x7g:כ\Ǡ=z(Y :~?ϸ5Ho!floiA;e2PjC1Hڭh-]9N9ګNfE-'4:lIQb")3 JPkX2~Y`;l.I+'$4~f'օ?=v5?/)5- .O܌)sXoa,zG6')wk/QXl C D-EM|;{P̷v PNL`"*v;s fS2qߏ#]Mg=%(7o8Qot$<-,`˝*;%:ӞRy\EIKQ'omP~CAضfkZמ %zڭCU3n̘5Oo51؀D-@Q~IࠔjkRɚa8J @{A̝Z%ZR*WJ T'Oc"op7$CăjW5v>}x<T'UX+&Eu^Hȋ1qI>`ύ˪bf-[d p! #2C[.DZʒkZXZIDkFZ˙d]bpsRd xֵ]KZĜUNs>!I[ Zql;?9E'7OӦ!yDZ!ň$B&KZ ڭfv{!7 HC U1ӡXa0?(3gEm5Qv[;R5R/Hs&l0Hv8R;a絶ֿ+\)N:L ,l(`eXZ0gN @rCH;oCusRS|\T$Ļ:wvUV xB>mT͵78Cd!+mZ]mmvӴ5f۳ -5 HI mlAνW;4CY4RdT&!)M*,ce4L %%AJ22D d"Bf3YhM !#4XY06FZM iMdTRh)RY)"l&mQa2ɲZM,V~-6k+MA nխolN2k{ܒ"``]auFvH 7Wv7x/KsD F"g?2(_oqJ:?{mZxn=Wyzu<i~W.㤗])>ϋ?vw{oG?e=w{7|}_g_}Wu?<#|q~G~׋O>W{=7y>cy=׍|?|yx7m+~_O?gpsf͛6 ͛6lٰ?3]?O~~<=oa??>~D'/7MUUY!,bX*` 4Ѓ(5h!,*1Qf+"d4U*J!!` @y|B/| c6cn\kYθ1m[k. `jZuUS33uWIc㴻ĹVm CN-ZՒ&!ms+R]eȪjjZTw!$ \$QQzv7*ckl{꜅/uWUW\1ݡCftIdD͎iުaM*뗧NMOgQU3337wsp/Urq33 va !+&eS)ʳ XFQ J++" dUV)klZmF֩mbUlKPHZ!ϵ|Zj.gΣ&/Jbogg0;sV<ܼ$FnKZź7TvNmlW^JӤlC,Q\G\-|w]w3ם69=\W4k{䡡_u-&fwJhTCྡྷzUIi("*b+rn\\s\]Uoɢ$I Isk ׃1cDF61bZ^UÜg8=x h eKS304uۻmmoQ$+lLc sv4c)%RO$*[yffvË6 3X501dS**2 {V%e,j4\l&kl7ӳؐ0,jDG +Ca\sm4U֜F,tttqmZ*K,/)}km[_1Tj4<{jM֭CY6+A$X슟|j^pApppݻnhhhhhhjx;CdpPX`8(880:$ʻk#͎ ffR؂P`j Aɍkrܺ2J(I$$$&U3TYtiiD!3<"2 &ڵ;P *RlY7v:0T*%Db̺,IgihiibFf5dt:`6FKakCKa(YW+6ߞf=PW,yKt/1AY&SYn^4O:í>>jOG8 m"lP:jm8&ajO .2'}2]mkmC`CѺ v:Ķ  3B2*BHgvr& 6ceEA(((QT+|" \mP:BРvuu"%@ j3TR(J c P'c2`dF R {/:Hz6 2d@=C#@ 44j  mC@ &BJz~Jh=F@P@ 2"yP52zC Ѡhh4CLш &2hij =TF4 $@@Fb5 FɦP zh5QDAi'ҞM64P@@JЅƪ2#K>)?:2SHD+Uo˕Tl% Je\&FCo>jAQmKc(,(ъ mUIR7-鋣8YwY+Qtyچ⿍r$:kpaFdT2c߅ѳk=6iߗ[.TL^K xoa[bN96$UU*k&ʕXaNp *3#*U֬b\&?3RrD(*i+bn_:5e$VY\}XK"'L`jP\sx=.:g4ȓ@y>lf5np5ܺWN\rrJsҼp0>^\]JVW{2Hv#>* 20-jT׾{}b DS畫YLOV55k.(EYSyxO1Y㥂xYSfL"S M SY3^$VMS\$jer3nL+!eVV*Uaɜ4wd4xa|DDo[PoX2ߣc͕.6h)YW:2/t}UGi1OšEK'%䲱eb͆5ZiOAlO8bıbwMuh{&Ųbk֧uՏ 勮նOd'c Pκ}8V­6v:>53O~͖Wb680 a#X:zC$j7ŒzYGz,+a`1^8뇥}:XV TR:?IT%dJY%)dLʴeFK&ff31I*^].wN0X~c_>wY3dGN^)Šc+XfdK"d,s[Y5rg.tqQ.8Mlz/[\wy=rmpc2i*|U'^nSTq,b2L2&2I+ivg{[.x}̳tLb*$bewSO;<4hʹk~ VuW_ǫz+W+\zcʼ.{Wkڻ#Hlk \*}'(t]j{Wkڻ^s^ o H $ޏubODD.KPTHe*U'XT,dlZ2+XbeBYlLܳJղPjRW \$+*%W*ʉUiU X)+")#E*fYY3Mm`iic*bŒLʩa)C% 2bV%e%EL"Y PJIV*T$jJ%Te"Ov䒧R\af#%VeO\Ⱦ`2>T+Q* ٬]~OC|eYb,HU(< F2m+wyj}%adUb U$OɳC풖ύH!d%`RD%KlLt;  #HH%Q.9 8AU͢H(9HR8Prn!`w2j$S9ÅfiD`%2w2IPiĞ*"] _qdGxuS qja J9;]ЕjGUS\NW'9WE'XW+ YU2aP!2*2fYbLFRdc+ Q`Jp8H%z6_=o `pOCPXI \B $M8`(&J^pғ%ʓh6* (ޟ@f&TB!()L /,jYPeD0+pBp2QQ"Q@;SR)ysRm[o%vBWSe%uUSԲ/Mc,ʰL0+xŕZpj$ĉjHzՌ7cM4[jc(5 ccVe|[s2p9s^[kr_W|hçݼ9rː!B!B!BUUSPU8a0a {d:Tm 0gN8˕a)yL|VAEH/Ϲ6]p]1N@}¨ /2#( 8)9K @ UݒnH(Xtڣ-`j_`]9_H:aI.宦.4atb$ewS]6Æg ZP[.%jIБc Y'a5Ri02I#d֍Ԧ38Z!rdsc1fItI$ [eZY ge\:p!"aH&ɪÉ;nL5E.cg8-@984L:mlI^ibŋWV *V.]]ZuRZbBbꕪSS:I3ydW.^sMiQFtE:0cE3FOl$h|yN9ªTdDNdD@ @DD)QT"qDDDYeTR%J9[YdAi7ԙb]($&LvIщBRr %ì#`,T) (i= )2{JVgj i!gyB…Ј-Fz;;H"Db}XtHpɶHqw8Q\4aŒ 1.Xtu]Nb08ʮو( bw4܂'WW+ \5S8*6a ǽEO"*KK\%zW _τ(JUwx~8p\YG Đ DwSX%B$-pM::nKI61/(swkVgz,R!zq4d3eZx\t|>Vm †H_h%yd9TQRW`ίPu).9-YpH]MwȤ jK"Ft5Eh^7\L2ijBXN\xn1GKĉp-ɩ)7덺Q$P환@  b6$]P2M~o#dNYWM+z9WR%9FhԗoW{b$j.o$?1]҈|_]dD&uYN \~S/w_'k-k*8!()Xjӽ;jn)̪$TPHy]؎IקAëaآO 1bvS,xCGiWaMجC>#0W:gFVEY)YSAԲVe8 !c'!J9MRnMB1m7"GtPa5GtU_WpsYVˬbv٥ `TY F~zѹĨR"TfUYF XJV *KآdK%X-52sSlK-Jf +l[hSMUL"T07d}Gް%@!IH020TD2ͳ+&S뉴K7;EsKW & eS# `DId#9j^M %1TǏ/ 4ljTᄬs9 a!$$MH 0"dQFS A^St8˴&pYS&LsY+) &_v򜱌&-Ԏa˚01,|Dv9\ӕZs(WLkmV#%ܓ$NyCC,Ilʼ5\JLJL0R 7W- PjT!1R:ݘ[*'6$]ʐi饖YvY\$G$~ޅG$y. [VIZTw32wg:tˣnX4HEbŪ )RiGMBǻwww"AI2lfl쇸UZFO%nI{TU Ȏa ^CO 8* p鋋4J<5I' uN>qp.J9q2`b]S1)ܫ\hwc6.{xAu!"KWS\XӛUu#-Q%<.(redX#@T$/$]HRr8n2}[T >xL<,eRUT% =2"6 tڞײ>J""""" I$I$I$I$I$I$I$I$I$I$I$I$J)h*_+PA$\^V־HGsUU텹Rq$iwɸ A'D$s {ȏ TvL;|6,to<.\J "}OǙ}~2,x#T9hԕmNpNcXH h˯zOlKe4/N>wG@ 7KÄ E>xiѿ  ={XY'(Ό7\ڬ囍{yJ $]BDxb SUʈ.,ه ya2FJO̤Ӷ@pu脆tY`Yy@Yx5Vz!/-ɥTqt"<<(Týag a -M|F1cmsN؎OT鐠iE,[I@Px /[q:gC"{!薲bhc㭌ăD)tY 1qYINνzutNGIB@QzÅW/ :(JB{Io;#߉ ɁQ4K(]=^^Z뻷nSh09yR} Fy8NNgVhR"FgSVU7BvwwwPNknoYɮUc|'ry[Cj#yݗQАBE])jJT`f$wEnYdE g tӵmYeqѧ'CA酘8=ZxFmhґ!#tဥD6aUV6V0 I$@N ̅"dGsOi6;{kd*2͇lh^TݦSmdI5DV<4ɅV2TP} .Ppdv2HqDR})ú`G;e/k4Tjw}v{eH)mcf4o}}'(Y̖Ni[/If׾qlu-uK PZh-LDģL\N^zNE ]'Df2yvo)g&Hw|>Ku #IJkWs€aBG]jUl6,EU0 EKpXTׂ(ϓᶼ^zϝSw˯20 hFDPd4OLJMyK.+Y+RISs-9۝ 8BDFhF/ӣd TH i[ kn\IlTeA.:$xo'=~woF&o$!$u!"_=VV.7gc&/.r d1$ <'wn,B NpÃ-M zVyAAyp_!E+?CvJ hmXAȻ)>sS `m̶ P*G_߃:| kU,fg0J:2}P%%R) իBnUVDaC4wKW@ƚ5pD5,Qal[QNqQc-g)BȰ6uٜ!J ÈB4q U"$d/zgu%9El2Ngbuk= 1}]״hvӯXU5ͮ'+8+A%BϘ>%ay]:2EyHFVV6[*YlS~y$Wm7z5jծ^[ol6w͛qY]Y{'ӷ=[NghVJ-J+-Ҥa̖LjtLooo㻻(ɓ ,ӆxR1^άY$E&PUUXx霻yy> -XR:3BPhÉ  \_=E!J79;ѶrJϛpp\VJR]r8T%Eb'>fgub_\!ujJ QJӫ!y~vt!at;.,,Ex$(7.Lc,jj J+B?yxۿQ)F0fUN:$ūx%3Ȋ T(f R(T vCZ0#V)ZVZ8%q>Nm[mۗepڕL,Oq\NYdvY՝MiʲYZ)G.Yd㢪7M x1]{;x OdWO ;dA¹ cJ)*"PAc %xhoZM&l'cg;3xjuxeUW&2|ɪy2eg[6yٖpen>іAI$*[D 3BWLQ4ٳOv7cD;>$ $D}7l :)0aDH$x`Ed.p]H3O |&w(Gϑ Izi) zYvw|twW VMfn\g/mSyS\e (ӅDTTQc7a4lmGV論Ya5VU욿VTU *kqϋMrѢxp\2i*tݮoeh̖Uv&¹m,V0]mvw(4H4.YGC 0hGal;TIz8WϬ̘tdr5hvdk&>nxhzII% P0+X|MS4M2z0j6a'VUڛab>`XTbgâbbqK>UѦV[:=6gn —Ѷ}^0L&lMɻ Ilxn~mkӇ'4(l% fOVI̞4S-\1V*d@I'" g@T;2r0´ن5/xd=a/a[x̚7{jS pn%mvջtI0>YxsV7dˡZfM[J53)]+S$} T|Tʫff& !Kcf`ϓ^}B᝜xGYDů&8c3n{/$ ʒ/bb00,TfX^mbVY@&KlQ;a+Sj LZ49L`V1adO;(ҥVY)Y_s!~7$1*K]|>ɦ,lY1XbŋMI,X"Bmq m~#~`1c1cV´Vځ @i @\rNJJRRRZ,ѶŢF#231XʲhW5CbʦT ̬2e(}$ WB*9IXF0IIIIIIIIId&1RRRRRRRRRRZ͵mibaKH0Jz4ImvZ֭0g6\-#id*UU7aY"UʍqEvVUn0#,eBbj\{pSeLbb&5YSY)K+3QJi+(AdQ?~YZnVK*bDfS*2ʙVkLd͵KIɴ<ȵylS-ز`颈A &Қ+Uj5 Z!ҩTMTD]cewzĚiƶUk-֩m_LL/JRvkִkfkSWI}=Е."dheeV e񽬷3pd1Dȿ /*TU$~`v)ffQId2gG3U*JPMe,d2U3, "~"&YUUUUUU$I$@$333$$I$IZYdRaK*a_~f1O_fK&FҒDkywN]2R,YjZZ"Twd ַp|LEI犇脯/Z [X%I9Go&J7E˲_Vܛ4j̕IBuY;'HeF\E`8`ksf8VUEM4s)R0UTTe1™"*qvx~Q 8"*ʲQ]G.#:$'V Wˬ1nixo.\pnݼtxr;sn\͞1&0$Wgbh.d>p_I0~^zeɢz9Lsf^pNF0D̐8|3F`WL>Ė~XQ|xx>JUNѕg_/5.92xmaNN^?x&O2i1;dv?H vL粻Zt|hܭul&=8=NV?'Vswryӑʊ'v]~}eG}j!6F?t8Je﮹/U{>^ {M>&=zh) 넬O& .n_}Zs;}PNkn.i*q)N u*]|ԭ=/ꛝ훂n7DuTm6>֪JľPWY9ϣ0x4ݭZecf2ʖfEWn5e}/qU2R޷rr:>n}|WË?h!ıZ2-1;ԦͿ{ݵ><_/{[? (GoN}J_/>^r׏H :󹭼k/ rw3pv='9w}m7"\1sڥ>[sͳ|Sn959Wj=XN>byKwn9rT7_ d{»9yI-d;UhZhl\\EpTo8Rӓs蕳9Uk=9S9166 nghh5IS]kzGujRxJŅbʎ&ؽ.wNئn]//r,b4Sf1 *l*N3a+yqr+x}PzS%^#"%w`PN4TWyRtu ȔWApgx^89/P*U~MsOJwXa^֍zZ\9sbĤ<;|FYv--{#lw{.oNk|( +v jӍrl%oN/ζaִ [J[A|KwWq!*w8]adN歘k&̴ad{6p00Z޺G'; sle}=~֔F V &fY0>6V ~*pŏk̿Ky!P28Xݴz[<-Qe@UؾD& \HI7ʵZ5-5p6i{ճFflgmYM, ,4h\kcM1jؙ?7UeXda?Q*8V95QWWT"6TGc]ߞp[eBWEĮخfY2jeGK4],Y%"%#+(R*iqMmU)*+sseфYa4lcS`V8LIda &BD )$fLIIicÃ-,ckf \n-hi/*e05:^GptR<.6HL $`EY2I0bYBYUXS,XYTdXayN-+[3NmMԔ=UNMJYI\̮TKwV#f4S-كbScLHJIc$b¬ bb%)RIl$RKIR)R2Rb6J%)JSQE03Mjjj)*R5K6m6Q5+6ZS"l46e6k͓f5FY6֓ZJFlZY6IedSJʔf6e6f,YK#6SfllJjf6eQ5l#,ԓe$œZ+RmVZRmRJJjYLek$mTM-,M&ȩԓffYjJi1JRTIYZTȬԨdY&SJJIf+lU2 56j6l&Tm&6Y5lk5)i6IS[%YRM-3MYI5Iť*-Vқ%Ve[,S+,ֶ֦YeeIXKE%bI+$mj}y]?i5c.$]Ҫ0=5=[I&20=XѳV_&rSu7VWXe'jhv*h톒t%Y0;e:6K5rZ|55n$щ- V'&\<+Bݻڶz™\+Xlᖭ9jɾ]>~هḬfcg4aO١s&w 0$gRHJJI>C訄:J^]IץM!MVh&!Ͻ!]WӰC<z? D;iwV9&2$tW/h{t{~?_^tq7'⟭ އKGɸ|ݱ\guqַ>`SY}S;/}GxaG8W$h<VT;R UG\޷ON>>[>N+w_75)w~+W2S_[~ Զu}M~<Ō29z/ _7myE_OϵqD = +iz|ڊ}wG5G/zC[<_7{ٳopY \y9q8UC?yu}u{_QIضlW:|YշDl%kk ed;Wmq2p}ʷ]̸ia+bG =^_&'2>ZI,NbnEЋ쯨*zFݟUGW=ǡr5z$]9{{;c{G%ڽuR1GA-=llʎxLOhUi6v9>:Ƒڲq'Lٻv5k=Y}i_c:W_:+dS+yuAP^}5}o'@lJTRu"Jy:r{Y=|7O_py-ǵwv;\֜i2Wqy9/C$Ǫ9=kvz'W4xnݲ̌<[x/:}^'OI<eTV,,2TB͖)Ѧ4}87䮍m1n D;ۮؔ~ڔ/SO=\'0]WF]+ƨX* XT/ww4~، 񻖩"5+%J3o֜l8l4مԅNJ[QvhUŝ-cuF,ÇLx\BWQx_1f(LQ+# J jՕe? nn݈JK԰wl?mFa!~x?_ ND= ߚݭO.X4P׋{M.+m->=i͌V$uG5}2q̟&Ƭr4񦢡>.qlֶكf%`J)Y20V\6e Ѫ-k 0tpvl%ei21A+m_g?WϹY"W@i ħ柡9ՋlEslY1?q~wjdvWoGzw>T턭Tڄ |Cޗﯓwp_s̳u=cOvJ2_.sw.k]Mꊚ~nx=*iy}Un^widl\[ty=ߺ󾷦~/mR>V {+Oõf428hV? \1cD+jO Us>ul68+yrv..)pqW`WEOW32aL4QFEL"!b0YaA=Q)Ơ`bV99{ hGP˽lG''bgrK$==cvX%-籵8Zx8SN{ < =Jn=1X–×nWR}y4 cн25_'MTʬ|<v}mzS1yݟ'XSVfcV;/{eJ#|WU|Ԗ෸_^3pp/85RJS)L*22EfdȦeL1iJb VQ+ UjmJVZRՊJ) YJRBA1K*F֔hS&UELjjf&k3fjYkbK6iԳi)fԲ1S-if4lU,b:S*UYvxourO\I+$2^W \yƗ#.,uc{q2-y86Zdlf:;yw&KcuKdEa&d²+ ڥn5,'RsƾnzlG:juq6l5j'QtȊ')DNH\ܜ窎96:T,/KcQ'=j W)YKl]1ha,i& 2ؚ&YZ5VV͟F6~/e~;\ֽGwq>scqac5xh}{뇅fyo6e',0i"bx}l<:Ss _ [WO.];5 E}e5},4Vdt&yŨkǃ.\5fM6paM7ra|}۵b0v^zy06Q4|վw 9=O;NZw4C}OQrz*&NhFn'JzJJ"$^XݕGά5Z# RTĘKlݼpjp[R6m1n6jӓ%iʶL&l&2533"p h֖^(c)u^qSާ;zi[8v!7pn5.]*G{ 6+%9۞%Ba0l[hd%ig{<+w`-{g¾6o;Wjurx&v홵n[ZMkcv 1U`-.oDt+sA+K*ѽ2*cTxL1NOF'/zp=M~,kh(Vﳹp̳)lmiґ[*UV̫)MbEh)"*,KraU`kV`JŘTX)1FZ3LS$d%qo8[iiƩl4i2-oYfɆ&bi~aW CSaTXelD Jc,aͥ{KLN%FYV%PL*6hfM#V3,,T*ʸV6ljln4ins3eUJ-adaYbV뀲,C 5aNZMRYYƮl\\tt`sLlVյiTTcMhTp1ŤD=1HiN 6eDV4hݲk,`ҘZq 2Ҕ݌͗\aƸMEUvwuQXմګR[`ٲd!*Pjl2nM pi#UkeS-)Lv&ͤh6.YXĪSؐH-"S,(+$.-Dhq$iXh4eYeUN0UlD-%KAJKp[6MgZx497lhi85Xz1hբS((:IH%ek ? \9nB^T"*~N vWƘtsS Lr#єK iYHiQVBMV9ՉŋcO98&.PI[ Edn٬! k,G&roS@+QF91^Ŵ/gD}R>Vmhm8,iv>wהL.UNoe#vGo&U:hlՅW|.7SCrݿ Im#<5~z.xi5|vhp}ݔJWqW'TE'zyO/N.mz ]E%nX=h_eq>~oջɮ ?ToQw'|m?Ŋ|ިij?crv8;^OnuevnOeHc:mW88x [) {FlV&$ʹYerʰ;T=XQ_&UPcfeZXZe}.XVUFXY0*ȪjVTe5bJRMYSz5gMnQՎ x>.;UCe{e=lv:&u+WJlW7 n491.J#LjʯjZ}6ٕ?M}/2\]ܱ^/Tw+v7{k~Kjd%p^l;e@ˬ0>%B:+ R+-#Ň&ߠ/1^g^ҥOvd1:=͛:%oGޫLvf|*tJ'1+Vʞg6}i2™UcP$1J\acc}#v5_v/S>c> vKgё,Xc Yޏ{c~-ޣ;ا/S'gػcˆ^-#cwriPQsx^$yW=/:; b5'}=Bd%{vɳя}O>O|\?{OR`Jʁ)fBVRJQöѷpyq$ެʚBYZI p᪏+%eF+L,%aјwL:y:MŻɉ;^1mZ[M͖dJ[jӶ.[,fVVeWK$6d4JF `7 [C[pllj_79Ot4t~?_kv!wjScDGcA+wG)Mp|BWy u ¢yzjZwsNCEѭگImm*RI6h**XaR* $cy5Rm))㼴0L MMCM]Xӕ~<(n9S]MSK$rlPwч"wKY [2*Lx1-}\)cAe2#Q'n=^D~zjBͻWln.ْI;!Ra4b4TMbԳQ̓E8X&'1Ҧ֓mlmvKP Y$#xGm9v6cųTq%Ea08bS T*URمU6%c(l-,1ذѹXFVT1-U-LZwJ;ĝ +CT>q+peգWDL=U+$q2Dx+i*PF[Zn Eв%dEjÌf}U焯Z}m;魜hԘj 4EExx2Z+Fī QV^:4H"KJ Ԯ2d:p ;> KK% K 9 ueD dZ %~գc˖ ݫ |^ Ih# YgF눹GAs _J:͸6C<)$9nݪ%5Z]X24.i&+dsSep MH[hU<+3!Y#5xve Z?u|?`rTUe󲼌R1鯋l »Wox_%Bүvn#C/A{^zx>KNHrM+K$+0:Civ ]d_E5{m85#SHc6..ܷlH}O{ X[;%[NJ>\/\R\ס;~S'P.)ҟkue,094i;7}P_W`i־U#|Lqm[4w8L26MlHWJڳ\c²I޶lk 1Xa1hMQsO`ԘW(cU 9Lp}Ƌzag򄫫ٴ?d>r}{>Ӊr%E{É=-IOv#ޤ [K,C Spm49B|}an"J4m%NVRM1>kYXJѪVXeRL#fXje1',̬ұ$ItYVdÖW輡(+ڌҚo[T?-=bvP'Ԥ?#y+g. ԏm4ygvދ:[Aؾ~ jm ?TJl,'!h=}^SR`d?.-U!bW?q>|KƯrv8,==<-WK{hty䴒iwK>_WoǡηO?ӛwSirZsNUyi,c%砥jx*Zm)@O{֯r6mAYRJxMzN?|%$*XvA@)\XhTHk ig|#-]:˹]fdLV)(=Kb(4B@BlV(m4%BHc8^fܹJ@;%P{*?cJa`E*b%jbJՠ&,%L$+!+!+)z?mmn?N9:邈HemX3 0+ap"RdDDE m\,㛎SqQ#Eu5DNGʌy|ȧlW6U۫uvM]]waP.\O*..y8s(l.D1^B\c_R,BB6ͭ[@,f-2YeYeDo^}<}s0783VMUJ.]غw:[˾9cs#:cʬyrr3ygx'̺9]:vs/PwaFp̼}aty9}%fmdZ3y8ΧM,MdV佞!מ6 f1CX̸ }/hچ}4U$R"ՁAhـ5n6  ]N]e2j PJ . }-*w/AFfиiNjU@ J TM c30 0ڀ8M‰+ $I5X,YeT(& +vкk]P^҅@[miVAf`k Pm)u;];U&,P*GZWU}`D.|{2zb2z#LOHlOIM# Cja<4j0zɔy4)m0M#'  )L OIM0ѦF&L &$OU$46S4d4&F=&&d`hi)IM@@M6CM24  4h4QB M)?ELzRzySzOPz2@ 4I+y%hq/ikc1G23?lI\4p(\1CG^_VzPtAl~;(o' iV*m+MvھJIEbb#cm3QV)1hьl!Mc(5 )AL%hZmkNrw鋆qg*'bbw&[Za?rg/v;ߔOhC5wZN|%ÒűkF9:v #?4ȲђRتo7_0b66& l[Mc5gVw\W: NaU_Gvbef[e|6\_t8: &Rablac8@/G`a8_g =I[BlKc$ҘT[8ۃoϟw#{:?J0¶M*2c֙m\M@`Rʴaq\k&׌Vg*>?,޾L~N'2(@$bϷ)2 q08XgJ ee&eVS, -)Le&[D0l2L 讆0[ vbՊe*S Cum*$k#''d2Vh0/UeQl І`ʶUw[Tڜ ZĮ28Ewhr%L/iBl RD@S!K &`0UP%:x +s$*YeDr .ȒXŭ) ă,HU"bFMa!ȦRIaZ Z&Ҍpmc6`l d92QIPmv7 )k*"L(h L$bF4:7p~\Hұ^&ⳟGln IVŲWX gU?U,\+͆Y纪Yk9MHT1UXU'ul7|E)-0]`嘙wY1+&*ZT7Xaaz=JR8mKeOQklmժͪ^ h}MKv:VK=Ү1.oZYiBcCQ'Ɍ\߿\sڦe|`aFl=B7Q=KuyPH=iI{eg+ 8WLCHrݬDacw JԗF׷-Y~'3jl$Qz=Wn Q®g&Ɋpm}O, {؄7uו2uz[4z\;yꊦʫ`m\Ujlfޥ,$ "=>} 9!}L k=E("EKjcLeZ"1 dm;IBLVI<&UU\r)}),3dJzf3`H؄߷ѢyUs# "-$JDy% 2ɊхI:"FN[>f-%5:gy&dEƩ3* GODVLEb&FT|'im3t΃T#TxV* L]#גr=dd|nڸT1Pɧ9i*U*YUhY"*"k6cbZHbŖ21(2,d22̩5ٱP4lf2Rem&)Sj)34le*%)QJjm(EDI[Cn`5#1cƍc]vg/oŵޓf$$T͖Di$ffi)%)"UT}ވzڧIOy!$|FǪVʽރ^u5:sM)CTZ6ڦ-/ .MmqޣΒwϞ/N97ri-vݾ7uSRSRKw6D@6HoU ,nl H)`*.L3Kso$+$JWH$T_|~_-|;߿/xɏ[|Om?.o{zm6j6;;\tnoU8`7.ӎ88sdT(q,2~^Nӧ9g9s5n":_bm|q>'xv/?ZזԩP؆kmSWe{5~nE/jGhΈ7lE,KdcHYelۏۑ١/;`\ N2ӕoog]%qIU&bk=G 35RΨIuW|u| dcYyLN~y3:|2eWf6xYN;5uyny <ƽ3.98p8/Nw]ýs1Xz(-^u8ՖS)JFjmʖQUC25 ĬJ0*cJT9KjWjKL|"RE,1EfH,YC 2d2F0`X}U+,[JU5id"AbcRlMi+2mT6jMع8[%.,%IZjQ[..fr)n\2dҒɹ&RjX,2љiL)MKRjTmrhIf6I5%d+%\iZZ6ƊFYUFY ٖ Fmz`(*1+Jh0E2K12ab!*V)+0i1Cf*#H!US,Tª2Y 0f@20&!̒HJm$ȶHYXU&DʒTʔeVF(VHQd[bn10R`T&~77Ӛ`d%~l دf6]{VYVUU,{9HtG.\"?u퍝f$HHbH#H` -&JBD&Gs G?2zlaUbvbz]9I$bL_QI+*%ETH k_sV+,~lvM.k 2ϯ爝 fEO7CW/|ZoC9Q98۳DыS$ѱh' [/A-)Y!0Ӛesz[9{L6ަ)F17PF[n5j ȸ]EwvU[ފ5wEaZ™j/ *fV "+c133(YJRdF2Ib*10fL322Wʶcg\J˗^rəQ1zfDIֹY7F|9q,pҁ+g.jI6Phٲ,4ރ l땊-QEGdA*eZ(fGbe O+rg4̢x0hDp la5#;eX5cp@ՍX(dvkm Wi%nN픕L-UbW6OXhDk@D+-HlPCKW"]ܹo9͢ !m D6i%>ފ4ONpC!mu(J+7tך$^x [*ry!3mC~\/ǯnywwC%WZ2y-?k|P `E2IGevKm-.h0$D!XaX0gdU,z4Yz~ jMA0/jirT)»yU0eDC&($zdPlch¼DgF^q."f"D;u&Gj Ҋ XUQ*ejjI39$ `1ڮ2XOg1r*}IƭQ5Zf1KjRHܥb_msOk14p\ca;kfUf?j >/ W0rn]؏هl¬mÌoVͽug~c|nO g/už,3YgU짿 gʅ}(Ϣ؂N 95BNh[~6GYaݶ۷oTqnfi@Jt3F՜9T٣l9;D̪+'Vr77ۭPh,c 6uYo7Od-ĢI,ѫeꅪyYS嶓8-g pٶI3BDYĭQ]lѮj6qiżi,0A.$Zګp kYL52F{4YU($hy ar[m$!hi&m&frVV(fM(/s(°lH oG 6U13R[ \%kL\H$Ių;Óq 0˜Lk_4[0嘹%BRnk7@8;v˅D6ƹIHo62A/5N*tz'h10XqaV >bXs9sD7|nwy3$ٜny\ YYl·7kZ8jl͔L7e%2d",kTHjYv)\fY08%W" 5araQ%ѽ#&TlvDh2cI^iM aɸ3;hS5Fj)TTPo,tQsom=aufoQ>SQ0& Z9`P(n !KaV]Af%ÖՖ1IY34rp*Dl#E.^yxnK{fLr5K|4J( 5d{֨Br=5[*V2ID_!X4-@IFOӊR_n慗<7Fօks/q[E_2Zz.CQz^Yfs@BWrފ(HF0r[斂r#=8>V.KVhfLmYAH;ɑ5ʪF%B_I^%~2J+I_\JѭlR^jf+2ff%Z f, 0$w D m MP0pb B^JVwĪ4tjl]4ITu{NJV'-ng)ú-P DGf! PqEIQy5B\5.\xU&)AXhm"@ًSƉ;feE$Iop T\i!=^QTm!k[ld=@im`DŽZzD#ZIV*p ^t%pt po79eIiIځi4mm$ 4;$!*FCTT|bTl=@LI¢FMg=i09{bI V;Jy@ UǼBB,[DY ׄZSD ow{`Z@iG| xɭt9wwwVSpӏުUU[5YV#uMMUru;g[UU0%BA6.XiIkxMM1`@ ="ѷv:./ze6qho [ :*D̗wwe@Hi v .FJ+ˢ-‹rì!*,sU5[%SК83 MhفJ ݢU7k xp՚jt [+)ʩ]ڲϑ0d4IX Cb [LKU,X+׽o}ś$IP(Ry|fs6oV &%b[,2`4WR)IaUk[K]_y'C!s&;Miƌ<Tӧv)٦+,l)h}3ut 4B$D|{H*F{IP$bcEK 1' /lJ"0Qf)+w}Ŷ?|^AA0ɓC"${HRf($ژeeL}LF>~}=zxWvuxĝVLj&vGFUJ&#RoGk:Gu uW7dh ǥZK > ya!ڙDGu4'knax1!i- aьV[$0͸F3e۔ &ډRW8ѻu b l98%GHM 7~oē(pl^Y}dJ$^8F䙀g nÖkvo,X rYeYry6 OyϗOUxa A%Ǫo]t<ηFkNp%$!S@X&<u59KA ;Zw Ynw@X=lJW/~JfsS]:LjVJnL0 \@΍\.'N-l"A-V~JB!J[hs'P*wgF筚&쒁γ3BwlsP56DUUqqåH₞\2 ՆsD{i{`DDDD Ϋa@39.p6 $j1l0h&'iݖtD$<4O(haފ9egGfI;u^ O٘ ySw$q.m44 8!޷l"=!"Bg7"xs3!"\#dR62vNfğ+,ӼnfrlB%1yu|&8&j7Qίq ~2(̸ ij朘is{ a!643:C0o߯WIYeYe$>!s߹n3&s&3wT;\o5l[)zIfL, KIFCSK $ȩ">Nֻǟl06Y껫p=l Ό&K,E(kI ἟gZs f֍ת(%halsDj]5PTd$Y#_E)K-xȝd3"aj-S\ԫ\ڈfL,RR[\epK\E;wpE4\i"F4h1}69Sfi+ߵNu"`ĕ>44qUHjU\r2T8Sṙ)3ELJ I0fhE,3J)cTkx(Ӣ#.]4G{9gXPZQD jakFC-/iQ)PGѭypk\m fbP5νw{̓zCXx tXZH `~ۻO4[% FᝊM#ڊy8(i:;'vYUŕE.(kMՖP]۞xe|j$A.h\^>nիkk0;Zt$tM5$ ם-<\aWשN.ܕ6jKlDa,7ۻMѧC֧~S2524hI"Z;(ݗqP==3I<֚ʉi bo<Ӌ+<"akYO#s6fn.hV @@s.7UEOIY*> Tjc)k f bXY@Sʠӳ:*4rEbNupZnmU:ӯUmՓL 8Egp3f0{,f6]wl3,a{6yNŢ0y$4>Vtz!.|h$f$f/qd%vI${2Q&,KA5BXF6`֏dh!P8)=HqXPYRGT}4Jf~3!73цeZ7%ag,d=-M5׵GI5I>|Z^A!*` ʰP|IZ]*d F,2aedAz_yX!GŐYtZ>O{3f 40/,fxH]hHI Qҕ/o; eiA 5u!q^<8;$_bvpа~ID f%Xag ڍ#gM,^O@Wyl2Ο$}׮df QЅiI6M7OGN3yʥ=E{ $Xe6oӆmM+d;TQ.gjMx@Yc&i]kgvyfqF%'Z]$ p=2͐]BoZd]X[&"תŶSvy&"\VTnle~* + KECD+=d\+ܶra3CR}$ mlRѭɡ9gkD6Rf<=':\JhGZ/FK8ϺINN'k_AR|;I* ?I0*G%|X~VfgсG؅K7-;NVvY'fx2) yeVn0!"E*A줊\$* j$ `ƘA' 5d!Y,a)LJ]x<9ϰQeJI>ߣEKYdc:bOw$Q>JKpz=֏paҙ$kÚX|^|jˁ³]Fm?GG=iIÛ>|H4|#>;^ϓ4d!GV±kƝqNTzpۉ ʘw<2}-p; ځ '.(z#E=#o =|fxeg436IXQ3:NpGxlҶaϣ4YT!w kDž+R~%|?^xngh>|хI4R~GEnȲz0$<fQ0Xjϒ'hÃxy|*&h4HhQ,ea$̾i1W҃lɪ6gGh½-t$hdA%|&$l?, aYLCV3W/}遌=w.X5c(CA4QhѲqPK)g$lh,76J =yaiQ*J7ㇻ%8?'ó 7M>S_[W:>%4U)ڿNJOIJ1TklT0"ؕe{lDoRK*OeFș*c/njVOujֶ{Rr6[YjJI&)+R͇*XKv(4FSSCi?O[Jp=%\lf&0ו{g$w7&fL)3d..s8i9n@[-e5mڕm1em[mUUUW]ú;;NNX-X H]^emȔII$$Dɔ*R̚ *U H#ߪd?CSfVJ䕨m`ħ(Ue1y t&U*e&L2dɤM"dɓ&L2dɓ&LͶٴMZU&ғYeRdQ|*$E!WŠvk:RˋXu&`[b%3֥3SdMCSUU(Umv֪"""%lJN%a̅0iSD+C|fK[f;Wm* S)3`ĵ\DDl&1)H B9{kL(0-5! 2:l99ʪ־P!v!h6r9ɪGRz^ 7#z-֬˚fUfUVj5Y02B.RŰ(ZI^T?wҔ*[ffK!ʋ@YH%Tf1fb%fS$2Y*Clc*5ORwIWI]8JʑR8&|$kZvyy睶>ݸrt:v˼Je"*yyR_׼*rQTb02AԮDfեmʒ-AhjI3x"*UU*f$͂łʫ#0<%ȄPmL3$333$I$ Z K5J#̙ OJff&}_g3mUUVA̔d`Ԥ7Hm s0կ/oS=J~%%D_Q^EO$dYI$ÉrK?j+%#/8m# #%D (Pf cȇc6~ַDDreÉs31s'ݟ)z""2 ! (Ym$M8': A"QAbט YB!JگfTYpډ!'n%YÚA<&][vuvh} Y5L)@3hLg;{ ^_+ax|-=ݲ=:{Sg 7x<(^zΛ"=i̤ϧ*OD3دde2\>ΏԾUhI> Od0VO7.N/o';~NjcX=WG._'^ngq<|ɾcc>{owe^M?/&az0 jOtE3c:^׺qs$o]eΏsc#3+nd::<ͧC<#.\{˅{o"*~N~a=o%>>s3ٵxw-N3Es|#n|/s~")wxwJ;/ǨF_[4[2=xiڥ|\|JV]-)+lj#_[WGn[3dcK+H˒$J,+)d258NldN7gǷJє>nK~ib>.&6unW%ݻ9lv#gwnYV\<0 オK,">^s=so,N N3jM4U+XŶVdLܩUBL'y6{y_/>5ACwY}3)߳k+|~|7'qUo޺>v籿q~}OoT\wR%q|?*!aOkツ++|w<=~=R>Oۈt\^柺w㶗9?IXҜJ/k^7|>5~? Mn\?\2zn7nO5*{.saf^#OXOU#C ŕ:іs4zV7.beºOޜw>Vgrl?'UO&DI̧܍pvL'G}e^>Mއ+SJ>r=u9?\Ѧo^rb`T~cg')c}#y<T{qU$8iw)p[8KwF=TqzgKP+)%xV$8Rul {6wxv^CB$=* |ޘ^;{,n7vw|[@{Oډ?siyGy=񲾲*=孳>S2u 4ꓟhv5{/?G: E_ IYwb y7~`UWyu}uxu7d;o8r}vZ?wslcW# YbړdB#\!%3EpRJ1e\]paLl4 _ d(%5g%?: w\;iТNղWupﺜI+yc~o^g\0Z0_LN\L(A&}01DZɃ|$p+2F !1 eQjUo 0OLxIGzH2˨al;m'esSSaѺ{[7f ~XC:Z!Qp. 2GDk%h%@jN}JŒGWe5=t *-yUp\,φf|Q%8R2#Xl°$ @1D4V d.A[+wc ު3Lz4Y#>Na>{ 0QsVRI+i̩Us~9~Fl{'uz!Ggw_GI#$hʫv>VUrWǑ}5m7ϓ Ǝ{;>ڥx>nT> dV{o2T>l+xϸxrQŎ{te^OFN7ͷ7_{g <>Ӗ_\fa~ L=.['VcVT%4SN/ 3ҤO"l]->/D^.E(=WoѲzܟwi~wqvNj;\_]^ }Gz_7N&^'*#'aO{+<]>^I+l~y?(>ƶ}GY{,UJ$i$_T\\p?&_ –]%|YM۱0sl|]>q߅DTk.vײӔ #2I]MSu8+؞_GwuwKKvxow>ӣ=ᚼ''g{+rO~g~~o>mD=C;S%< sy^ﷳՏ}Gl=G껛8dÆ0Xjtÿ WeY}ņ͒Ptg#  ZreeU{1vbw~.΍Y$' GLу,;Q Ljrg,k04;D&lD<#E]ElaccOi}k{ʃ[T)}14Wa>7٫W)=o+q}Ϭ/=7$uU.nN(ch˃C{X*ze]?FNwgfZ4\>Rul7r[?ciח8p.Mo=d6aѲM4/s)'kTs250ɖ8qis{;]\GɧI\%95en)7wyhx(p|gu>W$zžE'|^{дw:N攒{ {_[eѦ^*x}/4h34Js^^:6m,1q z|Ożzߜ]?& iR%Dll"rA"poT27dS6cZͥ7_kx'*;w9d^"/+𿅇xqstzHÄ\ "כ81i%bѓɊ2ERtaұE~U[BcX2LV'#B@qbnQIH1 ^n\nh6*컛;74l0z% e+ҥS tـk2Aaha% aVp3$̈+žS3)%@ZZ|+&!5i|@aC(Ri6 Y$P*D"Ճ$@HjSVY.5=ڻm_?ks]^62ՍK6wu5P.tbiYºq?NI_rG'n]?]~ 4JeVYeh ʔFNscӋcO'RӄՔ}aIZqUe8Wޚm4n1L9k 庤T00 R"^[j{M HB&puryNZm]\Gv2}Jߓiӥ-| ~i<2m۝k.}.} gkkcuJG.cY˖ZaK vVr{zv6hִy]qW`J4kLņgI_O~7\߸zޞJa'{+=&9[jK_[K鷫ໟCWYU_?zn˯B{7{Gޔ*䕢zMo_zWio:6gp[N>յWŸO<ۦon㗺>}8Ӕ')ƻc-nj<}]Ĥ.:SRm}t޺yIOUg{Ż/C~;M989Ua{m_'ҟ=yĽz[xYҬ|ܟǚ={+uc&y*;9o7iSe>~li23Vq7_s${x>e$Uz5&9rryqZ 6r))󯷳o7= pYlJ))eInqILڊڊ%( 2Rmd$-IIliLmkE,SkRK%$Ĉ}U? uakc]wv_5;[a|lU[_-"9}/z{`{"y1N-}OgwOrtd^EOݨr6;9*;-Y<!QWIX{-k_Sד0Q.]JuO|z]|Np:%nx|g+/y2qeRJɗ4z6}mS(>Eōl8ZgɦM<>{9z!zAGi?{+OKW3pa &D1e[̓z['sy|g;TYeQVSLsj{=.cpVz]m$̒WQJI+I+8fdזh-HDU\GXڵ P;i ZLDŸj Ƨ%lY-٧F:fƎ$$Raw*ȘêJ  ~#lz! " +ZcjocбeŻ/Qۛa˖yu^np;0Bhm&Ҫ~֭o%UX3A ]lɢV_ލõs*M `ĊpeNm,6YZcS tRS&I'kɥ-RZˆ12Č,)/VϽuY',8Xlv_;axv?:S &-6 <ؘWD a\cgG,dqx+ &<}+WQbWz+Ƴ Vбp$(Kxy+o7/ %ϱ`M4uL#C $Gy p>vsuut85YãMs2eaYlz'}aG}NBK|/7۳\WmI^#OG N¸yn}zWi}^Oo/F|nn6zG.]fOXٳv6rj 5 i]-wQur^OSe/O5ib)^N-82:_&_GRMZwp%To7ѕp12Ze9%L.YJjIPEIQ*#P4iiţ+t2ݼݭ85l1[91nVڶ0` `Y$ VˤqJfRgF= MXWԩ/2K207p}[}lzϪwGsʞ ghÃ}/Dn$BtQ!HnRJm˽ŠQVas:Tci!gEBZVSv 6SVek&, j"$Ѧ"%oloc^-5{IW;'",;Gwq=>pw8mI+cgKwŻo6pM*,cE vn\Z:_:e.sN l+wGU^FJJ%uH\^#*ma&ouV/!ٻ wW[N^O6#3,FIJ'> |K5H'Rwk1 w|tuTJ©Y$V Fb-bFf3[2")YR$2Xe 6 P"mi$LNѥԒXFTV+*L&*Ċ*d@b:4ѢBP%LnSB$"JJ̱5>XYde+3 +frfL ` "1D)b!\6UYcTőTl alVYY6Y0`*ثa2XS*T:2/ۖW:G3[RyyOu{p|8«-#.ixWiJdy/qJrS0륎4 }7+ SϓWGggoE,OI+/7Oq{}7 i{:/xSںwey5+ip&}73'ַQ.*}iL~x;K=m[&q{W} z8^rtcoEC^UW;!\VjuV$D0فWOFf2֌2C,J%ٔeU "96c=?>sr_&Og{߇]VOs;=Q_[n߃q/ %6vzwrGS(%(0Y=ǩ= }>w%T2׭DŽٷw;>»aK:uUVBIx߭O2'~L}=_f6eڏcE~%p1SWuIYb`%eUJΧf]Yq\<:1ʹ)+xbɳ'fxnXoۗ%t4VRܛ禖%KwIY_ %xtW-4\[c/+crهYYUa⤥ 0+(s C( 5v@Q(c< J8AD d> ).lKMId"II,i\lhV. d*+a좏(=3-ZviUlÉkvzalUwvWNã p57bv/Y&%7g/.Gؠ9%~%~RJvy9:ejz_w>BFpÝ78I+ER҇e4Jθwg.hq  RzHmAAj4A=5ό;9~ve#Q4QWnPjRsM2Rƪ;7G%u]-7*oړ2d3Ug“l2̱0[CxgV8{7$ٲpm~ |})ٻeKTۜq L¥U0T Ɇk)XpV5@ օg\qpqG7'Jj2e=Wncg e6pmcvV)6ciw.k\m~ LJYJHIF&;V"@HcZʕU\*ɖHRdJR,I&ē)Zd$ I-,԰%) 1ib)SI(&i"fT(*M,)e()I) A1C033bș$BLBQ#JI) Rf 0a0 H$TؐR~9>ƍ,iTCWqb|DL{7p+r+YD184j4٪tJǶWO5(k"|]/y I^Oߊ秣ft?jjlT@L=ҶpͫnȭtتLmRc [bkKئW=s$p+TK$CWP5t^Mp!!hA0R8I#+iRavR4݆WvnphaGh~mh06.wq'A!'$HؽIL .x.框aZlfb6TRs&#sz}̳ I|鴨ŠV`] m3ċ,i;ZӓuT7vV6ʩ^ cOs/ƝL4KNlcfJʪJsmVkw mfS(F#S*CJfHH L(FM@FTZ%c2XQrT&=7k o٥mPS3gd aBG0 %[rsŕ%pq®w4siKft5f91O۷z給U9au*YXX4um70 3RpcӃ1IZZAUԹUPي T.+)J,DIWnxV3wY%Vg>OG88e}n.IZ=M?wn~jUz/[zYE(>u_l>Ix;|~VG}jᅵl|]Mds$63rE;إw _'[ʩ׹|';kgN*x{P$?}>|z{4iv4z۾in'Kssw=QDv_|ݻ\D>a_C2)1D=ƫKz.4L̞'z>f/b;x3ھ1]x#v>F}~\ g+O[Oeo}Ce*{RXP|Wɲ=;=?_$xrau4Li~R\}M6jr<:͚Y+% C[/pKfR}'+e$<[LtW-sqSJU9_-$ݍIѹ2dse\4;[{iՎ 6VH4yy7+F\:&&!Mm[qG,I+gU˸'0˻<'ul]̩[]^e'}tF.Um[6ai}aEqcrF:7=gJtn\bkcMװUު^+")(M6-< 78Lcm4ݝ5Y&)i_Fz7mg5¨KLI!D( h2H MYF͉ ե;LIdfVc^HhؒG|5om On~o/, Ep;O_ږuzZo[=%Usr}4vԿ7vc/C}g=Η'_ƱeN(I+IgoܿU8Nkώro~cTP֝wwS]abyXscj2gyUXXVG]Iџ=6?fMjc6JBSX/"WW=y_WOI]˥Swu纳:yZ^N[DEVK?9.mi3~8xsRv/(u֏_T19df$vϯbZGވZS}f!8ukחi0^kgm{y?zSR.ؾ̸S)HyFx1O0Qbo~ژS”Tg26 5D|*uI qi ŏ$I2w2cw<_ix\S#:黾 ϿN'*2V*Q%jIZEiIZEjV` X4L&5HY?Z+@'{l+vLM1[#LkjMj kY =D:#vlLۆC!!ٌ3L.c"q,8=ĽdKs l"ڷ{'w_ꪯwwwUU\:s̪ɧUUUUjtcpcZwl%Bʘ=NT95 7L䚧W5F5wrf^d<3%=Nejr6n! 19X,5- 2STN %n"`eQ,HYJTʲ2&X  Qm6i?~(Ǣ7/3w\Χ{N^X縓HQSEa > >[go x`I   &LG14ɓ 4dh4hڞ= F@24ɡhz3ICz53O)A)"!F4O)4Pѐh@ &hJ~AF&4h Iꤤj4O$L&P)IMM M4jd4#4LAM4@ !4ACFSjOiz @:QZ$S_q*~epp$o?қ;NwYx9zxw[#UJ[JRuKm+VI0h4Qa%!HY32j(ڱ֊L!J)1& a5H1dK 5Tk!bF1 lQ쭇0S~xh_B/]Oد\:tN_,'a=ѡS~glKɞdŒ4Fp/5$W*ϼ%6 Ѱ> ݅S㖿l&9,_@0¼u|N88= |z0]pH%Z,Rf Y5~$(x޲G/eۆfI!#gJLYߗI !&9N҂cTA]>M ӯH#3a (۠9H03 АDG%/ UAʉ<Μ_jf 3ÜKM;?c!RIn s܊6ٶr= z~lSv!o;fdj'jc-UսV<qֵٝZendDk6񣽴~c,GXD@.UhI ܢPdJXF YR7LVyTZjԠ3Z #e- q.72fxr. :d"EM3L4 yiE1Y9]օu 1-:Ѭ4a3i!^%׬4{s"a+ 2Ę|0onz14FYˑ3}I*2ϵն0?HH0 "B/1[ʶ>Бpti&V! ,X!b4sWO,5<@{yea+ѬLOc?( {D" H7%{qr^յglo`4wz_b4:=Ctf46 52vE I>K$Ẏݹyrq^凛i>z3'fn]H^EFu>SgGmn\Ǡ6qFeʽK1圕vڌH' S9RmI~ƞgŷr+'m1(plZ̋&68lda`]*#JqYSG唑#uWqXeX=b>:9M+Ϳ:l 0soNbOd{S۲-ɼe#=):P"҅agӃ_ujOijf E,{eW$Õ|U6j1է\d:H'IpbcUC9RfǽNk%dLDfw1϶Ey+ƷBZN]YoӔ=SJfNke6ڧēwgLT_F,<:C~>ݘr,rz`kp\tT|mbXɩ,\8Y)9q2١ry'WMQ2w3mg뎿=={ޑLD>9^ T&9w탒e,B8Z׶#͇Hy B~64a91- }33`C+`&I c'AE(/ vNPX*UWZf:E{`Ee*OD U|:{~;,mby\GU^3w8""PJi,!(٦떽JA@c@kiFalakrLC#mVB$mƾJjU(=.=4x qj"}އ݈;j_^i0bbVGY, A=%6C-M1YfSɊəI5Z%ʢڨ[&`Y>` jW@D-܄E υU>uW.N$H--M,AY4qsP$,0 fA J9ȸQ: -HU pz`Pr i#E=e@)R)Q'۵5̙7nݨ/1Vav^XY[f $“%2v@ٲ)f:R&L ad1Oy)"E` 5A@dźt@yv F.۩RD42 &5+Ԡ"iSa9Q^fd4rŢoWL'nPc^v߁Cz,R5}T1wQm?h)!ěH-Q!TѬ) Y)m,2ڒmKfԵ%Ն̈́l֢$[֔iUcrjm0MkB|yI=^=Uvs1WrEeke4clYi-e-lmdkF(#(VL媼I<ܒ7+L5-nsk4Vd[25H1LK(dLY4̖*iZ,šh1dbŭƹ7s\j2ܔ4ntk-rH) XaQF 6PhdЙݖۢ+2S%5ɮ MD&5!S!VUA*UL,FV@d,%"Q4&҉ 3W&|XK1 ec$ RbXK& ʦV6eEdŌLFYVL)jmj-42ṶP͔560eEhhUV(CS# YXŪCP %1f Tbʠf(2fPɌ Rbc4*DV *Je4 ҆1Be$ 9 02)0YC )$EdkF-,ƤkTl[]mT*Z%6eLS&-JDɫS@0fLcrn5ejT3YTɥrZr-*llJEtC*'%f*$Gߨe!IJEb VT0ińU5 eY̶ [Mek,Y b6٪mZmll Rm4ڶ 0RI& Ħfjɦ$*mٶճM++6maL6Y55-i55-$,ڳmPmZ42c$(6YYAef٭5&maUZ6Zd"ll*ʭhթj JH̘efEf+TZd2UY,dD`eJR V*eML X%*76Sۭc,l UC6& ~DL4aPW9IfM%7zG{{z5^-K5O%yb99@>b\@ڠXuY&fffwNl a WHܸyyG25̟,~|?y]12ь,0VxFzv.0U9nq3dy| k.#)+dM6M.جrB0IH( )[.P_Gfa 2c`?C2 025b^í4c-؎ 5J>a)E~,+-T3mc ~9_K*//na{NK90j!w*T7*9ȭƇyanlڵ%ᶫR0yee\e(Ƭ|rEXջZDbtN{m*'*\5/KqYH[mCܜ&mGN1Eq*. :rLǔJ,pzŖ;UʢR50XPE1bZ5VZ͵[eVɪYRTͨ"Ib%+t!c;ZY:*aBK?hq?q9;=fM.LJ⢤|mTD[mw7S*~?2rs8G'ʘC8𓍼N-T VDA ƮMMؙH_y0PKHDX$M&L3Bf% #!ڋzDMw$3zd$dIyK36}k/W%۹aK2jrffa̸L̙ہ3|r_`2Cf xW-ǹm_G-rf"ٖoG naW7sB3x-JȪ{79+p\\;be'+b,X`?vxS<ߙ-bqy1{05& }pt|tͯ=8GP<9 LIvSR_(Wλinb< pHMUܺ"'MP2J>BM{{يqR< m'4H@JB"42DHQz@`B*Hj Mtg*ZW]I0(YVUby ӹ=FeZQF %#Gi$Lb&`,efHaBda^PYgGvQI B&1$pw'inO8NdؙX8"맦|LPܸ#7rz =9|F6g$GF3uM$ ;#ɡ<~LRspiܣVT]F8T EBp#]#d}xq[# 2VW 0k ڽWc`FK;SV(etZqvKdHW ."2" s x#DJ1fEUIs=0riA%ƒxguW9czm{~eTN 0O퀫1nYT)_/BSy}3'LÍFUʿ b}ɋK\F5y`Gs ójG~)Y\ ֔!O^KtWCTc=v=\VnlS荇`ڄejkr@f7)Aʷo"s\mTFQ#/ϛəʙfuS "쨮o89TJ- Tx{*nN%UWO'86BCdE4.9y GyCĚL9ɑhzN,00y7lsU9,pcS=R8ei ;eЇaOÚo's65:4J Ær*ى1l`f0 BNŗFDMF30"N+MeMqXҽ@BDƎq#E[). D1lJrLzy-uv5,3,F2hb&8F!D*O"XUTʘKUy|9r "Xf@ d%i2\2Ky|U=Dm67%;"JrSWtZb0TŽcVK=ONp8Fr陘eisgkvEAYW!cSQ9B7%F<Ȯ6>Vq.IerƷ|@>cSʹUmI$ZmeJ]g%`Lc6|ҤLx݆0(b4"UXlk-5=nH~N\T\,)rlrfY-CsAAtr3$N:oފKTW(Q_+Q_5Q_Q_*BzazmY rrDeDն5hpƮuy{ʐ jH 0(G\Eɠ='V66 $هy/*v[3LRt AUxؤKpCXəe3rdU{5Ic(ÒK&Jfp͑_0 ,wo: ap 0FIQN=e/Nͤrtђ+QQ303Mַ/g3ƧFDoAR7In9y]0I Pt$HNuOE57 b!$K:j2.>nDC}mn&*ba\KmkW7"9e< BHXmcXxr#lDa1mt.&}YOR+H+:1bW8t$.M!3/O~snG@U) ;E HLI4xH7tb brэR) DHpdVm8dr'#[dLն0G87چusos 1r &oaO2zyřW3LEK@499%lIl*YAx1Ѿ̍!@fQ~1l sx!60M ߌKJe&uAl.: f0Mnv!L}mcyowb&Աc~T'vԓn0s46C31c&hlÏ LYi©`+nIFc@ 8]NJ*C ry95:[~֧~#mYNy`zRلɡ1w'A1#zZ`` = $FG[tXzז bC|ng<<(Q=1!0t֕陃5V63'VZȪI!X;Νd3{-zMEb=B #m\FC`'BD\׋v!Je= PÐ0[ 1[ɞSIRr2䇝tܗT8Y/Y)g~}u۬$UUsSi'3R)sfr8JedYes%aŰBX\"GAN) : }MAAQ$pxTM؉r [8xnft&S rg>~FRgR*NoZHɮX:z:F\HKNTUGrwƮُ2BBBL¿$G$x7z>4rMFLJ3,aa XҪ.b)["ɛN[Ј0"FiM {?UoñUm6KIsWc3AۍT+g9Ft4rʓ aga٩Eq vۉ1ٙs #0bX@M6C380K+bvM5{mɂ˲ ٖ,=qvIN$R b!%L3ήd02Cl!$=%Ox&:pv[~ְcA^˽y~467n|}[&:;skm6$ܩʊ$p&%zE` L-YSnCy7qu̙)XKV$oј-<)$Em6 8Gr﫾Rn,ٽ*Bi٦'f-Ƀ6 536pJtmG tc卦BPh+ SӡmB6E0߾J~arXbF V 5oj][C. --Fv8N1$o\nq'1d ue4MD&p/<8a2vc;!ō;rD[\ݶgMct-w7y#$iqMuXn-1 pat*&r<r9ۯq7$#%.wa,!:TȢ$`v%iI\A Cr8=GqT`9B+{wniBf- L0 lNE'N5s~V":NZܕ!s8 Je a>$ND!]2a!f\,@E q1**$3;%}X!MH#JO5cd#mv8SJ#&Ȧw;hmI]zL\bhۆM\H<g؇$&I;'EܢZNUU'هS7A:]jlUu9noGSb'z\]RPc93`su$\Mlޥ 1OAΫ7 =s G=M"C<`|Xv{Jl*wdwq(ꈛ7ߤ;I:M јf7*tzuUxYdyxW#r#b mt7ƛ恾 <oi,!Jn3x\5 j 4i9|8r ;f@D{YɖmDy Y(6$tf\?FUz+uRrbPMv#2,IFEt䆱n4W"'ELvi-rv;Z]QgtCuSpyM܃(:e;,YmHCz6sx n%xNl9(lt{`CS2cá59Cu&p9f C $HHow.yiL-19N7[u#0藰YЇn';sšƫ "Vʒιd1wgF#73%9Jذh!BBg5ۡg Ŕ;*T :< f< &!Hx~,0r­(wCVo jzH[wǍ}} RY,N5'|θ~b8IIkmJ5:+DZю$p`dF:^ &ղF.ڸ4U&3Ӭ̲G LݝÍ$m@%" $\[О5IfMkPֲYHhPFHsk\+sh6u50O H̨k@tY)$#+@Yb H" !;.Gilj̫L\FB{^GK{ I1!fǫ7Oɵ5inΤIf穆:B&!`1+0p;V_jlłJUU#2!}'}z>snHo-eLmm~ IY$1o+ב$\H,*oN>1 \-BS3sǁQ~hI/>UUcٙ5'C]*^$x>O>ҫ1RxB唎yq=:w~Xǻ8.Hzv);x__y4}\U)Gxx:ec׾#7aQ"]^s^EWi 8Fռ)_nuoK' g&B>r_O9N DG]w`,9fEĖ{tIe5㻚Tzi4X+[.ϛ8^ ̓=dD.+Cu>K>/k~v^+`QfMrU sgbJ|r= l)z.$$~ f׿ "x;J:>a"}k(1V5Sҕ̢}ݏs8PzOIWs"!6mûC>?ݵߟ|Wykk']f]ee¹qݓNΓ7\iHIyW-cw7VWʅyS|Gl~g}|8{r)<{(wy"=>A^TZ:|7)^(iǕټ=}9ï;Us'߻|g?xGɿR_]m>}^%//{=y6폥|* ;ʟo{Cק-(D:gM|b4>k$k>ԧ+-/9j b#_wh9)_*^Q˄SZ׿/֐~꼮vdWY0^:{yRϠ^hѩoq5s?._+9r0f&FC$,em MZf@jjH4,Jԇ֕ѲTֲV2Ldҥ2a$aK8UѶشZ|'GCgczKC;GNL.UuHhR%#&lTCRmYiN ZL2eac)ͯKk[H1KZAﶤD2f!CŌ,dcZoGV(P Ok_g{_m}f@ !0ɁfJ̙eLaYb?hd>Q$G"~>n(j^ԽǼȹȹҧ'9%[2LŒLjVmnmm*ͭ4JnVMkm]im斮e[ Iwq6Fi$aqˁ kOwIrnD/RK Fi-a`)^$mK]ZJ2d³!joi.'I*-,SY+KΆJu0e$!eURERc$b &Xsm-lܶՙ VYTVL?*J" I1/Jo/\ksniI5VkͭMfbeAc 0gՀa `ϣ0 U RY_az\kHJmfLm[0*ֶƾY}Ç^w"w+̝Jӝ UeONn勚Zŕ)y,)b1a.4#d#}Vhi}-7os/,iN%> [ՠUF:IÌLa䚏:~+Iz8hʅ=Wp#'.R;.c+4CWeMX41j-faB PJU"*-ڱAm6m"mRr[z rn^4ImKjV[B\. UU)5e$f2mŲkmsLdX,26LEe*,wpX͒lJn$I&dIdi&HHHIM&l٦RH $I KId?8*l0`ѥ3ʎ$m]5&l+x|׍63 29ΌUeRU1+1PT,HSFٸ}jMX./Um#n_~QO))3RTWi?4*h*~0J2L*+-&&^gFl= Rn].dxhÍ}70416hG}#TAi _F%œsbǩPc A!w>g>;~ ^dÏz{o90g YvVp ڿKYCy'G$am n*ۥE Kfx͚uLᱫ%~D39jj]TYHta2Q*L<ȗt<{0 ~3Wx&ϙ%k}j5ɢIObxj_ x)Q>ם{ΌŇ<~Gކ*Hč |xnH{B2wzM7]mJv*S4m8#P}lFv4Ş/Y7x+uL "NUq0:Ëm| <4 T ۃh>WY D]"+rR*.؍4lR~&'HMl߹qxj*調>.HBGh6hWͤjy1RB*_ʼnig5x(Z֝oԁ$ڄu|a{!t3\0~K}b>}m歫TK~]+>䕆i*j~p4sG*/jKA-\D%Zܖ(أ[Z5X+lKO6OO_qFM/F" "m 4ot2zs}N#f΍\k.N5HGJTUN\'4#IFó 6};[z:ue*>XasyKikRl gh>xoUW݁ܮ:5T ƖI`ڛ\xS"^^AdyVHN']l4q=&:h}仸3Ltn@m2XssWÉ"bPe౴NOfqJEPO|cKLݱM'p'Q=}N-sDn۩HrF d03H-xs$TRƫI@&L&5jX|g7dTϱtŬ_1J[f!q{N~]&K. 29w `_ 7e {gUa[-q a0R`TWz|ɇ.]QROR|}{ކȩ^ꢻ* I`,E˒Pangu-z||9.HSHG'WzH:{jnCVfʺAʛ\|r` U Q2{xt>g9_7(TV..u%M˫[ 4 |sL+^n1Y.x8(CuY<.8|Ect~|W1Q]\W7E/wu-=%~^ jΉFw_|Y&?><܏~ӦΞ gϙQV='aܞmDZz$3{F<}j$aU=Xs*v+=6eӳԊ@xnO;EXeUVUoNɋGN6MВ4p7M Towdٳk*iIt+Iw;CsKc+x4rܒrIƝyn" XI1jN\2DMSXZt1! $2YXQXDUV*&$0JDʘ$Ș*V*+)ooKQ!h5$F1R blXi%MM*mSI34JjRfY5J53S)k25%5RiLI6m6Hd5&M!E(h5&4jYl%%@ELljYi)CMB[LJRLS %fZ&)$F5M&ē&ҰUiHZVKljMM͋&,ĕ6EIa(TE%eb5(RY*6SjM-,l%$dƭhմ,ѡ jF%$#Cl%M26H(i`A#IQ@ʅXƖQX&kmK5H56`*"T b2VQU*ƉJmi*mR[$BlJ©T4Rj-6f6hEY4f5)ړV*TŖ2ғW+Edz'Q[֗s64wE=yG2dbIS±i-~LΗtWI/-rVE d0l"Jz+3\%ETzgDR*kT[J%\;q/8do%~ϣYTzN]#^"#NŗK}+y5=jHgC"vU$諑Q\5y4oVK-Jc0j]~>͎da12@eI&?!n-_2*hRlу!$=C  Egk@ nv7ɼy\Y7ic~ ^ڻW չ嚸G/ǡ~ޘyK37VI\8_͡'2/wD:%x ĥKj6F>~^"t6n^?#]iiaETRWl0U'ޒ=\E3$ˈU8+#DZѳbCE#M]UWv{QLKdnQr$mt9QOlf`ۂ,"̹+C!y)WQAޙő;,IYa foG;v漐AJ{qV ]AȫUx,D%UuQ<$!ϓDbIQ aSɖ0`szP GS9EUA!!-E}(Jqgꊩ>%8R}>WU! bPFҽ{N?b*zS a0Ë<*YG '}4wm/tWGŦl5r2J]#ؚd:ddgƣ后"R~Jm2!X m~˯/lB-ݬ}4ڣإq+pysǜ*vyT$7 4p밅) >)ʪ=r?"90ijUa*5EbmV1חؤc[]6oic卋p6ul&2׭KKG$bfbhKX#K jz8缑NeI>|qDβJًv|aŨSykiζ`9FL*C+[U!rzf`Ζ{nԪ i[hME8P0hD1Je,` 4jdBR{FGbʡ[+|7QJd.ӻ19UTYbT'U„扤T73)=q-J#ܛk[Q *GO:],1q;[g8 guߊG~97MCuV!&zEn2 o;r;6TrX6qB]z=rڶsz|U jܶr$%!̚Q{=eȲs( ,p=ۜo0~i$ =Μlc1D]d!6Y蠛Ci$J&ftk 6kmCRR1 i+ "AX\*Pܓءk$"P侾X=XldO.0Z_\0wXVH<}2* 5=Jnf݋mj"<:h2̓/>Q(ec.]fmy#Y a󗫽1TH4+]++^FB>52@\`pjur_^3TCc7# &hP:r7qИLg"S=f5ۏ*KlΨd!GUZ( n]tp,jsGwu ^gmV%fz' ESBAA2*I=oj Xms_")v PɆ"> uEk_=zp:iֻo@"WK])k'ywO4n1raD~g8_KaL'V^qlD~6 IYa~⤶D[viʴ23F6f6'bH+ LbF2djUJhSh5MQE6)VpG"HvM3׉;bPtH,]+!v9c!*+R*F#XZxXamqAqYyg/gzRܧ%wlIn807|]g#bVd 5>L\NLgyЍ J*ٔfL !F&m- +c_I}?q'gu|I=+[|l ܺ/،M'a=_)ϲ=n9H4[ Rf9SNt'" \mk$,ɮ KpʢUԿ|+:0^UqL0T`)HSR"n\X^$/2]9-މU?tˌڌ[֩5lf)7m$ 8Vٿ5VQOI< /;αE`lIG%B++>ʊ˸ :n< BcdI= >P+R !&ASZS)i FTaHVRwѭ$I`!ѶZH-*tT4$"h@[=@ 0K Hc$60jSYkQ,lT)bВ ` 6I2{ؙ8Q.l7hM1ҢE0*+C 00ѭBETthcK(H!drїL!XF;ynQyE:KB1BqUfrAk*+6 ˣ"W[\e`QrYMm +ttdLdӢ,ln5VoR* 2F3N4ٸ͸W]]nJIHXvkJmuAjHbg$6u֡8Ќ^;Zv̵[6JTj]n&b+˹թĭ&TAHlI!~In#4@%&B0R Ryqÿ)$|I$%biӣfr-ebXcCN@ 9tnj@Cx;h.bZ1"Zf<0K1PLF[8C[G4to%nPF"Y[X c| ߋY!C{BCViiata2Lm@J0䘓j._[ "7;&pNp<`q+Ou5K%<%e)J4 4Ĥ#SxrL&D֌1#7g >}+4$yoOIC6۷VDRRseefL }B!h?{̺z=Og_}PSTWoؾn^UA 4S- )s .)_WT3j 'X '\>zd8;=1DiQZ(-BK,{87< wUެ:>eã'0a:HX5Z#W/mz{fU( 0oj 1>N_Sط5kx2e$Qda0Lu^)SS< *+i9͍r5={>zwRxeu9F]$(zvqS] `!Q_2lSʣ>lS%{ZʬeʥʱVH VtzEnHHB2_{ZM;+"W#UUbNݕYgΟCEltش{x ^?A$~f- B!wvO@3 D?W>+͝XbU>YXba1$11 0ԈB 98 q].F9O0Ti"aݠG(qZXBAL7rco\jzyó@zAettUԘbiqmf"K~_o{F&=7LuMmXV*ߦh%k z'<0ÏfyǤ2_`)6\zǠ%qQkW\:ë4/>]k~#DkmaLVV_*ʢ*+6fT]}/V];{vpdG9t3 %Y9۾s;y'^8]w%A/>*:/=\ZW몊eQX%TWsaEJŚز:V0V[Y*% g*m[(XYLf%Yf !$D@8Xs*Qh(ĶV-XLPi&#WSr3U5yk 9 T]y=sD* U`U&gTE{Xcm䢛Ҧ%i}L|bl0IlFE8֪'n ,RiFlbӕ5q,;im8~:$ Jű ݥV@ՙmedv"bYf[JJw/^Ewz=ã 7R3LiݾhВE:jDDڎ1ڴ׫ͭѝ  Ko6ax8@6U %XWk4=hGT3C斌e=.R_m9ke-'9fIqrdB.z{w})?Ļn!>K8]xluϞ?Ne73 g߉",4lKW 20 UE}uEvO._7O^VXxtT˯ \UT䜼%Vg~=*N0u۲4Ҳ>8l3Ỉ^V?W;}x񄏄q7^sڤY-IZx eisDF%((Ɛ$RJYV?vEZqy5ʮc.TXo'[XҼŝxydjCؗɘW!w k rh%ua$%I KՏ'Jnߙg^UmcvF䑱GSgbFXdy;%aa/QZ:xw lmS5Jyor廕m KH4m2̚XY1@I@#F$Z3Ubi*[iyEIh4LdIPԨ3 ҚM3Jj fl,HYHeBF#LfldDJң%),FҢfe) B 2b*TȢK!bP"eFRQ2YiRSPF0A LS1PkmIc:! z6ʒMvݺW%DAѦeϣeQBmg?aQ['Rt|[='M\"HI8e+ )Zζh1Ὃzs7x A\mg\5'6S9E8\-i`1Y1u(Q]mp0QXA-s> Ps4pU2ǼqqLU;8loN3ͧt@% `n0sh=ĒN^𣿅,aX[{$}gXmHnNPyu%wXĸ l.g唂֙pˣvn3vN7ZEvHb?X]pZӝw-ylVH{L +!P2MW&0 JUkf\~]w+官 fhFj64bZkF&&4jJԓ (F)IiH$2S%#dHRCF D+& fEJ-&RP&Ic,Leb5 KVYhO;T,Y\lTW3g k$p6e&O=2.9`ÅF}F !%j(# XQX*+5,z#BI'15[JBՍ *88$<֛0^Cq-C+ Nj'-,mj?z9k9;cf[ l^XG*e)17u;敭 \z5NqU0SDmZڔ"&;Hq!}κ{.%T7iлXI-|o֨ouEE]ݩϙDr֊GB̳.)TRD *RIa]WУP:$Ȕ "xXbR Ġs;Q|qzqIV!10}N~ڸEq}~6WK|_ q|[5׮e 9pxjp O%c}0V v7UY]n Al=5i#YVzV*ٷV:ڶd %7)zΠ{"xljZQ X8f2'#z>c?2Ho=sVe@pˠͅ=pV0n( IM_5}ah>ͻg.;Rs721^̒ 1˼N>"Z-vw; ݮĄN02D6=0ĂH-cĎfOVey%I5heס#<;=Dz#o<~NHT漓C:aqԺ ">RG^B|zaLt Uak"s nUgŁ %Vdj=:GtQ姷qw#%4ny6u~/[*^%d6eh:eoi肫O:{g,׶xcg4fQT㍪ۡקƓcًڷ-bV-FM%IT:F: M9P&TH683@IiӢdB)+>K=eFl1`uqJHmmD0d_'[mWvspoI~o W/14yX *V "FАv%R`ZI$'w,R,/s::LɁDlvQ:!%U/ 2}_g_e ~gwz}~?ߥ~W~{^}} x9vwZ;K뾛ŸϿOg?~gZlz;>i>/? (w|?m߿?e~7K|Wv??>{գ?C>7~o~w~'s?/ꎯ?3~7y;>OG|@{ߑ~F߇G~|/Oa^E~O(~u}zmK=_ogo??_egٲ~~n}Y=oOo_'O?}>Ojo;}/.? _%Ɋ§)1S$HPi"jTʒҢ* I4SJV*T) a-h*`iK&e?JiQ]{W{^ QTZEC%mVkE:ٺǸ5+f*-!|&bVU'y$s[+uqN;kFB ]dX)Ѣڈ+,!2Hd&dYn㹒sRl93oLSEEUK^bQRWtUUr[xa~^X<[jhZ9֢̀:kFiV̦DՒw*+ ȢZ~䕌R HBd&K,L+,SH̍T)e[,ԍ30${ ^^Y2s--c8rk 7Uj,̾p=AxTV>9u|==Kc\ʈW*qVnj-L/ljr[+orn2jK.c-޲3fs4#pګJ͈MK%kw/⬉̣)NĿ"*^ʗW{4UAdTzֈ׳hS1j$gqH`LHZ=}L5Na)>Y6)-:f57jqL  u32h5 t"f62,Xa, iF12-fdn\3̹,1)xZ3JwOđ낔+SΪ;6kUdMfiPW[EN x|`C]=GJ$f{pD 7ϐ ӓCw|=G4 XnRaovmeg۸^]̯mlƒsNQ0$Qdﻶ:_v`z+XXH OAF0@ 4@4fFBD!(mL h!CA JRHRx02 4hC&z0 d 2dh4=#zOU%&Q @4@ $@&?M4#Ljd6L 4Ѧ4 hAОdOMFMOD h@4~ʂ%Eb2/^!r";UuWvVAhFW<V[FfXآQlIf쵩Ux)-lmK_վr"/>?\ܤOľpDh~n[$"#l$@SnBBZ꿉\UƼs5r$,%sX$(<^ _UW8/8yϴjZjZjZjZjZjZjZjZjZjZjZjZjZjZjZjZxx|;͎B H i( ,KH(J[))"AJ,mlK[)(j$Kp0"D`qIK 46`ZA fMMɆoKi۸U *N$Xqu4UnEdɢMF[$m62hMni<77 MM URRVnb%66K)) –w))"DpFD,ml5bS Qh@S RညlBeTƆꦢRق'B|u81d,.T#˨Mjy;X)|󱙾K m2%QQDC.`7$,!7ܨc+x %i#6Ɍ ne; ֝hpq$"h2YGzEre=/R-1= J ^ET o2Cz\bNS%'- JH$:-^b8`=衄;v(ť h<_qrJ*@}C =hH- 4@$ _S`ĥ`}BDR[@1 b"C46aHnPD"@:@@D^ ~X! P2b a銃RGh]8݉:B߾0FJ 9a{ffg3nRǂxISWhp *C}CI&?F2;?=)60s̘K.؁(Rd֚!M l@*[b_@A"U|H$%4O$=h/Њ` 2šc S֤I-؈*yX$ jAB@`D#$0w= >!{ks&yR!9w jLmWw3#'2Ia50 $"wU}>ĉM$i)"QIIT$M$p<xBq07 H촱Ji%-$X)-$2KR"Jj )%xǻpU%ޟ;Y..Ry{+<*_TS@BTd>IY)*^'Rjof+6b5RL!8cJ *&=?ڊyM"aÃPW tte&d%ffIxaFc3 6ƍXhZ-TFS2& 1TF6ѤѤҦ4&M2MI4M9B-=VG?BڜidFS 1Sfd`$k[Womz9^ۓ FLQ\k[srQCo_I⤸7ږ'PΫSO^ e@{ƂM:hu{OsMywĎfk뫻p/"N I$I$O|{JI$I$ϣwwwԒIt=a'ȝA}A(<Ad]oF8CM3n>Vg}=z[|<za$I#,c e+{q:IVہ(! P*XB!X?Ysl~cZI~,fH.K+2 2UU\TUq|vWl5$%$ئL's]O|oyɣxy380мo/NeޡWVDW#2^YHƭ^9H\TjȁZJҋ̥TUVЫ̥]m>¯֨5GmAXIh s @e IUU$44wwH7M44M4ho4M4CM4DM4M4DT(P AC{<Jnf;fG=ܐ܀胊3di63&!b BZ M4v!J .QO- qӞ~~}{d< Ծk>cHFcffMH79h~'RăҠWSRTOY8^;]9X(8s74gɚw H`@Va$ "LQTbV%n95-Ӗh]WO *XEۊش[wKʈW.]"..^gsqv."""v+v"4H$A# #/}56FRLF^R0]SӦKZ-eN@7餩 tU` lœ|$*xMq!T ͕8" @-/2s8,@9cwFxbv4\|`J3DiVTwv^*gkǴsy5d968ڸ8 F@ /͜XgaJ Ec3HU^Kfc3UBU<C \"L%K;_%_PP+⠯tPW~^^~~w1œ&L DPȠS]/V`n 0bŎQpp"D"\B U5EoۃGws$2VP+,^}Hf~>a| VkNY k+iSJyt=vZmj& >Ɋ3ayxc_+cɔy\$'ڧK}|>fBȝYvwW ɡ34=DpTz_7O>YUnqO\>Rsxžv!>2'Ef*JrW˾犦BP2hǙi J='2b eeͺ4OpާB~/lOAq3mԆ.]Yz!Wy-@a#1a'u$lßa>]tymw4n];2 AA[|3~9Fm SdE;u+Wjm,FF߯ɂߎP{ECScϕu|!T7,&!뾦ϷbAP.J_,xs3Soz~f,}.ՁOJxP} L3tIϜ9$wPȡ 'w{o~]{Oπ|ǵI7{ׂ%*O~/kuK ϐ2W =19~vDsbvyk>}h>{VHtLJ~>$z ~}υϷ^Ir;oMZ4'dG5^Rc 5_g #Od}.wR9weN}=$IKjyjD?KNO#QtV*Ŀ?p>Pab F/->tG)hNv,ʌ!Xd\rQfYLʾ&[_ev U0U퀴HU(ī31VL{j}TRGypT|”p #e%]VpKfT`(l٠E_m\rYK5SCVaBao2 *AY+>+L,ac >VۛZAQfJ=dЌcE;omc]wxN'9;w7wy;s;;GwysHI'VU թӧUUN:t)ө**+NUU:tӨN'RSӏSMddFBĐ+JDAȆl᪨~֏۳Øt;BBwD;tH䉓bBcFqB5F.6]*׀ŏ#4DIio[WPN53"GLמ6@џ~8סћ^T#녿w?U{ Bn,:RI^$i^/nfMAQyI$2 7H5 mǬKFl)"t9Z錛,%tͲf_u^9-8˛%gt-/]lmX..d,%a֡oF4 Sϡ(ƛϣKnM}|J_A5 ) 224 {睻n5NhAVQx$adz>KjeZ0z{x80mSHgJ2m\Dvvrz1x钪4~M]x1U$!_Kq 9}u7CC?-a<မ2 y':~,>=HӀ2MMv,kk8f B1[Md!Hl ZH'^6uW_,7dT82ZXls^pu Qp^H]|M1J]|efqohZVH3Io=;rON}+٫U}B/bRwˎ=ivr:YBH>(BH"J=i-Hh'Ϝ6BKܠ c_|'}Ŀ:n(7rw5V[H CC,3R)q=fMclJ!n].䈄 V`5-)tHs@ t)#F4yҐ^1j#侓^+*ǒeA_ (+ca!S=آ @ N&Ml Y1y)gTp޻)B{תo+B8#|V;9]ؠvF3AȀ4=H@-j%Wcy×: 7!\|xv,ܛ|a*\4@AejL UmͥSDL%EQ,UD)9]t:t$.MG i Mt-_j39F @,#"wBLWpQ<r׻R Es0˓dea [> a lOӱ*{Eusĸ|YA^5W`s*g \GҙK<|=.uq&E2R(& u~Tɛ9o>G(c$Hd>Z:4iVbD⫴'ߍF5ŇMsVc&ИrzdUBV UBWxA3rX||qఔ{JC]! $G$ƦV'WyD ["F^ M{K[,$ s$l)%jBP`s$_v ]Lzⶫ ELeQ檣hҎZ;"9,:@@\ej\(jf37H6\q#/?aחbrX$9lJq"晤|}x=\E[J(v}} ghzka6%$z9g [cӚYqi0+ȼF(HuyS(Վtf١P<@׻;hFE˸Z@/h5%y3$H Hf::"KM N0:7GEƑϫ'DmOާ׎߮gc̳s/ZEEDp]Q(_ ިbz]+ϪvɑEVks ޭ+5*bJ(@ (gs>gӼOx<oqOA K,8R1FG @Z$K׌}CEk 9#m^] BEWlj I!$'Y(6ۛ۫©L&0$$"HOooq =xKTN8F1u`L{*sxX=FGh-YxϿn̫B6r>PkkhE/ˇU(gtdqű2CQq ܽqGڏ7# ׋kmjItyުoMMHU׳ͶϙG«ņP0LuB"L;*U!qpp9R+mO+$؋8$!EH"2uܠetzi)!=BK!r@VZ.b_QVhh ,}5~pL'%^ Ʉ<. ]=..Sůl+mS{>AO@(m;hz*;2ME x8|)*}77n MQ0~-H:.Q,ѭD=vՙPT(AD XJijX=!АVEjwSFt==adY=܄N3s켝Z77~2I6WZ3oc?rd\ ,aۈ(n7y$7B,XP5%O iӀSj*adk𱘹'>+O} K'ʷGڕ;}_~{gKC|s\,g#z 3s1њ-cq'eښI10"FKVڴZl2ddba Ba`2X&)L,eS%VVIfdŕM**U[i6TeMd+ZeYe/wtU'RFr əAN3h新y ) kW|^eqaٵߩT>ȦՏfM\V}}>Yִ\̪ sɫ)yZRV*@85ZTVٻ1]C+ 'K}Djf9y6>ZցCXRE?,p7` ۓiUBb1K%Cp`ͨ $|eN{sېS:P%qR^@)i-QŗuD1U~4**~@gP ` 0L߱ )vQHs!XJy"ŝ Erc]a`Ӝt4 nIk:cf$ UPB"H˸`JHYE +E%2 ِx\y$bAr‹4PQdn7JQ0-=$=Y=tFepN).p+棶(m'1^9DaN!S Cs1(7 S^ljFQ,Ro31V(jI{*[sxg>) R֜ 41HkZsӮ72\4B7oRl")*AʢK5)ye&RV \B=9Ak5.mMo΂^/̥E :'vww;p4,6"# R R,4֥۪Q![f٥ B!ckBG1 .h֍8-P<*[QdM&\b鵕Dbϛ,iǃ&W_5z"$)魹3%]4T*T"A V"*OLZ*%x{(Q`UYK]ժ@Mc3^ 7s V*>HS@U责l7@ S=0c;+ 56]eX&Rg)1)O I_G]&:LaO%%&3#ɧ'=9=(~ۼ@g,aU]iѵ<2yN= dJkm1kM# =+lpQ:B0$dN P:ϔ:n*qz{nqӑ wPh3IakX߻Gg2r [2fKH [BKp\EoJ9&Y#’nSzθfnBLxahϐ7V7g1gPN՞gQԎ״[f."竍jVc<I[! h<鐮weUJ1QDed !]}]yO0**qUuwpth\"*gX7v%O O PWc?`SOw ͽ5c @ EV5{pb֢*mU9C4PA("PCCJR1@-6㾐qn6x|+v­+ 梈Xr>"c>\hOO+@ ]#zbz 7/O`*jtAtZ(jL(_s_evb&X^s,8v@޲* z))ծåJ4cX8vC!Q]%|*ҳ. %.0DAD7қXcLoV6oEm8cYJ=4n1}ŵLzq"OE(|]帯[:UVnUK.F#>꯷JJZQ7?s>_{}+%͑dD%:I'ױh]\n{>2LW$tGKm{r}dª,4̅U,x%@|$WPWӠ^/')(+~He%;A[!XU}g@gsJ:o0G"ә b2^µnpVZJ"KM,Pii44,,,M4fifTK,, RVlQMK"DFT6iJilff4SM6l6iiM4ٲ54SM6T|333TdJ|#C=Â2d*Y_٧gB`STq{Yݳ׶^1[KE H܅k7+  >ފu'KMZzOuW=Sbtܦ8L]]mZ(0@`yRQ _ݍ+o4#7l^ I[=;lٗX]mq1MB+>)P1|!^Z2p&H.[Ƭ|ܢM lCBBBH$II$I$/ڱ_be@KSZm+mMmZ- & R"LbP0!$ؓ$@L3zdٺEX}Wik)#^' ͘C'vQV=5;&e e^] @"DUvuns@ӈ$K lM 2^GrPnJ>1RhgkwYXԣ;IdUꛇN QQ:&p~F}'t23|ߥks(]' HZUF83!^a rx~z ^  H=OIz3mlKh/ TJ}aQgΎmoz 3,L" ˄!5GY 7h YKZDqBI0H> $ ܑD `mA#T!”5ujeF&m\HsYVihBE! u4$]Jfaȧ4bfʳZb_d/ô֎m>$qלٞQ%vP H#Ǩn0]YU\QIתu{ߙ'~}??=F#|OMao~W~o/}<"UU}uwv_]wwww]wwu]u@?=uy}翮y<} ff*o>/s:9\nu90j)t=<[3>__y>meׯO~U;s~7;뽟eVt7y$ J41Q Bh%+Y(ԕU&PR (4Ȓ_-A++W5ѿ~?gL|ɘ:,Q󛢒shA6I)JRI$I$4I)JRI$I$ IJRI$I$S4II$I"R*$)$A$IRI$"I$RTI!SU3I$)$ $IRI$)$A$IPI$I$H$5I$I$II-Zw; HV+D_)+d"(Un;o%z7iݵfgv[s2mr;GwwmnNnNI%^bǻ2Rvwww06,y;4i;kwun9fff<ӣ[GMg7vW,be5wZCW$3܁V`ffesonlN˻834Ưw3gu^Q̘Jyz/UI$8ڪRԵ-KRԵ-KRUKRԵ-KRԵ-KRUKR3 RԵ-KRUKRԵ-KRԵ P-KRԵ-KRԵ-KRԵ-KRԵ\ۚJT܋vs%:eD rlEȋH+b.cyT 888 ViG{?u#9'1P8bN[~.]`p͎@u=<35&)`L& bo7Z57yo7yos]yÓi1sLw]͝+ #M4M4SM4L4M4M2444M4M4L4M4M'3MڷwFstçt8satËXVaQ9aX3ETK˜! &yo7u&y`p)'p6CEcY\0wQ444M4M4 M4M444M4h&M4  RUJ3(g<kJ+hhH)@~ NޟpcZݕ^*"툳+Dq݌Er{%vfUT0 {ܻlJ;uvݷA@ \Wq{oU{4RM2VZyh*GF+¡SM5wPP 0s@> !{EE"QRUwQ& )IXD-jmJeDU'I( J KfkVٰ( *햰AT[SA{CM( I7Yp  pP Q!"Q 5$M1z{%2fTcS!bh`OS56zi OB4d44h?TM򇦣OdH%4HM0C&FCa6Ɉ4ѣ hb0`Mh2i@@ѣ@hdH@MSɢmH0ё42h#4!2`0&&ш0&OU% &(zm2M4 @ JH@4!5O55H@Gz R"#B2FTlړQA bKСʪiMQ$d~λI99-j4q|_dª.(ۻfG~7q㲧 q*7Z};2m20LQ3XdAQ(AXVIԂء"2 udthqٖ֛;$3}ROR93K۵){2b=JJ^U4k, SWR UZ-y˧CĖɆf w@xunwzM?/c&$<`O LuԆtfIbMmV{%+=Y4Rdr@]HEAiI:f$"*SC{je٩ͻbVL2ɇئ Q!HA7wv17u le #`Ht~Ssx[vv<+h61&o+ڶgl;~.mݶ61Mس“+ %ŋ) HPO!8z\U^8`wU7;4UD(wrH!$̈́HlI vsxxVN:z&uT9IlKW v̙LTDJrp̪ZSK`f*0Ҕ-4U7mmrG6f|֣\0sߔk_*m|>U*“Hv'iXiTܣ:Ηw$ $̍V<+2D-Iy  pjPr!>4ip$q!KHh4uiWֳ^72pȹqs7r.KW43+aNS0){LP0OUUUU_g'VO~m˓ <^{x\$RG^I)ezeHssaϚ$(2evy)jJ%2C lwibQEFZʚ f֧7!u(91\LX=Ai\rWnWox;dɒ*<$ZCvgܩnrMcM;(JQvUd5ScR%hOIOg켤vtc(Izo17Q WaSs.:^g ΫιKu(ʒ>JBҤ*8X8O6=-=lN/\MR)JUEH}O .:fj9UI.FkqHպSwsL{ \eW U>\y|ϻ m];:|=WvTXz}Vd9UrRw5W-w}SؙSfMqGuޤsF>&%8zSƖٕŊi Z?PaJqJ92{/hUێ^2{l]yzu9ɗ&C|3ǔer&'KFS/2w)NLf#1i'N9rVw8 CΜzeԩvrhƗWwg&oڊ6UQ_+jNg=0C@K& Cqkʌ88K7mf9brrOK$D))7sR, ځ &3LSMhhKIkX>߷%锞}%PK osw~mɷ9wvs9gi\^6ۿɶʝ_^a"[8*]Fft|}<=߯NJx<8v8UUWZU^ZWGWuk^yz)t|^/o3o?wtwϱ_ O:˿j+",HЉ]=yc~vKz#;Me=]GijGl8¿HIzTj,阞YMlϫL #5Z˭N4WVR2"RU*UwM_]z;zzmF$|-mjC߳܄4n˻Wf\INNEXXbE7@*U SݜJWbSRc~5̈vw7w)(yMo/,L2 SyňS<>>ft9|Q$q+D|/w~j߃G4W8RƔKֱ2'.,uJ!p&U/΄%LBȒJQT.D }~oYw0Yod il~2z^4xSXYG rptQDqJf1 BBCđe`uba`fB) v3Jknݻv]vۗnݻv]vq&Ef2ÿ| "wd }g3 v8}xDoǕTOW&kjV 칍Z)a͘l̙0\nQްHuJ =\Ta!' du-Dڍ4q[8Sb 4û,ʊR”L&PIIw)Dqgzg 0JI3Bz'Ɏ搔9O( 꺨cl&II&*yB=^p0V$ZEHGmܐZ`Czl1TxSCzqnd1a!:Y$  G^o°<8{=:t`nQ e;m5B5nurM_.GnW=kkk{-6eiljTFVɺ?ݩF>ˇ-Oe){ҧ{nӚxgɃkV/&2]jݜeuU#~/Ņ(1?$G:6i6,ͪrA1ziT lgƑA1%:PA0Fa)C:y:FD27(6m[sjl`!6V58KqE&:tE*JqL,ijm!T"Ҥr(w*+Bfґ$gRd0@ LXd5TA`V3500lra0ER~-J)Yrm3D2|Tky.6lmC:Ea͍$v AZ>S 5GR 8Yax̞J*!'8 4#K](&RXCPi-EG^qdRa, ,:(xF dXhKZ, &D1Qj${2#a!҃A؝]2O8&sH T0vQ)Yƹ^CbD#u*`0H s @EQ!R4m 6Y $;tppͥ4k;@ϸa#IEa!= $m4 JJ놧:y ll⚧Z,,D ;_2Ĉ; aBT7lˈrXnUKCpe֕|8Siqt@\bLœD*"YI"cLUkmmգFo"_ė$(%"KV$bK/dI~{ҥOY^Sz잟g._:i.^μqqí9iQj|p}a:"ILmKFJOS1f1f {JW):[IUU݁D00 42IL: Ye}7ȝXd!*c6)`3x2AffHRjfaZc15ze5CBkTn8LdhBM.Vf ֦ "hXY8PDdվѓ'DK1aar`ݵ$56Zv2J\'L\95;^#`c{o`焺CӎC6YA+y1Eɍ} $Fr|NQ@`XH`x'!dH`[VMC(hpƢƝ:FʲgA悵+,ks‡kcNeDIA#erz [CsPnPLa)vR9$T9wnʹUKVznv7vYp0[W*d"),;XJjZJDU2}z:],­T9TY@w׆j[ئf= 'q$ٛہčPTr"F(Jn Q9Vɴm]vѲ;ĝ80DC,9G504DU4-c^۱x v.dYU%U^ u:SM5Jjbjĉbȑs2/ ӮUTq*suLFs:~YO 5UjzD^r$on]NևÊv]\\DcH1jV57wQKQLAR&hv0Rkx睑 /j .q R*_:߮$&ŽklsHS6A:/O+υm,ǮR0荍yhTfj5vf4bcF-x3jCpY1Lnc srQkf\G%=fr[ʔƛc3\g3E:*@ :kt"LUURm ak?6_!Lyams>P@R8rN;„T4i<@Kc\608 Ǽ &H|XE{ tiF,a$,'AI-DыIJU<Zk%={ aX{UA3~YUSN۾9zI ;RfHf挮)Ѧgz&( D+X*OZJة֕XM[,u=aKSJUU\02:d%q=ai9Un*򶨚IiiITEr N$*u Z芨"j|nX343Uh:f<jif~J-nuq^*&%z^ uL#Ow1 g}ٹT܍ި[Cl!TA+湱榩!&f]&J>a/ȷ̷}n3hEe2tkۥ;95u4U$:-XÈ #.٬qa*z(u&BT٭⒚ @( ( 7X$Xt>*.:͜Is,=0һp AI* LY9%gCtehz&Aexϗ9dHi&T5#7& mfr wM40LJxkSE 1ə ԟ6:㜣Mc0MkncC{CiF >pG[ 6MaeIaF\59i>n{rb[؄a}I=t{s"ENZU(])FrQӆ~bHoF;MAZqd&k%'M现A4*Un"D]]rߞs,)!yIGL֝uʺW|F&s֘n&x3Q ?ODT O#]LӮ+̩˞tSwo8w=Fi,``h6LY;Ɨ fc3Ke$k3I=A z7;+_Zsx!0O1fyMQ>>{[]:2h }jcY6ᥚ9axr A5 sO c*èɓ$̤- ӰD)UWx!B|ffe$gLy/Ik⒤( Ź\cTa_Z#XThw6cSKM^vm"EbWlu"FNץIvBxumfz$a" `5a,6&< #61J*+[I1e馤r3ctthf{ً F+U+˿~TwWt:"Geކqyݬ^Fj}1|C4CA+߷! ?3zfwLMEU /׷<k&BG|\K|_jձW]76m33C&-$ ͕ HanۍLVITj-e#^1lA%rhs7qKիԷdӏO"/;8v)}2}eݖZvm”7X̜( RS\)m6Z'8 yC@`OR(-I`5Exx4$)3l`aHHB,&ԒK}>pK[ FL(a5-1H٪fLOgf0XK=$khTcI1$i0bLM !!!7WtEL`-%x%3 5bQo܇L1h%~Alއ`4+w]ܺcelaiI݉\C 7|]_hd`Ǵ{c'נh@g&?}3寋/vC"ODi0[;{s+*ʩQ=sn rN)Hb++ MMDرB5Rm2&D:R|4aI~,^Q[e@4i1TT/R27*\xsy`(&y-Q'ܜD{yZܒH0x=y%8sc4y42eRs/³[E-sm84W*&%7;W=*@«" jB0e3nbMDyHMqQj=jܒ>ld? )i,%@%<+Ǡ9*N8XpaEdcPm'dYLPnL !L!XHQ{xkXf"9S3R%jb4;2ml:zjb6<&HBpYUN^l2'kU<޶xhVl 0麏 KR4SI-h[\7RaPgղ)gZRz48Y vhnR08Kub?i;)&&^R1%vijyM 0)M jp+u sxFX1M ͎ΣͳG ^{h;0Ì.q{8- <2tժR)mU*)&\d[jf\aiLу,,][78qm^7*e&NĦ͎6pz;5VLOrXH66>Z3lZ0V1KJK_͍&~hfL,eC,B$%e3,fdL,$IςObYff%&B5f*IEkjYj-eKkEfZyˬ^g^g9uҪ;pVi=PuwM6%Qtl32ieMTL¦PZťX c [D3*Mk(f"V %fYS Ȓ4LIi]*-R$EG?/B:0J(S3L**kՔ-=dUTOOR HwOUUfMQ]QyxwfvffW adayލ&ꑕErJ\z3330zuy;Z?qwU pG̈́om[T3fSZm5bՕKQRYUR~OޥiM%%%IIEl&l[R0,)ڔʴR>бT_BRl3 )B@̐ C2B$5[Zۚ"GN赘bwW>*VLٜrjZe~^U*IcwM#T$芗/~J""K⥲fs?Qto#O2Nr('(O S8CJ&C CeQ[_z]K|d΍vEfMk+{ܷDެ(?!R6f!effG>괶Gp,ww؈t 300!2լkrOxgWL%97UF" "w2&3:cyJY8 !sOS%maB"nɹWۗO'Z.w:Ss^R]&VY_WsϺa;ˣSpv-/&Z4xpt݆TZvlx6jÕ)5eɗ-6aͬe2c^{͇t#A5nչiZ?/#~IFO^du5v%?k>aБzXJx} 1s}G _Ͻ9nƱF'֏vk>S٣['4GdVE{8n7pnO4Օa)LPӴW*ڧX[ns0::}#z/J=On'.nTW{/|{]x:z}D|$pyz=$KJo>U]s] ϖSa^o)d_.3^O G;bz]{>oc>zy4`^Ei],3*L= }I>4*Oq?AR>tGOkW9"VOH2$8Ka/ /.'mI>-L'$Ç+A&}lIncYG~Z?2O>`yTOvS0|M/ |C+Wԭ$R>&^R{C@<"KC~ţc'g7||KC\K||GQ^zg%zbM8):Ҫ;ۺ#{@% hO>M_(d^\S]֣* t>)zlOGct0z'"=zj=_{g~ݏxsaSvL u&7<τ=sxJ^IaY; 7x?Z㿗mR>Uxbs}Cޙ~aOoPyqBK+GVN?$BKK?x٩_?,7VNr58Dkh&Ngx5;wR$N 8c1n%ő%$q)Jx9 .Ȏ2+6֪|[7lbRզZŖlpj(ݨq,2A0bU`EZ75iry͍&Rږ踖hj`PفrL040т8 A:5L B820dc5i r.`%s]cCh唷-m5"GnRoG~*zK^1sMeJCd-2bPK%U* aK"ʌ$JeTYC (,X̥0L!eDm׎:-.)Wsw-ocrxjIO)nvS= ą\g ֫ n;霎NJ+UIs6c]RuE* ; YԋC'%gTFQr8}RI4YlkTv2k&ҧd[BkR㱅Vԗc3 04r[+ӈ{i0 XIJU}GմޣΦ}rKw'|EYp bbp=oK*8搐Q{4ܨhsX냝&N2\o\FUo4dO96nrsr(8TKӛQ)G06dWYg:aQ %M&F23HPT(eb,L@+**ȬJFVIdc%%AbLUVD$beV1 a4ȖK*DJI,ieciR4JTѓFѵQ2JTJMRYbe5E)iSi6,͓l1iZjlRi6YM,dl$[)dSeel4QMMm1m̩֬15Z6iKEI-i)2Ė%6d&k#ͦFi ͳ QfIME2c,S SBRA%5,YiK#6TVT&ImRfjYm-ffRi4ImcTeTIe$%6R&fSeR[)I$IRRMK)df$I2$-"l0ե4mYiYRUP3JĒQ!I6lHhڙe4SI43C %3K)N)s,JXKHFmw8,JQBeRmԑJ[S;ӊ0^gyy>-S-{."lš) u=8VjQN4`uKRi;s J2gՍ%8I%fywu)GrݵtL9vcC;ѹ9c),oW;)Etط=5)QfLҘ_lh˗ 5yuftSUYQ[Rtz7qwͦTƫk ['\(_e){٬ճC9SVQF-#sلO"EGK0vjHGh̢/>!%rI,W9Zs~-ŲyǑ\_z^EҔjRFƝgGIy>aIvl:g||>-a)~CV\Io⽲2parI/^KǺ}z:(yr U?^Uj^fJ:T>g+Qu<;McH0iՉrRAi_E,z#{g}*r${<53<>q}ϋp#eF>CGv}'p~'w?Qp|d}f41͓0׮j;52~/}y8|^o>H*Y2_;"K,}~gT*_YC]fub5s2EIG qo@50v{>vw)vQc%pV=U=ǃxô'O~* w)xAρ>* <=P|i0tʦ5hqJ}xCwtKwzO'7s_Cȅ^UXpx>:DtWk38("\0}flmZ86q jaF1 De:ٛh$eT0QMh pOjMj~=4il+UB N~"@. ƒR[(Xw|x>.sԇuIZ@cɗpG̥FҚfןխu!*M 8mlsWc K=ξD`;K)LS{UDW^!t9Jv](Nռ()#>SGzR/6<C/\hŒ/vJde}:Si&թjeF>Pccay;qw6\9?)+,ĜƢKOX7M/>dm8\t{޷W\ .M*ϵ%_'E<*<I#*OoΤDE`2`&QCC윾tE:0b_scu9|.o]򸞉p?-ryh6}旹=!.)>*U{~Z=_Cg'JKXUOh(ki iO-MJ_)7:e%Sf "B2QS971x4KRd } >Qz|-Nv ] z-P|]Z2$mmjVeUYXʘ1XXԻc6јNTsu6= )&]BL2q&ӱMJ(S'Ki&a5DىNn$8E6bCxNNhR'^i$2:I$٥WvVpӄHݽyRlTgCٌx/;s]AF_k"ldBbH@,!K(hZlJAN,QѢԙ"Qcx1xM.M5':s_8?9ksAG9ښ=x1py DY0y쮧c?? *eFS[|T̶A"9Qҹ'3ccXM$12O[{60#Io+JܜqG :SXF )M?jhe-&MVv+"Cv'#jj]}%ufC->ߡԥh-b2]VSʪ4rx|uբSbDxTؒ|c9TLZC$(z4iK)RW>,ܦ`lnɱӉr,T>̹^-N4ҫ=[QK!gMH"'l}ở{/.O n~m4ݶ41Y6:ۑM cZeG>2v6;w4sۿB:ΎOii~LD$j$GKi5;dzуRQO{+XF<2>qy%-9MίK/{oD }3gQ4z'W<_^?zMi4?Q{~vTU@0>NI?/S0߉(:;E=e_~_G)b12QC,rmɫՂM+ LK˸p؉g赞bRPNe>p8BWJP:7>I;1W~[u)c2b0ch٦-JȖ1V()9JLJYS+3 L1Pf-3QY4͢HfL{V%BI:3v=Qgk~ ㆋe>%hwf}W*]=Xz+ex0uar>I=O{uGepcWst] c J_ܴhCaatJޥ6}5^w]NuR[>76z1?U0{Iix}wb+Xc>Ν/#ע)u~£6jq"|o92"x^a\6?9Ӧ<8&~U}kIK~.Uzs]~狃r4pӛsr{ S1Yb[RI VU+Ti6M)ъC3%)a-i5ffddd,1!IVSVdI5QfcXJVZԱYj*ikfMM6 l4AhelLcL!)D,%Ij5mFՓ[Y-RSFb2fTF6*i4eX}C[ ǥz\칻=_lö5|5|_7ӿrG[Z%-ڭDQ%?lI~L0{1 2%*⛩4J`n ρfm822lhV*sЮKjNs < Oa(vuYxlVs8b.L")Je_Wo#4h[fno7 m&t6lŢhvVGY6d%$CO>۷۱6J<emJ#+ȳ%$GKhRaRLdDCh0'<Y[ %L~:.0psGM$MԛFWW)|@Q)Rp9H9aJ0ʞ2&9.6=\<ۛ4e)~24=i9p=%%(Ns`߿J79ks.MJ}5arB,ݖeR*Iz-b7XoOЕ>ʻ>Yj˦:ۖdg<16vjܷ6p>Q5Pp`,>գ覨*='n0ǦG[WSsˇ[NFρ{+G-I)螗ix0}ÈE2>|ꭣ[^4U%\TSjO{'1x\apbMŌ&ac A"0PFEF ad,HP2ZbmX6XxL$ɒY&)&\zNpDL0ްS_]bYVɵE6L[b"c*X 16lmK&֙dV,fV1mw}4e{_&ʈw>y| t|w|[ DR!L,Zls7Dy@>{M[ʎuC|ܫ9ԣ,rz]kQvmi4ܛʚlbMX7kdM!*rnj%%Msw6<lj#MHѱׅЪzȩa٣ &i0QֹMm; Z ͛?D)Z{͏L#gw;!ɡ}[W{*>f59\iZ[Id֊KLT%]7,he;K!L BRɴQ$ .2okYK0aL%NvŒB% =@2PA2SNlla┌JQ8JAF FÃe,ercS 1eYDZ*H.Cft`ÃBA00L%00B#4@D 0P(0A(;J`2:)9)- YKh a\X6X-0hbr2(!% R0@EUBXUqH9a=42թplZE-k3;#=穻| 6[3(ݢTw8pVE2YL,i^B[i(ʦL&2nB8 eikF\el̏8٣`4R!Nmeϗ cb7ir2Q%E^LZ )`fea޼)u QQ $CSd"J9ؘw͔ vq\4;Bo$WE5.&E;oЫ44k) &Dֵ%fdV5&/C#0̥s-FUl)e^9@U420Z5hK UXݱɎNdɻ}7Dp8ڔVV7!1 \If]4b&ELbeL(ʑՈz-a-q6||hwN^؞lSTr8F8lfe..Os-!& y" QHxeCE2d^ky%UּKSM&ѵaƓEymnKFr7u+ZYThYe akf b6jhErcf0j-TNf GeXE2`7 M"/A1ҏQj'} nwwO(;!pQLmT$~ݷ)Ej}qmˇóg,+ly2re)VynbCv{i^泆-N5i;_.HȦ$=/G3}_|yO6iu|I}͏cyݷ_ 9>l>{5~GbY-YDZ,T٥hƕ%SSQ RyHJi0E#RSlIsF\kRR#Uc+-66c5jdiKLS/vwl;SYb完)88aAd K!Ky&KUPR-E0ٹƫbJ3)zαnԿ)2L٣:kW0mU['ySTۛwӂKx]hڪwk佦^)~klfRww;ڕKc8{o_氜.IU>]INU2CoO8"]ѣk辳?2%zdIg/`h__WwlbXYV U_ߙۊgҒ+iϞ$Dct{_GO +$IY^uWpw/Q/.A|9I"w TSʾ͇4?{t:pG2|[>O5+Ad j$, 3:;Q/}'ڧ_s,Z0{?-ϸ}5Z%O?R**UTVO=kQ2?ajarQ^Jz/ڧd=%.JIږ`w98\xF=-f +X2ȬK_s{I힪_BN[^tlI|f>mjŽZ7x⴬tW7/5ĖSuk~g|jcng[[IoU)ԏ$2JL,$j,'5ru8PwF2O̻Cbډ!LR_'.uHiݗʛTLZ$]D= Id\ñGk{q&eA'w{G]Vɨؠm-F a a1d Kp`0F AFe5&FcL2Xkn[EKɆ??Qc>ƎS_@YCE~t);D Wn4.LkRuggt~n^=F6{'='ţ9UPl` (HJR46iSvd, !D(X" Gc;QY,{33eeThJc # $ђhP&"& a1rZɌȒwt%螗%Vtx+{8xxWinb0Ōy9<.oMJ'8dV6x\1ѣe(M"f-c&1mZicjS &R%T+C8塃C{>eiL?c{I'ybw&TˣNg?!pF7:5kpw #ʬ1h-ɬnR-ږʫhpďFիaZk 4Ѭ8 iV/lOJUIz"K K:TCnnùOwlO=OD=g -'=MUG-n ) Pq(BFZaBdeH=1%KU2"0ʲlƕIjcQ"&1 &&4[᧑Ny=yJ:>R繴\# [ >&)›(JhU%/SC1SG)uk> .f,:ӳh-4czcU/1b\EŦtΕ x BK"K  ѽ~Dow5f"K[գ¦k~F0w ] YQݤ`Ιw40`ШʘY/ m+F)[fM.J2QJmomWFzh rl&qiRaᔳZ2뱱mlr l_eɓʕn5-Ġp}lC:R4BmbT1a輱͓,Lk#ŲВKYMKo7m Ite~꾚*c,co;?Ycr4D7~ؒ=%pƚ8wP}O2jRѱ2e(2FHO°qRK6Tm7x9F8[FG"KIHcbYItC,BgTHaO6sk]W;Ҕ5&@`(L{کf` jڲݩ)*lԒfJRIK)eSPJ3)$iT̉hJRT3)e2L-YIAQafLęIDI@e16L!&1d3)$fQJheIHmaS`09֌NmKÆ86m:O^:hC-Ol$ãĤ6W9Mҙ /:X-f,֨2=/{oX۳2Է6y>xjOӏGcDVK)0$zҾɷ6aWO$X'f_;ܺ0CuEOzQR{}5)W=a4vGtorG8=Nk>ݻȤ^iʒrٳw(FRc#>EOI|ȓ*2Zj&%: vǾK޽L}>ѻ|c}bKoi՞NͧcFI)8coګ,98 9dj[=&XZxojཝ\[/͉C-59&#Ct4:a_,,MnnP8?4iz}Ѣda~u7=nNtWڊ[='Nma ,k&Ԇڢbl='1i:򌟬2SsfUEM+Em9NU*TDjҧ6)-FY:ìp87ViL/ RQRS nђޖSDB]C b5-{a'E'Ca5)Y((ҚsbfQYyaЭ?C'uO0i'd;ts/юI5vyysgPdo'jJ)-K3!zs27a(E3r4| #&1VgEEkZlue9p d*Nĸni';{W{B_9U"W! ;4/!]]<9~JᅥMeƿMhֱ-qMQ+%5۟R-/o?JuStTTm/a~tY\lFʊE87e mT^=n0QAV">[Y)'JJ+k*='%ళ> L}8Q DzriXf˄IOl m!,8hPB@@ĦH?)e+MV(FR DKURk(&TKM ,V*),YeT5JjA$Jj$ ,Ogۻۛ33-`߾фTR!nNme6[Wգ5[c*7]ι2@Zē>;4L̻JU&%13:ΘH:!\À[uEƔBbefF D8 \ӊUf"mc2e 25]K0shݣS)ņX#(1A0oB5 ARfJaEқW3):Sޔ*SfEIF~_mk^<#5ki_ L+1mqiCJ?tĦK$46d62&fe!aP5UJeJO"Iwyv)iGI5arm)Y3vѨRL.\M\.뛻se&;W8ۛݬ vӎ#m1%30̳uCKqėtsu 0+1AY&SY?&G}w{b&p] *]z.]ѕ LLWܲ:ѺeosΚk] U$BMR%;z`{C;\7i4hRN|Vd[?SE?z`qn֭]mKWvov=sxs80t­4r$#ݎ"Ky raɋ_T|@\-û | <xrNHbQgDŕVQ_*st 8?X]3@9| fO~"48b1+~#ěܮƏNʕjL̕Ė;)߅Ͼ|A9o~IdCnheӆ&2ӯ]"θP}bE?40LxɇM>}1-ت*S*TO#gwC}1=([L`OX"7Zw$`;9L:dq2cXMYM.wwwwq;y4PEqt A0ѦKcn,A23!F81 1Y8$YM?kGJVCbզ}om >幌3r鮤ܚ.J޷Oq[♶JÍM,~4nUaZ8ޚ˘}@OG |!.C  .U 7۪&\Qwt@jp k;Ͳ#U&G{lؑœPHdv "?%5כu-wHHg.$Dug}X-&%\#(Jbd%;] c`wxBz J$֋nDٙ|򭙛kh nCL^ !,dD" ~z fn35U[" ww H$PGߝk%Gn[i3W*= 5|o6¦ouHO!ZӖJ>/vՙ#VeݻvZW"{~ô*,Ӝt;,eΧ|rnT2ˡ݆~YJۊa;}F<Ӕi9ɍsOg sG(O>Yw,R+ɐpWm4p_#Sm]x .C agm"J Չʛ6ɛL86Z|-$u{'QUjt_km{lpU%~Sѓ⬎tѓJj#4,'2f-%@QZcrrfǃ;7Ά_ۨU>Ur6ʼ+n 'Yn:죷mP2Yçb!/:6|cΏeG%beM ~N>Jw9|}Ӈ;ͧФ71:m2Jhj+>ʘӇɆݫIr/v74kO.?o'm7E5)GSu5顕L^ogTIILJݙ JԬb6 jVi{^3f,iL7$kGÕ},ϞrpY;F .nK%CĒR|}QqcMc;a q>ŝ  d:L2 y4adx,c++ᣫK]X%y=6kѢF3QSdOLo AdV*8GE]#60BIM5y"A;")0 ՙYI t;t:EX*lb(bK^0d4 r&m! P$&C̯i?LiYy2rmdȥXRş?")eNȮ_j{k6N]ᩥaowKl*2aegyc 'N>]'poUJ&#sUi99H}M,0{ZpbSUdҀlFiQsdD)쬔ZxT8o'6/}72o|YGedd4΋[=g,sl^&NH'y#quuM9-)#Lȩ2<*!qB9=%}_|qZ>KvCsPc}u+wl: QPR/}ה[0j`( IQlv:֎?"jZAHl& xU^1?[㷖Lݻeoy{~=X᝖=@ETi>pȎSUUUt6n turZY)ivJȞ$4V,ÅNZĮ5ke_j2Ue-S]2Q%3f--$4PGhcUU#, f`æb)2&ylL n+]t+b3#N 9QaH{%⒅]jsU>/oyK嶻Zf/ ˜sץ#cRLE~p⅚Jۂj1TWm6گk~(mWcZ *Q+Pd:192B@V;} ]S"̉7}WzCIii"zZ2lM4t{_ATksI52hҔҤɨUvxՍg$6Z/æi&ΪPF)B11fl{şN -O.5P`"ؘI.5qN?T1:iD}SqWUaQ#WJwe\%69@Q9*ĈCEW㉴VLeL7Wz*}==L }btcͺT0Issm|[ۈ6 XX-|d#o>e[~zupvW_Id}%ɪ&[ۋ UmV=RNT|}jtX]eMfij&iYLbcgjJ,e%bSK5 ٘(M(4kcBiTQXT-ŶC8'<٩ѝdPXݼGp)$kad%K.+.oHGl-׿15[і٭{I⭷r)I$)&QI5 ғ2,YlVϏ$S±,c)q^(Yg '~+1A=UԦpW^7Is}MzDW{&X[;K:kSqSV1x_ĨdLZd+Ut֗5- 0N)^C: M*J+8CJL?BNi>A#$!! gGQ(K{Ix3Ity"'ҒP@;G\\N8h33 -?~FR! -jrc&m< B䄵^LB1؟['maNj>})]ǧߖkCV:-}h 7T&g}t”mr(?XR4;,>ѐ>~6uYKZ4ͳdM^ Lg> 3 203Bcc|]-go=D78C+$52[[:^ݞG>`q'JNO{|o &3c!"oKVVY色OI:t[6z]:srՍ9ڦ hŖ0xmA3+6~vuߌ2ѿM 7MvlFn;WLݎ3-tv<.|nT'ٓl-F8(0D_eɟ(}꺶bT =@X _Amę[~5pz^95/&p](ikٲx2%]ۅB[{ ľvKY}v +ڷYnhC'm}i>φW+]JWRԮ%w]( G |IL ޡ~_-N8U=ɿ7y'g˝|ᅄLd ng e6:wK<^N\xp" 9h2F6ᰜ6=v`b\$R!<8L , vcS7ʡ21A,zm]x 1P^.9K4Z%8hSjJXiii#6#(0RZ٪m*5[C,@6lٰim* DF[mͭrmruثN~NQr .5;JcӑV6ySf%Q_!PdIm5U`^!$!hI5,jMETV%Q1|2Q9g!kZZi 2,L0*AliTnI9JFVBŲc2-ZT$Vde-ILYW$rs\e$he&R2K\WLHR*[(r45Zmeʸq5鶶mxũej҆FF)K6k&kZTi3[[Imlj*MX6a!S2,F+ # ٴt۔̴d3ZYe%DeR1eb#R(2SfZLVYAkM5Zj֖6hJemMkS)UiM2Ҕ&6+6iZEkYBfRF%S1bfT"%ia':JVT% H+S)6LaUSމB1IB‰,ʥX2f$12T'IẌYaY b-[mS%Q4fkV44`QVb9\ smMM.#rrU2ZV(ѵ*JWDLDbq'lU?nHK)I+RVDe*Q(ȡdU”J6ʒbSSZTTSi֬ji`V5ZaIe2¨-ŭRZPʔMEmmQk##!0Q %ekK,MZmՐ`ȱLB,XFVfi6"al3Yei6MiYB«665%f%fi4Z$e*f՛jL(ʳ*1`UT@%ĘU+VɨC,D Jʠ41SITc_;]E/83U/o>|YC5;+2 |:w/+ (؁);4Oɔg?7o?)a-`ǂ_OFW&L" VEUə7\Δu)<)Egrb+xkGms;t8soN9Gj~=Gx8t58c/4eƎ^*׈}xH-Ed-F~DC,uDk",vgܡ`SEdQ]g!d8H' L_o&7 tW I&賳ೣ}is44q%hLxN~8x~G ^ 1jxM׷/M#Jն/SY<>>^3k[{ax Nܲy;V~[mQvd|^妚l:cG%uaxW/g|^4r6z85>}^Vwppt8<<>^ZC* %Uj(5|1X^ 'r*2538>╜f.i7bQd@FQP*B g 0ڱ>I\sgvTnfWATu3.g 7b+Y/YCJZPmZ JqS(̆793;v퀹+\s7G6UHGIWP[RLpQ </AcwL#I4qhB,l*Ӷ0ɆD$h% F2q\bTP5ͳ:RTm^oH9sN )ii#&WpFj(H ҆ ?: ѺT1ha-Pܱ' fԬaM8p#fír+a*ԅd&'T8w1 rf#Y:J$.!1PέP4Y+I) 8؂y8“1GBi:۞9aF$@nl_sj[/ŵBqQ&-V(9k7poC#QIJc:hjNBr 4tCbp֫K1:EYi9iE;8,aeEɰv8m Uq5TauS( RFWF[KTscd]7Q%/3qUC):ڌ.T94pwRqɜFP n> ,}P4Rf4n! (۪fiweU;Liy$~*>(GrUԪ>T;RcJZ@F@D@P02(ÃyymC^P 3/ (PS[>DD0$āaq ā S:㎙\2㊵k%%ՔY t೤,Y)]8uD0F@l)**N8j⭒I-AenJམJm_ ?N p:GGu)4둌'CU4Pjfbm`=0Qܐ< A,Tk&01$S1L2y"A:p BAC*q6- HHSv""HdF4S *QV2d\<fv{gf @4N zgfȕ .vvu̎I9{Bp)z#jpoI'4b@tʂ(LUgvԇ*i&V\g=o FI1-%ia:1slFb-R$ɄmnV*̤Nf@S3\fLe61 dG#H`Kȴy@̡jI!H%@FkC Ƞ洭V%(Fzzf N%P$CI\F$ߙV~\GZN[$/_;߳cq\ٱV^j!LfhHC)iXv7-:I(iL@_a$Ygo56f6S= JPXc\6TMTAAYU-Ť-_q"6tS&&+pE0D(  .&gM] l%*Rl푥_s7L6w4ət^ʑ؁<IG4y o=oE@@ "gttj1:HtkR!j BAB wM!zx,G'q ,zPɪo%1LDc/3'Mt{ff;AJDCg0 Lj*dw[cԺiō l9 {`ߏO BP i<㸲LtYYQf,f&2QbִWhro[H֊֊+ Y5.~^d[&/ggyG$)m)i lFY"2!M bcllaf[bU[LT24LJjun ,8 $؞zE|@/wSU. PI53(_7݇Y{On;U˿CgQZxkJȾkޙ2'.z2762%$ /d$I LU 寤aȧ%wR$ Λ>a'嘱Jͫ#M1bVgyܤ:0I-VTj֙8O}?a!I}a,{씻TC=vHM.OZʻ xώМucoU4ny͏902ga`};Ұx&G2RAHp.[qx;),1مgWM\I!vA*ƒ Y6[ v0$$<;W搭:J"IjI'x<Ϩ˲srf]d!āg8ģ}ȩ2Yѵ}z9ό̐NOj$;ԧYVR&xjz$ogS9"2gjh$b[Ar/dPUv]̴ꉆQHf0t`KAFVi|ID;'2g{}Z!-H2g2;p.dQ $f4JoXz=k/χbt sL/HE *:Bő| MoϟRRpX/}X-uBp|o-Bq陾o4:6; I#&bXvfl[JV_>Y燍IX7tim;׷غ+wtPdX*@zۤE鋠=BKZ#LDfTIHfBYZ>4iw7)AG5xmqOa5GxIea.{ԘvpQ5@a7-g]؏*oړIСQ,_~,WfffL$=1q6)Gʎ'l[c2a2xɲ`J0GۇҔ Jj"B]9PuO$Ǎ)ɻy ~mfIXddyGwe2\E5)ׅGfq!gQpQَ1L#1Ɖ"Y8믯ndrI;`x7H=i}%$6hj9j\Ok-;$Y=Tqa嬹7=5k1EE`Nұ}׷`iK> -PcX}&=O<9W3ЙmQHbA+獯?Œ2,rYj-E_^z뷯Z#""<ْT2K{ Z2^F#jj]\Ub4VS'd `,o+d~AfCX-9"ؖdʋR$șKǞzd:Y&M)'Z,`΀2Ų_8㿋:I|%kVU[rI\w]ܷܦhl>_nvCFvG<^,x|qjc_xdxJԲ[DIIs!N^ًvsR +d%>ty4![!)n.(Ԁ`IZ:>*&`o|kkbvjk*4YTk mtAI$FĘ?+J9iw9q6ׇy-{˝O &Kg$~%/0rhFq}$f>\h]YlAt'KƳޣT?8=!I svP壓߉ dkmݬ\m;y\h/v^^I=㪮 11epV%ZaX/Z1oFmlto=v^L&8FP @ C(8qr $f t8ꃼ3304Ѱ⤓LU"*]R! A #h <vֹ,-d"ē FsXb{/ ]!9 @UNY;y|\ɛ:,wɝ)uxˋy @q;72fdu8KJ::Q\ع1|3uBs2:  ;oIy&muHݤ7vfBPpxb=hK|$!9m2iV!z0 l<3Z77+ *)d'qN1y)_^Wjrս:{010׍{NQP*{KO,sjݩVlF%[\)4/& Ih8$݂>qphh*"D&%7:B2b/!$ǙX7Kdobk,Meza۷}aN9!!$4]!!A]a9Rd;9%2.&uϿ>? ܆kO^8p@PtW|͔lfLG,(bZn3+H6Ѓ#b~yH>FWY-cE>|{ .g#%p}fO04 "P:~-2XZe8uAܬaҌE9uN $w)J\Rg/Ta`0<QR :1h+릩vlAd0a|%TWƸr}4u  (ER兤/V145&6 8+GLxt`KE,.b#R6k݀K9pe HKo`KZ%8iNت[83V7NsǷĕI驀7R C_^iܞeVĝ̜|e"L(Nldt\S:isݭGgOaّ[S3T*^Lfm0L:mݶmMqUtwY٦p w["[RQs~:Q<]!.RAWP&>:iIS##^xUO燆S2![?Hp;y?lgDl%)B/ o姎Hz=BbE\9 "/i@*#?H\Q*SWzAɍӞF |F$oWcPPxSs܄JzLru~D~Oc+;lYUUǎ8l;=qiPJw~x;zAhk .l~>??0ِtW{;@+̤H2ac'ٞ>be5<ǹĉ\!syLN3V5rwBáh01F#:KjI42@(J0zε.XT!wI|9熦DWMǯ]>VN< vл CN-j-uFT< $CBBP SLr<<@~xzg(^r&O_~fn>CF46㠁 /{͵;>5d#Lme.ok2 sԒZjo"e,:qgWRKmFٌžw6kfߩOE)G?6𜶳ONʦ ($~8,q ,fa0,cҁ==>Ş9ɃGJ 6 79tcմV4w6Nvypӣ[nWwW =n{]^6蜝\+ã;5t6V\0ja0°c<02,V[I0 򟺞GŔxhJ@|(u+F6K g,_ (0>jO0rLx1$9˖ʪsp疜t(7:E/iK6ӣ 9mY~ 8PH$ёOrù3ùŖ/0p,$(_D1a)N[M~;1ʏG>IE*,ZQg)uuS<:=G.dXQ'܅nY$9cIPAmn"9#R W1Si@mVGvv1tDzFN,剐<+{6a]N<4gHOYmR.,n(f9S!cqpw8sc;4͜ˆ>r4 Kz}$тeiG8rr&/q\TLyq2A˰8iČ|$rJN8Jc"%I"A%kfPMXPЙ`dL0c1%,Iļb^fdNsHqGQaixx|93ɦ:ɢ㳫y'<2dvycNoM97瑅u&=8>M*K CdG(?GrCшdp|0ŇͷnwV9WVOU}X⍂ bH焯K_ >G;?/5D$2܁k٢?zu?q0I$g*Mk&K2IjfT#Xj&YlTأ^MԦ4Xgæ'r~!RmlKY$nD[dK6Ѳj+cm)n*ۻ+#¼e풯ZVGΚ Rh2Iy[ƨy mnm\URY*[+Mr5LѪ͚ ,TYTU ʳ)q?j'fkLjlѴ&d%’ m+]Z&IM5JkjmSRۓe˛RRZҚrmV䢞`|䤇tע v-<9|%oro}?JNϏ,e<=q8IFdK Mm*UkؚLJU[&( P[ܼ/eo]FK20Mr{\| ٟ)j" zвz*mY֧G2 De֔$mw{ `CA8ޯO_KkZbb’0L3#(̗){d该y~ž&gg6{\scjV+fYe(K8TpV0aYaUd3FTDS(~Fj,La]l#Tɦjtmr[mfUީvև1dXo&\p5S(bԫmVBOO.yI5;F˅V621JjTZ-&YfYHy*jde0b?.vkkֹʔP֝mj ecS jS>Ǎ>_1pEQ**eC J# X ˽k,ڕR5VDfD!jت9]VSX+0̪̣0,DDE6.o ՔpҬ>=Q1c ZSr¤%YO[ *%U2,fLZjS뗗Z֩aw:V.l*y[O]]ۻ1\9 J 1-kQXŋ+dR tp- 1#WyR6zca-*,Ō,Z@&MĚYkC1K &b« M{xF\GGivc1eLAQZiȧhW"VʫVTHݑb0S1l(fi5Uձf,JbU*2YꞥV\)wTi{"&V,HNAkFVFec J‹Y–1Aj-FRb21۞):1_%e»GF{׻xqBwbvR5't'<7VQ4嘛tVI {.ng eOco{w"noɐ"Fflɘ$ JģEP&d D `- [Y33+VIms݋Ty3d\&l:҅X2^`ː L[Jا9_G[z:I!)ʳ3tW'ϋr)ܭJW.y,p%1fXW+|O\cC 9>1kA_`̩ȈFz" ?豃шosN0bHZ rL AHxɺq* 5*Z2pߝL{F)\*U:OwNJw (V/BQ1dj%zN8cO C|FUM4ǻGk\t$\M8>5zm=O͆>iIr yЬd2 O|^F=#OSx{'T#B>^(ӽ'[Xw9;&.Ue$屆Jg1pQGy1d gwhBX5șh1X2$644H,!L8޻|Q @.ax 44U$fDuƗ/C]LlE^Q]Ѐ*4I! ,Y\_#eord}`*zב}ѩDye䁢R҆d\J!ڒC"W[M:)5Wo5trjg'8Q|aSN/w/_7fy+R%Eb %.";F T5&1xo{*vXaܻq};V'9=Mr)-TG[q_ /z.wq׍ۯi@ȻEMLPa@a:ky,eǤr .DZQ@0 {j(/#2}^<ݵ\Uj:/ 撢;+ٞ e]]S}z6m6ٵ9OE9SXso gI{T^pli9|7pfOQX]]XYdYL5&eflTafU26;uY]y^!@mJ$XSir'O'f Y2$F+9ΤǝV8pN&*L^.: 129T@C>SM^Q.Es-R7jR1z79q19.rF&^Wt} 4}OW&C$x؟7K]/ |bg7llE^xO)%YweRuTWcB.qtԈ}.;R|1dtGrg@Nm=gj0z 5<"^u2bga␏a@!o^%OX1ؕ2I"bpZz nqnm竓c$4hv-=3Ǿ~'y299شitn>mX/$ُin4߄lq^(31Tt*]/'kƝIj2Ua/$bF4Q)s&m s]2[]vw8;el[ĕpkuro%+v';eiW^VG?Y`SL#,Z4ֈOtX)MV11֮ce]r)UGMܗjut8a*Mkbnٍuƶฝ.uQ&Gd-*JVl $'gVvvs'rc'(yaglv)r.p#RW z4 V"da1:&cifpm68c!jY:D$%ͳ;\JYWFȝ*LI1R*rn\(hNdRx[Pܜ5\[v];0e,Hf{Ki+&a{QQm6eU I* LzV VK2%{%Y011oAe%aL 9wiFZ c2T%*22E)&e),30%SxV(஌VTcQղLF:jjJ)Īɥul V55Q 6fyL)Xi s$u%;ތ IQ[4݉駩Rry693uʼH%$ГMxWzMÕ#',R ʩ,!dFB²C!bdAd)K LS ĖJF(ȫ%d*F89;4sQNTkbg-8tpbRνI Rvu*pS%Ȧ# ew8UɔR!NB($aerrŲJYmfq_ %hkѦnWC+$— YqXp#7JUQޮ W=.0u]SD%)AqH١x! "% \SC;=Gwepwģ~Q׶87%x-q &LԖ\P5-M-:7pÆ9yմ%m=]6y _nvǺȘ|( $2+}S$HwJ艕,eULeaVJFPK2FfBʘ3fJF22*(Ȭ eX,)Ie&J%bvcIқhԴiY6̶,di&k&ԛJcVZ(VeMTjEZ5*diLZIJSf5e*miZjV-+4Zʚڤf+dm Z4ImQdٚ)2MYTeHYS&b-*jHXJ(Ԣj+DɲҍhIJmIJi6HLIiMfeJTfԴ̦ڥ6fĔUM,iJjI&ZIRKRZɨe&5jiM4je&͛V2LTmiYLѱfkfh5k4FH5J(mEF̭Y*+Y#+ e-Vb&b3!FT%ΰ%L,f3&MHYlRiJSHRe2&0fīM%l-dxFI'eh¾5vzi^熕̕˧S )ΉNP:ŶIEpeȝ2w}Y]]έ;;M+OÉtk,ˆۨ +I~MtGʰҪtsѣZ7VpmX]VQ^X0ia#zJrOK1ErHe4ba͘pDQFA1s"Nam_,[#Y3KK=柙VI~#M\+>UMTNO3^Sϒ^ucQбc G!SO;ԉv7*pYf"bi5%hcv<˼U7|[=JJ%QTo{BKՏτN-Tnނ;;,Y,3.BD#iȂ`dd As?|\{]4 ِ>oAǓ]:2<*se8ou^}OWw =o4wSSǓʚmW˹z8Y&{_xz]Ռ+`4pA}A ӑW'oz*q}GRPx[^ƭcdW&ȶw=Ko1S.u|Os(OY*p++ٵj.Qy x(cS6{;[ēGybU 9\/4pOjrD9&\10bq1U~ZۓLi$0SI&qipDɐ"W9Kxl$t4ݚw/Rn\Y'.8imO>bde f4dU{beF׮i4ܤG<92?52N8,-G1c;$4Ӊ?|$8q)0LgH6pDžry^Mi ֯T,=XJi3PjS 8;882Tdʶ1;;֝{8L6p*O&n^F3SS8rN%[}yO Gغz<Gu8 VTS\638にeUŨchщlQ,kbQbܭDpU]5mmdW߃Qj0ő.'d]GŲtnYNcLcyߝF\ ,rͫiNH|T`d b/KlG{s{>nN+:-y{ԯ3cɷ}^9|_rNSץbM\)#wK <ҥ\5yUf|C+ !ڲ$ &a2^[;Q?,]y2aѳy6fL[j{طe30RU( }7W*p̥] ZE_#UMm8R#l5`ҿ#8g~12qN%ZWpoWayS¢sop'@bi.&$DXb,E%^m>xZN'+B=Ծ0g8/ z/rwO+ۧc.5U*7<_;+vW'Ã.n)z~Brl~FI='x*HMJjҭJVim6-RT)ImTeV*6AZح6ʖZ+IZe1bJhID0hY*VlbؙEZ+LM2حXSmMMZmK&Ki3KE!Ib2im$j2mJU63dM Xʓi͉D,D"c3,fR,FLEdƐ*RAZYFشQmjh2IXZ%6*fLQY4Z$5EfISm-EQ 6IeWgتB^xˤIp.0T .79<=?W_̪6pG曥Qo RҨp #yz<9t,ʚM9i4crN*XzipbE0m X~`H8Af`D$ɷSkMiURB?ir*^_Gc_N'L(6Ӆw;m4?-N8t#w+޳Yy]*U݌(~ -ԳSx5poRDW. \ Ҋ*T▚MZaTeUYPXl94fNѦ~-?S~tNOݕ·W=jtzK-"e̙JLo"YaCIk=q蹔UMóU[,~Su_Nڝo^`PBcpG6lfPc*}[^SW:Reo;ɐ\ݣUsMK(؊~H!jYKujn^gQqmmT66/}NRNspm89sniݾGQn: A ח!!DNq֢@AsdH1qh1H kLfL 1T; Fr)#l O3L X*Ej s1=i6l2<ԤNڳڹ\rm[say.)bV|jiJVX2+ 1\3%nbbL',N44?nI$Gh$!%Sfa&bhC?O~yTۢ1!*?S*PL\o|ס~'Ӄ4ݵLT=خ?Zlڪ2O QrNL՟ &5;W\1v̷SMVԆXW9{9I]9RFcG]64)X enl)bUNZ5g$EmHd+fZT}CNˇ\\N]w.4nsv++JO=8g=n}|[9M]q5TUi61Tݡ70*5fa:.j9Wx^n<{v*v'EapuncxPEICG*$qg7l!Tl7oRwι< Nas+5=>#S|Ώs$uorX'CvKr~'Jx5S^Z"US5fi2c2fi*T,Ʈn]vIo%b˲ɒ*I+YbK1hbb7v2a-3lRf!2[Z^msuWT].32jaZjIcf'AĒRmd1joI̲5"fkTT%ZPL04oPi4Ttۂc7eV RԲvf$ 14Vc.-W%Obf5M*1 麍%)fuV+JmURV2$UiQb>jɢi:֘l*pdXhdglVSU8ъ[%T&*LZV$j$ʚɜMKWFŊlV,Ҳ4T! :Xd冽+qlǟn[&yQoq+#rUʭb!YS *"C%zq8Jd0ʼnU82%((U CfzkeԫZ>Ɇos%+Ah8FI8TȖ&8ڭme$e,K]f0MG peW,2d2 ԰cLu| ])4rLnاcJԲ:}߆yj$N":[TZ[eRDͥmUFlc*U3Fm].S1Y311UAmj flIN-&Ԯ%t,f.-,jUZUi´dTҸL캙2bejSjDё#b4Çv@k$vfT V)MeR1e,I I- Jp8NH܅i̲6a pÔ'UT"%I}TΘ494,&UNsSz&e+vxn(lpfT 06SM5[1f$ D¶IB,6qJHI88JNZ4V qas Dw8 2lz=NiL^\sֱpSaǔpdozw>,>"%q|Q/Q4)K= ukkdӗJTYV1 {sO|>~wK&'~j=k/~eQy>3+nw_Wv2Cv|TT7xcT[uUO3)sjוz_;=7xzgbc,v{@rP(f\wR\+cgqUvaoa~Uo GJw |*~7Ui0bI, jz@\8Hi/jXF:Ȫ4Xmvc7|ךÜ"wf‡>󛦅 `Muﯕi;]UwGzX? 2L jL;Y7 qڏ B.n0KPKmv+^wxJ/Kh(.JrVOC.L.Qz^/;nR8q8UƮou;Xu[?Y3d1&=ƕ.u昳O3SFnS:po[b1 ac$H>[6;cQu ߌ.'GFYr. ^Vw޺1X8'{#jX1Ƀ״j!WhRJIշ,d 2$K aw0meh4XURĪQg Upj&<Owz}qQ(U.u\Kg&1z3a^i7r*NZv.דuc7]ɴ]cҩ4ƔiZEHHY1&JXA# 0(qi[LXF"cL=IJ٥H0$RıUc R,Ա<>zv9^U޻Ns*;+kO ZMNs`nmcgocgSlrrٵEE31jjg3LcSWJ~۴~VNe0TìhnddtY{#ܹ~N%Ns}.f2YU%;9,U0u+ &QFIek(DoCvXŠE1JnL[,9X4I:P֪m )2mTy%$120(d6EMmUp荱iiTὕpįwltq˼ÓgMaó+NNM:P@v'Cu:s9z]_եQT~T;z:oS~$7hrx/76yԗKRʕ9\N h!ΣYnxڬ)q1'ESLf6mKOK:U爥tI]z8Gm 5$׃ixwf2 @1"LFDUm5]d)QTq5s6)Y#H%c2E+31eIiE #&I͝'UlȽʖY,FCbO6-:i'MΆ\[HNn c/|X}tYW eK ʝwr7JD7bUKG)\&FŪT±WmƛρNڽ/2YdUyhj^X:\ jNN&橵ųhk_Y6nY!0dCl9JfdQRU j+fYKeMLfk3Sm2dY$,dh(EmI2 ,mB2HRaɬm)YJQ1Qhm+MdLb1KYf̲i1hX46Rff,&R1ҍ)2dA)dfMIm)BK 0*jJe3T"0&Ѻ˦ͼKIiAdpjv:=)t>M۶&8hM4mIqR[(Q\WJV i:} *챌R/8 -koLl po3).M,nZWXۣ}#Uefd\J7[6eL+&oG)75LڷB6ۮQ8esRT VY3Z ZukÚZ&x1&NLSBX8*σx` 05J抣K* =BK -YE  #Ӕ1mfP^mclVɍ+Tʺ;5 I[aM*pk_ѶIMq}]wyg2>D#NG%ݼ+SB(Wilce!;{yԮT-+(&r cΪN)ˁZd6R1 ,F4ȓlJIBL )$-鎪OȮ URJBDQ,TE%4e4kTͨbK& Y,Lb12 eB%Rd2K4),SSLcdIHJiMY-%23F66K(IYe PfSf-dFS1ȭ)&bفe(Z >D<9VX3(](\\˙s1Ma3#VY%T`r7\TYV w-Xَl4"m F*ȪaDnZۯu'.dcR}YwNJct\)E҉5˥04>NZKxc~ٳ~m{MᩨfjRDh`p<KΟpl>_a'r'ק%vuc3g=IH?SB>Kp8 Z]2۽vΙN$1owt9K(яWgQTkcaRCe+pI `z h3 r޲~8ݕ\%p202t;UNuL83v] koQӖmS"R[EŔT4۱)D1x>mbv8pa4)˖ M.xq[|~c7}N<՝mg~HuwܞuE+<I W現ܸYKr`I&I Ip\__˼ѱsku'tI { 1/`,\>!@T~z>A?}v7ȲA&!3hFQ#+gՓY}`#YzϕMb^k̥0E20*!_LTXX#Ʒ8׺Sb)O=JC-Iӓ-rIENΓ]M&:G;ǬftN(hg4s1;k4 iVmg~|/f &[#Z6x0[96bcfݘnj j|ɹn7pޛX?co_+I099 9 xXDŽuVpË^nRjԒ%QQ1TəY0fc)ʕRjZc1fhKEm*+m*lmEwȆxߍm9K`ô:ۍ쎹lξº̌gzݪy-[[2`v뫽zz yͭmSO6VM\qV]>?sװv׎^Ym ^s3K;=Y=ij5u/L[49$V&f[z/V%z3.;GkUm9ôU.Ȗn~xvhé-"M{sg6Z;wzImmʎޛӞ8~x6 4ʞ,θ!̹jo|AJ fa@쓄\K)@A@u: pfriK>T0̄2fLfN˼;ew8BZ1K\4tl.e e͓ujҍ[3LkIr!6uƲqebZsUk\hƸW2ef7Բۓzcku\\mbѢTKR̕'Sq_metͺF6Ke3.6G2BH!n 'n0QVcy*Vt+\WЭjf31&d&ʦ**fLʋVʧ)W:ŽTrʍWRQm% !Ӌnz$;*_+J'* ˰C,vw|iZHɋd?qO#,Yl5hj#,gZoncGɇCCӸ'#Y0fssqo nR\+G Fkjִ({wwq X\m̑3,߱cFL\~=@Cu.% fEF,xgF ݢHQԶa2tUxm۳?--!?F"i&`hC%y:ʸR _ߙ=@BV} hb{p]L3/ݮT-GˆQ*2*35ӔsbI 9?R6 lZKYͭ.VLS -UUUZC q+L(U"} ۫x^剈q B@BxպÈӑp|srz>.@#< )8t򽼟낷xί:+]:vuT7UkwH]sHuT@"D&u)A* $$xOEhQS3 44hѣSr2ddɃ F f &KiL ,d`ZXR4KdF`@ TD neA9C!82&ۃ!@|o{|mU_gcn#|ө"4/Y"R[v~kڱ&[z̟!r^e^ 0vD Ҫw1j~YkVg xi"j U 6W#k,prJw[Jf^;&/ dbԦBI>ʞLkm y3 abbE"ii ;MnI]h{&-'ye@:!@P@/TQ?wBH`,-C8I# %${tQC,PDs 1缧- &'sꘋ=Ҳ dDhd]0`;`lddɣIJrɓlS&LL4Dɓ& ҙd7ujZ-CW@xK|W.n.~q\V]=z?3rw'8`cO>L='Yg{15B n ~P{w)mmgANj֯ө.&-i>dz5 .gZ=qMd Tg/'޴'N@uO_VOG8"gIJQ1`_;FZO}.ؗ40[Y//쏉1G&:LrDgi=ζ.'4XŶ-APh4 I$I$*mv+׮.(;mmӞk-]/.=gʾ\Y\s@DXzr&}EE`V+Ys0y>&;<%LLȶ3'OY>g?x9nXPP[bv!+kӟL`ѻʽj/=LAcy9,~a4a8@mN.]3vmcvnw|"$ EX`UQ A# SYje^:F7e5Xn7xܭeދv̾?9 /JVP HTnRUDv=m{zƶm^nj[e>ekL)돇{^gwNjŽ|ȫ ([q<͛,6dweۛK5WqqJ7z}?'lZZ|frxlfcTG>߫Ws_=nohe^ ={mj/߳ 3_j})ӧZN"ETZŠɎ- ZD5SI_3b]Λ}Y^:Y8 zyfWk/unyVs{W#3FߏVmyw[5kM|G|IS~N2.¬¬¬¬Vح[bm*kl8Gt @~1PZ.ͫ > pE?ЃEF   pӐ_'п%d=wۏ133Y#=G.<}s˒Qo~Xf+2űcHKJ,w=~޶fk-\L2> 5m/RsS]Kqrp|dlJuHz>,}k\0>#Z4.0aEE)R=]D fk*\bx^맩O öˊy_snbԼi2{IS'|1,qt;fW=kĤyn%#c fbəc/E5gL&gud(W=} Kn8x!XJZEF+>J$ K#+{W6FٺyK'J$eR6V9YZVe004b, v8.{[swVҤu w;&ag_)ߙWm[n|ڥzgUd8N||dM%\\Uv5x]5HmԦ[+KZڣmDص ̊yz0aٵtvuCR+)*J\ZVk%l糽m'yiͼ˅Tj!8 ,L%yGkWfƀ ]0<8FpZ0n gxA`FQ#1#1 BeL2șbD&!LB\!%YJsmZɘLe/" y"4R}TeGڨJ v,'#+/M1cUc1d a4M>O>O888TDq7PC&$"ׯ[~1c*1c Hc)JVN^kZYt=Qb_IErJ{{||Wb08pN& f}ǒ@VPj e^/G*( +S˨!S[\ dL` 8M ia SM8=Ҩ& =>IxzHdW,z#=,Wr541u+ (}|)B |F se'[ME]Zy~l[3|2ϛٿg_ٔ\wnUֺo]C2%M#/ K &LB@ /12S\B\,N?}_m-[ngư+zі2`E!@bMs-hT֖+JRbk|fZ+2eZ`~٫T==ϸ1LRߵ8~y{{{{{{zd "*`$֩zpP`6) 6XY``$*[B216mR0SjR4Vm@`jV`٘ir\ ,cMkT9'u);GIXRib_Q*6A ,&S&VV20K^`[\9@m꺫\qS[5 ymi\S4![ZVƱmdXt6mMӛiIrIdLY42i$\&*EX#L啬EZĬJ$Tt44$1qU1(k*f>$_b Ԍ+2&ZҴՔl֭MmeJ4f[U,bZ͒QYP $ҵi45mZVif3Ln+UVFҵeM3`YI@X,-DFJVdf liL,I\[(FX,ij &IdLVXʰ0a2MTU&%S2I)RjFbXVL0&0 10ң.Z IYSl 6*i!XR1#hJ>+laYfNcHʬTJi$'jJ+kԌfYA0 B&kI[jl6Ґ֪Lm*֮$[[ssrZҗ#hraj`ZڱOTY$Jdo\tUDS󤢲"+A`H2VQT1UJmJM*SERVLkDPĬaD $1 20X1&ڵjeMS-T6֛lVZb%R[jIYYUm4TiLV̩fͳMTژflʪURET1lfMdIZH**FMdؒԔ֡$%ER٩"jRkV[MSZɚU&eVjKX*2Ħb̙Jҕ0JQ`$>_ݿ p A'_ނ.cqr}hm1C )f"gS?ƲO,}LJ5kpͦ?!'> i<Fu )4d4L= ^D@lFPBJU{"zFT5!V=em^+IzN%Ǥ^N䇃۟|jp٪iNT0O AjaNA'`.CraՀ#K%4-4PiM?IT6:MH}&,&҂Q@}=lWg DH~L?N]uVzwX30} A*Ԫ2Hģ22 IX, 2ҪV[[ &ɒ@>+Z䯤# FPDf;ce598Kd ũaLmAi8 -Jri )bRnhfC[ Af `~kΩxfsg+$ 7iJY$b&̄?a)1td1Fb.n@=V@8l‚I'7m˅ j3bAwJaU FQFXb9 Q[niD]A'n[ɟM< 1C0(y['F;rgEWJȎNQ\밮U:ĥ;ftT\g7m5\T2e*qm*  p1]# ˈjJQ3M0b40$1}ZHpt3\/U? Q~'aC,)?=~(/MtOTGcO~k~vmf=D%2Z2lĩH iN$ huv+yB+8Hg8Ya4S3[N: "P)NrWvu\qܹDFL@lL] aHP226jDtF)61STi-94%c 5CbRɦ]ͰT{bpZOlVκ˙IgLFZ3>8Mkp hOܳ;-ۖ&jaqpKK?Q|uCgdҹI֓ ɵ_=Oj{^8&V}l2YJb} d깱Xk_v3>qaRDNWAr3XrlF_mVf|?)H[HMDgwwwwwwwwLwwwwwwwwwwK3 tS B(ѕa-;iө&)a(XcaqqdW^Fĕ(L)T)l0P$B#pa,Ma GGIm"t'9-3@S&2 A'ah;UomHR54, IșE׀vsehAIv(-ZL]4[ N̡8ȃP$PШDd><8 'dr!M4 7+qvEeH[XpĦ 81H$rtXN+BP@nSPݡ*,<]Ⲝ)7iǣMclٹY%aaCcCc761ɥ73Г(8N888 'UeM HQS33Tʪfi*(,Dp! t0cI6MaaV[%95Bb(bq,8xʭ˾̮ڭ˾ʪv,FZnNdh t4j%&D! s`P+)M 'tMA[i`k 7r, Ek[@rIQMԸ,Ǖb%2"z@R!1hUk("e ldֈDJL-K;_lهBX/IJzŊCG  7 0&xVĠ3ALE pQ1#́SRm 4;`ӰܻQvsa(x6c&f'v- O'$'3}OIOzȄ>mJ _sR]5Q.'0% 4zɿmKdW]DlvFk990TALFUMd4pU8Px!Gˌ2L5K1Q!%)xhwQ2 YԙpT#BO8! JY8t(K4)aHL69e\R}ļ ^9+s Id8zoO~Hc2ƌS,4lK|>-| ,aF}ҕمc7X:PTGLoGn Oodܦ|vC*;IܱQ[cRc db#־ߜTTtvWY-`L1Wha\_7ЏxNW0!O*h͘x;c>oona":=OlH:X-0\xC6\}bJ$ b wL{йtS334'6.n|tɮq8qN8=^!5~=)ebK'W8lek]vӿnv`GDeK#p亀q<D0!p6C$ P@ S sGnF!idy8Cm|M'G?@d_Y0$$L0\u\79eTz,5;LBvAFu~`jf烸㰗oSϕo\Gvh C#x"u:טGg«HO,# 8WG^zĜCc6MuoHU=y,~+љq}̚Q{ *rhqxŗ} {כ-Y]in9s8 qX6Ku&76RtbcoV3>ۼ`k a)&FaJJn'X$v%=9sL2Cy^& pcᶏC]ÔoU4QAJAvo^Hg^;{o8e\ Z]eʳlyR`3]̕b[5đdBM^y޽yva45D9:iL˶sVHåHҭEm{x9:CLY#1Qj-q;[I:ǫQi-af2ێȊ[%X[cy#h\ax Eu*e XuZwkEhZ-;vws^1=Ksmιk.ΜM\@PqݭwN6A:`ôw;Ghwf{&fffffn[w+Mk̆nwv2r881 I37߿RX!a'pBp-Vb[*j;bCWDee{[sUs׷S\<Pa\;3[M8 Ö[*,&WεK!0; 0NY :g1癚Bq3B FFy Ckk/ :CRTãJO>ezNz,]Php~=K&`=#-uި1lX9(Û 6+{UWĞwqЅ?2ADo`54uHf,71f%sGdEiǝsh3>5=ÓnH1&'N{1JzwK/qq1zwv'dŔ&L3xHM~o8nø Ɖ6#dsٌ`:vf>\6Ft\s,т}|zw6]<ޖލnޫfgNwν!a-!! M85o `G5ӶNzr59DA̰dd4G`BKla;QlCTזEG ܳ((!/~u=M),vw)e*̩;mWmϻI7?f,Lq!焻g2E{bͼ}I'bUS_kww&?O3g>74AZT{Qa{c=_VUԵTO=ԕ"kq]EiE\-1wY#T[ Dbə+*IF99>18LRrƈw酞N > wzDYL7۽b$>6ڵvC;6pC{zI'w!LwI˄w4, /xrGϠ!=/)x]K "0O$값\VDms]w]+E]bU;ֻsJQkO=z_VH6ےeZkNPVoS@8p2=i;MBlUo|M[7gMkVFME[t C !'C *nܒo0]F,=@A$/t^yRI8nbsm5*aBJ606B\hgwV,Z)zxˌgA*8$bZH7鷂0A<{Jm)^K!k]NR!!>˴ QJ1`zwꗾwF Jfkٲ RXDr v(@ӃB3j[+/8}͵$Y=ZՌIjUZz1c֞ea82^+;]QS}xZZ[-jզqvNzoՆl^OlB1hKdVv{Y(2ʲAqx3KX1~sls˄*4:jkT[ 5U-w}6=L0boQ=F w wPd1#2Cv5ykB9֪&0gL(L}z߭aUG˄p3٭mjjS30i5U"ŵݩJ*YSe-)YMZJT)羁33G>!$I33qĄ4hN۝;rv˓mm\9rr?{O3U*TeJR)_Md|$+unɅ+,L,1?7|zTobUUjTPB٫B˷i(ekZSWg@_qϚp˫Sax] u^N\⇵#Jm̯&3*R*R*RPRQJ*K"YTBS,efL5aAfG ƫ55%6R61l0VڵR=ӕdL6'nZV)lT`[+vFEMF0Dų*m)ZeenUt+ichldF31CMiòg42Kd %YeTKjVY0KViTjU]V+,(V,VdHf03*pxmoWEuūML0̖SZ#SS6LԬ͵jUI6m%+`VHBxl.G(ƹk[C064J$0|28cW}|srhzzφ:h4b&mj5fk-R,e]V+?wuQs.5_SgzJ+jc,0ֲ5',ɤLU63Tԋm) 3?ލܧ/=L9(yݱbڤ;IȺ%^'>4snmĂ?F*$Y-ڹmtSX$ ՞Ф<'ʇ_lE}^o}{yˇ+hØNjv|WuJ,ǂJ+%6_-E9'/Q~FMݨ%X*UJԵ l,ْfZCm3x敒X&+5sѦ2b y1$!B!BB!Ac!2V *č_~hUɌ,d1/<ªJZ%QW5\W+[2&p D~fCL%Ͷm .Η-z&ղd־?Mic#&# UJR񕷎Ev\M4$TFG֨ƕiQ6,Y&?n˅G5GlcdRvzC2&])TVUT|`IDګ"U]{3Z~kgzRcRELP"U`b٥~e `LR (dJB ?8y~,fLDm83~yʳ8Fxp{?3 XƁӸ +\!H+2ĸ; ;3q}9:9>;Mp3ٝoNVYe7) [l[|G#D_XR4jv׷>czp̻ j-ш(=XUS4" D}&dP| J>XGY0sE|~m}*MeMi"y^y}ϝGջvbs 1}wry1|+eGzy Mﺯy=WUk(_H>UqMvz߮+M^VM_9|_m^1}|~_ :$w=̽د˪T,--k,()QDO">CuSy^ҩW>pϣ쯽Yu/^~YNU|;AU'+\1!W${EqUQcJ*Pw%R-`~j>ȡQ]xvxOY~w͵w=xJ|/O+}O_[K˾t~LJpB 7+YˏQٖYrCGW7'/qiޛwERWsf]699IKTs1'C񔌡4GWƻi#mPq;ZP-1J=sSqnN*}KJrsq/+_q2[\hhFɩ[\Nn)\;(j])eZ~ۋr䪎`ʪ-7VC6Ē+aPM XsfXidddeMŁefhb*Ҍ3Lm)Ev2fP̡RձZX9b3$VpݫZE6lPx&JK 5]-l¶edp"6eT.,eYC,L"Y#YR"YL$U! #XFeLbYBX*Y0)`U0++($"†%FCoͣDV`a[qu:f%PB\fJMSma:)U׫콻z;]j};"iﶺ8ܑ=+۾k˳w +<ٔPk75zw˓iO~'G';toJkԫ+%Z q]]XUSd׊CՃ);4JXs㟋k#ujHCiI32bCw>́IRʛ֫O&[\Yּz tC±bqZ5)[hSp$CX( 7V sj۫Î̫3vH(bF%h;U#7(̒1Pɔ,ř* (I,e*H+2SfQK&TX,ȒBbVR2Re S[5v[v)6Z,Z*4m5F-Ib֢kfج¤MZTڢYmdFRiKEKmjRZRTlҥ66JVjmm4ͦԚ1RX2hڊkRi5$j5Lkd56lkHJddԖUԚZjM&mjLM&jMfZZiTiVmJ1X#ji+fmUMJVX1 ,b+I3 11ıBjS[i2klMKJS6iTʛkZH5a0aRZʃ+N+aQF[TSmYYkm!e%1$RLR&6"dD)R3JQ%hͶRQbXڥYkCفY_y6(g_Fom oH&uNR6b HodFu͛VmNHꪌS*;$*nGʘLXսc^cDIUi~q<&nJzZ5HgzOƞxu۹UG_!rш`CF@B='en*ݤV"GG).>Zn=U]#zP^zf~0}2r#̟r'!|e\q-3BqvӰ/;Mut_[+1H@I\ 6|TWssB⽥;_dЮL).̴;YSV:z<Ϻ,_1z/X+m>A m|uLjF\vfVeE\;y\VB,T+^Swqz;~F7z/? v|GD$Vϋ;/ 5~<= 4j<^zW-&%H(>Q)fZR._:71<^>.eK좮#m0c yƲoV{⽍,[ nEj-)Ⓓ\ kGLRrW)T+Dܵuͬirc٪s[lvTԀWf\u;*ɖjMZb1^2VfqgKz.'!䘚'>ƥz;Uu~xL{~[{yp[wܷT=iTp_u-/6ޜmWoStJ+7m^4Lz'uqmtE8hx3go|Tz.ήm8ug-cz޾hR}9׫79ՋiQ"؇*K!RhC5FDS3Q2<&-0Km&< \L"\0̀9I basc]Bއuك+a׎ ߗ{vPY$R*m[W74hcbjDTmJ4lDDX5DAlDDDDZ=MI#tզ|vt8opƚZʼn2VQGή-c -Kvͦhd~N-Sϩ%D԰8{-~W=ot?=rzGO;vO?I2ޞ7 SoQ9{/]9? 7`O,U9^^\twʯ_+쾁KU>;::5t`ATG^"+Z2ky;R<=Oz IλKc34Q^t,Q%&(hSFR)aV*e$ʱ0,ڪ++0㟖1xoFL#(}a:Ǐ۬uUGboU1j$H2U2ʓ,!26Y-[NJH;iʝЯ,M^| ylj>j"XQoiG/1Q̲ɝZz'Z= :|>}Z)2śZTԶc1,c* !%KIM625F*2KK%ifFdZL3H-2Ȫlm0Rҥ5MK$KERZͬKllihj)TXԵTڌ5kJET%-)Jeb ff"kYY&jSh4U)e4mf*5i2QFYKSJM*6)Rlk4UDi4ͩ1K[&E,͓Vf[6ZeRŕfEO\Q_xΞV:ǁh|PWr}? `ID<};{fLa1Ɣ7139ﮬG[ZXcqpT_A#d󙴑oMm7ʹ8ׁD$TEnQ mRdk"Q9cUH؂ڢkCBssquƛ{U̹iՌ;[<՗~E#۳i:>+#~~Wn^I?^rƻÕZKޮ^:2M0BAD &C T F%H-1ɅUr,F%T,(3ZoSS+-pT *jmPVfe%KdUeJv51jlcff1ִ7jcfѵj$6Ye*oǎz-J6ȈƲ+ئo}j7wʩnv.V4`V4FƇt2d!`1Nx&&Kj2҆o^Qjbٳ զ2tVm"{6{3$Se,˴+cYmʄՀkKJa6,DU)3-Ff5ljf򮽵f ͓QJҥo٤hXi#V,74-5vr8jLkQefW-V.>D9G$N(%d[FYY kY-6ec#ebj[/5fL8[X8"VFqn:*n1iK,6ųjF֩jzÁGZ魜v+bU-M-!Khk|oFGe{NՌ926о[m; H}Z vmuEBB(. a *RE&&amZkZ`:lKwwi>/;՟Z~*_;DIErˋ>dIf@L9uQ"'K_z㿑K>E#2pijf(ܙ}_ >IWrLTWTYawuOoGU񬱖H+&1Q2R2j85Iz/4W?ʰwHF^/j~$vM7O5<^LZgOc}U t>v|:ϖ-RfJ"ʖ%0V*kPyݖU*1ds䫗=]񫲗uXwrzxe`0gy;Ϭl˩8أz~eXk 0cF"2(ʶٶٛXRdg-N8|ߪtPjp:S{w:KvT <-SXW/EWOowܑZ<R5rem(Y" zuʬVykFcyCi-ʕ8MvobGㆪC⚤[V4V B/ܧ'ҳu)J`\˸w 2V&:*ZPpv>srvMs]ҕ|L,]5c8=iRZ[`cnM2{~ʯi˵%M Չe8de3%-dIwWI;xUήٰ$UFLIڸ'zo<.c0#]<.eiylQ|d݂0Md1e0ݔ*,`S0Cz.CkâJs=)p1u}Gve&[3EKerytꫣمesZyȵq9):elJbXBGɋS5>33di' e꽙S\ɽߙX 2U"U\1\e~Eݳ2 rbo8ܞӞ4DgD~}^NWJڻf3yybZՒi-&-#f$ʕ45%6(&j2lWmͩIFʖ*56PYg3" |7efUVGH27 ].Q]S#t9$|.܆TYMl1,,BU?cC8"'O;{H+F;^IH̳0RCUVڦl350zW-mXK&eM$ L,fKi(ZPMbdTm5fVlز4fe)K6SiJSIR3*R͔IRRRԦiJSi6JRҘdBlC)(I-&RiIRIRII&Ʉ,e)fY4إ)JR)JRԆI b,Xeajز禢f62őVe̔WÍovK!#. žᅧWKLQYnVU*/99=Sьi/67fk.ld0Ƭ%1dqg@#RoT3=*Gaٓf;#m*EtVJ؎7sf՗ަqɕp$n:ض%SIq&Fw$̏IfɆ#]RWU[Y&ag)[ݭ7][no*\͸x*•8;9TyN)AċUa6[<,ډ5-$lZOUsp#ZdY#&fliS22Y&d2"$e3eM2ȐZFhl[%1J,&lPQ- D&biFԁcff3h,Re(1IM)fk)[3M2M2(5eH*jf٥$ʓR2 3⽒ofcfJL$m*tWm2f Iǧd%eYBLNk.V&E9uQn&W}2f *PT3*Z;+eJI]h.3pM'Oy>^'r):%#FI]Cj'8!+WHɕh#,"_;se<z|3ZhîUwA&[u8ԕlP֒o0r c9[ɕQUER%$$hq+>< %080FP!mE69gNw&.S?9{߷!wF˫Qu_{+̾KzOw+EJdMS齟n}wmZK:i|;o⿑oy}>.j=WReGcL!wqy[^G{N}K݋Cl,䑒zZ*g\S幇=IO_*ߨ=O7G-QC~CS}b.rG͊Rw$˹m.Kz`I?[uf4SU$[+._2\fDmAwqzS&)rv[+Z\fYlee窐fee{.[XԊzl'{Mheu[.:7>z{$x3l 2tOC7s%ON;9"_#M߶VMoq]1.GqUHvPi%z%Wlʚql]ԍѣ,Z&LmFkYQ5U [\4hI;M7]M^gm`uwӧzrW_m7$v{ʮ*L^wp:~ I !$)ڧw{esgYo;tᳯ8>+tO%.:g+#|~/q}M׳s{mXWW뻚\xc'5nvI9o+{vsW߃pC^C{=im|.ly8]*36']s}nwo8Zlrw9}#?z_3{>d]sG=U.Wfq=U쮋>_< s<.ˑ[v;;N/3VojM_X;}o]C%l7bqN,yşrTmmdD ccWpc%h .hJ#l]QަA.a}7'8Gr^EG)egsjVg+\i+P1a#md9/&V63P2˷o"K?r xs$VEL* Z Y*X)HIFH)ZQJ Y )b,R,)B-jҝG*ZGFH:!&^V[l^ :%̈˪r.Laavfa Mum9qvی^o#6_w_+jc%&7I͇O {LSvÍk'v^r{btyxD=EqUk0 ; Cft`>]CԕCQGÒYC"F $rL$ $(N8s ,0⪋ onL<3DD[2P'8:&&ٻ^sf$h5)U=Za$8r-ԷKsĒB6b"F"JdU0@’*Č+,f!hI*H?h۬t[_kg2f#JyǞ:&_9pm}!'⫻zwbr:+#f#xwn|}ޗ޻3'3wz%e٬f˻㭞ofjjj&)٬kQfpOk;NF] YSg,ڦ}"bsv̽z*؍f.Ͷ۫{f(xw:I}msf㯍lhrz٥߸ۖ5VCqم^Cm9my8E_g|np[t kmv:w;.wW8tB$h5@=&En6NFK;[]sZsH-Zy7@]N* Q,I]HKԃfwst> mqF`K 3b4 qCtR[*V1Zg_sdELmd /IX$V*UȫWqVƠ1o)n\Q\Uι+W:d4A.+^$_*G3}GrroVZ6888888Vp  ⃁{爪hhqE* $VQDqqqqm Ihɖ40\ [5jQqfqƖYqry &vff!qffffI$L#K>n4CLz_Ycq "(E6"1BcE0V0#i/dMg5܈Gտ{?_}|>Iљcbo>}4AV\ݗ fnPC&\ðKsVh^Γ$I"Ʈo^,\-Wsw0K je9Scd @7~$m&JXc1In)-åx5isu7#;oKz_=9c5%Q1iؽ}50R >Bnu-݄2@J 5 3`*0bRQF6\SM0cmggK@Ã3?f;9 vghryqࠛܔ<;"1dTU ""%D рA> 224]@z?iCbh4} 2IE(& ݋P9G}Ϗ""O7y((;1M%IL&OCdsA~A 댆ͅ?@ (?Lԕ~=@ӟm^~΋`LćGAu'0ֿ?Q~A3nhNBfPL:6 у?ʺ@oS>SDXj CsBA+hĨ0g=4ԅC԰ɝNL_Z"3Hzk} ϐ<|7X₂ ƫ\Q @L)H}ҏ0G̯ipr+q?uY'V~}r1H'wÐx 5= =J :,éӠ>ECvs냿Gg.GvvbD 8lKl ͆v0zl!=doLI9=Φ=]u 9{<|Ih3U؜q =d#1: uty5:4q OGƷұ0$_xO:zayˀ7?;m9g+lZҿ*~3>sqǠLx8~/smcMJ,+ mG<?p +'m2u}(L4z0J\e5T}C@n@7rC9=Q,k P׽=){L`r|`{}a4 Ϧ}$p䘻{up}E\!: '&>$y)&0xGPW54LK 4gRhsMRCrl;} =ȓРB2|QR@:ׯe7J(C/w~_C2LZưՌjEd$F0K }~.ݜ:&{p(nRgdP$@w[6`ΦG.t :ssI:뮈m$RI q䁰#d$#$]Q$Ii}=t /dÙGMdA(Д0 bpc"A8bKDZe6*b WHD}Zkgl^Y\/~ª}ZlUMkk#Y\Aou"kN$%]fݺ4 /2d~&Ki,!$|"A*vsY2,3c32D@t=JQ5d}B7` `l].R5+ 3B21,d63-lh*ilҕFlҥ63%3+뛓Y2MMDe2d&M*6ȱ$`E "O*L/< @=zⰹWB!!1a! i)C=|f׮[N$aH@!2dj*b65-&&,M(g)~LӋN\f̱YJh),CǶ@(yOe?r9sOơ~v(nHƒ?7FMvjx\5ԲY,muնZH:MU5]t^qn͹Jg`yu]uKWR룕Լ@޷Ӝ{&OګVp誚G`TOS}?JJaU_``Y `Pyxԫ h|4cqqEQG2L2=~_؞DZJC!tfZy\s&T ϟ*!u0\6TضTad ^ %J)EI&\&A>]=3=S3333>{k1URR^)8M w^5U~ޮȷrOh<+!$vŷúpr^]038djf&L5= ECSE>J5!FMo_4bvnݵ4l[3(此R5+$Y,3[;~<||K}}wwt퀶1+a F^4s=i&ENశ =1\0>ub( 'T'd't'\'d'NMМBnWdJ)Tn4ѥ{U7kF,ZR"OT>Rh5j ;SSVN/];k}lqA<vZd(-y35?e7uϩ]Hdsْ_g]wG>\^],}zYqqQEQ2I!0HWp#&W8T )ZZ A!f# Q*-5*MR lU"SeHfijŴf[e2l 5h EI!$aB#<]jٸ_"9S*0Uԋkd+Ѻ趺ˀ65VVH+wjDӒ!"4R#ň M5jjҾG4ƊZV5R̚ںګٵ )n86MV-nYc\ZnQTc6ޅKK)!L Bȃ*PUQT: .ga*DQ2ӻ;IS o~Oa $D~ğheW&>j.ػ8:NA>š?3>;>E(Q{(_̊^՞F^g vO- p[Bk1U"@Pa ۊ&P %ۖ"ȅ``%>O.2z2] m[[9^=/N1tUTh`R 1?dbʕh$_f{͵!߉yY˸+b_Lxt VUbj)ii2MNxƕ0ZUԵK*D63)Ne*4PMلRs փ`PcA0UҔ<1Ybk­Z[nݍd&A0ӸCr88Ҡ-THVQ Efc"0" [^C Q&XLE$&c%U T*Me#TYUVeQ3nO#vn<ϢOP<>%Jd}T1$vxD=]BɵJvߌ7 %^ S.L%OJ"u'j+'r)ȹLrW*{CGGDg(]!331IjIr܋c",;ktio*OM #kܪZ1wȒ$Qq9!Oj"xx(dOP` ]$z9 Iӡs 'Y3b &&AM(ɒb&0*#%^c z.`Xya9nۇgwwbKBӛ!ߎ V9%"c9V.3*qR$QI@$IIxI*tf>&A5rOx'ӼՉf UXUw(N<1p3p*;H 8h4DJZ"Ob+U[s>wec$̖1}7v~݌@DƘFTI&C&_z$V*l.PjR $q(P{n=)!4wX1, _޸*Oʩ `%@aBI$^iߌUb8dC&h.Ԫj hPerX sZ}LQIn_l,(;३Fu`~L& @Љ4{iC w^a5Cj1 $[!2;jHeueɯ&QnsۮdK&B! 0{DjSl!$I,FC'?)VAxywwwwwwwNKʗL£250ڪ۾ɧ3SC$ˎČk10U̕AG ;".KmFTA< +(Wl䕘#Bުh(5PɃp@SMhl ij$4#qؔ ;!C3LW+mlBQ 1=@K%q2D[Hp"QR }Jˑ*j9daNgod2˦zA\l)G8L&UDޚSa RfDy!frL=fg1{GIh+ynΞi/8ӬE<61Fnx60F'wM#4&6n5†|]ՖLkZȴs&@ M)w;黔(h1KRBlv|\˙h(#8p҂NFsvCUUU;aJtXQ;kkO>HUPvNH+&rB s0aC3Û;a̒=qjbt^Z &ܞV͛Յh V=YYP7a 3  ]!#^|vɌPjceLihzVnAJYvλo B*'殩$ܤ^8 IA"GtCl@Jܼq &C`M@)P1,IFsxQG3_;eT2s 4Z*u97<lXk7 'P\wkQz>58q%|UMm(}ݿո=.oldkLK%UuRF,<5'K? GW9ȽAzWH-1{[PҌ&m]}bﺾς96XyGhgK܅iAx 6* rhjx!=vn &y//2PJ]KL"Wz^xw0`8LԘ :n46Czj۩,5<`ރ* 'y~Ӆx2&q2HBI1oD1dlgw63ROxI9vdvImJ Kͳ y.߂jgSxCYGOu%4|BYC T 1H x@70R+W :dnHy8!b+ (i ]W@14K]De` ` %`i7GTِ-OF0"o+B]r =q4!y(#3(InfC̫$0:Iյ[.Tr1q"n`7wo ɱ.f[axW3Wd 6p'YTUs2ߘeI8,X UM<3'3|{=Kw f2B}c m(I10dCq%ב0nhsDh`QLuFhj("a~˛ּY%x7z*lkNsDuCRJfm5l?FpacFGֶnP> Ȉ¨D5:Ξ6DuSMh ܘ8@ꉣ\#.<]i.^kN6È6NJ4A*cq;oQ$DtMuDHm斌 $'";a@'[ܐG;`C]"C7u@h :.ëѳitԖ/gPt _P̗c\̈́'gҽQzyu%,ՙ>ȫ7)AMDGMXx!LN8@H!NA֢&IWn3KNmM 95Yg|V닽ŽM@Ɠ̵\6R7 '" k܍:gb>_5:K ǸTyl%e'RJ=RȒٟcЬ[m#cpWfHIl͖nwwKi_Dzٜ4MFSQ5e=g|K7(A I8@B rb6Py?XM9ׂl/#^,9+{@7nz vPbW4׭|SO)q! +F<+ޢ)uP8P %|^Kx̸rӏlZxh x/` E,RT:;U7E||N^GLͪ|S6Ɏsּvf7T!#;:VqTn N:@Ӄg<Ԑa$015/ytEUxJV+4Po7+6 &TRyy|Y|Mvur7;Ed.:ph陆&,93%#7 Z,޴Zbl k 0@@Fc[7TQJxY«*:hXWm+&nŋ>G̺m$ {~Y$d"ZS$ hC"pNb_o~4Ұ[ުv;Uuρ<8lfJ$|Ced7e] x4BI:(sJ&^n~gC5XC00ʜ'N|@&@ڮ45`Ev, "e06 kaĄbz{c!PzZlfC[ϱYg;tE&Ého=g{uؙlb}xLH(Ʀ'+Z .Y$g"C炡 b.SAp^I \qBA AɆLGGqzafX k•jT3xfn EFs̊gotOLntDF1f@`ƍ3J9qvØPf,.UEWF{n{eɡO=$J ;ZQNx׶V(=9 ۄ9͍ Aࠁ JT z(:POt4֥X)^|X2yPK"Е+λκʃuE(Nj*;6'L 'X@$^m,d:RDn  uf\$DPI,_7ϝ|`.5-[)2㉘$0k&pPI%N1ƼqcF|G3ʼnGdNg'V% sn$1kO=΂&KN$qT['hp<r􎡹H8c{M/9řD,Btyo""f]ncCmHml mf 6h'1zmxp0TsŰ4r0F "6ak͛9p azƋl;N{#(<8'{Ό|*dpv0|ځ"[*^/7>+N$I&{ש#]d$%">GE8x[T>hٻw.dRI$HgwVΞa.{#o;/sۻ[3I :GL$|'*Gylr~3{Ӧg@Q$78!8IBh- pQ&*, '1*!).X+!|<ㇱTT_1`C=g1t4u75ۻhm )?#CAGxIВ@>O0Ҳ*}@$ `JvF)N=ߋxH`*wǨk.x>6oIpDCj\$fI@ջkGb='8a!q}؉A営e4q>[\[x 9S!K[fQ=1p /ڍzqk0]6BwwnINi,5"醘N ӑNahaL⣳8VTXRoٲ)@@Wn.vzZzKD1M=lGD ksֶAw}$X>ՓFxDUCD(DjwB#f68;j"=4:E﫱;/ h;$(='pZg!UuzzmƺRqN,Hjv '##ɞ4@M>&ċOn*gi P%}wH JS҂M+6)Zge ']c&t>%ngjZκLp"pl WP;T#l;1SXN(8֌:C WG~ɡ~3Fm[1FL{t͂X߇CHvTQ3@a:c@DZZfU"D@$wX X5;ϣ1z>}ۿ^l__2Hhsqdݫ<#\ut+g)M%o#HTPdaAe HI&:t!}a&%ZIT*SUUn_~.G~`FKEB<8Ŏ>#"wLBgD fB|D1lzcJbą3Y9"%f(voT~z~|T)RlDQ079(:ZM$}*D݋,Ǧ6~dx"X wN &R'5C\j #z0`Q!" "(`zt6~?BU'5NoFߏG88quҸko΅2LId,c !~ذo{:[MY3&YJfX# 8F*4 ;]\ASܣklf0XV2F5jչ i"ۛnj,CWD(Q)!Q*$i FV41ZQ(I)8hǦw UfBITJ6j3c Xd^UEKeL>,sc4C?v>ްSP¦m%eŮS)JZ(ڷ{^kU42DmId '/](f.\\9?dv,m6ԙ%,&zhZ>QaS R0Q_AYaYR1ʵL%w_?RvNƖlC"3$m--FmޭkUdh%}^RRn*:qOÏaɸaX Czb9n0Ƙc af+ 1!J6EI2V &)cG|ErK΁M}t_j6AB@;CxrEdQKkīN{cdщh";lJ-KIIjfcѫ٦'T}nꔠH P Q3L Lk[-&ƤZԢ[[JHĞ'ö[Hqam)PyQFYjMJjifح k2IJ0P1;<61c0oFY A<3x@ lå=앎gn, d#m;{tJ^g/{8Wqă7z0nQrq~7lLꮌN]ٽz\Ѻsc{sg_3{wI'=>}_Ժ]i- ^3Zs' +V6_<q V>d&d& ekZkeYof$>' AȪQZpܩW$#;ꃣ>Ʒd7nTʅ]KW鬼iQ_=z9F[}-|gKzv8B(Y }W=͘sݞv\O{V<6| ^M#@LpL(ˇۚ°˹spMΉ}$z[g,lpa,)MD ݶв#DJHTV$Ʋ}0Wzo|>5~O})FZMY!vUھG9ԃ u~/'Uy[}*=u%쫛vɧ#IꞷiTR}֝Us:>lmv2q_d?7Vc>V]&c}ڧ8=3wu1=W7oc.̷׹z۱sz79n9q];ʓO ]Ur?sLqNƚ؍6h*Eid*ԲQ!%a*dȥ_!訬{OZ*j;>5[ΰ.Qz⣖ FIC+w\/ꧬɋ3-d1cb%h[Gs@ ?<ӓgpgʄEND0!4 byz]FVqU.\M{=xDsFɬr%>mx P`+ɻVR-Ɔd* szv< D%/ IrݩTέKCNnQU; ^.ÅYDH,(uo9-Uo0W e 2beUdR((ĆET(!YeELHT%#)f"X"ʱ$2%)b0*U93&DXM%F6J[Im5ţkѶB-*D[E&[4i5-cFc@%B1%C66JYVM͐%VڦV+meYjh1Ti)SJ1DV+VmMMdZ2Mi-e,l1eFm6*dRXZ%Ih33%&b5-&Jɱ%*S$,&Җ)fɶIFjM+K#%fɲI-MMJ"SM*5%4٫$Ʊif1ũ6ebiM 5eYm-X-dHƊJFjm[QV5e,%dac -YI,U("l֚jԔQJljmMSLڥY5Ե5KX&eIYei6ͤڭlҲfZq v(1LJQ,L%2fYLV`3(̂ [n4e@e6M4,Hw4)B`$CM4vrJ14)! z D*DXR\t_4iiM6k~VHYc*tvZH#TSXh v;_K/QyK-)<(Ks4`dI+FپbRr]:[,?I,K{Mտ~5 j&w^tUMаNfU1]N^U%/=57t=sQywUKFiOtdol'vCͯPYTxoTpv=/K)J]Pby4=b[$tvӦ{-GG-j]v}\ (Fcg77r\K'}j]-)N?9%.AϨꗽ2 =,eT |r;0uQybrjiwB#*}y:e^qD{ =e}kE`Z㫴Nqԃ}J}،Udn)J0nM.TЏuvՂ*wE~8|nXhV>Q!컖PX$PL}h|M TP T9J}Nh jk57y%޶TPfSbQ9$9>{TYJMR{NUH!=!uc)IqRn<ޫg]S⾒/̥Jg~|O[ (ΩRI#uuOw%Pv.%ce *MRJ]'=xՅ^.rIJUrB\l׺ƫ/;f+n6nJlQn]{˯~FNM&i JnFI-($)W HBx-1m SbGܤ$sAZt KC(p#LZfLm۔StV*ATr0l [aEEHn2@cMXGb6Gbm-SI)F 0@DbB%0SMGhd2/5o8*һI!ѹR֭RCZ-0Zʨ† TdٙUl uJةսnmymvRS)5 pe+Vҳ4v@LA8TL] ymܟeD0sl%mB &հjcuS&dQjYf5J5]" D|A2T-jD8.>fK]L۲8'UIP+ ։Gz $@$ [)n;enZcTC 3.-KHV3qBu͖-B֪ɉ͹nJ9G]R5"RKCSFQz~>ث*ܭz9"~S,2R |>{uƀBnd18wd17ue'Ru2e%!$_G#GuO]+%8s7eAo`}zAP=No3^oYP(\.Wx'+u0^ٛz[^UŲǎ~2iTWX{*D-x<.77**Cq%X JO2 1c7I]xq|+'^Ǚ9&MpUT;hd%^EGru-*RYi%ţJ ʟCNGJUo0o%?t K C:7_Ľ/uM^Ɍ221e=n|>g^|N=y_M^O 1Վ'm d(fF<{rɲUo^6hT;`%EDG}ɔUx>Ff I:O}|BY`QbFK`Pd%[8'LoG>QžnZF9*?IQ(Iku4AuUAAQs}*s腹uҭo.cy.'Mm.6-%0ZMbMֈYfA/V(@`"4A6?w Jrqf9pe9UoU/mER}JOԻeo[p=6XQVw7 XOLuUU98CzPu]g[slt4nGaIHz&&20 "RPpF&6cj 1f*Tw^ӿ)0ʱT9[N Ux=.EéfFHc-UJĤ݂*Ce5elE0%0bSUq{瑽9I'$@v0=[TD,ccBjU*5("D,@xݼH.`e0dkuJn ]H4ݛ44݊z,b ij)1R&AЊ%M,Ց PܨHHD3scA֪ ruӂM^;tvAʟ*Uت ou4?L;T/zBiڔRf7W% 愾 %t5<Å7Ctˤ3LLĖe6c15&Ś(l4Y6RK%og=Ý1E9Ls2gT\#S}^8(tCo0Y0xO0n(Ju9w9]&CTST2GZm[7Hٵk4ڮёJ@l!i2{&ֳڭEj*c  PDP Bh%`XSLјiЌ0QC MMP62ɉeĐI4HlʔD̲1"m(Ĕ$L $JmRdcfS)%6k$I$əMQi4Li JEa2 ,Բ `fSeIMf37#!fnM4[_]vrnR^T0X+z"UmB(8=2en^z~qOW'gwu'׳%~ߥ^iJ8gr[J}g"=Í|=>T} Q{s U$)cg#uEnU6dT1Z(6ƪË[1F翶B(NST8u:ocsjG9*.5U'b.;w^l5Lh 2e+[>v :Dk0nĉrdTv( fl?3̩!RݾI(k$JC 0&JYF)phr4==< r87%YV *C]Iy. yj~|WI9%=d>^+'9e+DI$I*)e>CseD} c'7W6Z=*-xl^7<=L(iwAݷ2MG9CxI1u/G*q]6x>}Ƽ.+fjckVl1~NpLF6m{A#Ak+|n>.j{ʹdzY M niw?7[rl ;ls]/; '=O!@A\ C@y(?SӨ~{=/=//O%f J_yC̩mvKMem6Suqw?񻁁2nّRkoc2Տ_3jcl7:eX-bC!M$E1TɌT+4-bbTҘUL*TVS4IdRa)1CZ%3,bE2(ȥ(Z%ԖhTV,HĦiQ-:>N-RTV͵ַ1@jT!a gfaƲdP(Kuoj kiz&-"a"#FHekh4n(PEN̽C3x#q-<S͐CAUʲO^T[vD$fQsDs>ױanFg ɲ7q!a:~5as,AͮHhʅo)aUC<OYoy"iNUFFtUy+kCEKT̥S UD)d(1a3eddIH, us$+~9u] n̪r.7 ;HwjyNJO^v+n 777kkz+ݞ=]37ս{YYSW%T;rc3̟{N)3i42zM4i$mOSjd zCF zL@GQiD46ihїLnOn*_iJD|3G FbNf62dvK3 xq8Riㄩe."BQ]|7Kl!R-fh"BZLHcI$* #^H D =PTAZ2ճQ0f)7腲 76AJEGŚX4&yxl{nnU]yuwe6nKpgzD3m6&4[ʟle2)el)KY|!m+^jLve¤SEIjh;ZG,)S%[mkQJ'0Zˎ,YxtT)DS 0PRvfqfqq̸ttY)X̫xu޷~ڼ;v[kn UUJʇ;ʄ)J!Npuygɥfai՗a:S[^^c#/c#1v=>Fǵ=U^>ffꮳf檎ʠF6^—Q;:J-8heWLe#Hġ*I,bWƩOE< "{s^@GRIRI衱 6xN/ϧ}j̝(||ʔo8^vN";QqQIR"d,eGuFfnu-wc͸UZC}+$ڑ(O]Է'nR I: Au"|&Gc*^aj{{\c+;]2[⪪Ԝ$t:vX .OyycIH#cX1/ だ c"Ȓ4]4]$I.4i 9cU=&ώGj'C~d#ܔ4,'z$(0H58P^ $' <Ԓ.o)$gٱE$[T'epV{=j1zR`zm't<{,7%~RF*&^+DIZCa®Uva'NW eڧw9yI$)JɀKQU%Zs͙ܺU` wfa(J4*@խ@]Y`SHe!=.iRMr6Ѻ$)JWC*x(Ғ(1ԕ)JRlT:*"F~+\B|pu'Dpc hl>i I"EI $!LN[]"!Պ E (R#sTN蒻4\Lj;^+]^fӲjYX<\Wc3N-_Ddu)XIHʏE;NDsV!6хuzke0jj|XRH#,p6< 6x))ǀWFMět\~ \7%sOwH9RdCQ<0) ):q^*ctl R3Qv;fHv6k5YS35+{4xA(O 8O{TtbhbI`#_ WE)ڑGF 680,wbx!%I$s'H)"bQMڟGmmh RώeR <+$Jͱhw`9bG%>?+ݕQVvp!ģ9s )_+MaėK[Xl%!iD@΅wZ]«-=p3sGe_C:qE,Z4| ee&+%RIE++)EY$RZRYJi(4e[5ik1FU2j0li5Til,ҋ4effRMJT2j YZlS4,jecla(3)wm^L\kn)[˃[02RRLR%%,,S,JIJY*Y)RRI&II*4.o+m! u &F!$2´EPba1&_Wn'kT1"#<*/.ĕV:Y>CÕw8?*OZG0踄yX}"GBI0nu chNf3y6z5q Fʹvқ<:嘳vr1AƗ1z}Aȅ@_R?^;ȄTJy]>UW=?Hz 'n3鐕9{ߋxVߝm}_W}_7ɷ\tp\\A⃱qSx;v)WQw8wmcm\sWi s35<>'}}'ȀDpxC 8tGN  /Ǯ/ǐJI5iS`>Ƨdmmb*H)#Ͽwjʺ%w^*Z^T-;mmS#rS[D9 AmT2&I5KRe2G5Ԍa\V,4k2i,65%湳SIla+Q [ i@jȊB&c#!X9.ƱߥuSY߲WfYvӫ,1q1edJ2Y+5rIcqb&)LII-˃03Yp10I @]HflMer乒X[v)ڍ&UKk5ӵTTZ ]hƺViVUKRԕ*X2 94dDedQ\$%HDUhDEQRŀ*(+T(]l3CW#̷Q28e ֤TV`5VJB$ G~ȉC)UĪ*X"!(UFڦ5 2TjI[f54462ZmQMF 6٭TɄjTTQkV"a+FTRիfb'%Ii*\m,JTf͖$5- m*!Еmnj7wET߬O}znf9sSWtq>YxDj22L M3F  T֫^]Vפ mu"""" ЈV1^y]켿3smLѬ՗ n?&z"Z9Qk^5T{Wvii !!!$$HH %!H$A$"DS2I!JxT_i~3V{}uu}\x<&3ޮ''?rxTIiGw$yURRj T>R821z*0je=(E&ƋQ2Y2Q_P/oVpOގ VD3:'e Q.n2,FP&lD`JkQF14$CTd1Bb,rt " $/ 2^#Qqc M4:VCQԩjWrЪUmjjZ*f$MHb0hiM[1MAz8@aӤ9JtjvМjUn-$gKÐq4c@A@Y]N[,-lg ”ѕQŰ$8( AE&84q$ .- (rԒX[ lxj0[+LCѼ5.u-F!e;j|ߐs|s339Bhi$ڪOe]<^Yft0Il~?;;KXfگNCԳ]~;˹e=I~t<V@>dR઱rq!+Bf|:{Jv8fe0333o -33%x$o6es%%Bdufk1xx;2WffkI5o6rj8mRmcj%0YбܭxZչe,)YB,DpзfѻFh-LT?agA| (I!C5LZ9RMhh,KQT[4[f ZC B`ƒ7mB(B4‡8i4INO!;.A*!DrlS\k76Bf9\8ѡFvd'hC&f^u~U]hx !tn y5jXL@NXR_$'yWj~Do2&`q.a2rl陙ӊׯ'Zk'}^[wׯ w72P$w{_G,; )]4fkO0LdSM[3840}}i{%X \X#f`'Ofː=hEȔ<(&0F ՠDXJbc9&Lk~J cK</.}j55)3E:ZOG{0kۆÈ"ť/K=+ IsAUuP}SR)΍8bJي;6xfiGe~K0jkiƘcR🬑?I%~LW-,~I,-mmM! "K[!Ari@aD)!0Ni.G$B8B0D,(DqD 6a8p$mweRe4|I;YZ@P r鍝4JII!s6 A^%`SpWQ%&Yz:H$u AY}E Na0e9̉4Y8y2[liX`0n AٻZ?}%TIFJj˕ < ,4xI @`=@XC^G ,"b;#|ntU8&5961Æ%y (ӵ8554i\Ʊb]:H@ceXB,joupQ"{b B̩s 8H QEQ'8(%FA2(4Hw[xq{68&3- ldP7g$g9f7q9)dkS7^uuaP;p&{mfAM>U3DD#Mps1r pmvCv B J FK(,7oB`o !˒egp]ʊɗ"Jg;(lqD\DFa@n98qÌޒҤ(DiekaĹ5X# (A%IA )0д9:ysk/noI/ av2,3n,$ 4&+91YHh(248(H q Ύ:жc데tI]`s! q!"('n5ʳd4%j,}dGFYnG8D v 13w8Ç!A`J%|0)}JL%zBWׄ$GK'BU*Tsg:0L5C5UeEjlR̛3z\̭DU6]*)RCFys+39-\I-! 'Y,9fVInR9@p!E (#AЈ5X* ($R![L@PI$o"I6]:iu!z "7j٪0M̓)Hբ5#,tv|cvśҸ#)Nq0[+LsU,emA]k< 573)җiL7 ]Sm"˩]4n$^TINN8p0ٲfXd&o9@k f78kT<$9} {6HMtFIRPk\ZvffaOR˿)a45h9=333XHN3@];C{ZF ?af&>h{ΖUB?' wߞ{ػ35OS)Ǎ~㊊b l!L'goa `n3 &Ub*IL+W+QJqNlqϢ a%0$6w&gʅd@CAь9MK/0xS&4g%"DjJ,qآ/Ӯlk6хM(pƂdGɢ5,E A5#DQqP3 @ĭjsRle1c;Sݽ63 n ^e(K՘;HKPW9 )ޚW\΃4m^hתx < b(@<<,2T[ud3dĝW"dXގ9a鉠QQEԼC0SI$EH;1 al8H! #0EV5-*SMPc{LT.tOEE00w{QOoACP׍I$9Y1HAI e2h4ݶ:X"rYcxmFmQV \XPZ^dDAAe,0," #٠wtE5ov0~s|S0؆gBCvLμ#;0AL# r>3/V%dϳ`Ԗ]ʥRI+i[޺eqMyx:D]SEԅԊˉRqmP$Nwq N8Q*m*-^//$1hhv01d{x"<`< zD O~Wֽ qfx|HT*x͝!dp^^ͽD7s%5M!屧#W5@aƕAW8t|z;<ݞ9\cq̻Z6`!RBXa0)M^Ziu|.&Ў@ #T}>kO?_LTdsjD[JYg|!Z IGj˜IfTyѭӊRjbtra*q>ӄy;5y]я4x}2xf lLQTُ&758$H|mbvÎAcX4A M),js,,pF!Fmc$:H@:vLX[jZN-4TdPj0h 5:\Y {X&ML+aq997Ǣ?As5;S$c½Db=~(h ȢT77l l`.$8ORaeۅ.l4xDyU^#*u2@}P0I\3yӃtǨx9>ỳceU GԷ1|eSk3{5 ٟ掇Ll˵-xboJmi$H;oY睺kײ]V|<3wk4 p=bL Gi'Xz#|3:{Ju=xKKڸF 30CFFȄ^֫$ Q\CD磻gߗR\" qa^*%~[ёn|쿱2e~SoKa Icsw?G_B0FC~18AhdN:1 ѧb#8da,I"EBn;F! &d2!0&) kH=>6 "[vE"F8m{^ ggZ9!?"Xv[#9nռV ddsvrv8%8**.^vɲmU![=zs Ծ3ҎeG=e_%ĕ5"wRURoR芦./^euB,Z@!6M[xιzuuxe,]4C&D0Js{l \ b2 0@tP!ݣZ4z4h8)Ps"uZ,!/ <3sp3ų&3)Có< >"z 37&􊨪 mFto7O} gF;)/tY$ ]tR9x}M=ƙ5^L;㮃ftl.mtU\r!7Yb]<3Bo{ai!=0l4hGAAܹjnEO;1\_YwKS{*ݪ6mrZ)⦬,WEdj0 +BWl̵MړNwH.̪!hZ9m1oxaCqW9e q@Ec7tdAjd$C7sA$UC%Kc'd5C ̲ @)bk$%%il@m  cXPV>Ns:1Hz&/kfax!thNSE˷J,[bТ|࡜i (,ӝ烤rxԕz W}w펽\kkvfM蜧ZLΗ;..'7͹s4μ44Isz$MC}c!GĠ4=T[L1iQĂy@ M[K'fbo˧,?Wøzmرm 4i6jl[F*eIdj&ѩDTTR0ئ8:jpi!;NAX "[co7`ee亊Ao>R=9t$dܜos΢8t։+̮ZL7d.!0J$<9I H>{#D9KHmmv$+Ϲ< \ & DjGlXI"GF܁Q suE[$fHI@0ka˚M1,bng&p]iR;EDmDt!- HTHkv Lo*Q$-QNò1&{bZΪwjx)E4 rH#ysmoTuqCu"3I |af f<@y^O7 G}`>oXidp٤זt㦜GpoZI9H4ӈ23MrM;dd~S~ (<<ӎ:C'#꣟{tňۋ0 fL& $`a"R"Zg˂ȗx^g<9z8,,(}@4B2ں--ّ])Jj΋p9n[Hl.Sv'*e*dٗ/ɷGB, =qP8?t`Qd ef:4a6[ţmlJiy)ʝ^ bl襼.Me `ĵL&-KjOFQ2m4'Fɳ'U-%aB @AU 0@zGVVvlѣѫ tjˢ?f\X&>J$klٻjoMpΌn>0YzpY&Q`^l0ÇG-ڰʝΪ4)۸h ua j`0>$7M,Ӎ09m=0,4BGуeYڸwaͳul˳Fn_GFF(L)e۹e_{l T wSwWVuSU'-pM=x$-IKZ:),8wthٕaj2ó&.ٔ5[b45npmZTM$D#EѤ2<7hSv +FVjLVfX8Se5lV> [)O;)Vp0=2w9tuq7lYWV)JIt蒦^).O{N;{ tl 4y0 0ؘa[2”2-^Nѩv<ՖR:=n\) j4-X=l2cZMRwvYlɕM]-nmV7&s䷉va8Qvt'&pgf9v[5ᾍejjKU0OؗM”lv\,-KͶem50-ha aRhZrflj١CwnUm|[ThTٳ8e칳F^<)GG.[::8rpq4YQ# ܻ7h4akS4un;6v[g, r͢ Yue-<݅Ųѫ[r1e7)ٻwV~^l<,^.XSE՗wV͚5eleɱe<g]4Ųêۨ顃9c]ݛrjU)L(NvGf\ԣ 9ãI<>}`,Ų˳ur̶{'ffZ{rYH4 w/|=]=uIy`~*P1-#8?TRՈjUc~A~{:_̉U֢K?Vǧ_#m35^ ՆWy+UU -RLUs H \z?TA^hϢsWؑC)v*$Ч,NAj{:,eO 10@g]424deeïm=1r@Tvq{m:6lZ F5 :r+Wv<0Qڲ~ '' ]DRD^Y%z~P;կW]mƇ9*iG"0l@BB7=7^~oX?$H߾33>wwwwTwwwwwUVfoчq{oU\z%%%-T ]395d&~_}=cmcxYrΏ'CIPD8R4,e˾?n{\0ꦪSwۺm*5Qjl%;6rᬧT4GC 5%;٢uuSl5e&YjPpp&ۻ1K݆ĥd׳FYOMa[mT쓻v5vx:9nh }6aG,n4j=W2᲻?lO\YTc;9s7Qr:GHfW<ڼVaA:5'UAr=wƽݗ:'hy&;)1%)RTJIHȨFT>6TFrOkӟJRzߓ~sEx~GWڏ*~s+.v0 %@hoԧ[~2h9lyjb$<ՕPl?'-ϽG~ף"xC)SI"ц߲4O|"Cѫ͖QO$Ѧ@DxtHv{L_?_'ۧʗeg;WL)G?iQ-{*lGysx{NIOY^l9zSEnŪj,Rl /l5Gc>ճ=^mOE"K8qL˒E:gLR>nI&u~tI5p<'wulS=}-0{~:D;5pr-Ok=h0o~׼yo-^uv痘KƦ)mM$I IP~N=jKS/r'˗/WH{gIǝ<"=?D٬9=>.k!ۇWDD"{Oʼn|HlĞܴpߺ~Qas8?Up%A4zxRG?-ؔ^PO黎L#l)x}Sh}:)#vOc\f$⧵SL[3i$Hyh )٣GaE&=ll67?>t;yx&F_4Lh 06{,*}t{_N^.z=!FɄ筤F%'%(QR;:1$G4| a 2WWx|ODc洒AhL5t hXIx'^kzO<.$B -IgܷgNXh j**|UK|B$0H}?A_E'?XS,Wh̅L-h"Rt XjEWUKGfxyޏ^])ݜF`-b"ƪqT;`Qgts x:)ѻ M Mږ?ࢋurӅa$D]85uSvaThZML2ׁ4x{AZ KRJ- DQR<38wUIR$Ce^C͘"I::dDM&)c*I=L1 O5nt`ûFM"hҚ(BH6hns Hh h`bprr<[R'Vquhr)ՃDݬM\qy212-.dT 2,J20/nIͪ3WN>j@FD3F}컎"6'>ge5DIfwf<5Iz7߻>a#8]+]a_E >_ϭdDf:*MR"LOn2rxbk*`bHn2LjT63#ȦavinecӒE4@I3]*Ϥ22xH9ܔw/y!J5OО=!КnJEjTɃcñd$0BHVe9ifVo6Tꦍ Tua]Z5SɆ2jiJeya B696a%(O-O&]Xe&u[vV QJlvhSV;0ͻvvVpDIcaY%@е0n)OM7hZ:,jЦyW ɩ0V][nlÖe)JSۺ2х<H$HHB 9IjG5~C/Weң9yH@M. `Ǚo7$EڔeÖp:)ly=2J[wٕ>o"6wl~ |_utS__AD-,UTv_%Q3k3ܕl>$O; ;N6ܻI'⨕<lhF02U&|]6eG;|U:dUOgR.;޲I?nzO?֥(Ok2u%zL)MɳȒD<_S{Ol|S(G}PD{ݚA[?ca'MGwSȤbiCuT^+LQTݲĥ7y)Ysun$oGKvh{>gt^q)ǩBHe|Ԇ_6h"=;sN$ $rz?x?۩uӇcxGuaQGgݻ˫>6~'V ?tbSRJsƠ8~+x{NGXbUJv{>g}|WW'z>%dnvJj)RWvlf6[g2QBr0%~.Yjjݻ ܶven[2T5S0ѢGR]eMux6u)Uae?mWG NOVha ZVV ,T)dvuxy$<pf>f?|I2~4=~ L):7G"w,;QU[*rM~-(FcɃi'\O<Jp~z>7G$-j|o䢓Kޏ^;qwga've$P#eǺY 6HH/fFf凵L2Zʜp0x&ΏVg-{E\'ױC X'V"j[[h|{*H&pDp"00ھjۧ-E7{^:}lD>J=D"CwO%0|5T}:lor;/[|{OGDINbx)ÇI=R$ڧ61ʛ,˔<'chB}Qƥ3z~_eU9rBVF_ æ RRRilц&SB4U=PJl]HMT=[TٺɺEP–0daJԵ0FXY QbS&Xkgg:tlXNR L)DI%)z*O7O EKِptRV\;2h]636ySUWt--R$ Dy%vR]'[6lr"HS ,E*ERek2UŒ--I0hš()!i0žu03$e<rj-)y6;;GftT;?6 x-G)$Ĥ[y&pdw{MZA)R$ˌa*OQцS,0WFnndV㊪lZDZY!cֳZlݑ5M40$Qɉ$ڽ-c iT׫Pef|Μfݥq4bٕmnB'B4LrI--hI&"@I/HG&e]9*>PDʃWM3#wX)nbUW3u_ VO\e/{\prvjnJbkOj("`xбhp畃QTyZU~W=[IjF5VYnΊIPBd]mDu?zQ:Hv$Dؓ3/'v_y~ <: 5W2sr݁8#Y/()_ ۽kkfͭUKnOħgSD߹t~CkQM{=O7O⬲Nˍ.j{''% $g>O~-dC&?w4{'D꧹lR=Q?|.!?7y'DL¸6{\6LO><eMDIZ#'wO_~?F#(=]OFx`8w6Gu)赩yjԧ|eH{lr6pirE>z {_~jR)~~0ʐH:7Sm[-TGMm%( 2ãgFpJP٣ٕ4nKj(tTZeOHO6$g>gܣ/,PU@b&=m q! ǵGD&Do?'F4IcEpz`̕ 3fTZT5d1ŒjCYYE57߽||y{^DzIM@?FzQ=S'vV>Ԧ \O?cC$OPHRa~N`~ "b%!~ ;+fd̻qQG$vNwAT%!P|&TĒI g}{>q~R!onrL*js~MV=-X8/:s7JybhR|^BL6bIuĈA}TF}}V("({.4'?Ji y26} &Vg*>$%FkB(Z5IlmU-,aLʖe[LHR)MTf-chRѦ̪m6YQlL6fXl4[[DڣhTlٰf b=ȪH0T)I-GwtzM"wz'Ď:SB{C^R+{˄˄|t,TY2U7a4),,J>lʙRa&1J07aMm7W"eP[?Cˣ:2IL:F %2ywG3gUgSu7[Su$JBR2YlM覍"zaMV?䷹ÙD跫sw%r-mCH[ha4hhNJRI2N:a:2oMDJ$TqԄR]L6$FNs33 e*)[  C L5ffgg2ƇL T*[)-ԣV~ hճ ěQCu2n'unO \8<1 :܋r&MX[d8SVWgVSG݆T:6_W=>WITFch_ MI}HhuI|֧M*jM I6R6ed٤Ne'X$ӻș&0O;2чɣ ߖiJKtmVUatùja''D4` u|XTv嗢/ k#ة{aMúëfgSJrODzpwy2S;Mp5W/&7iVZ4fDA,h$l]h?6ΏaCewOSD$fNb&^,C)>`YmH̹T!r&JeeɉRBI\2q!Q^>(S<^ 5Ny)7K\T^OSYevqcqe0 $n[F ޤnVai)NTZ uM\'t4z9?5<r&Q:e'GStvjlOKgq⮒p4\Ãucr:qF m7!HI#$"LB1 Gc6(Ռa^xԒ׌*xIj@HBqRĕwIJ'3xXTڕE( M11(` ȠHHR JbRSQbe0`` -J(b&klRZij[ Rᬱ8f;&fL*RԢjԐ B)I,"IbT 1h( Q)F)j)kh[B2R 0‘1hi P -JQ`“*Z0`BDh2(( 1AdQ!8 3gNnR d) a`G2Ιæ ̘c8are3.'Md!FT).pIr+t+dʞMڬ(jQ5VabR-ʱHf!&b7h=Y-w"Ҫq.tK:ˉJf+pY*|}K)q!?1(p].K.1NNqTa jeLGnyJk'#*骩0k˰1:G\,uU*Jj좤4n&ee14[T(2 c)B8a%QV`4a"K~і0*#]\45au0PKh)LamM>S#i$*(cK% +3OpCe lT<Vv<,5N8㋎86fllzxYItާ3:vJ|?%B$G ^Ͻv}2L8S0>s ń}_g>)>قG|$1y[TЉ>ZzYIl!>JcNϫڬ-I>mG'.)>(4jy#O%uĝzTߣ!̇jvuڼ͛lY Xv3t0ለ,-ʢ S``Vʍk{ԙPjN7h4Y)MT=l8~Kfa353dvet3R'EOȣʝ])^XL2&eӖqd̳q8ݖ-OQRS*R3cqc͓230)FaMf3:fٗb=$ktО?lRk}itϳz7բ>2Gn!ȶ=e)jGãYđC;>1#ͤNRI$šGëHS'u<$*yB :%Nϓ z-?t˂$e)z;$8*U!VܽUU>ǓGg}kN )iq2wB~/C$CdJrz?l&~h[ĂD$@IY x>cKM"&ghgG>GG-.}͌>UI{C3PFH1mq|}J|{rX J~[.߲^`hz>˾j쥲.B*}JI(`1slKo5w9KW>k|y➏Zx2x7Oe?sة#HG14TOGuUJUN{ ۼ$|wI8$нz^L^-'d'o &AÅeDSի'1N'{Th@,c>PY \DyŐNe&d%RJQ)uەf-HVX{Efq7[qm* EFHQ*Ę3qEbI(N΍pI"u{b$InDEA-3C@pɒˈD"Msi|&M]T~̹xI$)_2Yh3"\pc' Ҧ]8pC 1˃b2;i DD Zj:.Ѫ| H7F:8w.VgD<ݟ"6p)"N|!+!R|ԃP{20[gH|\V v1fq`̲ģ\)FZ01 $d((ZYB L01D!TPT*akU-::^>b\'DbhJJek:re= eY{ 2Xahy$<%( k$=cwRlyGwnцM".OLb&Aʯ\L'd -V&M Dݪ=$hë nS[ՠݻӍP޽gOM)"=hbj([veorYXc ̱&,1)d(4e]EpH'V o$RD;cQl}z|:b̩8jƞM^T{2N^'w05{ق$աFRᆪShh=QѫEIJRh݅% )K,RS ,Sl0QWE)'tZԶ-΍Ӻnsc e ׶I$I=s'i')#RS,@޳ eAݬ0[յͼ H%MIdkj 3Ҥ],ifalT,4k%ͳTJ%Ke,)JjI(QDdS4f4Pٙ&RHiff+1aJlْSS6IIiRK24%)4&4,I$I P0ea `HtSDl$SܤnGa0U*Sūc !&N0}dat\LSKt} =H$2S`*%*IN])s*LE A6&JTLI0nKƞwJ;zE];# 2Q/LN=q2)z;ID"DIu U_DMzjUPi+hgu-jU+bĤaMѻ+JRʍGͪاFlt NXs5nJUbjꥒeGEw$DEHRJ1َT6R;%62\jlaae?'S::Z$T* >2FEk6lთ-SRIZ(ʎr2jg TSIVmԨ)#vM\ Y颢-mH`$;r72[jR%) hi )C"\M";mkx7wnZv y;uq XJa H[wVf!$#dI&Y!JM8 kmkq./> A-L R(UTTR*36(eɘLfTȴJYfbK3SY$ee)$ԩMa)bRdd-fK*T*3iIM&5LLK6Mi-6bRV%)h36DɪnZaVYf)'nČTh,B&4! i"L)]M ؙ'pQNHd"ZV^%ny7"[ HuQ-f2K WHi̚a 2xrx5h528n݆e&F ش1ITQO8L!3(,/&N]ޯ#VfD~!RIR 'bzϽ=K}Sڳ/aG5>љoH8q'IࣚqOE=/YSur'ٱu?b{=dⶰ'"T}gx8{U!Tүҥ{ΐ+}<-~\v<ϊ}1 J)^ mnþ\U&T(SveVU%)>$2$H&Τ9Gnuj۪]U-z=.4ŷ]$%x7z.wo'qܕfh[q33w\zI)-3$(m;ڥ6^)kAt<jh(.#ԏX"JFMb( ?S;Ǔ0]d%:P)CCdM`aHA"O=VT(ݙ&=ɣ&$NHh!mB# F z|0 ~.`(`(ޔکNӎhKRuUēAk f"K7R)œ1LX[,+,*R3 4U3 ` F01f Jnhh(Ef4X1KF΃74Č(IK/U6l23X jQ蠰wx[scF?FGge~/(FJ[jF]SPإ$5E z"IЧA#"+)(veu_>4ٱRY$ 0ϗ3 JP B|HYz~2et*N5*LWX5,ׇPz)ƇZfY"VAA/^܊,e(2&qV«"R1#ƻfH~!ئ~x3ٳnq&qSl(d,Y| 'a+헶[gqy0Ef4O(66}tzCU榼lJ xt{iZn]P$+vcʵSms9?7`H] 1DɶL1-#L3ns=ŭ^1>8d=[S>AiE0MAΑyv3k 訷Xr NCJ_8E Ug]Y\L >~\&8 dx7QBI7%L A%je(r.KK.H,qLʑ­ TYUnr(W& MF~0h^DM[HܳC)x.&fͺ fUx5W/jS R9 IHH84q)(M#E!4ŠCa X!X;1&An'd 3;&ffffs[;Ǹ<1b*0mk,mac5740Q4)#f33!bemVY=KTڒdTXDPmdVVݾGyB+ )4쵝fuG[9=W>nnRsc/^{2eFWD[wg/"&sW>ȎxyȷSOs٬!cwv*~>q/ ̫r"!`sNz-q"J{xS;O23w.ͺNe]am&Dn);C;wKwǂO)ߡ$$6殹Epޥ`tT5"n_:9" d?}jWҮߝx4EMvs/L#qogmzi}m*ҪF_]~+?V%3IgXP)_b8QG!~?^q~?⬽han_1C$ 3~kKq~~$~~KciU#ho\i_C8=WJH -[&+)c0# UQVb0R2+SL:d*a. 2-k !,Ēb`UO)f))E.M$Ig"Lnq;SjUP%T}F3R>ІAa2 &A!l:k[C# _+G}_Tw.cݶfi8Z=96ͬFH/Ź^fum[1J"Kkgx^}R7# 3qk x7qy&8?RT'8Wdsdz[3 Ѻ5Ss0Y%lIW3r"8v+Tv5mg81j*1&qFrYYբ xܗI8@3LG=~ث&˃Yv4#kd~^xOI'Xuz䭦n>፹z쳿l*l((@AD@ARWN\HD*EC?q"BӳL;4<|^I9|&3=ӗo֬a$;0lS,f<w %y$p<93ˮ(xLxr!L?lTr7[{;.wȃD>?^rlTy-ݡ=`#맜=$wz`ѵt~Tco]|}?YosQӣf2TǹZ*ڽ+;=uѻ[{֍5pl}[4nk9^יt\|%8c/kW x`BvNaA:|;3;;;&IC>r;#NSiufeL]t5ϥmV&) 86~kٜ֟1\q+4gx'I4pjskgt$s;ph\gZky}|~^O<_/{{GѬ|_5ma[ji4l|ǽ)_ɶ&\0cdsvɳLݎWw}  OfǗzL}_u=z̕k4Č+g$t&y5@Akʅeن^z+Ms!&u:_q:|`P@8r(A?LĤ Bra>bgYuo tU'R2$44b0~_g/>iyaW76w><&}DZ&gYpIܩp^M2q>Ocsȥ :gW&ǣ   w⎄,,~ w/m#Iux>"|BIl^`[mZ4&Yq͎ocڪfI?BpsIMadH@'eg̞tSSISw:VYɣ/ϞT !98s8 xh쿍#>?bm2ɮS|1o0؟G fm"lҦ4᳣wUɗ7J"NYc::cKU*VYhҕi&\\Lٍ3OM]9OfX!Ïߛ).>lOcc &S_~l2|c+ J3g+'.ͫ+9e_Y){sf@;Kx1tu/C ;=W9|U)QPM?)O*`L+ݖ_]>wz4V>/ iv̽XneՃfΚnٳM<2΢ju fO^ QE\竮%Xl<#e0G_{"u}km8 k|£6}S41ł=/JǏW_CiqU+iT-\\w7¢Щ.}ESu<q2eFfGm=O+1.hc8Ta!;Y:xm]D>Y6q0GTMu0a4cұcV,fJeSHz0[-[E@c1%)eJmjHQ"SmkX] +_om08GEky 0S|˪zVZVQɳBd$zs:묯_/ѓHlOnۋ0Zg4鿇<$'Q^)y0L>K39mpWk:_-UU1;i2?S-/UUQ.~=^[cyV:i t Mmst%[$1. r˗.\(Z4ʵi+;UgǛ2|5RK"i$[ I~.=̚Bw=3^O<{Wk:wMbYNݛdGJOU>6ZoMsWj?%[afFo"o:VayxwU]h/'044H21$I%jOĻ˻C5ocli:}xP9\vGG8GRSRXeTVjT6GحNx$acLݣ=}]~?ߗ+?IVٚ9q+Ȣ(RHł_=$. KBfi )J[SZ֛lF͛-Z4I 0eު]mUUT1BX2USަʕʍn7)S7X@9Z\Qj.?w v{6XJL%XɊb&ɵmwVkKHAٮlnaFLjs[9jnr5˔㖜6*\78G)rs.+ֳTֳִMhjLM+6$ڸ`- -J,2R3K*L0bcK-3!i6lm&LH4 ]ST|Y%da*fK&xXl$UDV*K2Xě &0XX(deU+*3 RD5 ՔfAVVd!e FafP+*ĦI&4@tTA)2U[TiUKf5ELИ *3LUs *2I%"E\LưţUb0PiV+SKBrڌW9[+͒YPFUhiUqTL!X0MRP?RQĊ*b2!%6ڥ̌fiYF0*0֫35-fj6+MblljVaTQF1T1T1j[eH̶lfMJ٩M+56l6ZJ6Գm kVI a11ʣ!URʨ`ƔU4RԶ)6 5B`Ʌ0bdPdZZn[)bFJJЙUU5lُc9q{ 1}DŽK @-gɪcOjcu^*ҨpZ)1RП 3KUKK m^h^zxPr/X;/FyG'j;UC6Qɺl 8W:CI CImxkjWyv Cu]@뮺2uI WW]r@9]uus2mqsd1}_j}wB2 i0 'W跉T0M^a0 M8\k BiapkcE;Ԓ[5vcsnO*!dTUKq-|ka>| P>6>YDX,@PP8Qhb($8i-2ieXrM7pʇ4$BXIJR%qȤJ&hAEP+TTZl3LЮdb?&%elpwjӊ25DF 4m )&2%fM= -ytRs~}3W H0[m]##*1J$`Ȗ bQ&՚ʍSIQh6M[# 2ٓQA-fԪ,,YYB[GF\b+ċDwY3|fؘtg\|9N.jM$r\^Iو7%A`@ J$r (y$0,M$cZtiD8NiF9!ȶ1dr ,I IADI$9@#vrM6dʷieY9eQb ;5rJه*w-6,mmYe*j|тXV“ʸVUfYhIGL(d6 dAE@0J( <4?(#xh'EYi ycMqA${ȣKTZ٧שʽ\Na6Fj*#u&IDʬܹ٫؇tEF]ip)-#,!v,C3C6mjcBbݫMӼ^(|aQ27.o.fj!Qu\-p1.k҈wo/< ׎ <_Bð3vk#:Cm[:f]"[H=|fI\0Ch&a N wppL zpYi+T9'Q"4q0I04 I&aC9Ab$Fa@HafI!wg>`}rNFG:;(ئs'F 9<; ?F~|)4My_,?%(jjHhY %9n&xPMt<3*^gI a)ldD4q=$Vc}5 YjlAO5RK~쏷=OWT J)ONeeUFT'XaU .[,Um=]I'c (q$PiD \lU谎aeh)G xawawolw| TI>ZtCigT}l}Oy@! Do_Hx?'9WXQ"2%2 upPr9#lNz;&@$ C(H愗pC91|QGSWp0,qUry{ۚ孓qM,5~o) =!8yTHL*;pqr~9 {l'8'$pR' ,y 9CBŤ)۸9?|6)ÒkmؗcY˾ @DI%#Ƿf$VtGmMH#qBe(p)lK9[;-vBb;/,q#\@eӌ}̇;CC7ׂLgXupXY㽼'4yMhtڙa^6V(V+3wwIV$熦:0H6Wai6ewFАRLn !=y|N1RhRd%$ 0u} yp;U,_o&=MAc v )6Xe/o/*NFv)fHwfd6چK ᩈg Hfg̒Ah]JWՈ9TL<+˦\g;5R kJʊMI~Dy%ySt3KXm,XzW;5[6es\{}2 fC1QQ$2Ԙtm&&X|rDnuvaj9%zѺDI6Z; h²_M?6;o*ci[YˠxtrHLq)[v} 0T}=O <[Ż3ܳ@)Bd@nR!Nrkq0AɀBFC1(X$q.FYrY#ʺٕX7|dQ҇O2.V^riT1rq܋'Ͻ𺘑}ǿo.RR`Vأfw\n7l)" >p"y}6Lf*307:ExPEOQX'y^fkׄ 17Z.+D f;`FÈu;vIbgTOqhV=1rZm$ٝCe9G\KO 3J=fnROnhZ`fl`\{`bƑSSL ۋ. >KffdvvZތa} s_]z<0y[63/@U# c|Ok59Z|1xcEW$8v#;͎ZHz_MJWw-δ' w{V3IJpYqU3lI ]+.'!!vdN6_mמVc ?H M ̓9NiZ @PY! ٶ{o9)niɫOs @UT75%x!!0.2f3j\@!zEVv8Uvr6krlivYځGc]AFOUdM1c c4!j^$^ߗwsF:oh=܌df l}?CPL[;@ Ǟ}#5mB.c;ȋ!8>10zf@y^n7fID^ xxQE~F`dW9BJ]-Nn˖FñČžsY9cNytqE|0uNH4"ޔ_u1T$pL&b% 3S,sP8L}8øRMpN@xD갶h<  $0Ay4th'44i@8jmLpEkuM 7FL0)nAAr"Xc uG$k vY"anɅ5Mٸ4JӼgdՄwlg7ǭM{`<,,oq ƾ#D#}wX4P/8ip= f `nҗ {p5683b ic-O=xwym6玒Rldqzity}Ӟ|F(qɑjSsc0S3 1AtFpf@4&z] ucIdc'|6q 1sS{zk39 ?k.xĎv;Hf,ם9x1{7%_iBvC򷞯, m QPzH ي7}0ȓՌ @'. f"~#k- H8':ޜ9ێkU3#i&0;X[ml\<|bXMmף #^eBtXML p馸~0ZqL%:Ɇ;npuUf75"/,m zy3S齦g$]wGBϭO>h3OIȈ6sDk+4=!믊ɾ % Gwɥ,kzH]\qHpmE88Zh:=@[FYßVKlέG=;3 "8bbD22D;82]% 1ѿXxp N(l$FVR0w74A^sDTʺw{XF hj!dl p*ު);;ۯo}Z~O}Q~_J+/M331X)׭9Ecn5\Tʔګ@S|MLdZb2]_j.ǪAްAR***GZ>hҴգT&h"I9s.W8Sl9 VUD[{[w*۽YFbb0±LV֌=oOњ/Uah\^)]O] vF&EzrVk&LkJv߱=e+h'=̡sN,̘"XVeQ3,# vjb™]vM9(}%*F1ne2]5Wx{\W\[v9_I̹U1Sdi\ܶoH_j7~VdmDe,S(U&Ek,FeZʴ&#ŋW 힬>g88 -U5hKrÍdl9fLX2mmh3Ua#8X$F2еTI9 TJ9,UXZ&M)QeEXlkAU,Q{{K8aFf)rh+iae,OGn oMMJx\@' iIi. ZV<=%gxEx?;̲c(|j1l0c$lVadi4iiʼnjUNfV!fF,8WXDX$YE2)JRcj>0," 12%ʆʛVd.2 ??}&IDH]T\jx$گYX36l i5hںF2.GAJ)Iʕ+*Rʥe*TkQm[T)JJG? QE)JREJ2h 0°${u|ϟ>u(t$XřԣrB TE^{[nW.F(ereZRZ2i&,f&f@aXţ,1"шNyԗ)0̇Q ؓ C2֗=$%ڻ7S&R KMzUvUng= Gʓ`U:jI$I$I$I$I$I$I$I$I$I$I$I$I$I$I%sTRІ)[aF534|ҺM-VoiMOZGC g<꘏J**bԪ[FKsx/ySJّc H derJED\qf*Ե"c8`B.riU~6~Um9@b6bKE9'3 `8a)Q%~LUW3ڹc:nUUvqwz:ηRm f1MV+͚Dz3*~ ߤ?Fwѧ`;qɖHV=\> p :S^[3yU:[o_eZzWw֓0'Zs!^K+berX˜^¿cԫrtEmv{^J_u>f8S6Bp̅ N%6E㥰ڤ15nW7Ƃ5>6[m[i8By>$ҒIJJ0 `+ujOJr~ {O-SFus{ {8|&_֮_owtOsgmȑDUb8!_w-ד>Ŷ}t?>O#q3' mu=7sT%/{},x489U(U%R]o*bLYvTW㏬dafBZ̓d :"aW6##v"tU\\ROWq.M[9,S?xrn$w,pag SzH~W(߂o||<&>*I WжN2g }ʪTq/r{Wc#e16Zk8y-/Kײ\kVRVǍL>a8j|^?AO-:V>yy-79W&y>6+qT)9 V}Jw7zaTz=˃.h뎷po(w@UKLҼJt*u*GϧBU}j^ReNUƤ9=5{r/.^յ} _XVk ^1x&I"NUM4+N5Yw}=GX͗y{[w+kmA}U@U8iTN'r\3̹EUWBPgD-9*{%_.~NՑ e;c$^L4V1ګS1йxq|#uB=kZZ̔10cLUU9Wɲ5±˟*q1 wjaGsIn`eV$L8r5\KFɞj8MƒPQ@鼟Kg`%YT*\BS5$zηNj3fҬJ"¨ŵʝV[YMemn´`ً& M+eeazMiaM-kW:L ԔJ*$P5uJKfr5]]͒+յ2b[ wJ^?3;PI"L13nVNJvJ8-ڗJ\7KL22J2FB&IQ V HL,˜%URb,ɔXaYTĈ2EWDi{iΚ̗ēᆢB$\%W%9qlAVꀒ|$sN'S̞jTkwμp,*Nc_ϿoXm&iYyy`خw붃Cx\E[b]Hn/j7F1819V&bʡ;y볳33}g3X[jԶZf}=5&\CM7k7RH;7eka&' <'iL=&S}-4ôC 8HT\U6BɥQqvSQs)TR`TwsFjeX1e%,F+!aTYd b&ILT&Id,*J#,R2LEKb1T`g],bŊ6ƥ)M,TJ*Fk$m6T%II%JKIJJY))5,RR%%e*R$Yi)dJRTKK),T)+%%JRҒY5UI&)i$R%JXl6QRjHjڊXSMM2mjV eM k+&DM̦feEYeL"f%6%ILŪ5d$dAYX[HkY+)JI5))d$)$TlJVR[%(dJLi4*K%,RTMYKI4Ԧؤ&-1iɶM4eLIfS$Ԗ))dedɤ5jRe56*,Kd1,*bffj6RdfԥijBѪVf6MmV6Z!bAVɬU6C-Ě1U֙b12U\j5r2IXYY """HHdOejDV0&,n'9zL9eV2wGdfNLOzG?|O>Ӗ_ڶ[J3:mݛrD2UKW7HrJ\ >8&>bZeUV c4̯澣~_8Wq]=T5Nע=sjuG67+y"=H1BWV,>r{5l6wm7;㿌9C!9{\S_>[e?-ftKubI+oT !RQZ*CQq2IdOsJ;raw¬te\!Ν)ryxu񽼼$At"o+ fgc:ZeP4VҖt*c*y+±U/%ƥR[Ҩxֵf4Ōּ|۽ szwvvZc?6oG[l5`HIc3NM}&-#\k7۬(8?nM}m>qOmx^IyBH[u9^53ꪤ'{(}Yŕ3k*jXWi44k&J6lqK%# wҳs-)K*sJoWAnUV[mYqb1(TX;W$+t VզUe;wl{\k4?>^ۓ1|EZWfrjzcȵ;|RT'jwt͜AV)Tz+\vSuyoָn-ɇܹtl:4_#}0|;>KԯEק{[L>*ѣgg>k\Uu}O:<ٙ]̫MƶmfZOH)S&ß1PJXbь98QO7!LԴdVKmK&Z ġ6HZiY-#bHVٌ (SCd"M[bثVs{ofeX>^7|IX|'9ze뻹Z>n[qֵkZfXUj ev햭Rm օGm]cRuu T9xfpw]^ {]l|J2-y'RURxvc3xxnֱcQcεkZ b)iwbf^㳬iͪoNݦ7 ,Â^j48I2Wsurc{kVWgs'N v{)Ji#LP٫UR8ݭjwq*OOv3&ϥ9~DBGϹ1aH_(07!n :s?3ՉVY"p=Cz|c+u WUUs~QW3m]wQՄzC0; Mc#pYG\z6kMU-*5=lge~x5N{8z8*n]Z}Nk}QҩJnoSΙ)uònwGeYֶr+"t0v<+#hvNM7 ~0iEf/5˭l]. Y|jTGy c7qPkwW:vđ}=PX'|U@u`M+<ϰˏO#u2bڿ+C98<~"n>{7|ޘ6wt8x40xVm 熵i7Ta7asdڕ+4UU +F 0kL[5:6aU\gkL˧wsٙŽR>ͶUS~50 c%|@K%SJG&ܮ DyI%_#ѕԎ5! #U+[ZT'+)Tmͷ/zWiٳu\jZtKה'WcTსy:{T9|NuT_ u{{Z?lzרa7G(1Xc>Z^+]߆F<Ǜu{Uz;ꪳp xG<=^П pÓI*"b2F!,KSҧ!{Ru\KH;uP9AW;|vOYn*; IaUYc Wu7fj4ʆe]U &)80g ̍WHtUwr~=?LO3xLeT_kL40pÃKyKW.YUV1oF8G 8N|ZFq6֥.jzUlx9yLx})Tv|ʜԩxSyjW@Wf( 550h/W b$H͓ZbԴl._/jڪ (I$/m[HUKZ\hiZ*fZʴ6eQ+(k' ŊV+(3e,FA1xcyULM-ie9rlf4+v+&fZΫ}є }ϵ$~KKh"bYlut!]Ì;xơF\-:[Cm-&S˩.;Ay.5c,l\#~<=[+00ʱG" UKfnmmUU㪪k3Q$w]>׬s FhǩjUWUUwI:YۉWUUw!wS8-sKE]wG9ުt5s'y;IŶfTe3StgnҘt}eUJ,L)1J4YϺ.N WnTo+ :yťW-tY%6C|*VzzD$׳!<  %H~YjݬkzP^hbyO[¤s/µnvWn۷nY[Fi{UZmHY,TT6ЦI$ҥ*RRY%JZET)dTJI)-))$RIJI)fiR%Si-6IFR)-FR#2S!Bԙ )-CZ )&kK"AM1%%k{_&^.6y^f+'m %. lxh"]G/BכJ yJ*]E!f mAP 8HLPÔd [1pb Œm0ОUhsG:[j ݥ'Y$VGRIi6|)3n}{{$qYNe,1˅6i쫷Ro#M I Pg q*NEJw3cj=<1gχWOВVk6Z#W?{CWtexU$U=5>'U^sĒBݿlj9o]"m('8 TuMp3$UF-0Ԙv-W\/}ԎrGmUVK:\լZroYbmߍkZLuZ}k~LrL~~iRww?%™ܗ.gI%.Il,)eF̰ l5l-ST"ݜlU>FFUHa>!5;ϖ,ǜ36ωxGrkjn|ϒ7>;dNO!3"FBUlG:<ֵ^ B5;}KR=mwi=>͟ެ4k.b,|ҪtT.j@m!n[㙀Gbyx)IXP͔ժ{K m>M* |z Jk,D0,w>|?)=rV^8Br !PǗhCܗM( PւRܚHgC"`(# rw;Me/\׼w,k(m,*PeBL@]*Q"AF.vQ+l Ԃ}}>'V {kꔀOn1=2j/^@CJA 7@lz܎D4fRS0.8ҟK hΚ 8٫ݨ9hnn mFwu9׫f؛a =k}Ʀ>ڒK7"Otk!^E釼sv\j{dyntJi4iY5w`c@q {wV}ɠ ѡ4TSji=LFM=O(̑$m&M1d F#@z#&4e11S@"BDB&H OOT0Lh=bdd`LFh&#IIhS.!ޭ*MCUJf xf7_%_~τ;|&*i}D6}QhC缏BƠO.3|A378ff|7BG WU! AUI^۷nݶѿh49m=9buªIQ}?֯~E]xha7:t)ɪE;}3%RHHb1e-fe.HBmk:c$ ;3 kFHٳf͝C-Yc8o~gBv=ai.J?-)*C e$hRb4` 2(%Bh)@-"XB)atU|}u?b!A%7zÔA ?AJj=S䥥]&t!j=/ Ể`yPǽ}E 00l\6W1řA>s΍o<|~45 JfY)=_&ܒNV!@$D%(=@jІL ()=.?-JUk\|(}'_ 6MѮxy Ҧޑh1P(M1| cgƑΆ; ˈPrqJT pq.AI(#C,2y^T7 O"3G}@ "u) EQEVT8ܹ5ٽy3#$:3-v'E ]a |!_@9*Yqq1Bh`S> 䅙KE4 ]1 nGaCe{ {= @qPRAc *?H +EXX*ؘ/HHHHHXy Pu` P$1R~^ezrIJk5y{g%@o#"I! :(Kפǀ7| !P#{!!,=dH!ʚݠ򁳓k}0Nxa%A/6GeH't}׻C|;PC.vG̮+93~7 t}xA1h`y)&a2<ǔr(sϻ/@h Cct|h,H`a /00WCJ=ͧϏ?<_т-ntRI*)ɴyN5>ھ]]]goP-mh8 zA y)T̼S ,P[LGщw3Kق>hBO0A#Qr(,5c7EElH)5vΘ`=8@@NڧX}gEӞEuK?# dbЖ(\xK!.B,]v ֪zK-lˑ V0hSijKIr/{SfU62 (%RKS tS.d%  GH#i`%|ua4S@2tA[ӕ¦$:9؀@ \ITyGc<4U oOg=a,"ҁp;{p;ڔ@~dWM-30fa,G EnOu!I}h4gx^S׿)9E!S0MF6 EbL@ly5ufFS;a Cɨ[oO ٪Zx<,3C>x0xI$CPN  h Q=?==cnYGsWqء|(s/h! Z#Gʕb/{^ooQ:r`E/rİELN4*(2P?Dٛ2hT6 ((!* bȃcOy!'VZ-qXJ&OIwz4J 3Gx1 IA S^e=dµ_OBe}^`y+&ܠyw*ˣӍ4hѣF5e'cx<1΅em< laFO7عW]s9wv 0̓X`Ev 6MP^I%L 겨pA"`RN$p!88>G-k3>w^P!cc8;/GèPjOdΌ 3 soЀ|A!(+xCfww8:㹌 4p7/tk6ВU?O1O!NXtUe \B ɭ C | ,LV7wlct9f"LrS8F)X,9 I>D>IYWJ9W1 # lyX2t\\(bB (>XAgpBÄQ"$L 6/1BXfq${=~Eo?HW]+h)c -]-ŵI0Vei=]Q E-4O)y_=]RҦ<=HjG$r\$(Q_c {QT<~>gU WjCƜܧ;p)(qɲ$^*=GOAߕ=IW']N#2ʣhVi%RYES-SMLe+(FZ3ٖZVFV4ҡTL3b҃ c%)LZ#V*6%QmTXXTu"GԠ$cKz)).v\Np@(FFdIbbb I8kZ) ۺɩz| h: E&9]ҥvvVA?>~Tfa'XhIPoBSn:-rK׬w"6bMս?"ׄ?\rB ZϨ\s 檯bU'[zLs0$|RB{ @=)q%?+Kee(wzO!bizs}|O'JduO:4c;f ָ_P UVkZIsp,ĄɪDIHo^{U2u bX(ߛFhfjJj$@B… N "Th7 dwt× 𞣓_Q0(2A2)|*_)\%+_c \kK4%L!3!2-jgDvYپ9}eX7y|{_f|3 3 0 0K=>]FIFl$o*UbUUXMDQhY$v$} aq" F(fy[VӷVվ]mŌdYm6բFP5Hٌei "P4TѲɕ J=GK=b`B}k/ԼJ> JhU+%?+?-mUOnרR~< 7ɷI[+se6Vc5\x>T"EdڐRcj*Qt{B O#䟌@}qGيs1s3DTTW0V⊭ P(:hAHZ1W1pUmEUneUs3DUUFQPߧS <$@9:5P~WfPȩ%eEZ( ; "亀ws$R(CNȢ+"-SUS V ցTn%T]iTkB]Ŵ!,NID99!4]1DQ$U`iXQDE#`l]%+uˠءNB5vSBw*I`%8vMh6TpX#Qhپ t ` HDl5fSmeF)ب@QE$1Q!Tթ1j-V̴ ENޕ.S$HLH+~oD־b'"n^c_LP^_8g6@ZcA02Tּw\wy6̚l9ReDk!0r*L 0iYN"Qf9dȋ,lfF5@I``Y-YDfPK-Y`[^K沪m0]0[h[|!)T/FѡXP[mf*++lI$$QUHHH!>&r@A +CKaxtx#{4s͕U (9aR…/ Rv> EX9Ey< 4ؙ0 RG|ϼ73ʮ~lb&=3aY 4HPIrC GAJ0J 0`₝Jk:AtP6Fx9%YZ2WZq; "d(̫TBRo,~L5-SnǤo'92ы<<ˮj[L=.җ]'|w^ZOwu؟JiLC D-2˲?Яc׏kչ׌K869$:ę>sZqHz@r *tHL$$Z~zìY͚\qE7g"2x hN37}z z.Qfh׾deD=B{๏#o]qr7qW\$PHw5~ #?2gv)cMr4Ovz_kg;1C^%@Q;%zp1J(q3ˊ3aPK@; Oܤ}ʠ}T"!b?FF*XOMZR%0jcq_ֶ-Xpj 6a8;p0@` ?$1%@r$dȢM(('(FLvre@5lʤ3̨9sx9 8ӶrlSiePj)c`E:~ؑUGDl pf ycQp9RcdM#8; Y8v +uR2a1|eVcY$G[ڴA 3ՏDXehDV\#$V>[Iљy]be!EeRj"xfhЁ\bzA y/b~R!ԣ`(- aI,(岼ZDW/2rclT8e3!0w|n?0ጀ 9Mv寈CRO Ba1Tad->ZRL͗6,.t ZPH+MpJ&n͕e{ׄ)EO$PW-+AZv N j)Ű`x(B '(V6fM9x;!bQEۍTt8hz,nu A z59fg6Q2IidzBkl `̒yiZ9#܅0!_fq \(]ɹE `3ZbH:2z9'R(AnPCu2c|Q;NL(wS48[%%3Q0]L1D٪%8>C/05ā)a[}d6i*"av!vnpPҢh)dA1DAJV;mbW69Evi}n 5ulqRlZ1UV5f*!Yl!\qqDpl OGCt<(WM.G[ LxyFQȫW~ 6\VuxytRHV9!i%䌝5N[b snJ FeJXJqܚ$.MDw9e5ǔ7aRnf]l*jtlލjSP񰑍p ~0 b B˱`-y*T^wDNRR=Fr!,mE{XP p>`ybGMr 2!-Ì)DIHŶ1(74J U#&3P9 ހ6`䤝?;LS8 ';_AdiB3nl Jݴh­3ŷ*2u|l v}o=le2>1;iHT {pDxׯ|ޑ "i)eﶵ߮6]C;Fq;&wN*-N9=ssk RuӼ{*\,_Dg:lOwE_-SNGݭoR}OEwxJ ek:İlmX"X %K3'p͛H fI :>v]/! @}ho%fڙS/͝Ȉ~E^^fm\ŌK*TGb`PFD1^pQ~{Uְc8PA7sagDIY,ts\ps'P@g(,|7`tqTZBͺkQ(Nj- C:wB5jg@;a,b2$DQWƛ^T:ꓵěYY'IiY[3 %(MpD,+7LElY'T.V ;fīj1mӁݵ]$ U;mfו;ᘿ^oo{rjVz7*n;WZ@r۷j)L)NZR7/خՌ!%I\K@܆QTna]C3A0rZYYxCE<FbX:T&|WC6$sеwN6/'wL'fH#<:)>CE7-^\@(f;V<= 9oiے>30?ƸqPBAƭɿ}r@rqQ I) !3*($0C s_onQ/:{xGEzl35>4rJfrD\34 YG 1(v(v%J ev9W^9kQ f0 y7xҋlB)@Z%oGi3,$FpCRn4%EH`sq͐ ̒^.,z7F(soQŗxUKǭrEczsWBcL5nP(rTd(Tv*རQaIQͷb H]'GiN? w}D:y•Fvx*'fu(,q j+L WQHt@;>"tK DT 40qn7coL0dP=I0FRDPBގWwي{Җ]o-:xDلyxzls!ꎝ(P.˷<1ؿG4"/}!L3PY 37x575fI'Gah†.y Mç\dc̀' N|, 郅@s{F3ax'OO/$s>iTm*|[lqmr䰝BPb k^f'aUL/~8KYt:3F$0DtxIx<_g{\9S<;r+]fst3,) lMzR,-cHs 'yXBy(QU?LQ' )]r揢q|>,səz:~?U˛`gT4:9tmMխ@AJкԡ8կZYF ҘbNWC<.C94gI{(#D`3*5̯m E%t)oFxH(LJ%$ųܻtGMĥg]Qnդ=ח$:x{b퇰[ tFuI]j۩c;اzygV cF7m&8Y G'w$sL4T!uǣ,Mxl1 s7OFf` }II0< Zəخ` GHi&^=ǎxu| @B (A "/}C"FJxiǩZq,Иb6Q/~#tp4eA#Աn}'Geѭ_H3 B :hKf3Tn9 .>ϣyB ߱ςX|=<Ɯ$o_2"QAϜHf= fm &dx픻98p|X\(t/Jr^%~EsrɮID10Cf0z28Yұ H&k`='t"VյJYmrAve0LVsdNs$0a:$v5I5E]Ԓjmnp'o{9#xf~x8dQ٫U138^fo91fhFa,٫jmyjW:4yd20ۅG :>W 3b)o]KźeTiŠ); }U2^58ffoAvlҠ [0[/IZ gir-ְn8MU-8̈ q&YN)}x3sFR1Ucrm/Qd鈝nitчM/X)3\/lA.sf`F/v@:8 FV1 ePJ j.̛F:*!Dzohv@8`03D\ Uzo֎`@ A~HV[:7: 64Jd7[wfIzOz=+ ߛ ;Z ruwg 4= AC_!蕇y/<01.C@u}o:|4\_<"H̨ =m QrU\z  Cgd q+6Q۰*fA4o~S}W~Vl[8-C Ȼ킬(HBlԓUPE*>\gm[798'О[Ra joUfMw6gG|5fYQ3 Q`{t?\?vTV ((+߷ޙJW?w4Aqϡj|ޫ$+|Qע]?d.M=,31^l"|߹9|y!O3nbT3\.ay_(D/D0´^K~"׃0~~Vb@ }W5\aN~=!392J}E<88n2_=p]+t)WSf\ڜ2]f$%IAOld>?LO]ڧ0cz|s|<.o6/ U_ezp3c_|˖͂?#<~O^SW*_s0<2{#wn+(["E"J2Ǭ^ߢ&_ |IO{/ј^eF8{qk\<ڤsS3/?rߥSI%=-Rnӗʼn>?C13:e\Ϝdâc%-Mo/~ǻrw8 uě굕O4EL"$8^??E^%n{Xzs}Q"W|稟24VZ/ӞRDL}U"vT ĻUcLqcp_}ܯg!sy6ɭO+wɐo#tǡzLqutz\r)G-n£\|Y{{wH]јyʿ)rNs$qHK Ty֑I7xxlXc{s7⧜TTOUvo7{~p?OU=ʹx }Mqu;rIB12OE#ȡ? ÙWb8Qh맊,W^J}v/07 z5__.&a";+m)l5Wzm?qWT~{r,_w}z-[}UQC1F>oɵе#bvV̋!buM!UgO?|w1~6=ߍI j3-W>d{uGr5?\q|.dUJuq~Gg>sAh=|JǨvd29t.n۹O1]}T}'ש4~O тsAAf+I!,Ua {OubQNK!?W~uR_EwU+ ZH ?_Ub6,ٻ/O<JdeէXK0o\ k?!!Aa(6m VBG 5XU+9f5&p׿Ml{+zY&T,Y13)x]5S5M6Sa2 Ȃgn?> ,"PJ+ͧ°=Veti1~Couydk5\K%hZ3o;# 8q9s)?/\!H_riwc]9 bQ$ ժVjF VKj+\Z!\j4CRtz֡uJ蘭Zj+^~u)]e^5^WQuPeUJtRQזȶJ;;N2m6IK5<,qRVQdR"<Ϻ~Uw֧lfm5mE1Flҥ~M|9~}5JjȢ({܊ /@b)Xѯޝ]zp`U9R|7c򓪧I$II$$+I]Z֮qϬJG /h\EL~_CyEg|^(DWqq6;G6HƍT1+FL2:a'K_"zS&+),VR^۶IN&eúB[xhBEJo*.yN{e(eCzUȑik)e6йUZZ5>u5˘ZWF$"""e~\Q.4N4sB)ۊnVuv[V*Nk ۽?Q]Cl~ۻ~5m'nI$I$I$I$I$I$I$I$I$I$I$I$I$I$I-Zm Xʒ}I:+:tWEc~}U)?{Jy>*iqW<* `COf~ιεSZ֡90)4CKxRy W ;@I}:Gm}m*[qwzwL!61USTU3-T~3N^ݪnG3&CԞ9imnA8a߮CAE;83,~KӖ1cp+A_xޱŲ~i -%P {^*M[M6 ), uԣùVS8r D2Ч|vpmI+?gZs7{czj'q"]e4apAծ'\ ڐ! RBf.d!7Jl8?bkH$rԬ{Lz0wp!KX᭒MI! *a3f1! z>W a V)@}ѾH3-[ F8 >`:vyt .t,ϯ_"Chg'"t"[gU Zǐ" 9nav7̈́Ļ4˲ Iqa3 .j"J|ZBG6RK&4W!"!p@|"paWA,8@Tp0 A"v'S|l 87!HCiD ͣ22JΞug]LפQ :fHזFW2E mO-BލRvfO@AP7`Х [_ wL{G*shԒg' U||Yim#Ј*_\KKPm4H5WstO݌"lH7l >'BO}E!@1>-.vB-E, 4 ogVǿFa "&0\OE(>31o1MZH ;K vsx1v$x[\XX \rd̘8*' X[TڴmSTQq-9iդ\þ,ZMk.99&W 9ćG Nӽ'͌P-$]ϹHgx6yz@UUV}(v|$. T;/dkgvHGGֺi. K^ ڀuvl/  9_8qd$jȓaմ' !Fxj8)]ySw2{? _@ VE6lĉ#A۶ 9 $$vLlx7%44l1qhƑ"Ԃ ۈ_|Cй_Hr>uzKj5=3s<"f>"Hz5G4=2+C7f=sA+mӏ,-fŸ@%>(K=J$̙rB)x҂ B(Pij^o_@{;R!pnBB!mrwgϪ|ךsp7#f3]}"HY 2̈-_t"Ԅ'>PD_5D nT ȈU#|Qs}QT ޅEHaE(kٳv%- M%-rijܬ esp  pu~w10d |=-]x_<&wtxSA_?IPW>FY=(Bh|EҐтT]J, ufXN~;ׄ<-<; ZSx mDvR+E˲fR^b% o0ft$x@xlv DFuBTz+1ˍ|6V1 sO E^#w)vEc&WPVmnQ}kN*߳{Oq{AqVV'N*}rҠm0ZW< (aD X;S[G!`FʋBi뙶nBkVb3JJ+E"3 q_׌_WtX;dP )eȨ\WgOCI*_l;P /DPɛ*ڽ x0!ik\1])h6ێ)Ft3;N]8w"9TrȈؔVT.Wq:avo7%xOMy:r6Yљ8hk֪qβ00^Z ĐoUV4I B'TT6uxQ8P&Qolܙ\A .t*"'AP0[У,9.*تA ZLj$LeFRV*#1AE1f2KM*,АD,ĠsWTQ'c.į g룃ҎSbxR;EeHQEd}7jG))bA^\J٪AvC[D* :tpm ;].~&gfa[@jK aJ*:n"$Z$785 Dfv $;wl^`u-|4#(i 4 R9G|:ys#@N ΌDL<5$_!srl.m|l -d~ fA9TC1h$+D!ZGڦSP0>8x b*@*#jHŝRGWf#hV5ZT%)(jF"15jLjʒD R "TXd*T12)jQimj43(Th %)&eI6k%$#YDd+b6DmhŃF6ecƵjKH0hM"I%FMaQ LJlXiIMILJJVRK%l5FKI,IfS[+-6Zf Id1 V*JfY JD% lS,@AISRjmDKlSQC4Y6c3FڱQ$PjC&mM!m6dY031l&YR)6$EhDIMP)im$) J I4T,IVٚSdYSQ Md[)4d)fYSiKJJMai#)5HR&II*[MERi,*YViRY"$&ВkfVIjZ[&)RգlllʛB&[*2@3Qڊ] ȥ"mElZ b8.*\_l)|<1$ޑQ0Yy tR-γ{Xe;vjkm ѵeД8Ȉ̧vtxB] ߞN@[&uE&QNuo~e$5jݵ[r)kv93Ok# #ͼ_iKENA>"&HNO=>˺b=؂W=xm% n2ͨɋ{dė;|obm =}ǂpy!_!q$|\*<{SҊ⨫EN1ս>t6O%a Gc#PSHټʘgf~1"!sNْUq<,ol9ЇqԱgRH¸H$03q0ǒ_$qHtv(C:p|F'u;pS&GQ],$6Y 3}%Ul7α_XIl:Z'l 櫲ԑ4y0b\,拍FyS޴z 6 xǑz;S>Q_deTQT~ߧ}xv*yx=LתSЂxGi뷏BBl8thn ÜB5,,1^^X&ehmٰ4^_ vf-Y%7&YT*%jBP$yIQIVuR3ڑM\@l jX3ImTM=92;z Gl$! }|(;+% _8B2G*TV$ q $fӴ0(O&h1C; Џd{DʔP.D5?f~`~Q?_}u^S˭JUKlYx. .5h%E 4--"uݼI~K.L00؄ :ُdŤ@NBp" !1ۡPtt R ,C}nDM+'R}(Bm.*XݙavӾ {t#K}_Mf =})zA䆼"vaJEC'ZbA<;uGDƖewkD]C8>GvVw@4ҒLB6w\dU~9=UfjdrAk"{<˧@wf\ xx{_?;;"%'>/HO=f|mi܉ 'ċz75Z 3$[x,Rjb6rzPD_wLEML̄U`u%ALVL8b:1OclS3sbzӳ ot}BYTkT%D?!Qа*|)NnJi}jyvHf&. "}?(-k׾T(#xq5EzC! {O,{|J}qzg rQNH9"^(cAT1m>&ۉYIB6ݺy l)`|FCCģ B$N^\쳞4 p4ps>q` ddd+apU QK@U*E-ĔlKadk1Xb!UuVA JSgmP +K7:JRfc&9pv1s$m `K ^6seIc̮~20ZTa}BA򿚞\5V;\R dST֋iG˜.(o3nwDZЍõK-pJ"6%($q9Q2 1 Y%tp |t>S@16hS<[3HdR:A~ 1B"ftR7dd_?!\@lj ҄5klC2@%͞G/qpsrV$hF$RR]gKADDDDDDF"7iVI;UCDZy.[4Pۨ0t!i82fmCt#@}yJ,Wy:+kD$Rc@2|X58R,kv:."E!Y^7nΑXI h n ̀&l">VQPSsC{vɵv e&#y+ڻ.> Z1`&8<2eyM$]T4)_w'}%4۰ӦXgK[ɣW~p測@нbuӌIl{yM²D% UTXT샧5p|xww`ֵmNED[l(q%%/hh7c-Mh4)⓪U-EiK)[jRC*CL!6 dȲ$Fm!$CZH([$XibM16ccmG(O've;F{-I(H#lbL8U0'5KVaܨ΁ـ9K5֮la:5 Z{#KBp+H 40`g44gA8i!-ϔSHz!UqJ?F׳ΏmQ{$$.['痪!_~R .);N-_"ȝfLn툙 Lͪc/Ɂ!MfS#NO^{vINOGr"4{Eq)ss\Ӕ Oya(XLhhcs20Gc^6C]Fpq]ɴ[/y`kmrͶס¦,#evUY χobyր }<Б%M~( 3J/7OIJ&|)7BD 3V`"iAG̷r+mT8Y%i)&&@`%Ä$ $"/N4 :i8,enN.+/Z6m Q(ۍ!N<+=#!j9͇APIU#0: ۳/Z5 4r,V< SA^yvW|hP3Յ>yH ȭlkf3c=dh"b!@LA$;e'L8)A4 I4$5$)f Ӟڱ6ݐaE/Fx}T&|9ὁr_5r+B])U FR4̘H]ʲ@)$<02%`l6t۹YM+ .F[mڢ'Oi;Xb,@Ucsc>a_d,!owhg 3`JP0Āa(ql\A7IS: AHH"" RY)$q9 G8p\H\*kǷjN@OlP^MdC CATamP UD0 Cs1GJN f;;6i"ȥ"P1L¢<`@lTW]c[[1r˧EnUqrQ0TR'RINγ\UXZr8j*t-dQB[lƠcO'I *{7mvf{5H&d*9U2E9^nm ъ2l]L$:aHh=/)ӻZH;$2Z̦2$)DQ lY0k[:B\RJ 4YjJ.C$d\\ZL,òC:֕C mꇸAW]G>Pv*(HT瓻:☆s92fQA{ "$E@2N2Su \Yj'6Rs!i(=j^^ws fFn$l \m!!!FOYe^ЂήZ "!B|Ka`*"2XM0 j,.t¥1Bb C:ytv͆& א"I4|$w+Z،Vm!Qq0ގ\KqZ416m b&'o0X-{u7IUMg1Λ.qN"#-l"黃knKPB^XTE*(J!MU\B2=\0&pu-q$:ۼ`809BIg-I /SJ ou` Sj"!9@K┤ sC JrO~Ӑ7) ͆p4!B'%g KԄMpIM$=!3GZ@j .7$u]MHS^1=UYE%K!BYe`3AթtDn R H!urV@6i8pm8ygXTX: ɵ;¹$.κIs@C!#|j G/fiB{CFވ Yi-X%Jʫ$q6?TЋ16Ǝ4< _KtfTOL^gK5JyVlH@9s|=gCloC``}_;H"Rv{=dO|N Ϛ!Dq "3sW6Ȑ N i,AŃ\@!7ލ\dl4՜H\`5TbEN9 0ɒVyZ!^:qn,I=ZF"!ApN ug*I"& [k/{(I' aγ;'|I  H,bsFE.* n|eIىe fu"?:ߕ6U騢!=ĕw1SʐI dĩZ,<ߩ g=ͳ6eQNrӍqhQO*TU0o/DånJf ! TEQ~15'h$Q08[׶@BB̓*#sAH}E UQ3yХ~mAO|>jr(@ @S EK'L~C70x: U]RBL'-T{w9D_e}Pב%]D;bT!QbP91..@}t>%P'~G ";{ذ=tCzi>jUkd'6B j *&b^5' KA+$Sfu"G vcm fn׈EZR4r$`}~{™'=~h=±k|6xNhUTVRdR7@fAy5H:#!, (*[=)AdZTEM,T6 "[l6HoTVv_Hl €mz,GYP#sҀp97AZ1_AK( b yNdU<=|U.:,"S WPhGoLرuڨhEBdH@d:ScdY0qdH2me(C F-jf(-Aa.Bb c䪴=^UVOoڶj`U駞-G+HQe>P'/WPTpht>(S+ ㎨_TŽYc*@;Y`.^.@v3HSDv,UFb̕u(c6:V{m =h)svM hHXiH^C8e&o0 t؞Un[9G|H!W TWt:C~$BTdEEk%6"Ɩp3j=f̢XÁ oqmK}ؑ1gnQZ1&eZtd墩"*S)sS&čLgb`Ad $j6!.e%;$URPJ"xo <79(Bv[jeU(f+'`5 Lp+DX1({NoQy+/K%0&XwkdjF  0vevOg'U3QEJv{1Q a6f3K0 I8ǡGŠfad_XB8#nb~*;M P! wsi}ON.$DUQ]̅WA;&ȑ;%LFy|1M`Tfc۔ajY#h[{h*) [ҷH[0SSZ3j1UHj9L(DI4Bc6h)4JJRDdf@m֚)A"())Le$)%4!JdQRJ%ԩ(dD(#FJ1LH#FJ$ Ab #=WArn -`!<[v>i׭䧗'"e\yopT lT{=#oʯyK>0B%CyWPRޖkà`뱘4 zV(Wg bر HQTAKySbRVE.I]E#3Δ̣CWoxz/!y.˰$Usˈ1W0l5tf6AW1uU˼`D$CX$(,)E(?G/up۟ U%xK_P0ߨ)noZ7@դmxK64E'jLN&A]A.+1B X @@N"(-? |y]U,Va/LJ#Q|I"N$OWJ@NUWڨ᪪\@R!!fi0BIF -mmr*Lr#R2 hTi&Ri12VՈ 5*"[6dRSH!"(!6YE%f,bRL)2ȴ()Rɒe)dSI#,i16LDebl=#M `*G;"9]ES`D!qww0Q" *QV:|Ssu{F =|}gxhkPP A+@w$i-/S,sǫ+w8QJC]B8Zrȱ!HH ܊3[%cRO=8 Zru2=(FȈI]č]II)m-܄TWrRKH/ўSYr[H`[|"I&@$>;~̠L D9B>Ӏs?xs@ (a.N:Hx얃<-KH߸n,DtY[aUJn&onH{xEݷ6,r#vuC /~$Z)ķOڈUOw(<0S(;VdJ~*ވ oTt*<ֳٌQ2 ɦs']m,ɲ6Lq)Eb 5߬9 97R> %-f!@\J0!{\XjQB@^bA8Az;5"he3㮷R; 5pe##RE *V)mkU `TXi]p.{$:1f2K$ZӬsB0'g!iB&"Agw(k 6Zb{uވaVB(^(;R r}dz7jT,6_01Nv< <}jU^nV h/zRѡ$P:<*#@2J!ocY7=u'k~_}>=~yU/?|~ gη~?=O|_/o}o_r|>'?®S|{^{c_>SzE=o[._ҴU߫_=|漿|OceO~O?G_>/~^y?~yCgm~߶uM}S%oqW_~F^Z|ߺ}ߍ/|{?y?i}_KI0B@ֱ3?38J2Nr2RWdRȥ" ("&̥J9"hX*r9VpUCOS%)"ʇcկߡ|!8@ɝ\~ⷎ\Q32G~r-GA 0A@!( $ 0G4Y&Q:&i4:P9:9åQ_DO,*LT{ک:Ra!%㪊T3)Ԫu h͜(̡([i;d;YdmwEݺZ1&E;rQȨ*VZm-՚نLp <.Zc9[{ɑuffʪ͜9fW[9s~W&*]5{|us:+ˬ˷5*piSDD(wTLy6U dmdSLĎ Qf<\!S%Zsيf#2+>ٮr!\^iµ[5r%5X8|*\LxoTը0 6҈N](QB$.B4 S퐼;|BwT @-Vk|V݆ʮܳٻVW):4ʭU 4JEQ]tҪO@l# &́A(J9 'q6^EQD4Ɖv(LUP $ *QSQQqCڎEC9:fx2\`寶ĖF<pi)$ Z!,[Ha&Y .y!^8E ^BՁg\;Fmmk8(Ną"$JiLr+#3]UQ ۓ{mIQ-FP 8" 8ß1Mhci# $PktnR2ۙc7$L"?W@ YKΗ>n,ٯcU禷P^feWIJ̪ϓ\r=[gᵶX)\fM֩򱔱Z5 1gJZI Pժ=*۞M)ڶwe#M#hLR MA}_)R԰]O0&Q@nL`B$l"*+ȰU^JP0 $Qe0  IP՛!AɄR@KEJ% ƬYLp/ɛ=JkJ< I1puM 1Hm_Ywߩw88dof?^I(-Ia_%: <ě,D44H X s<.󗏳'۔& J] rhd!0p3)g/!QDXI-)HaiDtSCLӹFGzpm21''4n,¼ I?{/[:ڭ¼yɦ>" ;G4JL  r9ˊ "%a^R$D#:Ng=oa1BRٞ(, e,4zԠӛyU1nޜ)E(@|xgdHةu$#b?u+fd*2D4pIKAu*r0A"8{, )ܴ6=⎮Տ;g&.nm\SMںJOOA &JCIMs}\:oÛ676^卣||mOC(cƄ^^(xhʬ:dU坯/{ɚ|y=϶f,nqeiK6j1mYU{wwEUx+p?~i++ppG0i`Xܹ TRhSIŬ3d L{Ãia#>Ú6Yj;$E%ۏ.n Hѧ mL54mg{=wV+%]uT89ѷ7\{w6|UGZ?gg]5^|O1c1nۂd˻q縗 C.ܹx3{Vnxnۃv}'AxsYa2֑APXc+))=ICa*P;'V>"sR6_1:C,I1:kmz˚۽<m/aRI 3gll****m/c231 *Ƙt=-~Hr``Ȱ8[GmN ֜zmb7< uIH t9ӮyYts߮t+k{_)bNܔ2T,(#ɇ$"%&ˣjpc^ו+IX/Y,yqnʰW6g3Jۭ6t2~j!Xߕ+}ݦʵ浦XuX[/ ]\]'\5oi^ɤ.Ei+,61ߖlbe\ $MD#tْje^ricZFUFV "* l4CZK2i4cE,\6SXfɵJK323,xܮUd;K7t(&R@H7/ "t'e?·YK7u7ܨ){=jHG״J_jUKފMsya+%je~w4[ŧ5خWǓwW>e^n.C8=NM2ݪJbWiy+|_sWgw ɘtJkd(l1af$&faXeݒP UQB 5Ar bmL>k2Gi* )4do(/qTe)~ksf( v[";Dwe9|xßթ61hȁaZ3LJ2q.ZAmR)vVvmUU]4M4E[MlE!`6m 0u9uأ)ٷim60NvA4[sLP#1I YJ8)J~/ dSt̊]38juDRQQOv;-}gh)II;+Q^T XԔΛosWE%I w,8\?ҫ n:2s~eÇb9yN:\]"xŽYfUf{1!֫t-~OZ+? [minVJgݿrblLYcl[s+[0 4IH܇H77xMt5kgerk,**jK2*D'\Xr᪣9@C7*+^ﭫܯ_5]+)bDG?M_K^Ϟxk4bd(f*Hg2H,j44yXZg#bܶzj\pPMh1Jf[cm̷> u vegϵUSj]!MҫL&%o[IAY>1+@Ҙe*&li,*76M\3)%iLfi4ILfL$եknI K)IREESbbٖf[*jdhjfdL c"2̌+nVW61-[}^TMz؛IWKL22L))µ9J*-\%pÜ뫪b0c&jƲ2ZQ6+jfyEmVܥB6[2i-bF2 k)lCkTՋY2ɋhH d\sK)%MDZ^nMSmqNɮEus$ biB e%Xhȵ55,sss24)6ɯS֩yJ+, 2K6[5KXʚl[eLإYQR6HUC)&(&وLVcT &Čԭ5ŶҕjZ 2 j%b5T2ba[CMե+LL̰C)(,21f0%fS,a0%%,#U#I&(Kd!HY,IX"ڔeK"BTa1B1R2Cf(d U d"T;UE1.Jj21H֕]R:rIXi2mU&1g2I99S\ᬋ,-Z&2fJi*5-̩.\ʱS4V jVYfFFF*MZ0J_JIGI~ Pȡb$"bkUʔ-mHʀMM,%4ښe5TJͥJRJldi`&5m6cm6cm6bfZږiEYUZK+Z*D(klm%Fie 1&6e`lh*VV)m͑liJj4[ST4ƴciSP F5[iiE2$$RllU--KML&ڲi4ҖMl֔ 2DԵ[6[5UikKflڛlͫA%Fb3hRX*h 1P0-6jdj6bDmQe@ƍMhi( mثf$1YE#Dl E+I#mjU[VܵmdILY,>w~zn)nto]&U %t9תkmR-a6J!L0#]UY fE%z2Xe&Ϟv\r͕N7>I.z/A߹яtbm25 1f (f4 A{3V,Va̪`ʘb R+_Ur4K4W<_ZP#)oX?{!ZЇ9Б6- 8f 74J2;g?_$sF#9:?pw`VhjHz#h#;BGluY[qJJ֘m~M(zoj˲[_KF [_͟"O؉:9ϤgQda($ 'DI$$\4tv`~g ˊ1yey1\@+=FK&I0e-2L0$:=vȢDqI&3+fs6{uGfgdtl,Ʉ|]0<|va(=V X꼪ۋ.t}Y,da37ZCai])`uW#2 O7fߞEkg'\6~RSoe0 F4/5iIdCpYJXiMTkM4i\VQq+Am 1ki蒷o֜ed q :(L&oeF-22 iEAt \sXFz)iŅ+2*IHRcRX:&f7a{Z1g@%$4{$`u6'pb9$b PJ \M=䑴AŔ∑bA,6]=H*N]X@Fpƒ(pjwe5Н(A6( ͩj)qJD8i q.ӝBk7`S#+ cC֯y&It梆plUI\ dḷ1E@CnnU9F [E0KC6lSߜY9k6| (SzS3?V!>b"P$0?"h*)T4 ~*ZbM@3ZCjV4kműjXQcړWyI.U{~'r}C>g?D^/x=1=MxWWw[q}Y5qP$ţ3+U9\1uI#Wwu{}Kb4m]#di)~e1 4~-1h$668I!L  zϨ(͔MBr.%FI 0aM21 1M%͒M6%B( xX,ܚ^jXe %%Ouј*Rd)JV5D.o7P0L[3嶸7ujRpӂ)*8JW ?h$ B>Hv Q5:d4QƬaDg;d@tk©XOJV*_50jIޱތwfB`: bH'1*⣎cћ^EF)MQ0Z35J9ZXb4NjE7qf)i5J дj4sJ%,JaX d#5&0[+{$^!Sz[oD- D15LN1 eԲxlVgC0Fֆafhe j&=o\ZPvEVulZ7 a3i-@LጲB^IKf-#РdMfwd.$%R*T&'Å%5PY R&eThҠكP.D[۵%0'ki$+Fk@klXj'Պ/ZzIK@5Q9[o kY +v(|8wqk"7c[MqrhTJhUe( Y4E*C,٦pĶbf@]s 9,ȊX32#77)In"4g A3Eob BH8Ie.M0hZ3`T tl#ʜiXQ ldf6p Ѭ҅|6l0A_(4([%""M^W9oBk\c-aA6)LkkIa oEhKHޖÊ8XkaǰǚjYkc9zՄͩrT6j^fH8h6Z-a$Ѧ,ABYjTP1Aw]1ɜۅÇ؅-QwȈ$q:= Ā4js(ҳ@he(CT^&"ZHZuF!$qjR$);v`b. OJȶI% *!$6bV<#te%Bً`* [hMk33Agꩅ߇F'IժTwJG.;Ŗoƞ6aS,KnSazV2N hUBNJ c9mFV-3I6hB`8Fk޺znjlnS.GuF*̩-i 0djZXڭWvΜÂ~]uQu3NZdggFZw gC!3p Rmֻ+hC <{gvq,J;A8bKBbTM-D ,~`tuI/ʃۖCjnJwХuy!]- B CT ru!mjynI%Z3רHw老bmDYWך%(?1} \8ipkv8K?"m>o3b_)%6%A ^̬}AtJMQ}b~.-iK 9Ƽ7ѩos#ZY̾nw#9 Ni%AXliyKlc0cm 9BCQ K$Q/f-ncm5!5C%\RRJHub6ѣFrxhB&XAٛ/'Vu&Rip;Wk%Pa) 1JfF,*tX$. H;Ш|΂LfRR!2a&,9ϖljtz/{Yc3LkS+P5mf$F0#`G# 4Ԣ!B~+k8cQ o+j?H$4 2 &2|=ܢ=>"Dn5k-YfoB} 3=)!!EEzJ]ڲ͕ =@۪V1DHb(q[ajhtHhQ\߉@H54䉤ѐ{ Y<8Z-ņ> `4vja$4 L7Z fY/}rv;]5ִGbfm444/'`L̄C JQ2B׸GA0[]eVM1oFVYcr&k)~MqmNpkoF T NdAF TX˱HD*ՒDR9׵:eƃ2'lXћ5it*\Ѓ }FD"`Ľ^/2dt]n}V3, (2D6,Ip'EYX/)᷊ےv͚fFG w<5UفẚXVſ>];ط30eeXqN.烛ݝ3ac(f+}4cͫLUIm,,4b)Qtd';ri/!SQ:Ti^7 oaI8b!>w8= 4*Ms^7̽nwuW4VU\&%ZHٽWBWIx ?"sa #tB0]p|ՁX2Wa!RC-#68ܽY!/szYEzF )B$$0P<6v;f,dT0xNm$PIqxA;g!HrWbnJyGKte%K(P ozh+a1#f.hoi˳EåeuX"]PeXJ; !DH5Ps>̛Eâ΢"hj&NNo;\5=놋L:Kb;qTdEh8 ( Z7Ofo@ KdbXQabީ#}B IWAg+Qњ]Tt:]XE%*m9t|`!4@!~:Y mB hK<&fj.%m%ð63C> ǟ&VWT7d;r AЏ  )g>u;ԡ|+v֋91vl;gq}f>EK0;H촾CP|K2}zG؉ƐeF6qmˈۯltu+0mfN?]jNcs]7߂XJTBD&# u\]h=ؽ`λ=6$Zb! kC %]hۉa}DecJ [4i9S9֣FkZ'Ny3q1s$%p4 g81W|)IMPR&DcoFsXvԬB Uzq"a|U6$d'#Pѡ nc9Jh 1!aVE*3K[&{z;{3*d>kGY[cƂSKD+J':qA`L( ˽'eWu[7pI88WC a"CpP߲43K4 Dt&UG4&C%)H  z@ Af4u>f05(G(iW_}xZ0]H]hY-7w6 -XCrހ:Ezv~|!0c Cb`͖cX-j+B 3L./q.} ztuL۹UY3u{6Uf>NUΨ`m rT,b I+ڐIXB I,FE ITmKS6VDGw^9:A()dH\E$t-GΎ!BMS&K\QZFFZ g>3޽פ^ FF;Pab]N:!t-*b\6jlGURR0hٻkkrˇ4U P2\3qG!$SC;{$[`Jb=wI|ýpA ;lQ7`l eBYIMvoAH!Q3Jy<<AA=(IG(K5~lKJ6l*n~4~Lc&y)B8^< b\ ;eq} Kr,HM !^HPc^Gb kUC { >)FY3zu^'}=_XyrsNks:Uhs\cA0 k2{59V&bbcIe52e6mF+M4cglXc m q@ U fjim `֏9#Th- bjyJdNR:ni/cf|mRW^l%_G tMKfz|p-da;ٰ"͑@vb$Չ1$ +;tfaF˳bKGzΑ:eZy ڐmT6$JZPi2fhUpрkG>}:s٫KvӺv+ $KQ=uW08h.#hLU3533(fPiq߷m6ګ=YS, C`n)0ЋWwP_)HHHhL3(̣#14i/S"AIᝏƒ]` JAcK}zI(E_ uĞ9v!p& nabIx%ZGM!5<$ֻߟ-K͙~]N' h^v"Q6 ڤ,ɖpc=nKFiUvt+!K.vZD!J LD>XxՎܾ>Pv[^8OX/"13Z޷w7s1xMo1ppBtlMY&2uCb؏WM-PȈɠv816E$IV-(PL%0tbLsY#)]@@EPnD0NaƉUKQCѭZٴZ[$՜hfSgia4G}uHnB#!#U4+躣D٨nV *ؒNJ ߼ ,~ W(e^k4~tkx{A)A}1id@!J"bN@dNE "LDVמZhDE& :Cz Dpv0!odgR("NDR6e~Q~QE Oaƫ1/SauuW-Өg\DՑQȩ{Os|Xh4DsitF\|v~+_$Dm#UߣN,%ݰ=TƣD, '崎O]F&}`@>XZGI xA@L-#h;HF$HkeIF܀XI'*ɧ`v0p$OXIoNƪl;YWv;Tу]-˦K~|Ukp뱛>O]ϊH0Ͼۯ)M%<;,B桠.oIa @;_0(iVT,e%=ֶkw'-"D 89(1T.#L).:AG?KlWBw9">ɸ+onҬ-^+!f1LTLS,c01X*jk"ĶMLVF MZf4,Vg)´?aE.,c+~ו@fafa32Ia*̖Y XGlrnr4+ȼūSuïV7>-5ƦT7.JITqDHLi @ԄNIMF($210 Ur̲rNf\ۚ`178ۓRMڹ.NJM̮.6q9f͛5+6m(Cj8ܙ2YT\79˜U F,lLGO9>N011 LLň=4cFZʱiX/4 `)J+2VҰS + MaaF_߳{c'fpɪf3w9{+SӰ(K^v&11#Jʅbd_A 48 6B #{$)JpHU Hh_Lzi-+[U9^_<"Yȗ̳,x.ڥL00X /YMZ4c5Y ̄Xɳ{=zaw5-V0wn 6n46T)e +ƱpŦ1}=͑J,vaWh5me:t/5WnytO? C%]>-s]s.MJM^ڃ=.K/ 2!tFfe!d2Tc,b2R!\-<,CjaYr4ZcJ֥IʳceFX{?w$y<)ױ92[FV0dW=w2ZKGg.=Kc1.fƴw$MUG58K,{2 &IOSBj0Mi,X22Ze^W|r46)xߠGs7Z0 Fڙ+ iS%k2uF׷#Z3'-ٙb3ieZw= jJI*ԗr喖YcEY1+VMQKUjjjMJedXy|,%KdMۗyӝ6&єƙgje'c[x3m5k ̤V{4iV,- ʫ1x G^z^DbV K톿363_~gq|sWD޴5s^GgsKV{3F͘w`K}e|VV܍HOm{cRHkb@rDؿ";vSr̼9TۓJ,eba`cWRm]LXѕV&A+Tr}: 癊|do~;|VFae+%ei4ɬ?Ge{k3,^=kssro33o8|DDqomB ^dF݇ YRZ0fOk#K X1{*dաTŖjV10 ɋ+zQ_ }˲'k8CevIʚy Z2+)ښRV $3.Dw*v<#b+#IFLRY1S-SJM6m*m3mXMYeJlVڔXɫK6,)EεjOGy< OoMewxa:(ifR&&t~35$z5,K4O5?(ܟ]:J]ϧ (͊bO+d|&uu=e(}W0;n=NƯ&΄̩u%+(jTe4لѐّ,MbՃLLјcch3XȳEU)nW,֮Zf&_WGriqszd(n.KНUά Ux7&M$aT0&Y 7rRzkR RH|66`饊d&JI&ij槱e,7hLnaKXŌ$yl^=wܦ4M޴S"Zm  t$W$Y_Xq1=/Mt;:}CωeMY<&ڳ md|<4ҋld-rN%8 *zR{r.1@I!uxv}Qt qx !W5)JQ Wmh}sf&}QǍ2@\Л͒ydrHX8i+1 HD,M3~Z J:>Kp K.)(J | _HlM^h>&Dw'4)(̕#X@#BBH<DC_݇9s=.7-idId @P5(a#FRAaF!y@ۼxZit ɱa{;m<"bW1۸7:izt7Uʍum;Wlgl tt?Br1 E)-.T+;n׭%VNWx|%κJzꚲ}NPp>LT{/gƞ.j8ݖDzmxm;9_ohq/Pkokx!Ւ^K &͙-K5y/9^Iz¼G{~-9H}[cݳSՉ})r>h,c$aJSkXّ2vk:!)ߵ8F( W=flp;(\Dw'WW݇ɼ (sQ7 @vSsݗOE[Kc^sbZ/5׸N}bwUU^UC#~w)#޺WJcjkGZ`f|W۽n|LUڑH,Qn*/]^?eq%ɔa+u<+Kzs&XͩC3m\#FJӢ`H-dF4۽}SN̼=M|{ws}H`:x,*ՒdĘe2Ք222,/U{G5֯ Y}U}47|ם.KjWQ5xC{A^AjZY^iw|u"RCw1x|LWގ5O]eƟޯR7^-_Ջ{.S8/>?q~W宷8]I)m|פ5z{.C .WY6-<љݪ8~^WhsoWY_{G^&,{ՙb$[[&Td:,S@^'>% Wuǵ~z}^XMȥV`]¯i}:9Q{vzj~{.߅^'% holԑuU\t׾[&o,u`݋VT JŖ02²2eAdd++R"0#20X`eJ (e3c!YTI`V"(eF,E03"K)W-sM-{)$\ӕCtvrío5ϵpdU[\@ٻdÕX$q+Cع9nHU]el[·4t%o}ԧom uutq7dCLBX1+. le`*H"jEm@nlaoV߸$dטqb_EX IygL6a\er[VM菟&`;vD=2rux!+y{WA*ЄJ1ФD &={bEȺ,݇< 6W;degl4qXu+Xw;Pӝ c5kL]،9ݍʠ{[v9ޛ)3 ;flDjJLI:mӒYZ5Qu2b&K"S"2T`,PLXR2LP,eYfVeJ`Rda)ddI 2vN![&MQljŢM,hգjR6֔E&R4Ͷdɬfc6le5&Kf[&kjfeڊPѤڥfFH2X2A,L ̚f!RR[%[TRf)PL%&MңFC&ͥ3F2i6ZT4ږl--f*Cl&PiM-MM1֦jT*TjR14SMilSM)K6Rd6BILfIeL6*KBifKJlMMRM[fRkRRL6ͶM6iMeMDYK6تͱ3ik$ffMmZ-HʌVX eNr[믇]혋R֪VFj@Z+bٴZMMdDA-Kj2eXfVX12X)s749s;7vsagsNImvr5QݑqdܴՍhWk3'sZ7qb%ՅuXUⴷTWKƋ=?Mnhr^|: ۤ';*Մ[2 XUGK\>,b2|}#x/ߍJ9ϕ|W 7UYa ^isȻL/2$9~A{V?bsWN즪V6-'%0{ y>Urj2=iN}Ϻ% GG]:#A-e $d=νQJ:{'%t>{ŗPMC^# [ӴuZ6Z&[6_)׫ɔyJ;.{Iy|[bV-qNl7Hi!Ҡ xSV/C~ԏR_ wqM{^q9eut{6mdc>z/*ߧ֧QM+yǒqyCj$ӌeeW享ˌatrs,a}l eVRIԤ_e%]W/@Tb(l:mryD= e[}ݳtinѥM7 ŪFt 2{m8e{~B"Ԫw::qh| C j1L1mZWVB.T#]} U}-PWT[ŗrTKr.~5ǃHO[O O7V]p_C폠ci]{JVj__x(f%O$OtU˧WQM\]̕~“ (/ʻ ȲDvs7^?~N'!aɕ1f1V&D'OկSKL2Q9(mmʼ#{8kVrex!+rbjWZ jI2as;0ifn],^,"ӢN͢xUd2~, I RJdDAB-5feXlbI2}حEG)[\V]{~^n,c'x ۸!K"juyңKjv䨎E'W*Vw6q:ao6Ɛ6m;3iu]=/9Zv26~TEn59ݒwK=gJʍip?k9dHZuMCFr: y'=R1" \q``p~ˆӯCcY1n՚l2o=upq5o&8m > K&zqPbQCd4hZ(Q'pAٲK@I V[ڮbh2GQqhl`eN tUsj2]*.aջQ4ӟ޲aJy~< uN埵tk<;4<ƥRfU& @\sSntneoluf3dDZ6ŨQ""""""""""m-lNr/e- C+%gQoEbL1BbW[^W }_]c?DaL~zKA#T~}/q{2%]/Z ԆWkff$m#HjH&Nfruh4bpc[Vw'rXpvd8I #u2Fߑ^}ZSõ\3|:ܬӕ~dۅiqz949|j~#?CnʺXl4e.~^GcNwFjY\B^,M022n df32m)px- Ua1d MY$KJfʦYMIZi)6ՔT2kjuMœlE)Nָӗ9q cksxUy+vf%qLPb9d2GgBgb1- }S|fUО}oU˾#{G_' ֶՂf4H/mKR{,{!q}ͮ Q4mCfU<N{=}m|kW#] h_9=={=Σ8Sr\]_s&eWby<9үidsGF] jNO|dŒɃ16[JcJVژhm3IJYe LL(dlKX,Z5dimJjԕMf%ZJ3Rjee̴SRY+,k[Pcfɦ͙4 1ZTXZZTҤh!K2K RiEEʙPL٦a5&b%&e$mVmXmi"QZie*Qm-RMe6ɚVVaVc21|9IΨ)wt<? ^ԡ(ɸFJƪ"C`RTrt2c(l)Qؑ16RJ!_TBwa;NvVTV7ll56 1`1R@ԥ16mk7_c1յfӹ'E{ﺹ6x<=O2#&T`vnm`8NYEq2Rԭ5. 4ћ(}6a$)$!$$HY4ҭGrx:^!r@!BP7S mĸy+aaGaP3G[q:1mޞ֮+yQʬh0̅RQeY9;bU"w㝟SWOmWڪu?R 2`ɆX;/`B(+,%dPZvW{i>vܶ UnRiy_2$᧘XtS zN,F6Q$ʬZV2b# -VdXV6fWMҭ%pqb[մ-wkS4Fe*0pl{DFNO^In2Uwb>Ý;Zd&UXʬ$[0P#{9-5HB\]Z*SVګDiTXx]Os5ir1z79G3]6e>vXSYdً5hʶ1F͂ɷ[v"̓NVyƍYcM0m V4xs$X[ncYee3dE(˥%I$$|A'U@7VDfQVFV4fA$& I,2mF7Zj[ΰ̲2ʙf5i[^d:PI}(@"-"[l6-Bl6ƖVrhjbp66AjkLi)E#o߶pfRړ*Sw[]ܩ8eF R05VLe(5e5Z֌+zh+#"eU6 UxVU/*6Pc})b[bZ~mǎ5f2w1[ոn*,Q͋q0jmR9fmbVYmm cYB5Z4&r6"j3Bj\I ޶M8SQ qb\43(7!CHڭ жլVeCj_:qq!s~ӓIpQ\*nZ.LH9 siN-U|-5\BT o^tWݎtiiSmC\ l jmS/CqWwUf57b5671d.saF̅cB`V07nK0qU+oUlZ}I)qbg_;-NOh}6ۼLZhep1@lãggD`gf]հΩ9k:S*U~uŏ,=OK~XurC슦)QI= GFVi.ƫ5`m33^I'c.SzR]^a^JB[7կϓnU%aתr 6z۸E:r8UoMUJ{.φ)aA~SerR(ߗ}'*be% gtؕldղ&Ћp;1ff{D>YWzw ~ +5 WvUdhK*#2R2R02I&EIdEOޅ8;J+S-/] #w(KUX_C7\ӈNEȪddBv%Լߒ9ʈN9^w`5V_rrtrZfL5M4J1K)4ɕ2;0mc.)7&QmrekMU*> Kz]/)ɍ*E+WhGz f{8jHWG~][x}+PGx106V*cLkXjiv7SXP̕YP[oopF#!*qL oWB8[RFJR7@#x>%ʻªl OQ>ivyڔZ$C中KLV E^m6d'k2 =fùcYailMT]jڭ)d̳31@(骻yN*:OUow_gNNmɱ_4F#G$N,00fþع5l mpDEkjV%FY*IbUX|8nvU\W ʎ_' ]M=j>J^.ucѥYMcUJʹQS&P݋LѢ2[XR `c1iQRl[Q5 =6>_ve1pX:qGW/Ufg\52w,F`֚q6\5-oBm";@͌1Ik怄]Q),O5-_uJ )+h6U)(V)T:7fi+Ltcv0ƛ22U:4ovF\ݲif7јӄhw+[ܵ!ð)%NA]]AIp8tFirn˵G_rsD+WpԨ@NWb܄zTYln6سbs.\/+tsY.Ss﬙cf٥5ͫ[ҒMY,[T4w5\SNƬV{7c;ޒ%2]G7ds$F.nֵڛbG 2553!2_ tCTGoOD;lL+k]7YY\fhi[D,6[&cEISDc;[s9̶XRM5I2@YXl36L%aY%#flR6hMi4J0H+,h&F#k!M&L!$$%2I)M2bB‘%2L3BkRRdiLL"c5"2m4VMbKe$1͙2~!X⌅C;+#>F--Ja 7WkWzVJ&Ԣ\zЃ"J\SC_B k4,ќ v B J5&+bHH1FU#%L!!A$R"SdYO6ҹEMo2TNfMQ%vrZ5G{ a!z5 y9+m6W5utTJ\^gFg&u-SznS4MXFVh_GŽ#Jm{4\ !wG86&)1մ8~߯;dYIDI1268TlYc6ٕ*TYm%5ID@x{.eXK c1C2DrZلI$DI$djRQM4jFJ&jmVV6MɆ -&QYRҖS$Ѵ[l56RJjU&-RjY%i4LjaK i6VLѦle"TͳSh2fi*efmETmW-xy17c&/ چB]A\'RMenokk*0Nꝕn!H*v4D~tig@[dm 1o[YJEĻ)X T$2eeI`[6F[ ζt<2cɢS^3!h`{KLR֧U6}iSG= ++(Leb 9DV@-FJU1ָi;^þ4M6Jl7sQz%,'ډN+!ݼXIJJb*MOO %s;z}cj>C~}*am\GvC2~Aocz_?ɇqD!N+I'zt*|+r>Tܼ$o|w_ǩZ&Q^xlNz+ ;~TWNuO>\?3RZOZ߆'cQ!-7Sg9~;ˣ03/ec%=Q/]NOg^uoVq"We YF'Q%OkvìzĘr_|U/zW} R _ S_[ƵjZv/2JmU+O Q{\t ^56I7sH f×Wa8vo^o>Cx\֦Ox4ly"v!Z2Y.gyܫTvNbkώm*/8zT[++͚Y1kM֙9q ͧɗՠbH 1M BM9siU~|[:ti볹9gu;ثz,V6mĬŮZ=Y҄yb Iy8 C,!Àz]'W+WnlGnv;kƮ> jWMI(Oϣ_>=~_ONO|~o?}x>O^xzvoI?z6x^ֿ|}tzoQWHWkcw}Ga |wxcKXwᵹ?NZ-9ng/[mm _~Y뼟}u|N?Y/6>/=ϋ/=}룮Cؼc/'M9֯hn߇w qܶSG5:N?aat_h-u>[\jx.j1ǿp{~~{nw%a}Ӝu-w]/=uk5>.l9x}?{O_ߓ~Wsy;#4dO懷'|YqKߵuu,0Wcb1|e1~Wh_k~+Jlwz]nݘl7m8]mvw8=?k=2~^&̏  HVBcCZ5iEec JiҔj&1$HX@™0j%dҕ3 j(̙ d XdJ4FS,jjF&Hb&h%Z4%{?q\B^Z«b,z,ȫ2ai feF1} )%p>y#VjAD$BBǥ*I\ͨ9MNdCc$yڂ:.+5hg d] 7S3e6=]C$ކIjN8G]eUCwFF̭TlTpd;]9 mJ.qE# g fhb,ɕUwY&ʕmZEE˂5e3-2ElZ3ͭ6ZG889=SsVEI:u|˜VXqS15ww9Y3Y̾p0#*^ks 5k}Un]s Ǯ<9JDVUomފsZ5޵91G+wW;&n*91U$듽LDrEIZnkzr7U V毄kWs3򨍲ws6a|:yE;Λ%-'V[ʭj[D\̺QW<D2VCksW|0eުwMV+YfE1;D4AeTmmo 8q+79(!i̐qp"D&L8vΪfgsSdd(pm!RTB4WŧLh0Mr -*HVlc6,MY2E601Ěmnu-4q4MCl,050@1U",Ԣj('lM } I - eckKv ӝ+l&8Ctod3.mmm526mJkEt9Y7*߳Õ ӝu9duGR9#+ڒA H$Aٸ&UU33UUU( dPmb7$ViW0#~*?z!Mp~(pr90V\H@}6F ${q%TMUTE2RMI&Bd4hg 8haK J2UTjɪE@HZD^1 D6Ɗ`0  U^FHۙJɒDuɛ5u 2ѱ5'ieüi@PznqrȚ(V[Y=uـj 0@7s \&R]D4uRB{cЭZz w l`F@ R m55EOAhD#@րTXZlcpsoG^oo7{;x{*(*wu* ݢZ@P eyQBʬ<[n@ >w,G N]tt{I]mfKﻜU*O-J(U1tOvV٧l>y{w5tٴӪקL {ס.emUҚ!QE]sѤy {{֓Hi@T)I4HDB=#ڞhCCFLM$I<SOMM4<6')驣M=&d &CP4Ƨz#F #OH1=ySA'PқQ4 RB& 4&FQOQ=AzښhH!Ii64M4M RV$bIh&IoKS2}AN"q\>МYʛ4:G Lw,)~*P$[;)Qd[nDZ #2j2cɒf!M&(F6i,m&QEI!Qjhcw5tB|a(T! ~4z^3_:uWl{_xbׯ̈́hϗW֟c9=AQ( 堫OBˇ'Œlm` 4rw8+o (4P@'bެe.PBڹ˺8\e$:'ڒY$3B7WNGoDs|qKJ}E(+vu*nM:yGbcApcv9uKgᝌûH0V̙݀ ׷!&@td! #RG):EQDs.HhY%r$DAEH(nm4rl1pߏ0_^]pP0fC#QEBVzUXC;j5ᣂce$CW6ҪCp&(4AHR!QU Re.KN0Ҝ L/@30X1o@9?OH?``kH(f=Qbj #]QPĢxp@"DddAN"{!ޕ$1x}ߏvD4C0  Adx;9~q(|bo<1t}QߡQ岎=u0@=Ǧ΁= v!9"%]tSTsɻTq:u9Fԏ2]P'1=lG4Dgj9ޝ1_k}$!"\ȯ3s x5=7ָ>8l7"76jq7whtd'WXp}S$Zi}n^82. 'h0{zMOX 8ci0GR"E>¡K;E(HFxEv;Λ`鋰0oƯo07`TS!sr;EIǧ"V Q R ϫ;Gy/>u7Yb)hnŎ@Uڣ ڌ =; o-#Ű'fw^jGUVIaq80RK v TH 9 n]Fe)xK1j2>B@=E16!%n)y#m:ϵILd=W臾9!+<7YL+r/21C9 "2 ̖X-Ѣ2 qûRk]lik0?yӶ/W[l:̽NV9ƛѧգc䪝.+H)E!$\e0΋Ю<0ZzewsIf)*'nGЪx:?^i*]QlU4تRJiRme(i-% _4q|.e:Q=|AE ӹ⊓D .UJdZd$r*dyz(K%QCUmN`a[PʥS=X0leyMz$q] zl冺1hUJV +CT7A)\0rRRTK)d(im/Ůr#H$$d1bm,Ԙ15L,bS4DƦS يVk*kmͨ"Y*٫c,ELYe$&E2P2ĦRE$S2QQ")I&MQSmiwjKxWp INI dH"3I$Bf3F1L%L3$$7ź#䎔Q|$DV'El]S+#5p`{.n`s{ychyL[[y UU,0يWdRBB_ww==B/W ZA>#A)JRLp13䊦0><ߦor}Eэm+ݝTNehVkZIݺ$ \φi׀ zt\4Ykm|q繛'3m&QI$Ƒ$u.Sҍ$R;qj֣Xՙ~_]fVlO9#^:tӳlҔ)M2 Em/!'*}a;v5b#*>Z0!#|JC$P )\ӥ3v?3ӣ]"5C|ۇV_~A08;@^QbX RURTX^2d!l[)gږt c 'Tk䞩73%2O*~/@:$E &伥)r^R.]I[$$ReX BHIڜm6sfg3LO+NBnq$"2. AގBtMt39LMݰyQXrH6i;@>Ⱥ?3b?lFwmlTi&*;>~6)F1Ն%DkwwJh*<'(Dr#J1B$QIwRܠI$ ;=y dMLo 4Xee6)jKPS[KZML$M[E dߥrNTwj%oK:+UKRX,KCiJZI+$ʋTfkx\ULB` `v2FafV-2BKTjeYdUVeVJ)fWeU&ˍŔeFiKQ*™W%u4\͹mVnsrq\Iq\nTQ2p]Wrdf"]ޛ&͙$IudYw: iՕ)n\34.R"ěPM-Եɒ83r+! 2Q $5 ^(()BJhJ qTf7&Fz}SUVL&!KRU4fJFWd%lU2m4UmdVʩeRMf,*fSUiCX%JfC0a2L0$ EdTТ!JШm*!J)T*0 DUeI Jv@sXr-rlCifʶ- Y jJ0jU*2PI)(i$Ńfe&1b13aIQKDiKJlOz)_)]aX*XRQRG*REX+!JRT2dRT0Ujܸ TLVh[X0jlԈ VJͶҖmc0-KPMm,ڬ,"mmکTQaLPR &Zmj L( մҔYkmJڥ`Jfbj*TC%JFRO=/OKE+e{|f͟6lc#ZeId&*?H4Af`)'AWe3ѫ^UA辥 %Fm$z'^z pL _4Y7At{ m A13ð3rbzC?ALy >qjs}O٬#~ HRT VP!}W] _Xd8+'HMCDWaAq~TK"cB X <!&XJjIC Y ~DNM&se%25pSɤd"&nKDhlwgIH7csSC݉ :ɖ5rr"%&l)iVVKaػ"M|`` ``e 'd &VԶUc$eXڊ+m6ZB! d ` VYi)bdeBbXEa *e*wqb@4"*2Ǝ{d fp d×q_ 9UOc霢$8D>C&]Y2ѯ0 ZC?lC=x4KAynJwf[ + DS=oemD`iM#Ѧrn`]@A$95E!09bET![M.aS8*b5E] JAEZx!(%*( dlF1OC&2服!oF?"[Qa.t 2Xt?IB`,0tQvʡH ( rG *ݴ3vE0*f%A2K9$nm`yYfsQ ݎLiG :!/|*Bd.L1Є))w_α&A[>|tQOb `Ё8!"!-2#^&f $!f1RT l"ܝTMBMu坼bq할Ng $})٫QS&'fd $q^0 ~ȘKfg >/]ukT̙a&u׹9{ 9x;<;-@h1P;=;т fe YȂ*$z+{ǩz@ܴMn$;D-s}FD-Q ªfA Ӂ͈{( yĨIƬ+os6YL ̈)5Z!>Y- FOGjES qnٻ}kf̘dzǷu0yVpBm-P5K ӑK.""ffPJ?i Ec-Ve4? *A m%%i34 -8TPIPR0ӎkH$L(m vI2lAj4cnىk )kAT!G!L)VY0R=qѲC.f.qS=%+q djT9Eb9KaK"̺| Y--HD~@xp-2yM I9NDd!/4֋Qj QNXn9UUȣ>AU5f+jQ$B$"aa(G/i6׭&ժ$; (ilj+UƖC栭Q“CW.S6ur3nmUq@-\m\^1oYMU" ֆr Df yF mI>Ƃ4rKBgA- YsXSӮq"3-2Bc,AKfdplU],°~43cCd[2lo *MG68KS>rB&*(!%U,)QVdf=5k[Vqw%CI59e9v"q-8U [m+xE+1J+J)]JE+݊WRyf,=N؋ sclUԙˣm|=2`kY,EEI& hBLJ-내J@W4EQ("w/z:)fk֩˰`(|p3C=0͗'B >>1Ur("(զ < xnBMXzH3]S"$qffцo1stu3cMx@]P 0 etdG*$`gk2V; 5l[@C$WSL0</nu.3&-%4Eӎ% -vhf fzMtv&az`5;Kj]2E$W`@`H 6גּ7;3YS99Jz\*U(nj3vx ݈P9̒9GE P'TnDp zq袛lefwv]̩ٝʙ;'wMSө]PȮ=®kZpTRBjB8Xf2*Dw3=o3' ~dta2;xvHyjHz~Q1b7Hi(H= ҁ0ߢ]C3E`D9`, ]tv@D9KJ~#  @읓g :'`L#D7PYM eZFwas5@o]K6t ` T! } :Ӈ/al5=LKD|P`~md$rrg'Օ}S<;y[ e-&I;KH"5(阚O̝@(3 :Jj3nW7+"`z۸T@TӰtd> =4kvWhLVW Qԋcmd;tN{68G)YBdI`@H1bg-̒&B2XhԮ)tP 00! wmBN3sj)0JJlߦKuѱk(*t='}a^(1rQH[FI.&L!`ELl^ E0r'F:BQ 0Qd Vau0 /*nɈյ!h,KtbFy=o.<(xc9\4b%Q5A|4T1w9ÕnMZ'*7 ^]ơ&Af$ {q吜Nq9:'f BT\`!FDV`91b򘉵Eqqڈ@yy(St A(g_,y;>:i Tn$cA29ljEXi+N8|2#||s̡9cMYBFB)RgABeƩZovZ{HDqh*uhB:yC18f@;L$ UPC+*ŅO`b֠BhZAFslێ63kM,RV*LeVQ%!K@X5"D)a,X [;`IIrZ:rАT~66YnձkmHup5||hPlG+gE\SU7"lUR-]fe @Hg! :1ۡ*j\㤭n914NRnX:4=?Zx8NXRlͦa(TH37X;[[V56uq[XX$I&UÀ*I,șq+ylS2 t`)(c(ö60qZucxфE7WS׎qz7!(s8֔3 c:8)_ 7%dY|\t]T_Em$QP$@d d.9Ҍ&ξ[Hxb7xwЂnݾ;æv<Ο1ݢ|vp)Qܱ0@vnw]k0bJZf+G'$=1b!n$IlrɑЙ*&ZtU*dƨ * 'b˗~rT7vSĈ"fS󀁘\Y#/`&A7Lq>x me K<~PBwL,$3{c@YF3}S") ,ýNh!*Nni+[Z$F G9aʫSz5>h5z\ȕO<9xʋyN01؈17svo=]Rd7whjTm`ttkHtCD&Im3];C: Eװ!9@Z 9ɥ4<K.+rٮ烫.H ۢ g+%LǞ:=-b< 새TJ;.}EXTVN]7 $4rPz8D;!HK֏Sl n8tn#|,K;u'4;&i:{Q/f6[}ƓPJ;$Ëp Ij,X;PQcP[Ha5ǜ)wjWA; v۬d;(X v'M U*.CZ) ;!7 )cP)9+&#ܯ'( {FD<4/ yg}HCQ 4 %BЙ;/0jhf\( h|P'BCi &Ac 'ĕr ,h0$MҼ 'V{U srq8 Hy ˃uM׀Xyig< 8:Cdz9ylwWIS V,h;=~WC$ϏkiP͵|)u̮fRs/vlVՌƸAs4dQ$XɖQ$fQXNFEX(EDsfl8(Լă[/CpE n+A*8D=enu-%@G}xkݞ e١ 3 |e G8WMfC)Kq hoY ږۘu퇯;w bnn;Mt#4.,J΋.wg1eU-nѝ9ae;e.J6̺6ڗTV@}λn:bFZ4'904qX=<l|R8 F3xD4_/@g9 U:mAͧ -p6n Y_t388ki$q,I޻Iࣃ 4.: kX7h2K݌=6hl;Ԡ4:Ve 3 <[8[ժg]㉉T8Sp~ 6IlnE>7qK%nײcbVs7 ^@82+1*G Mx1A] t fGH辱Lwa,3 ;x3p><^ß@D.7 4\8#:h_~˻n&pU'@pt@fIb &aXzްf#9!D`Q[2Pn9W"!PGuvAռ6aЎ';:#S]WX>r2U?-*a~;}mFt_)E Ȉ,QWdSa}vT*}oeFZn83bwz_u[}.RSj&vT'o'g~vgydÈ5}7( ?9sXbsStYK$Xx=+Is|ĢD=xbSk>տ\nvi_.|7GOPoE)L{sUڳ>Vʆ^ݿS qcU֧=ߞK~B:>.8w>AJ>uvpGSY]/num׎c_t]W|m|G9z]L4WFGoX$`㒎ׯ'zF&t7JG1쵮^w}&>TsR<~m=zqD~6m?sboD &q&8<)-6GHA[flbG6 +ǥ+ +qU qp?`UHOŵ  =ñ8ϵ>GJѽI@ֈ.Ϭs]?ܼ[vͶl( v?Ο\"}^g v[=ϫZټ^+8ZAZmNz!B"~a n_9EWܒPO^_[ǘޖwQL(~/N^]=>u*}i{/sT멖G|~XCϾB~}y a81;|:{|GU?OJ"GQ<ʼnz.#3P<|%^5ͬ_ɉWk{k]E"9V}l?WϽJ#WT\|޿:Q+ Ey?MJ_sy]>'#(ކu2znyj,Y"2NuTԩ{Uuoo֯l<_uˍoWJ!q#>eDl2tKvissi%:E2<;>}PZ<**JoqD$yudz͋1MJJXN8My*t=\i)?Ͼwe& #+Ԙ?_I1;=B]P/iaf~-.%UX~3_M8#v!Ju3f,1KUhhV)jFF%?VYXd0~"DrhQ) WX ec VCF#JэMk5QW-W5%y1srTbWFQoKݡGb (MW{4iMIe-2bHc1-|؍e Sv[jmWk9fSlt1feMx{]koA&EF4h6[bca,aihAU[ jЙaa\؛a92:Xךs8f3v Yb! љ Q f*wڦ&,mL/-}Vlĵ4} %b3#,2XdaҖ6MSQ Ϲ{ fiRZSJc 40Ų(Z֩cvlCF#=*1spsd'ދ B FȐkI=JY&Xʰ"ѣ3S `Ł]-&,m1LLP92@BrDS}O~}r;s9n]9̻fs6uΛyӇmsƶLm% 1JQQJC*͝9Nk.@ /pK$O׳8HX,b 麟fx1Udz΍T0b`ZiFiCK&hk*jiaJy;[oTr !(FΧxƦ ZV20YZ&fKW5l[|Zv\ĹTy'UHDi}lsMf0bc c%cS5 VI"0WE+Vl;'nW̥mO*':Nȥlkn*,)PTx2L8m4ŋ*%$f#;E<O}}ȩ\G{aKǞ|vyUv^.۰֎6EwT6Ez-Jxjf mY1V1hE PbEl(U-T,bme*`m,n1Qz~gv>AƟ90GQ41ͣC KL!ae-#)M%Fٕl4S޵Cvª5K1ca2MeEe6Yˣcf45FS74idcaLʱp1S,3'kP')]1*4Y&+LV 밭>[G*<Ǖlat䒙{!Lk=s}2Y$sY[c_nԏaќyq2v#۶dKNOW.HQbd^JRQmyyUܻQEuWUԪJ@Kl h?Dop]|La  F`&C,d1)0@h(}_f_VP4Aj ^TEIFFCq=c8"^+!avL1 @Bx86"`g3$h45~=kֽ<_cML[14>& aLy4QdCFa$00! Z ǐ|o\u5*@~/߽݉31/9>{32C000pl o9Zmx+I<9* EYx@SY$K̀ 3 4t4ٌcv+ҩP͢4`34QYhqt3 j9ήWUUw۸`I$$(:zO}Gs:Cwu$HܲXD~C|$!܊o>uwuwuh}1&*Of3+JJaRzO6_I?QюǼiŻXٌr|#$( dO{Nq>/yX2gюzZzcK-6cyd mn%&[JD&mn`ETD ^C4?VRzUU|vѧn 9̞r"=d+DD$9L"=lXgl-(tcߤ{]6v OmkJw:O $%d*X:~$]NAݞo}v߻gW*q:勰v }c=Rb<.ky]e$IHonqBpDҾќ^tzj{O|:vxĽT$F9dE*d/WQ7h ȹ Aӌ vz0nC $b$qvdď LWfÇ 9q#B c^Gt˪K鿣FJDz/>Yfd a)jjgc9@AA'$upXۅfj9^09apmF濅vmMڳ-HӺCj8ڀ lKx}X7؃`s 5#m̢j#7b\߭$kޛ$T  l0ґ3)OItp`.j/mf ֨VЀBV !m*6{a Fv0p #_@̇.)j-NQ 0FoE̵X|Q̴ T܍27N7 #2I" H`q~f1%o[pj(U Qed lfJy{<6@iZU}VI"Zx0q( ɲdgVCnK pS1 1'4Qduc봵`;+krc]5v^(E:CS?& rAHܒe2HTh"!gp'0 qm)Ӡuzwf3\6NBS .^InW[ɸc F MKr0 FQbޘts.{]q+{_ygW\<9}wgI}3'^U;wzGΕJ~e%(EK{QoҦ;ZG?-':׮TwK g%9xk*'> uJTu4f4=XtdA۟PI|#>E1x.#հ⽏Q~h#ǤO K|41qDK7'%N8~t+dqǒ1}sϞPMʶx{hҬatuwn#ҧi111Ki%TUQUPWrAF,.1VJ2]^q@ }RO `C&p~}Fs9!y b 8>= gOb@ ODƇV7mEK{]ckgE+ĭ*Rc"4}(>/1 /ƈ xGC1e[df.PP/Y&5i ];3{ԔR[,]=-<[&zg;ݹ/F výU`H &Bac|Rq0rcl2RTod7<cIWRF~.3 N.e6$YA‹-n^L/ø3&=X4}fX@$%kYCezC$ U+l6#=\E+^npI.0R %@á42p@a*<kۦI94};Y[M:eoŨH aSC7fJ|TzTDq׈[`]AhiBrm Vs6@u20'4,㡳30'VYukŔ A @I#;]ʨ)傢onUj|p2Sz2B7Pgua+7z{_ͷ, ICB(O:/v$qR^KK $uNx~PyoP淝ŝZ~/&D9dA=D||v_5"z&I8.1SQJ?\%p%W>D*{pːRM~XltSnja*oi㷏?p\}l[|Y!S12b!_]DŽk|;Ѐk=_pLi&39,zBJ=@pf2pL1'ly΀? R~3o }RQU;Up6`/tG+G'8˟)jj(iې@ƃ!=hy87YP0B΄V{ I$޷м˾JcmRۦf"])D&f4bwxN@ơgE, ~K0BHt%‚SCW/p\;uZ3{]SzJI:Yb)6D^ f}cr]Dzu`˪cK|S֘)AѫYvƊ{O{.z!V,3}u|0B.]A4B:.b P+&R QnOaqzD/ozYeytQ YtxMN>:Qm7fT=b z$ 6:EOpVddC:!*V<#$6MWL15o \B[4CG!` = w U||Bt* /+gUzm"6@xo7;y6yÐ}Q}(@Gpa^ؚF q 3>՗NlajvO#~2Bۀ zYiG]g3$H "gFd h(ߍIA40x5c͆Jɹm9<ٳsc].s.S$ѤI D% 3Nm0LCݔ>}3il]`fal0YOڤq:َz4uƌ>9⾂ E+AN!,ÑP͇!ބH.{4񰵸uKyff;KszIsRS7wSҺI$V`PB<5v$-=䄲/d5V(QlԊ% iFRZe% 5*,RF`F3,E...C]^ yQc+uCpC67yy_b똲9HOQϡTUw(uz=}߆ ywHy<}_a=DD=W4NjJAuPz6H+c3٧zhsU+z4|٭fVcN%I149䷨GA\KB]LHKvU:}{YfBOqL{\adzI! #69=TwP~aBUk-d =kk|7=й)[R~#і9v+>,9@4|бؑH`PHCXSK jڎmnOTѶ0YACPmwK7ؠG54I- qZޘ9G]!KLzOWnV驀6^p¹q͌x޸(W'=Ul$b>![y?VRۥMlX +".T**& :źWJckt%*^ %*) b'}l [X`ț^DP>צ,qya|4Rfр83urgYIU(сh)M@>q_YT zH&z|}§ZK>%TR+QCS Jퟤ5d3*$diQEcP {i1y.*П":{y˜ʁʱx_/l[u9:31Z(̀ b$jۋ$?,n4.;KR#&[db>m2{DWhHt(H"`g h!dENM;[0PiT,ns1g#U&F*W^7r< G\ˀ9rGLݏ))%R!<Ăns21e0bdPJ1d0mE+8Voi·JSI%^Hg%L%(;dՅoaAF;Ieso*M*[ )mWh4 yo- a r%LR_6Qy-8(&p[ݭT{ bt9r"u7ט $)\MTJk8-@/A$(˛)&*fQxPҍ+F fIDI kPL`%&JQrީd38ę1uHVr5knMsDپQv1>kQ2f}HGx|Q elL s#&EǞ o"]=_~;{{{HƖu:DH+&'szFT3}IZ@a AyDQH,)[OE0 GFZў]>#z*q\dsе> Nzg9{sJa_U`I$H6 9 tـ.%QzB+`u$ 2 DYwݻ ܗWDv: _]{ q (uŌqB=-;tD.}bS0R0 i Bk')8像l"x852U~6Oou}B*uehc T e 6N\DYH|lT=ӌ+UMTǯUKV|n%?n,E^p_+*ւZ Ȁ140BeD$xj[p06[Y Q:a=5r$nU"Ĉ.FQb`"JE&̀5ג0&U,s*HH\ D3 uXACD@شTL?r*XÅq[[-w303cՊƣ[$$tG/{k|q7<5RW bY"BM Ԋ\$pFv€8}*twN5Va;&*vg  Kǁ]/#aztO_>ɷa=hbZ8;vŋhf 'mQ3ߊxH)]| ݲDŽ{6.L%@bIQ.1vj7ggNtRU#֯]-E1Ɏ8(BJUDdUkƐΝ)4sr$X~s~g/^5#1EHqJOzzO?".=  Q0pUi̶50 lfiCPO- Ǐ[$6%, TG@1( `,FD$ځ HRv rK.W5Vd] ;|_ / ,TU+j({jpjaPΥP^|"o4; FJUtIE0PUU aQtHA*QJʈ7aTZօPPj6F.)T)qJ-bEZ`5.(U0#B؉ *1@\ 9zؒ&+{ͥznc 1_HşL$&&NhxYGR ;LARbMR13@* ̱ڤ*TDX#EhLbP8삋IR_j؞}%5ϥM繉txMLS"]TD$QkKr[zBvrx ψyeWߊlC㏎u둵})򜞾f8Ϻ{H$BGӮcQIyڃOA#XmWoxDB@vÃptPY1@R]lՅ݃`_@Ɓ!/J+V h"(  ;X=h7Nk:wfFQmAGlZDl@; 铲L3ƨ3g: nSO4 'm1!:3d!:LhVbeES+ ae2336,܉ EhFAIPga0 "bԄHIjntr9^Hё\o(%Nlנd5j["I % wEQDU D%%LUPmf(|~ϒ: ,z |>kxL"Hf/AHc8IsHTsRw,552"z[-[[%\[I$I$I$I(T`ْJ#&)RQfRXI)45)IImHٶR $fmdFeE (4C)e) FfLɌ&M&3J)dP&RY&E1L%*VM)MYIfLJf$ĒBJFJ 0{gcKx<;dqc$껛[a FBBI:"Fh,^5 Rpp`"qQEWT 슡>;R >{sbџˣIbؽhDڐdsOAUPmYiZ}i 2klRS9XrI@(j?Eyھ޺TZxy2gaWe&HjkM;x]ۘMظ!Gke߉Q޻6NVs iϡsK,VضVh`% %Xf:|FH:cqB'n{8mU W/rX33XDHU8)66c꼎cD P@nRAox$;ZJ#)]\At(Z`h7˘Yj?L_; 3=DEF-4ڨCuWrߍG(F6i 52Q,jBPhCgB)I$IJI$Y$I$NS6QR1i-@b*fjJm)0 ɨ),F fTS1SfJ0iF be M mMDM6,1*fSQSd% b٦icMVfM,4JRfM+-IT $1=] `Ę<ٕXqpmp)]oȪ|oMvs*n|#qR6 LݯޖB[q:2a,犡gY*'v`IL̵݆![ 'vhGR(k$ 픟 ރ1﫺3hq 51z$Cdݫ+q  kD {i)T"&`WX^?s.Ԕ\b;RV,T"rK^h}kcIJ<5f0 jW K#432dBVoج C| ;uPef fJ3 _$RUC+\c㻗Y?Zqt |AHx#!{/6<tsO5Rj8ZA-s:hXE0$ l:<7|y8 - woש)<>MD!"ѳ/3 $Ŝzbz˱.(v4"k֪e*^?{cs*=|eк"pz#\;A 5868K/'YqBҥ$̐vvp@M##\P㻐 g;lS-6 $B41;# H` f63C;-Y0H[O aݶ 0I%Z F u2ACT_|kAz("Y]%A>b6Y.ɭy?O G>n\oUinH`"x0?1G[\vT=qb:Ƌ`_\[a rnq#`v4b! NX!ͫMj\V9+> 0y6Tn$ߚvoѢ%dUB,A0]BP 0"fk7X_C+1cȢv^rٍ4Oia5uU};59 =oYV׉fh#r~{*Kt:QP_|L]EP)cܞyUN '1M| TUlͧuvK}߷j,w2nv\h+3ݾ^M^3(|^dmk+.ˋOoeoTgR;+їCU8՜K_[Q+q-WigcuS2og%g|puv>=<'+y nZ.]mi4z' }a]uw˭"+p;;lw]o#oʗa'gt7̟/M._*jipV_.ƏeQˢ|  rR;@qE֎p1??eIcBʩR- iXM1ҪZUKU%+,™2 =*ȃL(,ЪT0PRCZ&~/ЊW2J)X)YG̣ack" &Mx 1&LrX66kÕ_W:ֈ1:T r1?*]kˍ9%X瓥rrů|rNˆJR{obW%$m\޲wk@%s3YCڙ3ru깒γ5L꫘ܧ&99 !DxOYPX6.\O&fftd;+JV@c%T,UBHZ0m6`-` Dj5e]ߣԨE7x*9U:''}^o&8Wo9|˪f^̝yOȫ\2b&yk2Kv"jߜY-Rj/uy{Zi>gosDPO++8Z[N&iPUכU0 <ʻkN,ҪQ}[fQQm#϶tz@6@7K&I%UlYK;/w;ٯV([o"W^֮D;&ز ]j2dhFƷn֭j[NZ}dNmkHkXRj0 Q]NDٕylOѡ-kl7o 떽>_&"(PQBZއ/GbpU5l VG BJ׶w4=em훳6%R1w`-)Gsc}q1K*tPi;褂NK@3zf狀7$ @&MFE<y4!ShSOD4?T4ji4 =M?T=M5F̩* @"!&Rz4D$ș$=Phh4i h$RRh4HH2i4hIb MA4#IlSx4HT24zLd@ TUQAM 0E)>_G-a$$bHB+)XiRE0zih;9:K:8M Q,E:R."ng ,(UYdmlZ6D#Phض&Qd2Āi;>$SRX]A 7m+`uE[ŘnܲϿ݂/ {4YX`I@#,`HNI%.Z6 {uYtsr`W_G0Yg#sK ҽ-jI>79D;X7G,c~(AB>5'G7=u;ztM0 iy/=G(B4smNm}wSEZD!$Α~>WҌ )t!$#M,F(4p&>j*4&Qvب#$$o)> ϣ;&$1h*%ց=K1$#e(DJtOÆBCS,(>Es/C@ "1%ܩQ=h~mP5*9l. $ 1!㭶!!"]-Ec7Pj3*9X&""% *B8$nBX>1KPqb%HTIY\8* jqb5z41`h=I@1lof"]^P A A " h8Č6U2=8WWw@mEOq5PCqLD/4|A,ǑA$TP*p;PP;Td :fAVM0G!@ bE(1:ʀF{{բ(f;q=@F'0ȠdPy-z INB@(L>1`b{EC`# @*1P.8!74G.).ȺwB6L0 zb)n`AbIJ#qw@ H= \S=b $-:i$a:c!VK!(C I 0A ɐh ˀ ! d %$HP C !Q=Kp$I? F)ԃqpDj5pNGDWdVvo$W|/G$V'AI<@F,:8\s:K[g)l[X}ޠ@A&n%@iD K<E"{48 ^!d oeaZWY a.NT{)42pY ('@;* yk[wSkYfE(Zjm5lVhMx PqhZ=3~,ڪF4TEJLjE(j" |p'X" BtiQkB@Pg @t0؅ LA/@VE SW؆J8h!1,f vbJ6QKjf4aDL$̶lj R[TR̶YRXFȬVE3M6ei͐TFTқL HLԶҖRhi=l.w{$1BH,H D4RELM$6iDĦRR@`1I/~I -/O)=]C,L+MdkDs1>3#1M{ *DL~~ @IE'3UQE* UQ@"0PE`UF 1QP[nn5-@jVֶ֦ej6ؘ5@ ֶUUP@V5jՀkXT*m&[keQ J "A@ T@( BLDEU9w>R }! 9}_"H [DEV("Agv n  z@b 0`0ƉI$BAiA}Rږj:h8 4l@,8NwdT$ ^^~Đ}BjYqZd i8Y9p@r[Yݟ$Q5h7 YۃY-ɥUT|x-Tz$XMϪ3UΛtG)tM%ԇsr쟽3z˓^ms i^3DCB^u%>]PBP#o͆v4"$#bq[T;dv"*ED_(q3HZ8p !PU,$LxŠ7dZi(1AN媪p.t/!`BJC4N 煅[7Pi@dACX% J}a3.~5ݭa-FNLbTTaw=0Їr %9fl${ދ)I N\!F8CL3)HS> ^:g:05x/C xγ:쇷q n&VTE* %j8&ee>J}Q]%p"4LBS5^\SB48U5sb1 AuSq8j}w!a2n333P$9qÏYvx-XhYacF4HAƞ܉lOEzX1=Y'@l:O`Oj3;{IOz20>!ά.2U $ [6PC0(-ES&.՛iB4-  rJBLȤ(7'x+FU0htK9ckSM;F@gafTc8ɛ%/@*@I‚:=Qz!n[^SY4$Z\,14M+XiUr tnS h)V"jfB'9lb*ȧK78需%Mry03=nScɵvf(T@ޮ<.'\.0AaB b흙@oV)љR-1FytUUD~Q`2=4HQKѵ5 DwWUQf$vcIY{Xorr25!GMIh$fpu:h0qmꄜc .nM.z&M8+5=DrS!R[-#igH: ^ \Ւey "Z1zm&N9B*$͜-%"؎A`R]ƀPFTM"40뎨Ny`EFuFޯ3ARJ*#*(Dfl5*bA ;n_+hCHd͸l `֨< "$!  lTEb(hml:8&qR9aS ]*3`\:b!&hvT  fv#UtEZ2v ŠXd221P9P6T58I$ |`Z :FEFiR/` +'PGeӕB cNhCpPj1^^??AD`DHA|2qJy4iƾm|RL `$V,f!1;"]mĆ: S80IKNJl5ՙ,Z,4 n9E6p.jcch Ia 0H҄"d`-lbsu< ǧla| >7Yi>bd+P*f -_N¢\#;^`J-J@!i318/pR%7h Ƚ Հ1hpP60m2LU}( (XB|ISwj9𜱇.YbBCpͻboP;UeT5!$@Z 6Ƭe)c#: d %۹Bdko.CMAe,W B2ŋhб-)Dg ,QgCmX,j T4ʗBn3p g,^w(谐R) @,Q@M &X ̆op @!5tN"Xp7Kd FxʷF cum>YQvKk7(8,۽d)݆yI3u4ʬab!2M@6-NVmnK=u37:̜X01 W @-k7ћ6A̷9oVI ̭1l<(jo-$hee56U$RAA;%}Guo83 ,^gણU>߂ڪUUUJsy(;;˓%B"H 6=ѫv7*+œhpn]g%1m u²%ͪ"jlV$E#)V _a atar uIgoY&#b``;{А!JqYP`HbDpI4vX $i坉'TՇ!NrMm13Ypv+UT Qه65&T402H`Ű4(x낫ALG^8řDH (X @" OQk哌.Sd8dA%D @ qķrH.9g\"8C$ICk[znj3=MҚywGnBI[ nYFH5F9ٔhd|-9jnvp3mtUKCL=co75UP⢨St ntB 6A @U%Tteh9$f  nM wwsUu39whBO-1sf@˪L*k3jIm]UT3R"P6I!XT'Qt9qH!vT1 q |Q 9T J E*M{Ѫs.c"3*g5Re+-u4,[suBf:8Bw d͵GGf0/s6cudo(ѡ"HSX'p.r(*dt6Г39DT.g`6ol\8fv +ܾ kl=ۆ <SfL@xHpNfs1&SNoi.2;n{0nCKo{ ppXnq 6% ɛ@^0w9?g^^5r>H{E yfp͖c|mO SCZ 5Tn(ئt{RV}Q,^)PݮjѴ~m=3L!,(eȪ҄@LhJ]G/t>_)!e>0<ާB V=T $rNh߁1pW6jh]Z8; ^g6 ʣ;_~RPjl,l0׮bhAp'90!6T5@T́\`ױfs̋"7iORP>YV25tJgr?!N(݆IXGgz0|^_rHS<=jICkL4HMS @! 2QU%UI$M|9j5⌴`lZgaPs 2[p (r@A8)%38sKe*?iva;@U!Ks!U _/ W7GL*$njUI[trL4CF 2(Ly("3"*MuZr7˘iф AClP~ @Ɣ{=uשXsF6AQc9:)B.GȰ詁9P:EK2 mVbղU% @~9~, `4W^\1\Png`r!l>an/0;z$Ɨ,l;p&!rC10t#J#, rU)I8 + Mٓ|h` cJ8벾g /M_ VdצL?$ b@ @N㵌m*{$&kaCg"*-ouc6 m뜚zqs٠ x>Uo'~=FjYU=z#I^1ݪ/ _Qpd!Pd04 T=OD*tNe Cg^xD^Z )$fe uQZkޜ ua8҈cע<8(>Xez%8I8sa 4fgxϺA; >EYآ9GՇp\2{|.-| B_}C+TyŪu>شWlGT6^9GE/\b[ Hf$B.8J~c(J-X_…v_ZY@esb !v11- hK۪A¥eb9YBZRRvS#&{ROk@uhhYI! B .#]p[[؟ 1m\^[iEI > %g;Xb-1yȇ>lg`G(QhJ`[!G*b]@*v8D]}c1mi.kShl6D _x箯O5<&rGy-5DBNe Ҿzt6@%h9yRG דAɺ/.(0-|^.B2P`\ VfW(0¡Iχvnd#$116+0a6/0ߟ:bIGBW7ڕwl9 $Ou$ (,0T#v];-P(s>ϩb{50^i8 ]b wˉ"l0gSznދps Lr lgc)lH"́tQS@~ݶTʇL8$P1b0"5ט%xK*ބ# >_9g-ŵ6 "]5 L"7DF6G:,. 7vCpAH "s'x^ ;mބy櫶fӍl/ tH2Z_=vZL$BwxzE+:_e0802>{UZ$:UY 2NA߿l4bv8h oy>Gk6D[Et` 2!6^E,=70HHyY@mp{6'~i"&H:@tǟWl0UHHyaRp CIe1 -4#G`"Ns A|ZNq{X0]i  kZw*çbTp4c\u^2 cƵy63f#3tbOd ^˭K<3>R&faR]y`]nS0fdo3"sZv[ornnw  @(ABɀJ Ա 31ox,| ʐ>.vF@:(8fTvw답jYiHRr\1 A98V9Ɗ :O TB`z[m,[ݓ.  Tưpp\"NYlهB բ45cxL5(Pq \ۭNf0Ǿ :2Ŝp p n`|B^#AG&",AKp`:z@GuGv 7Aō6ɢH2^ F{ uW\ZZ/GB x:pRd.8@f#ezr8ɛ]} sDIJ/14P#xX(SpETzo:=4z8bkZWHaCHQ,'3Jz (P/לhw^6(;є1䭤|y:rwC`/^H<\4Df Uh\( #9xǡf |rGS =Pw<4S0m8w_@rD/?ID0 Ͼߋq>JԐ̽תӴMbǝ2wȆּzqx `""5hy,kPf@Yѧ`=&5S Gm@ GcjL),0O!0QU-2X>0U;Ws/2Y. 8_jlDwdaMlcm 3ʊ)cÚ]͚.6Rx*pt_3ǃDaI# $zMG4R W5P(Q( D I$9f.fYqh B44Vh Vޭ4rBo3]*<l3X= 85X[7C# ܝ(pz"V^ؠd )Y%H AmoyS~#q ːht=VD*8ubA FNАC82@e!8 {U ~ iڞRqk|Mg N=sW*:&Cc@a$' w^Q7]C&-y'ml=-Cam[kb\;8Cx N@塞Z!c 6@r{W  AzU[K@u`<@Htn0\CE2^ DE/p&ey@w:Br$b1\,A TqNah=Ӯ.qaԊF෎e{v̬yqVw(Dd71 !& i@!Jǩ`+e9XT>/qrI$=iW=шBڜXZ6*Cn׎&UUJcw]a's6a3+R»bi9gv9{8ތ]3#jZ@ "$dV%TG|Nٖ:M%4У'dvBNKG3®s?Q&6]8UR5%O '3  qhq3[N6(Cma"u5ªEϺv@!! lZ rrrCanw9E7w%׌uҜ(AQHrֻA!7Uy}O,wmEC7aƋ;0'YhD C%~};q"u.r΅x{'VH'CPr+8C Ba$:ڜl[;[p 2(3>XU/k8Pg4(CEE8 rj@CHj 0t@u"7<@V,*A n b2eА(: DmDb@whsL@Ҝp W٘V YU2(~U*4C'c8. BH/<3C8{e~Ox{'  *{۷{wQW;oؠQXeIecY%EjMM&mH @#J+>l'fsY">} VzU"EYF{9O xx ,[ BL9åT͵6@[rѧWsCԖU=x39baO(wWM{n&F7䳒71t=}HL17Wi -h !lIyCzGxQf^eOs3h[oS|0v=p Ǚp{jV"h9dڐ?nS(lQ1NK+œΆrq1c 5lAicx Cݻ}QYći,j)n9=^G*Td$θL]PS+;x^Ni:/&hVM_}PrFOE"R1nbHg^"@3mCY{[jL( btWeC7pYQk|ĀF'l٤5|*w1f;(1ZA%).%E+" pN@ g=9 C(SYq ЁGm572wsq3J{&/7@@H4C}ˆA H>g?5D @bsr-\[p>,ͽKuq~ZB.FΜ 7Z+7a&PdmkI6Ff/ڊ?C LccL8ved WqI^9Y!1}?s!%wpۉX\^}#U5[3u WZ2}n&SHJ[d`iX9b8∩#` |֏mX= JvBlM2t˽[rd+1wgL~S ~MC|8m6q0ej{0c,0\.4 sަN4膧8>Ca.ڝQac{Dֱ6`k c4]ֹөÀ~_zxr_L ($㻈r۪c`Æ˓g{zlnzJ}[@IUiD{3\q^G4LP=/ |#s-+$6ckcJxck81c4SwMhV.؉%$)}AHL{l[ׅrC6aC[yK49T˷z|S81[Opic0qT$ 6Ŏ\0nOp.]Ks] ɆaS7R|&uI$HEӦk֕pvŐ0ﯥy.k<2ȑAbdս dHpφؿPdpIƙ Јo/" fΫM>.k?/TLd)RsK V5y:kLGq܉D]CELSck~ZVyK]|) )pWeee#hl} )#;. eyruڐ ֱPA.O~Ϝpr>}/@22)}5}ϓV ?Ə0Ԣί/7̰s" 3iwkRȇJt=_uo,sYM5 غ $&~rMkcJ@Y>ݑF,A3JggL}nj"%~ {y}4>ࣘ@\ ]U/ly$;L1Cdqs7_4x`-=5^݋FF"Ց`]ߣ0G18-=_Uцq1bm{x}CPwG߬y`OOR( ]0t|4%ldn7zqܸ喃(~#ʟ@x {Vbr =~=zȍ/(OoF88o }:{}v"L|&>H}oJSsѳMahXF_ǀO4r@r=.tV#I5fޢCd:2еD0]њ~@<=f/:P>rl]ߊi[  [axdp̱ȗ56LD&( `[: >}C)NQ݀yb(ZY1Uӟa0W©gKSǞGw`,C `c}Hq9~#_y>o6G*+>B#0aJJJJJK2e))"!и(p1,! T6Dؠ(A4%PT  \ՖdZ䬝e7)RRQzյzs]PRZ-mWMjT moڮMmM-)RRRWSj]ۀ@Q?0L %&K)%M˒ͫ-J*dMZTUWmٛ[eU)"@Y #PE:C4r"*d@EX XUt[W75K- Wjf%5rhxi&Uj݀M",E A (0VPNO ڵi YkmKRm+MAЃb @Q PB (`rv "D@H[6E5sr+W{?7sVfffDDE[hn )BX3K0E8tDlDEDDM4ĄW \.K7&faI$KwD PBTZ";XQ#@~h{}x9C $꤄Ub @(A{JF0`B.J!@0Bh5Rړi4Fڷ]Rhj"KSy#ʆ QU]*۩.EDQ4R-(PAC~4 )\Aa 0 Jb *HK-*ZyYiSK5%i,ԚKmI\@9ܘas<@!JR2lvE@߸ 5(Yjm`.( E$EzAB , ޔ0C@w (V۶.mkrUJjM !7UEBxk 'y@rUpLdH TbdHDX@aOQP PU> :8U (AB0 (@rhA5 jB~Q A9T@(x@6"!E`XAX]*RED ȊӤ!RRi-eimj*؈0@ .##Ea0Q*-@)VEƆv&De,QuE/`0,`i @%*]DHSl('PM? I%p5Ї}>^ {=hJ~JTQN*WȾ0V%ZV\)YJZR9\r`Ra>>ԑ֞0t_ kWB!<E01|ai;,}Gn~ Z;." Z I|҈`+ق˨IMbXaIK|Fm؎(YZ!_qgD/MEb|aS5hlW,Z9 BLdWdχO11ԟ_U2|<<U:~W\]`wU~=ywBNDlAlHr, a41&TSBA&$$8aZ Ca==?fUG+uCV?f(՚8^f RZ{;r?8i]6i, cdz7 "AbuWdz6i$ N j gShbqqͱϗsIp_Սغ2823^V,2[XrpAtFh:K^4B:5Ó?g˯ cAߋؘ/!}*h)s绢bl3eNUnR{ N˅.UEۻ-.O|KqE({€ @x9&o^oR=bܞDnSGO>+נTaѣNݳ^I_B|Lb5N}CỶ$NQswvcr .{ mACgBnH"FF w9f֮[ÎȠtO5* ~7L%ۿUMiEȢSbɣ!! {dD9^P(q`jn2bG,WGciYѯi&%/nC'+saf =hw<{!<ݯ5(8u: T">B $I H(QleR=g7/pw=LosL@ށw&.W:vIhhrE]%hnnQOzpaRsx.ftڄtF\xk,ACQG#=1{~NH{y'2Nc(pSUDœlG̮Z [ g&ICiUU\j!m~@RTl@75 c4o/Q0[ö lQQYP2rlJ β7$#ݫƎiW3-x$G BBf@}:rDlg2&j%n (8aLP.E!E'.P3q |{* 5%9 ˉ!|#n<0DŽP8pL>28 UFEUR(qspcq3q j#$ Tl  tPQX @`@QiEi(+QQrU" D$DȜ9.N6ml QUclZ5\iO"DjI hBI#ZjKcSUQ,Au`xhh9`CTr0Pz@8Cn̖uCx{~v WU {RMt[M8hˢ 0/cH8`Oo`7;= jGYwXvGNXKQGf*8!!XL D Kiv4t94"0-u0C@-9@((@T(+Z Vei}B,UQEQFTbUQ`u Jj ,̲ef36[)ffL"&M4l5MjSEYYRJeāCaE@IQDR4[44dȑY3LVM6ئIhiQjĹY)&31&ġQQfƪMlb6MmcjUJ6ԢRJfd$Ͳ2BRDbBL e3TJ٘HidQHiP4ٲjYL,Ef+jP"%LbJl&J)RjY) QiM-56bFԳK4-1fPLX2P&! Mdj2)Eji6h,mI`Ħ֩6Ք̊`RB d& F1Z(2fefҴ$EKZiU2U*ͳK YSZ5SVRiYZlZZmڲZjQJ5U&jVh:"QvW{uFHFxt 2A53wȅgZuv&/AZOe=[/O[MF ,@!2s9eHs?!.x2;?/(ŧõK}E>E=TpYegɗA'ىkZ#MNw֙y[Q&(}[ۜ_axttsU{~3xx :ߝ\8gX>%q>e>aU!A.5$:8@mp=4߹7&G1!Ƹ_rzju[_*4%4;AUTns@ꨬӵ @ a 7Mubzџ{^lR ݅0kyVje㭎`b݁^!G5*"^jtJ܀㿑J7qu{MS3X(@P@E1=)1#Q{Qt!?QwԚ'|fp Bl44=Rx NF0doB &]OS |Tb$KzxjZDVH@*pJ%UQl]O?X_qga€Ȏ/HtXYˍaaNsYiD >~F9",(H"x|ɭ1c ;'Vwk uB^'럼@2IJm+#‘#"Fe[ԶoU$UN_iB !gq;N4k \GSgY99Iy QR߁i`-w`Z+zރ-Qz240^5wrDZA/PLD\Rqij@`J*n#Įi0*O!uݪdN(ҁ4-5}}^ ÁN Gnt@Q"6~:*yxš˙0|aӳ]40 rZ"TWà^`$7ᎌ 9kRNnO*YQ;EsEŨH*lh2qP:J) /qGu M 8"B59hm *IT^!!wkb/l T1}-uڻ h;%Mj1u9Xo-ŃF, 9(UT|-)8ShAL*\5v9j:x s&}"6{(;}g[&k0 nbhhsh.v<߭ ("a_ 6W8!{D0ةA E%hjܸ40DG! #keܢ`0'{wi4Bʅ5 Azyƕxr&AP*U26# Y 4܄IW+C. ѭq Mo0 .,Q4&Zlmw5_eU !r@ZPB٬((V@hE8.G:QPZ Cm1N[xji"n ,iEUΊJb` UqO~ l iblTB]BR|٥Qt\i1QEmVU qƐ2&m7RY%`PBwSY6AX~q->(|r&H%0*NX,yB 7B614ٸ6MZ %yml9jcE"&suvJޤwz:-" {GNL!-$pfX 4H|` U SsM\MxƉD#>i}h! \ Dh7cHuK7  * ßS~lztj5K}U@HT@Euk y1ySDIgg7k"8- ̞FMN Dl{0zH~tQ`|.( /(?Xx/1HEEȢV`1W,3 -= Z>ZpUThKO{H|Ci 1gP23;:+ӟsb )w. >#mWh 7tQO(+ HN=$Ӷ*S,$ P7A4fiXn T#7qE H$DV٭-1XF"*,`H ZImj#bE5lY%kkQUKYKW0PT4 P6i!t+ J4+*_f*>U>Ze52j P Q2wx˯p6[L? ^CR;16+hDiCPcp׆ l“.0 DX i…Ylc5^B;ch*DՓZ5+^OLvjzt/zXl-*ϣvH6ATP I vA'eFhI  F Lm$[[Mf;9qY0Hc"suU`=W:G*k*F(I@Mim!PccIp9D_i4jyxNͣ Ѩ5T9)C]) yy*_bon#E"GP w`}9vDq D @КC9K* S_i~u3ݻ5oR0XCT5 qޱ|>YdQfm^m ͏f =#\Ź`m֚kV/D4X栝ptM#&BYel,=k791G8Uhj.w{wtb@Pl&T4O+*,^Qg7eDEL/Z rMk]ICx86nFjdlsuI$Lj5hEWH|b4 K82Nnx]-a4>#=1B:4x:G)EyreËT;Zӷ+z%HR%ؠ†0   4V6"`ZC>ˆZ$oBr *ЊEQUtSAqUQ=MF$cԌI'VBm3*!EnL[$Q7,mѧBicMX0aBB"TEF ar52ێIrw9ٝs4] n6Ͱ8Ij$ ZWQh+ZM6->Jhl1bT嬱cF'`)ZiX UE[P DӀr, 2Uc `Ei&,X'lF&U6df'R42VDh5"48lCkh}=20  (B $Rhz7TlHG:` Vf[ !%A\(2(:Ȕ9+=,2ګMPEDTJU¡ \FH E "1ʄJ#j%ymAc0BmClDȂA#X0 Yrk\x~_5Q5;| e#mbR DK,p"VA(62`K! r?|5efH[lsX&~g|iU"1EjEP0B\QC%*|DEt E!P"}+WM5QMP(@( 9bŠ(wt0"YExAv*[k$@0"=V+Mai0ޱxsp69mh s,>#8ӗ^Lcΰ, {ET"qtm- V6T4T;zUNqfJZx7'TryœUuk5AuPH*EPy֨1@0\0 IY[dr@ SDG$ U[0q>\4'gbPvb>7n2BIGNqq]s'x٤ǟt$KM(;%jE!fvWh o]a·x琘s rO=RKy,0lrrr=! ` lA5@9xҫNQ1DUm$JRK6PEUn{W[ ~<}/#Aa"hK߆ͭ!:x"t|TRt< 1@V)7yadz}|9@؞3ş:k< IH=+ 8& xgи`79EK NJNG.nhhʜIF(AUF>Y [39r:[N"(` *NfVc,"3G[_!`kҚQTnYc:e;UmcF)k4!\?'J9䊈?KШ@ s`mD<{}x $P7<.Y$u*D^qwfAMj 59ː8Q@ (v]mhZ)ZY@#@pPBX@,1@ -5'Lx~j2ciسjN?ߋUyւ,v8&޽e:+)wgbMgxs$@.BN$*FBPBՓ*0ro ]@pWYޱg0g]Pf.zq-BakV L% w&ZiUF@dNOpp8x8'*@4Lm@^DM1&9o y'<0G;E8F+88R<4Mq{QOQoz+{c]}mFw6AL __zEcgP[UQ@c<5gb aN(adY)RbX)%P5,;\UM9R_TtUQUGnL(c`/s|IU+>19eɭ19ѕ7Qjdv˩ꪢآ7% YcrڤGu@Y6evw9nŭ'TB@9Ql0Ts̒9ߋa@X :}v&I-@.ѡS:0^,~umg&/ܭl%j6]Ώ;eM@@HTEZ9+WMml(S+E%S4!!7e!X=@ZT pq$J0[L0JV%D8 *@0@Hm(;2 ۭyW|+`vQ'uZS[ }BTr-@x8͕H [k*9.}=QKrXeUZ˃:LkH7rE@uQ8Fӧ#+Iw~ƒUgbCf$F$ި4v[TWX"C1P%5k+KL(̖J<.bhQF" 7[B{Tz*Ҟ5!:iijQ||c KiܹӔ=>W#j&yv'8xk|3!.i\v,_:FGsrW `UU'Rqg)dp{zǹ迠 a1BGGj+H½S+Gxo]fd2HHA(|G{z65KmS|s:J,h!*C WI#J?UZCK~V:֋|TkZryeփ@I$ 8y1L[U,FH;!+收Zv+f'(/JRwcC4'PH|vFP\%ms$~ttJVXK`+ SAPd-I2)檣[d2#Jj PlBLXo-$B$-% EY}'5U<ЪOaɘsߦX狭 pP v(5j@F&м!^|udrcĴ \C6.*rNU {b8pzDfѼ\*>c$)PtKݶB)4)&Lٶkyv,,,$I$Xe2JIJK,ԌF6Zlc1aL٥)$@RM$&&)2RI$! FM$$2BJDl0,4&Y2B0l32c3)L̂ "D$,J1bsO,y8=cc5pJ,$L;; a@rg&@_㰝S!ڪR7(HUQAL16 syh%'} AzNETzD dR)Qld hDA-7$ " JhpXɾMq**pR,DkGZ\k!?ޜXni*d Huwu*>aa@oD=mbk~h{ MT!A\}暠+ݞqp"Ck 7FDGxǨ 6vYQ"{A;EY7&DUQsh ]PnC z/?+YRB)yl9hE\# j>09 EM1&ڑ$B1g g]u\u\I$YeI$I$I$I$B̒E  !BX %R[IJDe%Q2SQ54T%d&&P4d"IHRD F٪I ld *-$jͥY4&JȍLlB"8%y(.ܼL!3 :/ TI".#sjp**Nh@aϷ-{LzK-DU-1UQUTxP~.5x(CET H EEJ՞,U3wt,J0MbfH|rʈ(ǫM/7"V qT[* `vͷR(3>gלF[uHJ-?Txih@$@Q2d+, Ja:UQbezR zT#-!###@USA =檣EFO.;kH{{8/x LY)3?s>(8 `~?ޣ{9*4m﹇"y~\JX}~V`*.g"p;B !^rrΜc^ѫ Ԏ5 JP|ۆ϶[ k$NfT{%EC?{\&+`n0'r O$! I{bΠHzK\ zǠH޼)~ۋ}xEYcGU:].f8  {=/wI4z@ XO_@v1<螟HjYKb4EAc;1zvQמL^LScܝby\jr : {'kYa+G.x@ErPTW|`aF!UGr>AC@cN '_&-g 1E)> DrD*"6*1TD'/%`|Q.#s]A(cI2CW;]nrkr{~\\Y(PB=lc\t/ ^箸심C`e ߝN0 <Mo Z_Ѥu8s/d$v|XjDz0CX}!-.On (D ~n{ՄbΥ4)v mp"QpxEAh zQ&0eyQifU+f7R?#~gWo}?ϟ/ʷW}o?#KKOk콏}Ϸ>oW_A/|7~_CWkO#K[?OߺIn+OƜW?CDZާ_G_/#}}ǻߗ/}̯_ ?i}z/~x=Oo}_z_i}G+?_Ocgo<;c{.ra ccp>A ch{2(l?yto@BHKF _ThH i3 eQh-RT)FDiH-ƣ$E(" P)@  Q?*Q#B_AV @DaIV6[O\8X)h|QM+ӎ:%•PmeħayR LLXyAbj|zؘn#Sd]|˔Μf^Ҏ(FH`ɃD!۰$8ha>S !s$TӗؘL71F!`;qcVڠeM7&Փ) T`((A`(U 1 bثYD@DDE"""*" m Cf~{s٦V׻s{wƁ6XYsw2bo'&3#u3NH̅Q#%-n6v#]Ӷqtbkq B`8h|קwob^*n^o.dK=[W5_sPd[m܉19YWHmƩA5Y/!DScBw`Qb1f'q rc!!Ъm !ZժA޷ ,eu0(7MhpS(K*mɶݍjZj4Т)@`DȂȊ$$F0JT\ ;viJ%IhE"‡H86ðl H@(„ I!A^:@?T}꩑삶d UHB"7-$  H)&Y! ];x={~{;9o[ԙֶLцpa/qpՑc64%r22K!̦HX7Y?bL\z_K*u|/aTP@ JE&h4 ovq#P@if zB@!GMd`EQ%(pK )( 1UDHKD*Qzb;hvel 6 m0#FlP`7@ JQJ P**@!*00 TBRHUE.QR+-BA" * RQBPH-j*(Q $]eR @E KX)" )@BIH 6}0Q)L4< 6F4=&OHF=&#F4Y~z6}?OΦ"Rv#z%UH ,XX"{d1T?cajm6K0>[vSL6i̸llMRp0Z1L 7mUUJaLZmUe΁uTSmYYFi*-3XMS*XfFɊ20,eI"ki!@ sW}ESc~`rpn nne0ZXݣ,)MLʎ͊2 *e,2l ie,M796ccQ.%1KRZ>e?y?xay7auhe&``1ii4s eppٗs,)XMTC=_D=p  fi5)S,M˾]LNM.IfFSY[ɣ'%nݲhtGe-2͔KQUT ؖe;a0) uSG'v6)Ҟ 0pl):)á ;21RplnʃE0&RZ;6݊ZS 2֦-*lif]ScNʧCvƎYRU1,jaJtiXvR6zq6!pJ*B ׂ@aMz]w(Uvaw^/ 0Ú>?;D*YQ8~,N ‹j"yM8Hy,C9 2|}[c*QuGnUԉ}*PQ7b:Rm(TԥvRa) "!'~wglagjef6)\5Zl" *D01脧,a`UQUSU;C^eQǔ>[V^jLS%::QQUYX{ OtR(3}˼Mg7q9đQpID{\tTlb# qhHjV\“Yk1$'V'j8O/DB[LD,:p\DSe+dH=X̊ٞzwDCd`hJ_"%gw3R=!3RJ0oF[:j+U[uǪV/Ean.q'IUtMRU9*-S3 O'\ tIӶ9hIg DPq/F/ᛯc*j yprÆa&""17:ZVDE"ʘ/ܵlɆijl%6b.mŰY-vYi"-Ɇ[>PT'!">y QA$SE9,˳W8߂E FVmZ]oPΤ!7TPc*N9\-U+0gn[97̵QV2V(pa^T33: 㠈ŻuyTq&jYɲI$@61`H[\S9)eIʎS0˓|džc4qa=FP_tU/4Q4TFNm;7\ ٣djv咩'' e :r¥UT1cS%R|38Y+D 8*qqLrxP/*]Jn;tTӑR@D5nw:b@ aIzBu!WL_m qtb\a'Cdlb"s.9mcG~I,<8X\}ّG, AMIFq 87aI˃' +]lU:'PU]lw.eybumcIPY⻖ɲʂߪ6VnHDnm2:*lr[&ELڦen6P-RQG%ŽdN]YWΙǤLBFLf:UZ3f77UV͘ƕUtJD&btIqF0piEaDVUUē[lۇo=Eklti); 2 w7ddl:%*S"i@OA"hp2ohGTax`GD:p@83 8jiI  :L BUDZ/Qi׼i;;8ͩ-ۜb4gQF)Nq$tswGDYjfөyFGiډ!T0fSt/Yp\]+'U{ZDBhD,o5Y-%I c`˳=-Ĭ?I.2 +Ra,E%iS˕7oM l[E( n-S,Ksk lv`*-fq#jEaƮ|Ox[vԮ)JR)J8*vBE`Q@Ţ4éhHt&A"4BH!'sAQZinA!A.dJTAE81ex[#'lN?o 00UӉgnzD)KD$to!6t%klo%۾љj726ƫqW<RB&*s益&ԑj>cjM$GF$$c4ʍ."KF7U7^I* CD[m|tBCjx&T:$QSYrWw36jphH%OIdb*ٕl[eaa)o2ٲiԲ֩݃i Z0(ٳrᔦL[9ndXSÆJKnKy4eg%,)-iU-oIiNYui SGIL<=Xa6RJv0ɳۻNZZu rG˗vZ[ SM&nSz,cN & QDQ-;0\0(VأEE[efM7u[GK\ efTnͳl\T٧FV9fh䧩76dL70m,7nͧC.VԣLSN;M+H&[Ѽu8dɦщeLfSٳQ&t2Ό3fcԷqՃ itnمE0kbίiٕ9pȧI>BEE&P1 8RִmɲӺPYʔa&JنO]2iٻYKrra0)gW-p6v졥8Qekil8JYgERe)oCTT^ms ëLQaɓNc=[F  Y˻i7xg rXᅲccM&*Ye-,T rNԷR e:#7Zta2iW-tSӣ 64uy:)\:6:<6ppa6r&˅Z2σgvF"am8(9-TuQFJhw[)zE0Qpl:llӅZ̨v{vSTd(\52;d٧rO6KJiFTM,ir))g)#u.QM(JTb Z0Rݜ4䶎TN8rʓ/F::(rSvp iICz ,,s ``gcsgIYGVR1V /{QF{vJ4t0SՆlHܩ+a{leJasNهFN6m[jMlі66nniɱӫL0Swnx;<[úy˫sw eIj<L)L 0z)ٗ KnQ̜тRY4a(FÓ[uSf-cgC+TMLvp(ݻܲv\ݦ͞M<4TӀiS @SJ0n L-Yjninlʘ[GJ:6eٗVƞMxabrͭK6peu%::;nrrq=xw-s/ }>"W`-%_&J$%X2Vʣ H!I~E?nz)g{T6m8S ?JBDI[*.&J;0UVVXdVbUhL@0 yX_c3ct,1 %LH2+)>V*̌+i,XFқVZ!in! bEla0,0a '/aqT5J"L, 12,(d ƈ\>D-n:F ~pUGmUq1,3jQmc01aL30YSB폨a18L;v]*kkw~>*ru9SQYhۢĜx|,T"kfc:*ru9Sq/\䫓+7QQtR<~Mv%2no1J2/yd` wICK2v6=NEGOYala4  (`((0PѰXأ$RRhK(MAؔ?wULBM8mHÆcIRnMnBOmK701Т⫠YV" R"o@ڨT&@mMUUDZ$KL wEednQOUUUUUaɠ(A0& "d%(ō(d%(sp E(%(d7K0QF)kmʀUƮEr(!7b,E֢A2*EIسCK2yㅒ)OdX$!l$^%I}wn`Ŏۚym4FdkRR R:M(&V6a"%ٳw˻MFDNw lWcɃiئ)xYln (trm)NO-`x.lwgbO#l4O6,7WwgIãɃb9;:0nˣ-˳]NX6igGOٻ D)OP}{l*YcsfP۴eGj={>m)IL[>gaq6n~/ܕ'>WE2{ߡl|Ggω))ѣHF?>Su6iv*a^<4?\~\ϕtv /za= 77z3>E]u/iE>a%\^3eW6pڧh ^Ux/ZUH>{:cOz;!OqdJ}9`vW04Izz2}1Ξ|N2x $Q{OI)kL )-JRfGcMAs/%vH6aj^fSO;̬FGwE1X+Rُx[D?c [gcՁq6((6"p2T.9~i4ZflS$#u$#y(̽[㫗zϢ :$\ɭ)±EeZK)QuA:r}ꞧ? G|2{h t{}RTKz dž=7bXs)eC}vQy[4f͇}%''o%>j7e0~O%0尦Ǚnز .SY0n짳;'TYM>HFjO2vn3e"DݹZn'օHObxėk֫-H4PBGNOseէ>b"h D|Ϙϐed0$aL/!rzȔ% Z I$-!Ȉ|Sn:$0O1D`vrEInȐl6rhW /nrJI$:0O|OnKpx%/UOđe>E& ~+=OG亪&qtd|O$@gG}{?;ĎxdiG}!jl*Si>† 95 4Xnto$gu<JS"K^kĽRz/iJSq~iZ[}B"Prʎt0k[a6Dl,ɻCvnq;FT8\I<ŴxCxNM[IS ܔn1*MQݤ~KvHr"&ŠIv>cNSIcLMFS)fpwH1Гc-JSvrdh-v S*vPҏԵEs / '>UU}c$-:c$z<"}CMdIy짙L6{d>-S'R9nӗw3K{]QFs92| =p#HX%a;lF>$ჲI;9}P߃~w/}~ ˕<tpyyy+E'so'Nƞ)#M/>Cg[R{NSu@#0~Gr)'~:98&~icOgm-8aT݉7tE"ܰžjKS 0]668aѻ%6n̙Q6Si8Sja0)ص)أi?r۸NήYnټ[,0FU9XԦI4;<i"{YwlDgQ>0غ;/0L1R)H,)Q7rɃ=^ó"O'd={ҧ7uPʉe|/CA1-9{$m$"}mQ.N$6OkaqN#R"y,Pԝ)X]H>}Gwx"6N?<;tAܥ$:zi(:lu{-jII}!ѳ8CZ}&a)<)6(ʓ =18/!`v+U_HBѲlNm64Do[J[(WF ?ف[=47M}o!-R$~ҟ)>*>r'yܳ.cIԇs#a)D ҁX$c'0cŋѦ{a26t)SeL4(yf4JpRQJR%ZZ`Qf4aIl˖eBJJ8a )I )l&U ʈ)nNEimIPH0PUVwKLT764`_G1GaU/l9lDLreE0 R~&%40TKYQ$Fye6;{y,e%7q&Ni4éirH'/ 2Ml N0J$J"0ąG)i5 I eDR=F(Xa JAH܎p&Ju<͝\5 R{:ݖ8`"%L=$୑q XneїJU0(x4TAE6 OdM񣆒ن$ ==Q=N4[#PDe|lne!zGaL&>cCɡE 4"'z)JO'wѹ&hhk[80p+ .% L)G"}le/SOzKΜvʗ"eSɃvHDl}^dGKJq'> ~톝 w<?O\0$=|j~);~/g:|OeR?}LHo{Ovl'ܓ649)ɹry}AMGܤʁ2}Y#?#sJ~|#tȵܶlS>IE~*O l'rj}kw2zsH?{ϭo{>?9S>{Lf҇aɇM6=R&6QhRFX:ulJROss%4 W⥍cp| b,b%E);2&ɒizZt^X=.,;Z1bbXjkbɴԥKLMT[1tyˁU)32$Dh5& dcIS&hE6E **EU*)BMRe:(*v:ݔӖt{LNYvh4iDQD/NΧO >R6\)R{$=^N4-U06/&m/󒾸zM}OH|KaOcXPA4ǩt;WzJOcKsb}M(`DK'D槱=Jُx%\l\S jrgɲ OziCnك;=Q>{ΪQ!7Txϐn܍zH=t{#^r:41H<; ¾r^4i=>fϴiɕB#1R,2ʊYm [Jfe[ZfF6dEdڬӹU\*AX18:[W>L!> L[!${_|I}|& j0zʋI PS YPR}S+vS*4-o0S6Rݱɘoq7Yl(R~N[rV杋u1ƟzV^-hsXplQMܛ2Yid*e}XSei/"!gtn\e7vIp-DJnez䵬922u _H>R.q% $ٶ4r1[-0QR)B QJS%QRQv(d[f0%[>6ϲcYɳ}W2lpN9~Qt5!-:$ʆM[L a&[FTun:D܀?C\> Hq*dRq ry\GI~PIv8/O'Ų{Hp #_C9"ۊ%yO(n.-$xa-n`GFqi,QTӕ:Y%Zbۘl0KTG#Sb}oiv} $짅<a/m$=O)x0xY;<QMԣRSn|7g+u?2v=M6Sr~ftyr8=A-4d2R'4ؑo%M(]T=ϵ!-0rL'رayqiXbj{.Du_5fZa)J`cQXihlً"*YbEYM)0kd)#-iJ0bnLWe2Ka&R mF"X #0Zd00 -QdٶR 8wfSFT/$ms|=#{!Ϛ{$Vaֹ%Ǒ[bdfhؘq<'B-c-0õG&KCtw$J Oˉ$[p"_gJ0ZXғ*0& `DLX0̔)Ky>"m 'Oإd{s 62ӢR dp}oun+|\v>]]J쥲aS2liٳ Sg{8a0JN%;(C5^^4h7Z(EO槳2R~?Ggy>g`GbS wG#w4gK̨bu`l姨DOi>Q}Oo4Ihh 7Q>c}Nd:I|8݄= B>rk6D?;f$['ԧW -0>Q$~M4&̓7r,<ŔaY٥DZZmZh’krj0Qr)GyaQYLo*%&EVEE6R1&1&cҭA͑? v;aOs,&)cR)P&Z\iE,R(ʔXKQ(RG FTE)-K`F*ٍc 700)bVwX!Y`pPPWt0ܶq8^<_{$P\GE#L1DžUyf08'eTmSq0ԑ0Z|RRD<Ot}VjWv.RO aʇ;T!).ckzӰ}p4RKc ;ڪfP'ݗARb"ndzk6^(_"=3;BK=2zڷ] v/$Kc=R #ިbv4^N$p[ǽ蓕4Ч8QIR"/Q 1ʼnI$SBԣ~n}g/g1=?',əkO0z<ܟꤳ}kwa[/똽 0b(P>G/}|M-:Gg"vS>%R="z;T}q$RZR 0|܍Ǡᦞu_4),DJP{)=aqRԧai5웼6}|'ط &ɥ0Қe־e%Jzna=K_tM4vl}8ό4I*%wFGTI`DJt:i,'-?J-eXá X'sC+ FgS-ܾ)oSK-a%,MM-[݅) 9`'u'#+SllLpNӗcIn?xHM۩O`)ONiH-aQcaZOza 2DRei)I`0SP$Z%d)0UKYeR.!YJ-)60ǔIxD .Hr0)L-iۦ]'f>4c M 9pl iO,2Z'e<&S*aj[u2fY 2QN-rabijS[)G,tf`a %-nalĥ5>%4YjlN7iZh-2ye9Q,[iparJGVT-rӲϱ,=KKuhJSy2ъURcgEŹQǂOlZ`ˆNTRnrwn;^~K^'btaãE3$B"[0>|L s7pZ0dɡ0"'ri0a,|+*ZrI7VN ޖatK-UUU3 ݻ6mGehÄtph\QTnû9fMd!L-Ė+r¹ġ) j(h=_55p;fǹ6hIKQ ҧYj:iP4ۣ")e$S )E0IK)*̊TJYj,talup<#< tJVr=b< ,~xw:::7aSb0Lf e260Ĕ[Fćr (Q gD^&lՒR)JRJ*R)JRd%JRR$$ց%Ie$K,IJԔQeS)ifT*JYee5ZZRRT̔"A KkN/#r>VRtE t[>2F(RRp)lFx fF <qùOxDOoAIzxN<'"ynFi1DO3 02tm 4R8NDp\UVD\8- -Hp[I'iiʋZe-%8aKijRQJS%IMŽ`ܣgU,'')JғݳgU(&huc+DP$'R0Kxva1M< 62-:T8u~Z[Œ2Ęa0B#wE#Ifq=31H )c6.NJs& ܰSDlebt[!N)I&iM , .G"$4p]Jy)n,6LZR Fa00nĐZ'BxSq<;Cf,mbKL8c-8e # ),,%)JReJ*TRJ)JR)JYe)JR6Y6TJRI%*ITl,lR%$d)JmRYid*JVKJT"=%4"l&TJiͲ7084\ Kf n=ƉDLOd݉4nl9-Z .ml,Khpp2[(I$͠y9tu0ݳdG8`2l=F '(Q 6Z`xwwaa>ޱn3;Ԋ=0R {i[#l{8[KT$)lE8T8(l-f SYmզT4%*DI4*]EӽUYDc. s{9[Kd%:YKL7StiO&LŖ r%2M Q)J)MI4hʋRDTGAJZbUM;7I V0cIC$HIl%}ښ71Ո+cag ^<$rkgn4G6Jבg}|Olϩ>gwf2=d=Q:~w}(Io:$yF=m2lM'×S-#D+u;E1 2Җ0⼅E>0[zJn[zϩb\Qa>jN~g>Q姐%OZzOGT':G9O֧Hei= [i|';ު#* r|V[O*=-c6o]^Gx~1X $~N YDv?2))I4Kv -*G8. )Irtp[V&9lKeE(ദ#cOnbuXţ 0#K%Ƴ332υn9-̅41!e&6]ɨRlK0Rt0t<4L4dyO4b{AG2r~ ٗs$e J*JF\ cرnTZ4l%2GM0ba B l$Qݾ7swn} E/P\Spp-FʪeH$4إ#):  FTe%)&6i-5%F 0[ Il-ikRZRQfni\ ʖ; qnm-0Ƌ%V:`S:bN̰Va&g c)gg6d~ 2-`iMID;=$GgI-6xJ;ܖ;LIVd}>ysfY@ A@ @ /j>ヸ[{GXp[ZZm,Rmp½kb{+)/b.m8 iGbcigk#J|]hmPy9+9WY6TyՕ,kc}!SfJ*"֝jֹX,Dƶjy+YV13n6ͺSVn-TR>@\Vef4;46D46S=\fN̖.`ϏkTSJڳKSƬ4a8mVlZՒ ;*I$e36*,=mBېL8" m8DT,Y*JK)Ib,PRF&ѥUiU[ě*%I $-IkZ|ءE|u+,E*# ʪj+'C>$=⩗>5w:EUGVudф!aƖM%BC 1Xk1QPVIslܿ7_9_׮*9[PQ!1@Ay}|S@ŷRrjj4@H F)  (H4h5;@rj (=Th6D T 1UUJ:4Z( B[fbbRin=$횬DITTQ"REhD@٤ PUTRUucR3DEU"ΤE"R"(tDDCU@=fad &||>8")id&y2jzM42zD2jhhOQhhz!ha4M42hiz=FIPI2D)hik#5Tk LUNϛϼ7ܚr4].u"f8ěHHVUcPĎ'Fq/ QpaDp @đbZB8bLūNmڳ kֺ:$"66-)E'#l+~Cdu4E%4Zccc[Y5DmWskKm[mmpRfߥk_@Z[8o\it'87u1'ZZc1fJcM4bUb~-vx1)eWΏ,Dm~DںFޢnH!CCA]gC;˞ l]JUlԕ ҷJ+- 9av?pW2%SLL+L)S 8TĪ*IX,wCfUVT_+&)$~%Vr\lS (v:Ai]lqbrNFޑy`cͅ7u1s2'Ip&Yk'e1b,7Y#*y8;l a\2'mT$e]+fW+zba4'u9bURRӺS=wT&FYf)Nns*әr-Dz&V/ S;,MP0klCH9]Qiq0yrc2)ъmXXlY&YSLؒ *bsi:Xl0,(8Tgږղy:x{G{FvO+¬g%/ ݶDBr$prg.]L41F} AFcSgfN?X0吚!d?S1sn_g~<,+:K2~)i-PLvcgWD+E..eO6l^׍]ZV*WR!d:Κ,YN:7Ja".>0 0lhtV?ke-Oخ̰f9?bVe˗S +g,SOY$bR-Daʛtt:ZǤ,񮕜⩹cMؒ9$ߌ3wFybUgk-$ `ou$$%]d.fvv %oޗT_uӫYjY{vmbFΚdl`K@mf浹-03M,ʡeei"2l3jH6!1$R <)R%j]>_0+-a:=~[X~.2|Xy2~JësL ' S:Nwۛ::8͊)w( S,ILLM癕U;TYUiјII֛J#cJ)D47.K\Zjmrr"Rɔnr42.LJF͛4R.rl#X+Jd+KQc&LBI54RqʒM-&[$"i%[),IeJqrZI*RW+m %Qme)w5x A7$RT$b ʤm XQK!KX#IA+̪H,VMNJ %Kb*تZSb@ªXؐ]Y„u,hP X6T*RJ'u%J4AUUYfY540B4e2e-jm aTIe*XSjtUiI̤XUV*K RT%h4Y-Uee[Jʫj*ڶmmdM Yf`Z6L"`K Xi4J5L]򔟩T;?;V (Z(jLBե_H/{n ~E_ɫVwgvD]گ7>&sݛCOPU/HaTY)KICǸf2|ca+v13ه-74i:eDpH_9L!DNc \ !ufJXd2YYaMZRЩk)!Hc*%,e)YXHʡRĊrt\UJ˫ 2ZaURU4Ɇ[+*iRLeXa[+f0"6Vf[l[5˝)l"Nr:rF#iصcBxaY@819 bI$qrd"+U,ɷCLc³KV>'C^/x3dWY/ǥ]pI$Ä@ʩBKfUgR RySLĴ OE Yh)aL;K930u)oIX4nA |272iŬKUUX4sF$&?0>NSrgla]') nїg)¸lY0 VXxYA0ϱR9_&K;ig{%dUkf0n84SiLd஌M2YpL,` Vx0i֦͛W L+5Z޹p▫5n) amkn8r*K (D9c]31YaPQmkwン_CטŴ|YnC+xi0X30aLq(.TUYSqiTJ@SM+JVjm148z ?DUa:Wor%ݡ)zh1wn2:闌6wB F02vGk}wvƛ*@$G` x-KRߨϯOvv,(l42 u%U7$|Ifh;x|UUDPt_HcGӦLf; Yv *7J~}4\+d/MG$%[-ve"3ZŸ"1 DiXW0!#Oo(PMp, 7P&UMyoߛmmp˽UJ|H}$ >U/|oq7184TYZ2ne/ؤЯԇ<]%8t Y*ĒEzݲug3}_D}G_Rn|SLn1Iąݙ,Zsh]Pֵ8 yٝ[ģEQz1ÝlBцHDNCpܾ`ēVM0)jfWG5,DT,d68;3ʖEibƒ,,(Y;w!!F ,hDÍXH108:A"&J! PA(HCxZA v1%[5q<8 T !`@C1H_e ):էx )xgibUXaXAU$7B,8O920-MC(2 s .3dӸ8N04…rdhI$Pp!֥bI""I"`>@ 8P:sN2u"F4(ҊXfSD4so Гs`',ÒEV$kb5\[xIЕ&LnHa|,F (cf4}( ,PA&a,33s47z1Ac,d $.=ybb{,apa@遯b VjC.BBsp' Xr aa&r !kh6Yʒ.p̥ب7(Ĝ ӁJ0bL ހq8Ɯa@%1[ prل`g0I"sFXQbaط6PQpdO*,%,qkKXD!C 80&1&ۀ0٨_TU/|%RT*]q'twYlLcgKzHVfc<"hXYLHĒ8c9'p$s[6*e&UƜ"(0mPF_dA"$9\ vZWW&bݜӲDXH;;7-LD0K zd-, +S& 02aBX."\v1\HE Ǹ"ٗ3"6vʪㅆo[ժFNuN$:P: KPاi,HLmc5r궧k6ng"wz CpraCbwP $0w28])G`~VLjLg1w.ⵉ,H$!Uڝۢsɉ;uI # 5 9g3Q9\3k0I!P~r"*[t4XOZ\U l&6j! f{|򺤇~G=f=x$STn*U=g{bOa̱1TkMkX[~~ؓW<}[R,|G,d'1 a^p $= =$U 86CLDu~gÝvgO'q|OI,}*B8ӺW>]&qn960TR9800?w|bK 5}x$#]ߊxL<Ůqet_>#X8Rξeb7ajX%۶DsYh":wlc93#d?wqxy~nNo9͚Ӛڽe 8afaffdpHg7&7 "t&omW[0#CZH$ZnGhrXex8i \R|bS=L QEH;M\6+ uĂaEvr4',9:?긝\=֟P\5}>8u_R :^'.[םsۆlɝhy쑷10a32k,䈐`,59ظ )08!0*&,:'81Г9e`mSD.3d;/S0] G D18UcJ&L8( w)bMӃ6s$MU*1Ѷѳ)\"OOO#ߪznV VT.FI"#~ySg ?|Sɩ$AN0<:@pV%Nڝybq-#Bi7^'"6eb]s}UHM-*jD(I ˜hq~C=2hqƫ46#{{G槔׮A|b|&#}3o׼O}% cՋIY'K5Pb׎isdx,;fތf&)Bidm;vOGHC3$|oXK(OixS6֨శka*=}R27_1Tc,e4He @_,Bʥ]W{4I$|>}N2CPtyr^8NaAڎu%)W˃:0w.X|87ϜY\8!05d^ght>E:  /~prw%kȽvwv'y RY&af2`HC,f,! K!-NӆK.Km,>w;^++[{ BA>pQ (z1Ap2 cUѝl\2HANhH;JEZcemHm2;:Գm̋J"sXmv:jo`Y*p;,uƅ ͑hv3i,ML4S+\ǐFG׿=LM>0@q8x|.8滋:&%I|QG^?e~N8Wl|ۉvoo6ˇ94ar1r" fta=4wYX8˨Y~[;Uٍ9OqDŽ-V=3 TeibRe ]LY 0 :/pаw{xs/Hl;fL0f|G&!acY-%6Y8e&FYW0qQ lngT›%aF0+d%w²pcp_06򫝇׍ Id),&!&pY9#6 <Ny;|jF|%z yq2v7vDݮcQcВEK"8= ;pĺLpfPFc‚{2UQ(BNde N)mp)3 w:X]iLj%_E|,6鮟nd`9upN|~ro(ñYkEp8iffYG8N1#'2uo(91ѧEZܸdwm>zǽ1%a^LqfկqS$sy=[dxh32AΏ,RHa̪ܻ2i8|Xjc3vV˓fyaS ܹ*XQ1H!;21V C?0̽"@PsU I$9 xYoM6Ikx~@<((`H*V9ئn&_8 3F A [dRu9[CѮNa1 O,g3qq< ^d!m$A9 ``UyLpܦluxt$IWs$2a5 3 2RssU*8I]NzE\<qPYz]&eY,)1Z4rj^&}QFQv5}9 ifmSu9 rP?ҞFElbY $y׌X哏݉kȈ`_\%fzy'p}Lsflnl‹4a@H!wF bTa#0"4n2{g1d~xp8b2=k7+XݜfRa1LX)ReK520a,EUg%hN2FJlU-I8ME=|сEaXV'nm[;Ή|,O&aoG›3g$5Ѷ*C+Iis yLia2Nv-lVU`$A& N>M4 ɴ!8$Zq=垤iYˡyٓbU7ZN H'x@ܐY~ϞA5ٓq$vqh;R3|0z}0hhA`V! ̈1v XRRijUovXvNPU&b tHk`o$e'zӾ1becz x89H w 3qdAD=rӲH`02" w& fxYˬ5$P;T3*u%ٛHi7HLRDedФSNɌI7'yb};01|h֪|O_c22ytW.k<٭͜RkM7rqmx MDV5+i[*jm5b;ZpΚ$(W䒎FXCfA|:v#%e7pHHq rv, dyBt9G*u)1aX* $) lSʮfu8De[F]6sVcq$U;Z$hajDFv1tatxD2ə@10$%@LRHċ+Մ0@6Hgof[HCCw<|ܧrwT/|$"  "BpB<" "VA%q/TK|1ߟ'pFRdfȂ#g >W Ӹ@tã0ULճqyRAFɜwOBV0Q3iL)X'jF:,^EަJ\$Vt2?(v80*̈>UT0 {[WEݰ1"#ZCv4-ֲaU]$Tk&.Llٝ#Q㑺ys ;#&j 1Dv!|bzy"&ظ?9>qSGsdAd[$VxE[ e&mND}'І6?K椒 y?.l8<7H ۬}ý-4}yzU^c6ܘ¢236R0K0s@ 1GᆐYtVv ˆ ũ#BM4rA<:Q~Qp8`(ıG<,>sf4᳻M9aӳy^o7fʮw,2v]7x\˻gN͞ 0ҲA'GKχJ4é)rgxN:*S.4ҽn˻,+.][M:uea֚ICH:tSL4lL8n{*Y[;:aѻLB[6i]^^N>{~3{d"/.KG![[XfPI |ZMXL*- Y-U# eLL`T Ub~Vy[l߿WXKi6;iJҩHĖ "R4U-& bVV0,~8U*JTRSc%pQe2LI*-J ~j#JRhwnMXQ.Aƛ0f1ʍIdRQQG&$”|nH=.Pb M#4P2yc^Qpap.-(m%5%)iR{~Kd`{k.}VͺCP{V]&&ʓ"xL4U adLQ(TRb"UF)ߩ&7*%& 0^gZE?nG3?8á9J4Bj0LaTU*QUlYXڢGtNUQaF*iXR ^-^4d.VMfb;`cjYF ,c`lSQRт+Q2,aϚt780"q[&+1]Mb\ՖKJSk&# L22h$-C 3 alb5SH  `  ˌ%9t貊((ƨՈzh<ӌPbPJ@IqbDH)".}w}ɗc}RJR)JR)E)JR)YJR)JR)JR)J)JRJR)JR)JR()J)JR)JR)JR)JR(R)JR)J)JR)JR*R)JR))iDMeQi%%%%$)RRRDJ)H,DDșDDY8:KI#&*=wAqX6K%-RVie5Xi*Q}ihocb`Q* *>$ (NKbIJjImwkJJb}bFjaK*$,Ȳ2Cws\9ݻNNFʐMtK7*Fꁡn7v,aePK,EM2>QN4U-f -7d CǾCԑNr}q.ȢкQީ8 |)"]\唥lLX&4ƠV4{(ړ*K)*(JJ!*(T?o:u.U JMUc& e\(["R=>onyA'ƶZ-]mfoQ #r1l*UEOq)Q"O= JjZeI&[RV-eȖͪ)+F`cHCKfitϖU-?RX[mI+Oy=N܏{6&2P?lAKqTD-KKU/'ۙcB||/޷Ή]UwiXVU<1q/as O*e8XYpcK(K,r$ 7:-ٿ*{Vg Ѧ&߭:za-ye><*NA9gч;ợ9a.<[=vO_':fҷ>Cy풕S=/嬗pŋН}mG~ŇҮb4cɔ9lҖ[}xYy=GFaa>Y٧{uP]P#y^HN|c{$YTU.YZ:]ƒG=-w$Y7A:ƼO~?;re$/MΏhs|kKI<h$8&9T*:ǙոE>{YTӻLH3x+T*孏TT9G]z:;2SCۄ=wa-n|.4ښi= F>c{nSicW,02jhK۹GL=Z{G~V}*?dD[1mdOI|.j|'Hú4؝>{7'2 SX}Jm{>g^ngiXQ~xNU]IO?y|[Joxx:[7Nϸ%WwcӆE{<)!#~ss7ˏ&_*wx:_N] SയƗkO6&p+2~fd˼&N7䧣9&HWlnO+DZOkOL<`Wc[i4+^S/өpa4p{jT©vy)I;đNPΞ{ޯv}iH$A'荢^Wz:5) != Ъ YLKbGJ^/z|bU*1ω{[o4>G#xtnn~"t=;5ӎͥ?Cݦ0=ǒ6Ot~\)yR^67=.my0E;G>:S'q+\8W'k\oz ĸ9R:6RW9nU'eX;~#:a[7]7:_C{TΛI"t}InSN6WtuBVJeW̉bIZceaYm#͑Oùq#4-1 f\b=ۇ&iDن͔`Dvtn(VftaX2t4-.ԚiNUeXEa 'TҚ2aa0dԙT-6޻u es0vvMʷpNm rnk*a9mksM:[5eU˫,88:$8A4`ap4 jl}{#KЩ;:>F>K8Rrw!4Iƒ2JC)$b"'/sOk_f:9>χ6zx1C 4iU 7d螺0ỗ.7uWVK: I)eURe*XJb(_*hYVK)R)$DxQDYdURdmMKY3(љэ"hƒRe$iJU&iLZmJٚ6JԖY+%i*%$ePf)X™,XlF4ҦV463ZT#d)IBMd)-,LZeHe6c6I6FM&RTdM5$6TT2T,3mRUV2LUL,2ʘJKdMTVj*K%f֒ɬ5K,KZRٵfڳ QaX2f)K \ `C|Y '%f?#-EwW/}W.kg/cwfnUfXlif\= sd0ACJDtUly]]O2 f\2զt{d朽U'-M0هvid%sXU(8p9ГÅIP^831T [Tv!?w>=oHZbv/q&TPW>;Kf9;ުSov&|Dȓxѧ6mkZwym]9N:ru]Z]FwSR  ъi1zԺ'KKlҥCai 2[`D| LFI^V<ō|ܤKrnqT;{n>lj=!cf'u9C_^~r |:W4U}JSn|;%yM1$8Y{nxtrtr}.p纾'؟Y3y[z+ѓIwpK=~uN>/r(Ga>֏YW>k{e~W ,0¾웜=\;3MԥCW%ll^ *nnʝWVXup¿C ]].ͥuh $Iz2߄A /1$[bu'L'+F=[ռ뮆æ}ωT)VOqo]qyKs^:HH]m%~+7tTꏃ;m' ;$'۔n9|lҝ5y { 69K7;S e0($ZGBM%vpgI4I*Άnffg K٩TDz8/uJ{IXtL8o|{^utF4x˓K:U^Jۜ^u뫱zIɽJC`iOR樓51gsQ=_3*ZdJ`fI=j[ҭǺeZ{0͍t'c䪇joU<3$GBXWS*TFQF^rauݴ{7FYbdᖛfj*feRVEZxWi 8;)4Ѩ"0bN$٩76+&6g"n&Zrsa7R1팰Vi-m1Y"PT X*Lx4SMPtd^?Y٦\Sg+YMڏ1Ud,8r)s`4ن=O[aIL\Y*U6W9I[2,0"Yt4d\*113TF%ZGbaNbJ]M67ZO98wrNHSG-Oq{1v=.c72'm/nƶɗELMϡټ,;Ī^@scp9:©hO'誇|-u7U.4~%k em6G27~ S_{*;lG Xx:S7t+U-<+H+}kz^FlG||?cUaCv>7Cz䷔..ʼ1ea*\:[x9l_yiU{u]y+%wlɻZO:/#)w>%/TcT-Ԧ3[fgܛ<+uVί]<+.. ąs;|t|oUUWݧfq-$}1SayZJeU-6mV6M2 H1SaFSMZ2XYb,"RȶIDnpaef<3mr:XfwWvJa- 흏ecu`8~l9;(Bo ӹm*/edJRi[Yx&]5x=lK>v ,z{$NeM:A'?K< r}T|N>wAosc* )x?IÉceEl!C0*!XLD6KŭEյYLJ0c+2-DђZ4jj+j-mV"Xf2CIR1v9[[/g$syTPImTWJ+x{!ac?,[02ܚL`YIx+Ji *1CjvwC;<3͂  i_uѕUfGbAՁ\v4pW v]*sU]Wur[5&]WKriU\7|m#ɕ4-)]gfj\R" R0d£**YUUJʊ$1q VW«/aG/I7Vߖ8{]6Uhٖ`xd <$h]~4ݧqzaK}Pi8,a[|9jl^N0Y-'Gi U]f5-Bo~|2̈ud0ʛ2u:'նOSg &Uݲ=̧GC͗wh˃Va =__s*=Rz=S $><#0eRd #S `+ 2T%XTѦ影l6Dd*VaisMm76Ѧh—[01e[#=&LJ֛J؄HJ}żэ˂hM7>RʅGQ{U3ڎ-SSUQ{'7ge#UYJHEUTaL+F$naTnݹ}/F0y;uiJ7pcL4ܭKcM3* jF}Hvgq4ss^\*siNRΦNL,T'64crNXwCdj|w=>ӽ4~l?XaGWUSRe32g/IR"|\v9|Y'[} ꓡVOC<]OYrrw}Q<Shiu)4?Qyó 5'**6Osɘۤ1$n_cwteae{ _"\Xݮ7)}FiɍEI)aVOW'e|fn,%KKA1`2$Ëwap|7w}.WWIzGWn5|©t$S).=x7+|\o}<INƼߍRuo[Ȣт bSERya^FNTN7EwyXR5;[瘪[8?/ݞkSMRCJ -;0= aTU,r1l4^^ӽO |H Qi*-oulK*-\OAT cOr{=6}n#n `V1/f c2LVUV$p )ol71 0c{Uo YV,Ř[X rb 7k{[inS7ޫu:=UuTԐU-]Oinٺ[lGS/{i!)],C*T#V"A-ͥ%\J+&y[%z@i@bI-20cFKK;"XGž7w6RyJ׹5JLgk{EqF H=$iHUFs,hLtc ;UcbL>hpt4κ-:\Oz&%Ň⼌ c(4?K@0r?,.L̐Lq I,llm hBjRD.1q,v}LwҎ^/1wNʥ KJ_!T9:JcӁYKPZM;X ILu`:80VkN(X88nLZp\2%(& $Nq e%ʶXŎxxVi~ uHWl̲$O5$6nveUM6r_QUM+tҴUeL+ 0˜VaSĬ*ɦnx]t.1c9甒G#é2:1SeSLYn֎-vc!Rj%)JR֙dҔҲ42m$IiK3JJShUji*)6iICAl#$$ȧ*{!jD%:LVit0vquKIФ{4XyO ]ovT#4x[7<^HhXc*CܑN-͝[kg"qiŵUyh͛R*A:Nn-1LmG rՆ.ۓ-+gVa' ,26V}ãMtlݥnax7XMXs.s-*UVHM2H[:\iU-2o$ 4%ep&wųѧ)Jӭ,7TIz2 )U6-M[ʥHMEsʥ[:䩲"y%*I"©l0:r{}FMrǑKgYퟘǙҀx^.gaT*<^Ɠcu088[{Ӏ{T)UEkJi[6Z^ҨNڅpA( *HPv0Q@(I@ QB (K:Qv IJ*fȟ"S ȡzV! p*M몊=Z/x*}tUfJGZ*JQ)>m%Tp P]p} E*)t hRE*裕l&ۭ(JP<,Ci^ҫ5HP|xJ 2Sh#heOe2im@513H= !@ 44њFFe=L#&eSL4&HDI)OhԛP4=C@JbHڌM4COP & =R$0DmG4M4d1`&Aсi& ii4 L RHb &I)OS &Bzh&iz@M4dR)#(!Š,Z?>+c,ca9PE"x\S@,ʪ/~MW㽮OMd4JI9*YEescE7 ㋕ \n^a P4kj]Ž\rW!"Jشmm5Zhْث654FI5Tdʅ.Tu9-wРG,zpʈ~/jPN/j2JE ;c7?gv `< ~T=3@pnH "v t Y6p5U1Iog^)Q8/NyfY2tΉY]8(>ܭ 惠($^`|k* B* ,ӷK;.&,txZC/%!i3$qLXH Ůt"]v<%Ž BUV)x 3^޻v'[,zZ6S%Oo]\Ī!pAEV]b&ƒF ISLvN' F3>U;^G &[PVGk>&a,6D3Zu&INэnړNQcca˻ܼ I%SSؐ0$*<򉷼DOfU!SN%D>{BOr`aM,ҊD=HDI6ZR&͙Npe58(%|:D 2AyQ$"ʤ8%'(=٬FYb!"DD"BH' C$>Pÿp5HrEEzZHQHB)tT7"BD! y XL`>Ma!"uTPq0D(fy=c518! hߣ><ƚ= 9- t9TyI95kC(‰>X(H8w[P{(REèQb-нQH! FQÊ09 n7\ ZrK7&XɎ2c*c[6ᠯZB8tY ܠ۹Fd,ޓPq{5͔U(Q()SQNd83QMόv16yޞهԌcdk~׬&=2ݿbs/DʥOߛ /ĻI[sg[jҞ=M/z=RРCL0k!\m>фilմpڨj;c 2N!sb6 u~ߺ'͏*|~VsUX > džw rBonPt/hT נetn`P'`[]=O<$w^'`9&A iI|y^ƫdMlMmhLBCaJ2HI 1H0v`rc$II(r@3˄P=ovN}e+Y⪔ QKJ>o7߿y_>Ƿxy}y<I$I%I$LI!ǭ~<؟էn+PI&䵴 YP'{.w;ncAEiAU^.83:\\gшۼUXb&%LD)T Dd.=\3АAd53W4x0wCg2-@%U:Ql鬪L]6jW$~G*,-S+0F2n>ZmnqsQoyվljb?lVV:ͬHfgkK8Qք $$#m1U YđA,e̪m2jk* CJ\s54B *Qo2sm1X-[8k.ە@ؠ`;E1#V}6*_,aWΑt!LvFR) wY5֬Vӭ¶zO'Җ.(n:o~ I]ǝŊRj=LhNHjS+[XI6X]DvHa52"" sQ&AʨE1S5U޵m_ m5c%F]o6x(1J}G! vǽ&lMci *"*=bXFȒFl،pҋѳFDwҪ{qHNLK9޸"稄Đr0`f3\g(CLβ!2&kUUTN)쫠|.g}gY>;|B9TY$d!$ɱKrM3s{қ9ӌ u̹ksӬ ;7BsXM%UeD3UUT^Fvi-RP)ιeQc:l0(@be,IdPd $qbx~Ya9J9#I!VGAiȊb2:LeUuX$8 E0|̈0Cb^y`}A%0}LzDbthĸ=RfHIb4&&8pÁ9*#1D7:M0j`ȳ!"D3%I1Hh^*3nַc7y&п)c`)V,*\Ct莣uL)ҐAqD|H;86bHJfPI>iFQ^UMAx骞DOYt wTRHE<\TvKA44iYUkZ2sl D X!i0Qea!Ӭ;lRʡ2TP(]<#d9 =ʪ< uU bD A!+ъfk%VUJLJZYC %״r%I"А6makNc&AF P).%@-mH(8*[oF ђ p$-ًٕѫeN{ǩ|6u6]="ǚYX djӲD>ZwAc-VISr1iAPĒ8Ĕ1EεnJ N9T=`pvEtU\@l3)u!NC*8-R?ӬQr?t_@IjwjcwyfCCw*53KǾc1(hb`>[D\nAR}gyx ]NT[ҁ<F*yI ؐ͟'ޟMhF*}x?mP2nD UL3`3z5]E8XMysr.SR$Xx|*1Le])RAsLe{ c{7Oku*ղڈW$tCmcSBwǵpysKI]&eUBD `H$UȠDI!柎|^HJ 5{bCB&Nӄɢ&P;sЈc%ΤM7b-1P i10Sٲ"4sq3DN厄AVHTɄ:Vd6w]#7>%䱐Jj~6peb@li}{4-鈚3Nɉ;}kk#R+M L02*YaLQJhHA1h"Жpes9KREl\I+*[)tM*\ &$Sp^ E&({9$#"ƴ,-s(2cLg f.L3i'w590UP1JkPIV~Z.ax.n}}q9V 8tI01_t^LȫF8탇3Z3"3LlMNtJ8O{0t2mbk;fNg Na`n SO_1-L4 fHHPhK#9wlDAóSdwU$wr9ˣ哮)g!ph@Nj&d&Ibf,iyݍRtfaHk4w2٥e-O# Ú49ux>IOɖgf-ǕM!IQPh[n4R ,yk"Kw~}d R t@^M=!OfT,0&sw,AK퉖cw1!\; ![YmTQ(5J_O:./UR =CDʧ;](OU՜J# $G A$ RY)L1x$ Lh RB6QHI AFfB&[F8AGDFpQCԭc[fd1LaH.?W]GNo=<<Ɗ^~8䰌 q=E! ܜV|žACaᯍ51/YJ짯/K 76yu5%TQn b QyN|ʈ0`"mbt1m,5N7/xS.oURgMwwDbS\băY1j54\'.ٳ&n2q4}aqPVYan_]0ywIwM' jQJ !\XefS$#gzs2ί܂ONn.#6~ȒcAIDM9)/|FdkUA2 +ʗ5ł`! 8o^w5A7MqoF*gGWrdI!CmQPϐKlyraa2 +؂ʗ$BKs:TMR^ Soo߷Bxq(Lp"&R7?Lcۛ?oʪo!Kۋo?bM7v&Y*^w^kQ|>C['IV= '~.vzI03;Cagd`xКid靘^*ݱĮʏpK;lVd$NL }?KDM}0EK Dv\jq2 < iaGdVYl@5&Q;|28oJ!0dy|@r)B*4!i4B #ܼj?xQfm}&Y;;[~a%^Tx~~ͽ[{7qOe]FetT]ݲnCoD alSB@7k5$T$)YmM̞۝>dXm?I$pnLw}?MA n2RB;]%o`"T m]]L6${ weJU2uj n c!R{p"ip:Hh\Jy*7x  `p*%^p8n58 7g({ dA Ƕ=A3gZb(v!߽t6Mfì?W r±mBxjĸ$ jn! yUD'coy/փzq*@} іQ5,*wyABN>BYC¨*c]mFأJh@$%[1(5qL&yngsj@P~d|Gyy.3>%w|lsJ)ûA22Y2*(wv;ߎ]ӱٵhÑ$>Z,#\㫆 d+Jf`]E)=YX9^W N+)G0Kd|2,z xSyb!l$ C>ψց!4Xwɽ\D>(F\F@I~Uh=^@$N,9Ed+Z4A~/+Q|n o &/>EyuC1[V 2k9ǯ۽ߟ%Vs49 &_b7C<ͨ 6bR Kՠ~$F`<8ǐ'єwsǚoa 4߈.!nK.~ \UEEc2vfBrMGL/9w*Cc}> ?-5ǿA[L2c$ю ġ׏tLt'I&lO 2I;vP@1[5͘g98Pz*g9賿y9U;x2QtYg@󞊇zwUwbòg7;Mꛏ#Σv] m.&~{|&oa;zomv* llɑ ) )7lOߩg,vQ gC\9 HhHBI]XK19i6LB>6X7`SI6׽]t6s)go2gxy0y){Qe_/zyhC\U'P5s; -ɨDLxԃ2H|V9;ȓHqKe\敕乪^0:uOJqg ix!')|)[Hhvd<@F%¿=Ps]~`C!vK٩719 e$WiA>xxFy73|L;'8rCK]s_<^3 zGGEx/S_ƛ&*> #ñKz(D8Hr1K_Š^|*>dglfNN^$ߕB8PaZOUըHǞɃ <;&C/tYćHg l|.g{$ãR^L>= 2t*+PblF<ѭ5EMe{qﰛ+ #jꝥYY'Z3$ #Iqg!>}D>8=kx;T9yhtA=$V.S3;HEJ߳b}*D VC˧9&h*/߾OP 0T]G ~Zam\`bd!)2P$ Z0d#ڦfު&Y 6D 2 #g#l~ҨǷOUy9^q;p״=.wOFz s9O}w5S_<[!Lp-^jpq "&Pp{Ǹ=p5mN#*˼kX1\EګU*Q^IS}lT(A}oo9s։&I֞?*Yx}|q O֬Ѐ}T 1M1jq7U fM+ "[ufΈ7T, 涫N9 3ՉٙPٮϥaL`WC&!D,ΌLU"*=@KǠxz!!=y'=E7?-dLɘaz j`'d_)Nj8bZrNݵn85*I :-N&5AI%3jL.h`i%Q9zmkexLO!x҅D,(>oXQo2R@1xt (9按u2;j1?9uW tg"w!qU:N6V?c32%D߀5K4=v<`y^&z쩟 ~;Z=r_X,g#X);C7}$jIqzg9`5vLhh%T%&[ɩrrDm ;*r1=cjsٛ؞CL~2%TцyuA Pd|GrXj"l NX*q `E; Hq۵u!t)va82<`c|ܪY'Fqؼ05)t((r LX; v Cr%6VS vfpu7Nƒ) @)]_ZM`w'$IhJA/p pK 1v ' 6ָ/b*diU^g tF.s-1g^,uu]O;:Y.z=Py9σ=\SgH"&SQ\DiyG P@H~F9`w|@=Ad! |:oiV`"uށ Lo-ÇU;6,İݩ-{@ *)LO)8vI?_dR>o;l1S4;pTQ_CE2nN0ƛ.wcW=ì7* y'Pm D9@[*PκBt+>{Vb$g\%A=IӶТ۽S.eeI>6$ľ7$xW}[ttU6{4[^SD&"6&=sը$xͽj!_,!5;i<}qP+i„í |x'>Yb *If|eg" !SǓebiyc%8Hᄃ~_*sI>K&&DύL[2a0? 7mmhу izKGTs|xAF/[|P@GZМh){ h~u. DQ F0c6Zb^Ѯ!QRTX2v؍8SIE`R/nefI͐DpGQQmym胩F¤!;Mqs'YML 6HxJ6 nC$Sh"@HePwBd݇bB3[Z[,1|b<>O -zcH ᔜ2&:2-$HCG6&bn)ڙt;5$CAc wY%9+\1Pe:כxflPBM(EaK?0_~ѿrrT9krD~VjZ]@~K^1i3/p:!݆H?9PўbNg4b9`p($tByD3h 98xBd%z{3?ENV(3)CGY@@ 5[RXfdSlgУF_~7ц~V}F=1QGd~ؾ{gBYTEQ q}JEKѺd3%0![718CM tg}Oݏ}' 8Q:֩ یz' TAVeDx )`8/j˓披8W更 l8_(Z(3_74aƸʳ'/:U'>:ϞD=d燤2V.|TBP}?2>Vv⼶5蛺3/w/ݾwl=$)H3$9A0d/ N~FREԌg@T:oi7SsFzn|0897p4~?2W|ýN ë]ߛo_Tӿ&F>×"کw!wTd:z6|%P-\䷾(*%zz4@ǪBc"Q!~@_qGy[QwoHHN| 'ߑY\OS3dG9<ٙ%':'d,"Q+O[>1?(>]T/fs:]pfKzyY2Ru!|Y:Ld>S3Wy%I7Wycf+] H`֕ÔIT'("kھNwHRFrn !'\1rDي>DyCK4[cD@]RDgzeSoM'`h6,%' L;B9˸q*C96F$ 4I^L+0,H#φ$q$zlba `\}%v>A9ocDSAz2Vu[T$S%BSmq.gyG06kdf67fFofKmLsQ}SzS5̶FuMk l6iQYUWD("%ʂ0dShrlGo & l|dX*W`?wI4'q@Kmۢ8EhJMHިΊns.h[ <Jb8> T=a>yG-c=RIr۩ Dv ô8(XC~ x&ClmX0^ N–=$,Rz82aHTGm5fID&Yq7bD/M6,,r0x&sv&H0N0;9] a\IT,Ja +Qthp<Ì8d,45N ) Ksx;aL^"*+z >i5 kA.L78w}TݸAכFD=cMG_owecy33% s4(RHU2K =)WX~_'V1_r5I:MIN"!:4,"F@ (T9PFp]0H* BZlu6KXhjd[;lunӫukY]rmwEӼ6W7Gn|Xxcfml t $np9-~ d20b1aMSBNb0 c ZJjRJI-$I$Y$J$IRI$I$%JI$I Db5Ypr| iC[Buy<\H2®Q˛vլŤn1dj.-+p2l̬i5J+ZUtx,cyɽ8}`܊:v]n|üt6sFX-LiFj2"ZbVUWp8;{E9!Hғ'9<ƁiAGUUUA(B/"m6WN7e-4tvgR4vU5;/K']u%v̤ۯ֕◉KYhۍ;uڻmqKn);ۢnjx*v@7Gs%g 䔤@"E)I$nxQ f/!])L ssnIH(0vr̪RƈiϏ+}NA~y,fXK$Ѣk),`Imk&l"k#iBFĚƒZHK *mQA2,`RXe9۫\`Z&wyHr\em 3JiutXT7̭MչUlv ̕9x2fb3 gI㟒:~ĶolەhbQ*4窺 G80sny\.QѢ DLY fT؇ބRvH5|EU($r_zUc#C?% _HbLFg+9Cs5g3+4wAk=rn[${ɆK[$x.L2\xpsrW*2TJ"TC?{kٳ]nlfW]/H,I5L$ IJPK,IjI$$$I$$Im$KI !Bg '"&)orl:?hԝ .@r=Lq(5ܖVIRǹ^Љ pIz Kxm)[R%ĒIB1<p -d^@e$I$I0c @?ڇ~$[z!W]BIp\z 7rQU*j\HS)3ׂc ɋ&Lʧ!k+!srT&QتΟ<9C3tO"]^nI4zv!/3`-Q6w0UIzCW>u_Mc,ɆyQ@IGzmD@rb1$cdfʹٕh3AhsJsflG=P>Gۿc$<$ʩ$DI$9=" 慲6V l A! .PUI%Uq&Br4,v&[̕A~@eI]zEu,V T2U&i0lm@0SlIo`c JI$Hl٬R&ji$K*TR*ZRTEU83 ߻Oӂ]dI ft*z6< mI$ڦs]X_ 6.05oI@W{Ki1aw$:!_ݐ0IU㭟ܸ.#I eRsM$JIaaA{rsrI8~o%IoI=]9Zp$ 2)N$n:bI68.4cEW,~˙$UFdZ^ sˀZ6 ODVI5lh-COճ_ۥc (t)WY6gd_[7d~ U)|UJ_QPm)b]@E /i/?d뛈Pv>u(p_l'D Ic5F(}䆸9is=8̒BH㸎U1ԇ[1  Lʳvnwsj(UDD09:;N~](!Aeٰ=X*3,3}M wi C y:('qx^j‘,1Aj(PGb*LRr=0\J2nm'^ѬI7ujNC]!+Hq`l CJCg9CDxCcԧP۬^*|/.ᢚ%{vMV6'{=(] mW!.\kU$3#1Xp*':>(f-w$/~=]+_E.cQg-ͶSZ<4x_v'SkNAPtk;=b18RS;uQf&AjvRjS|B`J^&/|c6ΉJh #6}|l{[zOMv'>73;ʻ##uop*>pdU@8UΧm5*pt.x:fݦ}z{7z/Q}L-QEu%,Tl͹//<_G6{fktK"!Xtx?,GTl.Ң3 AFGFņ)jaO80|Nq$rahpseZ{ vciшޚ=]dzP\*mqHaEYR]Qup~cbwc[n|&&{!t~~2izosIced]?o×S|fe+myه[z]*^7|suw^{2_@Lغ=:/+N4Syջ3.ڟLN܎O©KEtG>v{BDHt?r|ʖдh͹Jv\)>/4}=G$ѯO펢 i qRI$Cdw=Q_bF;i'T[%z"J,mzZ7k22I}]]wBD!#" RAOp@4}*yP”X:!י'a_3=VƏ8گTuv({襻8nyCEWz,ht𩺾f'E+e/F;؇dϑd3dK1JEkQPz-Hep\?9IA6bEY R1 d2n;75J[ mSI79 '>aGAhv` KLlf4J^E Sgߤh01MQ\[>MRS%:"$DQGP p8GJtӤ-ek(N\-j]he)kcE CE߸!– 8()^"w&>*Jv]^(')N󊪥R݋VwEƶ>*B2(D'\~elAQ$EV+)b(ڦCsZ$JE*R!`7w'oFJ0hܽfY%؊ $$ueYX֫Kjb6Z6%31np.,IHQAlQX cUURV=IVuXL)҂e ^1*ˠϻ%S\QGJa 6ՔfK{1U,+1,US2FUAdʙXL0dLa2T*Y*VSdĖ+ d1fTU$a*%һۘlAQ9JHً1qX-4@䔱8l5cE. o`:d[7e7r ;Bq,c#oBn2flsǢ?¾UJgv~c ,nP DR΁L?q=( PF#FK|Yue' NuE987Q)8ɨءӿx&"'eq١RA DΫX%qyzS ƟG$: :Q|`vQ7 @2s(C]q| %Ae}'U|{QuoW/:oeޜfRxSjFۨF+JU(RF1U^V/n:{t'tM!Rvwo߹1黸_5-2{xU/7ŋW}]/G~9JR英hqxZO|qsRˢg//'_ ^}6R(z|k` '{I* bSڡ?$p[O~Fuų$^]T+=tʥ-jkTl!ΚsLta#g2b,Z'rGKj;~md^v^/71M+|g]3apl/tg+9Y5^T@j e>\/S|X^{u/Z~ie<s0.K%-;1]'rowucsopgfJqE%YW)? b=ςj18W쐲Pnj\ˬ{2&"T3B -x|T\x~mpИSk#sh.VS>XKt7mvUnk׭Yzoy}JTUiz"]gikZ{vѽ;.ug}ףcHĮПQe?7={U)gM2Xϩ38=JXb=R|nءmrbcPjkom9?gG87S]SspvBf i$X@K!5ULanyE %oB; tZv8v)vДoi*Lq:*F1TvP{bJV5O7eZM(hӼ!WEǝE٬&ɂTXc ){).(h_HhP`(4.{beMRo}qsƣӃI#-ɴ=ʨ -VLڜ۷a"ݪT/Öʔ7¿0Z SxCS]1:VZcrw۫] Gsu7pʤmz,wҏ?4 >*ry& ҄fWfQ!Wm ?Z_'<1@y_g݅!Wu\<tQ dRgB II.@cт($hi2hF!22Nrm&tr$暑" lH&`n橪l0pдVQzÒ`<rg?57.9seC*@H5/+ĨUD=,TIn85 Yx>. !j2,e,}8cV_ճX{M4;g>}VQ|iŸ]ܷ^)K-ˎɎ,zUdȃF|ޯ>@l~O~ ;U7wo.9|Xo]y>}u~KŻOMDŽK }V~(EDXjMP\Mqҫ7attM>~ NPs9NKKgԘM6t4OTV̽Q\{Ec/q=+K90W#c宏 ڥj[SIM*jŵdiVMTEXJ,F2d-)f*Zج5J16CIhbҤU"fiM6Ym46mkIkcX! TTihJmLmiiK&k$j6,&YTl6ā,6D )MYLkD-jFMU4YEUT43M4dLFcĥƩ7*z>(FР vv alrRrO|!٢Z9IƎ2bH2b16xqF1Wk 5fX-(d7k1) Pᬂ[OХǡ>~B@v] Qm!.s3hMiژOmJA"eew͵ K{ ,;M}"/gEQdqHmSHTҷկoh;ijqmOpl# ð+!%KP*-ECxH"V +,j*=6()Mml<Ȩ6#us4A~xA;UaݔBH@`X*x[*oe"u18YŸgL[>P?]A}QF;=9fc:t.(bDD`OlHuOh}ݡ 2fu"TRXť0Bj(RIUWp !M46KEVW]!T(ʚ1r3vMI6'Z5  X57:bq/޼W/U}`{\/\IQnJQoU~' ŷV.Һ ֭x/hvҥwgW$aNW=;\cλgUk1<ӆӊ}B'ɶmtxX1z<9rye{xjϋeV3Zu滴yUGdT**=*yʟXpmn(;UV=6(CG ek45R)7:SȨzYwҴ:6c5W=~ۢt_e*D&A[2dvѸc6O]| JlFZ1D{o,Ct^[dP mRؗ4oy8 a GzneqK$ewwj}7]E ΫdJ1IW7^mvZ9ߪR5<4g*MuJYO}%yauxڣuSmCuR=%eZh*ZLJMyiu̾S^+Q<$%:\/rjE}zi*A(Z -F*ƣ()`T1Xbue&`̘a)XaR1mD)Ռky|鋙pT+΢׃ 0d0E>:EbHt%bI\Pa$)) (>ܿKNNNsܕ E;@bMbIV wp,v5E2mVֱmfd2R\l VA "TB($5f ] dj,*;jb<eQFߔL"r hED Bu!dLS2SHR2|.`fjAj"JhL IBD&x/&Nm؇8URAq`>߱UKR䛓^$?=R|DZ:"% f@*@8[klLtrQ@C|~`){ @,L)wAJDԌbHBfV6)cTM)hb қ$YT1 5e7^vWBz8VĮYLD mh[iK7tnui lW3k&bd3+1Ve#, pk؝^1TQUeq5'uOQ1cC]ldx"Xf8H>DI"uZy3LL),C4&AYԵҚe2a2RI$Y!$ٚLcI̤D"e3D5U%[M-mc332A,i44e2SLA XeYh!lI %MPb54z_uJ2\ڍ̩Fռ݇I#)*n!$&l拍!hxHñ 'sMԔ>\VQiA4pK GDdBCҌKRj?66Qy x=Y Ih!mVnLfR1 Z@͋<A'ȹPoT$2½͖h~cNH(, hJšHFcq.K=fpdiB#0:n'0luEa3nP噌gK1Cx&@f+p#PA.tt,r0dEvyAr(gfHAH MCST̔4 kdI3JD@Q2Ɂ[V(R)-llbQJmJPfRDPQQUT%-4TUJD-m1KYlDDDDIKIT6JjRJ-3$(ZfJU.7gS-RUm%].W Q ;f:lƒr"A2ձn6feD4--Q RF)htpBQvy5ߜk JȪ9ٔCBh3}tr:ϳ \HPd97n*k8C ^$|1U"q%FJ^ˉ8E6I jIXk>!)a! QC -(ӕ#b(kq%YW2Iiwʐᅰ1\GJ򋅓?[t޷]-J,vo۶Fd˿m0F!D8]ݸqc[ު6ñ9ەx<#)* #GeRrys75J_~HtTjW}Tp+{KI4zPJ-vH]ַub:+nu7}.yc;٢,p''?s[|ˎzG]}zki}Se;۝guG)?qx]{ʻd<_- -o;O/x>w IO|y?=='xx߉w=ǸKWNƫZ'myiMo?'ѷ,~SybZ7b29FTQ?!4 Xi`@:i¶|c,Z8 )sW뽴{M=uw^:Ͻy}U;#>UπiD-?>G>~7Dw(ǥ#~ =~y? s5?O>{g36S>^W|7}?cgs_-lݗ']z !jHԫU)CEABbEE6ֳUsjafcE-RTI/RU$!j(ıOA%qzoͳ&yu7 6oNwc,[ovi!2fffh`fHEPtEPhDž+C8Ɣ1%XnB f0Ǚ{m Fi/Y[oicDK0#Ud[*2_c*^3{ 8+{ɀ C-fLLV6CFOt5PRkvc1p773w^K|oIȡT$ $9&`\s ED> H~"vUy:PeUeQc02Uc[FXAkJj"L2S#. FIdRN{&9۵9[LF<_SY[{15nFXD_nwwwws5]Թۙ#S[ףC<[3wOv=]Pϊ:^kmiܪƫxh"/^."ݳ*n*6c2ow{ooܙi&iek;⦣v[ۖ%:Zyw;i˸fێ/+{ٸ|]gM^D7t={=y9Qo3]wTau\nftf]=Os ,mx>7e.v%ӭ-#Ko,{{T)LQ菁n* m#nJGE X'$:EŊ1c(,QXF1bbŊ1c(,QXF1bbƼN W(ZѺ9GW#hhKI]"4L`c! dž#z XDLxJk&dC2)eW Q۠G RJd+1K3 īr9Fr1GSpQ~}{>^QCU"*$ȲR YGHPy ճ1c6mŊHd*Ki!-B Hd*Ki!x)tv㪗 \bm{>+/S~CGϻF#2FTb4j#(b1FQb1F#b1F`ňdXF#b4fe,ʦT.R<+y*E}X++t#Nڊ9-Кه.1a)Nvhlʃ9Rfkf"qζYR86y_]QVT7F1H (bFSH!ĪPRjFTU%7P^0A2ę&7IFC* U4c ֩JhlTFlLC( ҒRFQb11c"1JdRj ʈVGP5#Hʩf#)dXA1UFeK(R/rR+!oK-kZQd2 !R4I!a2FC d2STVRPd2SIjF!2XL2FaZ 2 Qd2L",M`e FTb+&b[|+JC! AA>R+V*ʂ0TCX%P0#*ab2#!e (d1,b2b)1 F#(eFbV#!e b2FCd1FC#b2b1#!a#`mIjZX ST(d1FCb2T`b`d2FjS*ST5-XUjUj֮kJ1hĐX1Te ,#)xJv>;sfywߖ]\ofN{'o/93z"""*"辄`fxxݫR(eyv$ebG嗏n+wA;LøxGd<|4Nk d>O,acPD(" mm؛blNUAEI=z|OE_]iPZ1L[3jXRd *J̩a3fee@ʔG^/߳_'hiU]QQC*R";]p$StO–\})@U=WJk(A[YTY&6-Լt\KՖq뾶n0 *"""""& .M0Icҹ j1Y JL֪㋧Xd!L .9"L:tJƭL\kd5lPI$&!2[)HBdI $lxI hӦK\ {zzBU7Qx W*=uK|D k-bNЩHusZf;f׫ZtN:xuFyrG\^HCԊSJ>~@~*@%dPC$y{3333}k33333Ǣq7Va0YK)e,Cޮ+߁ V6ɲb"#&"2b#&""#II[v)$I4`$I& `I$@0I$I0ns3332–b^J,miѕZmg\&agî3LFEYH91O[i-m$Af1&]sfHUڼ.bUmE 6xx9jڻI$I%"xk}fq99ӺnKv;vcnKv;vcnKv;vcnKv;vcnKv;vcnKv;vcnKv;vcnKv;|-mmmmmmmmag07 P졉,4% фh);#hk44k(t Jq&0!сK[mmmmmmmmݶPpdeװC- U*Y!EM\îN(t4Cj6K5C5RRiMP̨cLrͷC5SNC䕲UZRTTTTA`ffL4m}g`mQN#QStd;#1k/|īz^*]jN03`9|eގw.k9) wvo0zUUUUUUUUUUUU_;04G2^*#~;ַ\omZJmƦE,<{Dob28Lm&燇=sQ:xdMU4b#z" (HlI$J}ӞݶC6#n*=6͵KjU8ܶ"#!#0`%%"b<;ߎxxs#Ԏ3YԒH.<ƶ`.y,*xt翌9P^އ>OZPv ql-:;imVYLa6x(X]h|7:zGi1U7Zَs{5ʉ~6xo~w=.{y"6@쥅+֎þ7#0ۏeZ]3Zߎtn4CD#RlF^ Y8hkNYě2p qdHĚUK&Idwfsc8ݜs^yswF#,pꍨƘG}tۊhkB־:N=dYYt^fuGQ4z(Y⌹߭rߡZڻּ:g|qjZЕ5'1ͤZ)pQB^̲D)ѡ;7}qym:-`#Zgbj=-;8nΈ:mN0{Q^:t֫X6I ` ` m-k$` `&---mhrf=MUA-k[oxsKe:w( YJfQ+Jʵwm~]<WlٔVRpF *_!F[Xx5 1Y F̨-+]ځtVvl[9]vץ׮uoRz VڹדUx $HC}s8;c7p]|\Y9k k6#Yqȍ.-jݗ]֮9$!| a|:0xK%ey V![+2&K lw]!–29Dcc\(d-u۳~|58>>ԏۑHG쑵R%_B@(D( ~/o}~q;q\~oV2oȥ#|ky񁂺%w7.^q')܉㨢72g KJX`щ+DYcI4:~oR,;ڮ]nWF5F<5\TTjQ5FTjXTU]l aFʇˍb0ƪ&fbJeK|]'E wUzԿKU9Qݖ1ebԉC"fKTs)\f5$3lmߥJ;p2+a6ٺKP(U$CfH0[ky_]7`ajqt\kfTDuy8ŚƱ l6g)z)~nT1wBl4+v>/yKQRb:V VR4#\vYp FD&U5i$ʐk+VYdL c~wR.YKlvh,UaN&Jҗ3,01K)e,7Ou*QlsciLlƳZR–,R:fc{i)zП,ZV:)u9>Jmm3ba0MZ麉"JtRҗ̥Wtu4[Mmmmmm6iMmmmmm6iڄPPPPF47G48G/WG[^PC)}-"T~m~m8cPC’_%1)tvX"a(b?]#Ё숝YcR݅kMm]zP*1.qkvn5 5Kmmk)aK\ӆ- ~VOm~[(T(bTӵ$=ꨁRTA}gRLDUR>Cs8{OF%Z\n*ndVDxLPT]KJ뫜䍶D1]WP*tE *m<HT5HHHd%_i<]ozPC~yCTUHǬIҡgJgLv( pxt{Q_R˪ '>-a1Rfkq)'ݑǂ%RKPUԉ)B<5L0JRyt0HZDuG^:8\)yH ^/ j3Gh+rI1hRa'*EXFh̊2F23$f#12e,RIʥ3d1X@AԭJԱ tRx=CCQZBhFI#2+ 0U@UK #5JW(qץTlԒ{Ҡ`T;~hws5ZF#n+w)=Uэ)aiM}x#b8iTJRD! @# W( ʫ;.pȔ`%a!nf7I U |CP#T("2H8Rʄ5HKҖI#J4Ub(k͌ə3V@`dS*+0&0C fDʭ@cf Ud2VB]:SeP2La,H1 FCbX#!dCd1 b2#!b2 f[g]9㍞zozF^px4aiL rlBVChvuIw;U~H2_SdyTmG1"*P=^ckxP?9eT/)v+U΁W3RkF1ϫ(R7"CG\OĨsIW1@[C<>:$^)UrSU΍ѯ=X|(7F_:5#Y[\0{S¡T W( [ Eiuny9}6זȃR:T5"ޡTؓiTjc>}>QKV g]HESE~!*Hz%vKoUI}gC# ]$nJ=\$FY@OD{T6T?u"]2AsPH%&dR+ J(uy_ Xb>$}?'G !OPJN6c320Tm*6F EFтhQ`0Tm*6F FтhQW<;VjQIUkm>]RReFA4iIׯ;:gkqDTZV֫6 }H\(xT2=JJՏ4u1iJ~JJ}h۔~ WPWQ"N$CںJiU˖*|G/AX̕UaJəۥǺ>j=oȔ>yHC t)iԨT{J^AE'6茠JF1+*Y++)fb!)Hde(e(bXTeFfb `c&1]E?\q/PʢL}7{xx_c^1#^FT9T0X$,Մf33&0ik}A^TaKF>ܨmQD[$-^Xm(&7Tk*}#j@ VT;u_o4H JAԥwlzK5I ̬h~GQQ;c~ٲ5LV"*ꤚxV{Jw9BD8' .P~2:!㈧ɪ FRF >IjJTlYaQ DHbQkBxV5oݩi2Rɏ*ҥH^ U92hff*NٽCks|1܍؏QW\.RtʪKZlRPoJn/8* ԩ-ol R;qPҶ:AuFk T)sÊR\#2DK%M*/~"}'8E$esSJ"Nަ8aK)|"UT}cEڡTMl}[D.{UG1YG}C}FJ"Ĉ'>JC#dmlfbRē"X$gٽFn.D!鮾> ƎS!ϼ#B`>F(2L}"O{JUR+D^H?jJHDbzBzzL`g9}SĨLAcs뻔qx WQC{CtG(2u~߇T~l|—{gKZ1)5AXF1I$N9$I$I$I$D՚ʫMDibXjIem*Jmq^bb uI ϒT:>yyԒ2ł7FRf";"-*Ao5 .TMR9)}$eA b*mP WR|XR|(j4V9U`Ԓxn$UR|bUkq%YJOC" }`9GVT7TDR6)X61YIT6q3 $(HP%6F܌F7mjZRڑs .HL֧)qF)`JeQeCX1sY"(dPoU䏜J(χG'Oբ"(|'}Z%`#($AW#ۏj>WoP.B\d{%Ȟp⥌foHДO+.l$0ԬA3,2̈VAfeeCރ*Jn!7PB Wn D0V+]|Vm#IOj# (RkmAZFHOcFK(9#~D>m"CmjulK{#cy6JIN(ۼ6UU͸kx !c3 ^KڌG5 Aֈ1 R+U z\o RM>=/y}(  ]w'HU%=d'g'^1)|JY) e}(&U@=DTT]d~~ !"ؔ^:0+H"nPD}.U64o]h BU!$AH{oixCJ_C)Rm"^Oܔ%utN#Kd(ҠC}9a43&fٺ AHvOF%w%-ԱdtxxC94& >?$a@%ee)}6o(L+G9 ʅ}sxl"M*cyXeȿ UWrl7cqc[WG8H*;[ b 9PW)(-%KPJG"!Y\!534uyRU.,O V?SguUsLVuΘf @ 2c ̾1w⅌@(4o1aE6ijtsM)Oo)Pk&m>vl6:X$M_=FAzeiDɮ7r,2_O0$J{]'tODݑ:NMߙO!#ؕ+yUViVCT,(șF mlXfle2L͙Lif̦S4fS)e2+ZlZMC2ɓ&L3&L3&L3&Lc,V4D`VԚIJKPZѨRK=S,5 dؠ lETmjskk(,YQ0a5)Uփj/ӌ`|Zq)]P ʬ²e(b+j''#U*J(bae)F@2c`-;yFw0ݧt];p$ tیqκt|fP!:bl׶UkVaX`Hvs۷]t'2&jô tծ[˿xkwvmnw qkskvumK2%Fe WnLaYJQRQRQRQRQYJQRQRQYJQRQRQYJQRQRQRQRQRQ׉+q5lfL̝hjW*2X1111cEkI rGNEVcky5FSFI^J#ѶS30RX'm !H0s]5VгIF[.Da.d<'e0HN(2A*I 3 3La A)qA~{coo 1"1v죁@2Gyq9" "" bbI,YlDžH6y ~h?#Iq9@$HI$$I $HI$$IqTU~,BZ T0RV!~ HF B1e}XI9hϿqR)1*E&%Hĩ"RbTLJIR)1*E&%Hĩ"RbTnnn2O#]%S\ubcG9Ͷ Yl"əR}դթuZmX7f[GUr;utn˧nu\ ̄wuuv%3uĻvIJf2ر\Wxմ$oʎqpЉnv~;]߮r;u ]w\)1vWR˜.Z[s9J*~!@#rު(H21B=4xR#Ȅ!,}HKB?!}vm`m"#5lRDRDDDDDDDDRDDDMI$ I$M0 `0 `I$@0@0 UUJz  'wFGޥt˗.\:{O^s{yvmG^{÷ӧ^}sӷ7B=:m%jZ$v;!o24FhhNzׄyuGg:Hpu\^5εķ5ǜ*Z<#붹{Ժ o_/woNuyׄzv.˕m +W]x|q߻̎vs75u<6\oۊ|ln&ֵwQ\srG6Rz@`\]~~:ޝsy6GCW;ބngMٶ͋[[I߂z{Mh)\UR͵Ͳ6RR ;#!ysns9G\xv۬v mk6ʻYYj7N\q΍Í7-q:\.\Ux|{sO(-^=WZ}G/e,']qCtjlT{I]ž oo7#չڌB;:lݢZx#ϬxvKewz)R\I'qߎm?Nuy=e/$x㻜K#9#sЎsso?MǗ7qfn)e|f7RWmGD#*yzn=|ǦGzzzۻϷz?=#^HbPB4QT>Nrfۀ~hP`-F:I.HUWTC;vٵ}6%C)LB5)(ug_vݻv( ( ( ( T;#;#:F#;#:#;#tGd`/HG*(xUWI^?OP5G)@PUґ9`\xG;Bh !#!>D$0|5C;Z!|>HibIjQUZB?">uC*"aE_ȩMQC龮h>⢇ U_P>UE*wUZ B1 Hԕwj]>u|)RAS yA*T0)`20#Q:yGJwȨPu.#x쨡ꎠz+TAK*%x]Ns$ZֵkZֵ$I$I$I$I$I$I$I$I$I$I$TTJҏvGdxGtxrEKHFB .2ww!\"_zB:+S@HG$+$BJTQTpJĒ`Y eBHFUz3#CHdhdhe-Eb2KQdhdhdhfg=$#҄=+Ukj""""""""Zj[YjX)|R]B!JP?hG%C{=j({RB4{WD#w>9iA.jKڥUUU(tB>2Ҫ,>J|!G~uC‚^H쐇uE"/ |qʪ `(wG#Ѓ^@}R(CIdy#:FT5CQK@HC dQW})CR>Ct!#:TPWTb(QEV(+++5QEQEQEQEQEQEQEQEQEQEQE|`jUFK#&chʃŠ"|^wAGRJ~uC<J i$`}/ U+PQ _POx̪'`/EGNT;P=w~T' vT>e*C/* d*R5TFG`_"(ª>JI}xGGHGC)z')_4 A5CR_%; OˁQrBT=>:RK}̏CP' B#CʡʊPʪ@~r:#Qb=Ԓ_Z!%;~0U5 m<ԣ# }'}D!ʇ؅Pʪy"UUh>!*=Ag֒^=EI$I$I$I$I$I$Ffffj j9P⪽Zv~B2@_~P/JI.{CHVPФy!Y" qG#?dQWrj$#CHGU]@{B$R]/\yߴrHG$rHG$rHG$rHG$rHG$rHG$rHM߿~wGtwGtvGtw##;#1vGdwG;JwUW!xQaUUhTw}BGd#HGЫY Kք>̥=!!.CRiUW۠{=Z+j5E5*CrP4TjuBK xUW!%'mm5SmUUo1dddb2:d!$PER^rTPCAҡĎA,!Z{RwA*G?@ C%'I_bԄy)*nIr_u{-f#{W _(_VB=H~H@E <<@.= vT9 Ȉ"   @ f인>.HM<^8c-SMc0 +GTרZ09<$w|r^ NV@9}rwؾY#PW@w 9X}up&fhפTNŮu߮sED"Ŭ"0t +j_}TV6ܵmvhk[=kZj5K;:]j?"7h\Cs"-kb\j1ܷ9sai4zx oD-vga_;;v_)$K]zev;5Z%-1R76tK^K^a+{z 惫Gv9+499󹏫ΞgA#0"DDo2?B9< jر,mbر,mbر,mbر,mb*E©x\`,`~  v*/B~: dd=Uzn6Yl\]"Uj5rEvjeNeۼYwYwYwYwYwYwYwYwYwYwY:b˼b˼b˼b˼b˼b˼b˼b˼b5rEvjUګQ-u뮛,CQPdQ/UU?;H| K)e)#, `=@@0#2222222222225ZF#########3################GU|`|U*5-P*'U, Tbʒ!ABDU`;8q(*mUCP(UHq)X(` K*Ⱥ!hdhdhdhdhdjhdNq FE````rFD.C8 #'8".CȸCTK(GUH!JC!ddL VUb2422%4223f@FFFFFFD b##C###C###C#"dhdb\ (qiQQZ@|IU$䵽hs}m~Hgt#{h>߭=I@HI$$I $@$`v`vJJ,U0(eQF *a\F 0C`X%ĮD.!w"r!w"r!w"r!w"r!w"r!w"r!w"r!w"]J-ؓ2rn^::q{8SsR^.`J`\30Ȓ7\w;:#sxK9Mx\0 /,K⺮ M 7rpAިxz#vwfejou AUG:GT>UG*|T=Ҫ/Z>UCUPUUC{[\U*TG|ّq5W$I& `0 `0I$I0@-VU }0H < oEs79qqp\ˊ.9QryכGZ]k3NicV R^n%TJ\q`fff[N.3Z%[d=^ARI`z'*I% $r0$XʬI`z'*I% $r0$XʬI`z'*I% $r0$XʬI`z'*I% $r0$X`pjwFGttF#Џ4qdtG2:G<䎑Ddy;Ե-KW`mtJR#kƪ<{eR8`7`8ZTpUsUUWm^U-\W۳\TmKjOo=F|G={t Itmv^#|v]ы|lz'UZj#F}8o[Ν5։joU&cM`⤮Fi[Iu\]$Pyns=zۧ;q96q9\y}+ʷËU(T[9\瓗.%)B'jIR]W ޷qq&nihAAAq2<#ڏxm6֕*)IIKlz'7fmo)%$TT` U$\KJZTq] K˜g=y3zމkDU%mmG9ۧnʣy‡{wq).IE9Jiʤׯ+oD5m.jV-\KWjUJ@G9DvXOo9O]p#⪡{q ޷sq-*Khwwwwwwww`v絭o/<+W9݊Qso[9h8I7ªUI6]݊)jҪ ` m-------6I ` ` bUܪU^8޷uyvyFGwGdz=U``U=Chm 6ڨTjQ5AChm 6Chm ⢏`;y胞[ rA,WU?0:*p=@&;I.Z~@=Ǎ XeEw:;wC© P01)tJI}/Ҫ#㪡zUP?hISUA؆2bb1-c4[h!lC؆3E fb1-c4[h!lC؆2UKBʁ%<UՈ׿\C> c倖GU#%rԒ)$RHI"E$I)$RHI"E$IEI#q#:FQq#`/T=ب4('ߨv(~  #08A T?L ?$>j⪪ mU-KRԵ-^V_$!N] w~uC>ܒS5T> AT]UPT P?P]_!P|U} /mUI/ Ȩzv*/| PC|5$T=eUzԠ}TaO IA𪒞^QWPbb`0Re,RUU)z@wEp@!UvK8A?4JURIMPBA H$ A H$8Hq82x/`UtG%@ pT}#Ъ )0pUZ!U5T@j\T8Q%#@F`T KFFFRYKQdhdhdhdhdhdhf :uPy`PЪ|I@ T;UW⪃Cʪ ~ T>0/j; ̌ *zJtuUPT> P|U@e_5CꀽIUP)tC#Cw*, rRCCPZCQGC⊢:Y%Cڨ| PEV`UPPIx(eRUWPST=ʤ E0 }ꆨ `E^#檡ʪԪ^bmؙ0!񀏈  6*cTkXƨ5F5QjcTkXƨ4m-;Ab )js+ipWO $}2GP` K2dGT]z

{u_e=UC`P|`C*=JT>i%5WTX*FTʇBPH{^*g/ԑO0/Gp/JJ)^!Uꨏ *x *`ydUZlU`^KUUPQWC*(Ivďu.GRu.GRu.GRu.GRu.GRu.GRu.GRw[uGHHt:FFGH)/$.G Gb2##,PPvꪇP5T;PU"SUG*/e /UCT<ȏ׀RZZDDDDDDDETDDDDDDDDUUW .PZ<@}`<_l* ʉC}:Ct .$ʋZ?U*Ԫvҡh.C @PT:v;byݪzIܔ.KB`UT0?(x{A!ڪjC/K    DJ*K"Q#*.}p_"uj{!y 0! UJCK""%';w##ul?E}/ 5ח?W}'o`vvV=km\}w7 7DR#H~xZJC؄79:tG9g?]!xA! vcōL)`°nxüVߋ~^Ю+kVۢL9`ȿ'fQUJ (9Ktp!bs'uYbBL8Rc%JM%w;RkJwuyדϟ=9w\hA eŷ4olЖYe[Ia,IJF*Ho4S2W! :{~l;e]~ۧ۹u&.uh{[$$梃 (= 8{ &n1$; ީ.McRU8+r@0*xN>`OC4?{ȲÄU&iShM73/Aj}Tjxrf_%^YՆcH>&8qƖh*HˢUUU AI80Ʋe3K6e2LS)e2LS),ٔe2L)2dəfdɓ&L2dɓ&L3,̙2Yk50(ȣp"Ub&+z 5XQ2dD¬Z*WA0!p##b!ʨ:?bjH Uo%Uo%%%%+%%%%%%%%%%%%%%%%+%%%%%%V?ѠŌc1c11c&6$@t k1JF&Ld땜Sh79˗9.$?dMeПH5꡿73}87)ifjݘD@ oxg\`ީx 3 UzzݻΣ[7QW uZV WJ|+;O'/m)+DmDVZ"Eh+DVZ"Ehڙe*rU}DLuy*'ϩfgԾsM4cj]O\%2Kɤc'+$Wn C!nBu\E{/%Mm{/[los<]lΚ诿#{lۥu򽻻w ڐzlv \NJ>mAUǠtU~)Q܃jqyˀx*}^3%")M޽-~$WY gmIЪʬf6    k+xڭdz|svĥr|R]2Ԕ&E|^+qP2Fmn[kz'  %fml{@$e_Y6-%2]i)bILlZJdbS%.شvŤK-%2]i)p.شvŤK-%2]i)ILlZJdbS%.ش.شvŤK-%2]i)ILlZJdbS%x:z.:t/ F8+(KA23)*D9BIr9YwFJ،d1Ъ*Ц#ёdeV F#FR)1Qe,5J EUUddhdhdhdhjC*V8VUeW!hdhdd1qQ 9 C#C#C#ʬ8!hhUrFphq2 ? >ʩ#eT!VHeYA5U#B*5H5s2a,S###C#IM UQJVFFFFFFFFՁ F ,04222422242224241 !0`VFF Rdhddkj4222420FFFf#C##Qҫ#(hHGHًjAH%ZB`(㽄J_;S3&fIn=o]' Hѡwg^t*"""*ڿ D!AWFE.ҕO]:PWC<]u^]ono._gţܽ#6ӣh0j+ jT<.3 yk@&ffaff `64ӨX$H^7z3fVRP# dwIRӕyTC%eD3kRI=LuFW OƊ]$P*{H:[eh\g.3FˌѲ8l-.3FˌѲ8l-.3FˌѲ8l-.3FˌѲ8l-Ua"9Ƽ,ff5gL[FQk, L[E(QK3v4J]7FK  ġLCS-bYiVe)8n:Yj[]5.8(ܙӨxwgwtƞ/fiJ[jQ.g\옻WR/exkfvMu&RL 8XV(TD%zJC QGEʕ4Du;+]NкvUu8<6f5#}^ﳖ|DJ"UJ+wJwԤ~^R!)֯>7گ)] krsmmZső11R"""")""")"""""j0 `$I& `$I& I$M0 `m-5VBkB ! mmj=^*E:N)IsJ?Fn[ț҄jtFqYiuf%*Z-2P$IRFd\.YѾ[m2No{wtHi(e=mZ󫽏jU$ rnfL̪(nk4Ro$uj]:##;NyO>sy׷79:nͷ~}iBfwt5g-9[0q<5 8Nxq[D:b8ݻϢtHsӘR4Qv{OC߮zq9.-.խչ]pvjc r8PKi"r[kq8ҼnAs8uټ$̒2nLC7NMr7-i(𥉉\uxbq%i'<;xB9V(tɒd6'L̙%di'Ģ:7.qygS݊/cZ^=syӞۯ_>Zt""2陚V3&&I2gL&I%dhN1>Og99.dόf\rt4RoJInzyWNή?CR﬷FAVvΛ 6gWǮC=H'޴33:A?|ۉ.̗Ao{z|שHCގ9ж:㴴:Q*s3330 HH>.|nvmGiQHG O_l{sq Z>D𳶽7k:cs]T֧q] s 9Hqw%!8ʭ+bx6m-\88\8ノUm-kmkxVְֵֶ[KZm *Z-G##C BIGmAq[3Oz[qY끴6 W-Oʃpb&NvSe´.G^བྷ;zW*w/:&ENҾ*E:ܕeJMmk/Mդ*ǛY9 Zѭ5,WWkI E5\jTjQ5F6\465FFThm hfGWUU/Z ]SUrn`.$WB+˛l]źyvՃz71q&7o˒7yƿK.W^.K}m[RA[RsVMcMh!b / ƣu&ʙJWႎK~XJΗv:zWx)[k;tZ:zI c ^z*VowvU#{ͼܺZ^i$* _[x,Κv+r˲YU^pQ O]++5U\Z,4xEƭ0:ɢ^Z#7}ȭ -j-o 0ͻSV[® ,v+ڱ6[X)YrYVA- k{̾E㸯Gu޷9zQHYCO79U\m9pF: Bָ\( WWab2ZA002)W%2iEzޯk4ϰ+Uc^) .+X5Py;, *BTl$U FEQTl$U FEQTl$U FEQTl$U FFͶmjU9E]qn=eZEwy|^5| wO:]YEey6ST d=iU}r58|*uKCkX|?JTȮuzr)I4Gz5SXh>%&_-f*RgD>1O%?)I(j3'dVg{FUzUSO4=*OJi<ҪO4=*OJi<ҪO4=*OJi<ҪO4=*OJ{dwFGr;;#GdwFGr;;:QK****************$ qG|+ RAU-j5W?܅s)Jo CH4 J WhoPȈ2Tҧ`QD(=TP<U+H5CjAV}R..H< '4JUObEEWxvAG]v.*)Woe]I9!]U)]$IAbE)NjܧeF )X*WW/Sҧ#U!/j)"K܂Rx"ĭT "EuUPȺH@$&ȪCSȩUBZFSJC"bPd&A2+#*R;Y6 lԵ,b22;NENǐQ!ʯL&j" *5êUxQ%(UU%|J|J!ċ@_3Ӵ)Mڀw([*ҠڕqB5ʈjLVj"N* %T8U2fJYD21NQcQ0 iHi"ԣSRZ2 E5V)N`CEd24241 FCd2Z#C#Ajj5I$ɁF[k($N+٥vFnid!TfPZBAW^»-M*]*⠫X["UH=*e!PSx!jZOIaL EWy$]ҩu")EJ iVȕwBEWU@HX|/|mC2*UU(舓JJUUȁWA ċH«œJzJ0D;G?"%t%*('mW=Ć<Ԩ;?-*2P~wT+P SZ*$J_z:'J%Y*<|jU(z*wQAyP;%T>«ml6lA`PEDX(",Q TE*"A`PEDX(",Q TEma{N$Q #(tFFFRddd`LFFS3̈cCV֮ZXb;^*J%s!*vPGՕ}!ؽVoU{2,H!xU])'SQ,J "d.->J,0|W*a)&Pi%ևXU 5HFQ #E23(dPM-RP:$[꒾漵HSȕkW4JU2KߒH^**օ[UJW"(B$_,T@PPC]#RbX}QzTH) 2N *.r;.j#**|˨w|PJA IPJjEMQ*⸡tպRQJCRШrJ(FmԵ-KR:R:(w$#)x[(X'm%hT ERU9*x)xIT:NXҞJW'_N"B88⫅;Cz_&BKI+ {J*T4S BH{D)v"yd/]vx@F##QGCQ:DAj{P r"BjIV$yJz}JhP*GGB5dIT]*T8nRJ#Ut }N+‹J*EWW+Ƌ$US) Hʂ!W\U"G#@GI/&N/3UԪI$I$I$I$I$I$K35C*E!_NȶR;iJpPP(Aj>^ UP^%!*tQ%!"$- !lBpHhJ+ysRE(7|bRVW"U %!NJGJ JUYFHD6(}UR KEUxJj}HtIudd*E:* kW%zjW]J*.W+"V%ws)+ E:UkEW'QD= ](>"`DȳZm]jpԵ-JԶjZZm^JQ######!jZZըڈ26lɭc3;ENQ%CtS$W\|J DomB4& үHbUnEBbԅȪ)'[n[Uw[U<UIB5mV1jiS"PdʐT8% VeTL ;T:vj#B<D\TUYhX_Û.m/Դ=<3ruȰvZwozա7a'DŽ`ȻS )  LL2زkY&KG/إyU1aNXLDDfeՓ~0/f_:N3 J ZǎKhٰ*nWTy%jjV S:&uX+%\_Y/GV *WAҬXYc,;뮫ePRSTe2ALiTWMҼtÌQ0S*edSJ71)9iԬyW7ھF#˭.i85sr_wNS%_8z=)Ф}*3Wf&L2d0ĞP>swOeMtCwe>لnZ'pl~D{5í_wN~M3ޭtĞlGxyCwWoE;"U^i??an=xyWe~S+ڽ?^ OHz1lyϻ+=8~iz ]̾Uk}7_U(Mj+jȟS2̌32/L %/7G1q< <4<~]O7x'k§IZE՗msw2[ >WGd익GyyW O5UM^(>xY6qz5^o"y&Z /|}ީix1vZçHt֣-;WOZI:a_^'|h½e9+䗹*_MU={#l*ZmUIUij[f&TҴRmaS:uG)d%%%$-kϪ׌y|/_;_O({ j HYU\FM51TbR)&h SI F 606i""%1DfɊ$aC"f0LAU[oHw gwrHǮyɓ&OA=kGJ HL0uJ纾x+CMjeίפe{x<Q\gx9alZzhh ܝ>b˸blCrc66jR½2ZM<4]HMWUtԈ ""vo8x9M3Lw+VXWu0 JQshvMbA l6XT[ߦ|g|cmUmabct&Rh0Un ppTp#$*wy7]cBeYhaüڪ\8pTge]kx:n4rF-Ɗ7u,McW͢\1@\D\n".7%DIEJhH*6mѫ\kF[6JvX'Kx'2c_ ]߅T>p/ Dۥ|En׭Jͩ$m͒>Ar7kwx0`0a#\2.\s˒AY,bp<Ե4w^2bg={rXF,l7}9[vNF2Hdu0,BTlAu&)2 ,9V:+R9Z\QשWϯ. \5C%Ȋ617nO۞wu/|^4pFGz>U<wuDs{d2d1f*ҚRԶʛ6f֙m*TRJ*TeJ*J-KFIhmSek6YԲ4M&I,LYmCQ 2dŋ,>:q!vS$K(O'HJI~/Ѕ*V$ܭqlhT椕%jE#I iH DPG!0  m4sP 0̨ UY/ JS fmjIrdܳMrʕIJm-11TԦUb!>>*1JST52 ȣR0edXʓ(ʕdYQia*%h !beQ(Sn`bX,PeS )L,+&B+QRbă 3,m%.LQ Q%`M"Е *}VD⨫ eHRĄ`_5KTcrm6ԬJL5I4if2aFU))int~;AXT-P~S*U) *)L a(#ViTʔMVdX ,LEb)e$&ԭ-mFJLFlJVե+meXɱFVTZҍbɖAI6SZfFi,leZ1kVfJԵM4e(6P`1U)ade-)TTF&Km͓mj%ZٰXŘ+ TEU 2f,R6Ɠ)Q((Ѧ*JDe2(F4bDP(TbIKb(E"i2&DED@$SM(-,Hѱccj-chQQ,-m[tRJ, , ?![4EIrcU*ʆ6nP+ ,4!DI IiISH3IةQ-SU|?O({w;m's5ֳY6^=/*t=ؿnzźW|pns[wD{_"Yyg7}`!a RJY7Oj񶛗*ZfYp333<˳_WV15F6?1ˑ~ks}tXdX\9=euiWg?fKf11%2l>fn+)u=rXj3=Wݡ^͕lk3^qbSTldI^ k Ekc0QxppJyybV\H^wjHxVXb!_2YXdX;˥.W 4\FJtN\\˱s-qn_f@nYH8Veַ;P\Ut[evŋ믄]PZ426MX:its`ˉX}wc{ֳٍ#'Sēh.6qӯl9yr˗7YU`1l:[aOI=ͱãv:i1s4c,[0jMKJ]^^ֺUV.%`u"]r Q``h LG !$y@M.>>0t$TMDA!w{|X=s Zt0{VVevŲh˄W;'xsV+jC̎2tы6e-+$ IH]&6MCRDc8Н<z{Ƚ[vEԤҩ]f$eZU)3,2r7*mS6\VfVlA0I!%QwV[^I4uK䖧$z*sz|jpI]%5GXM:-bgbIِ+ȼ mW&L_B #:!VRw?Z$мW=~j{x$< _@SQ9=~4^"GV_ uiLblf!nǽkUǧlcbi6b*0<f%9a Oa4XNCḋv8շCQ!F(i2e4NI<6n}FJ溧'dS 6FC+U&SC&L?.qiG'6pks $M$l+ssW-sjrYD%.ise2JdJ\s+liRiJI\KhiRҫ2RfK*ʙffAJRiV4ƙ_YI$TuLG -02+wkQXIbY4j^ϻc-ڭ< skVb{ 1+r6W֟$ZM%K2R:C$)TSFY{=_tԧ'6=O}d:KGtqN .qeʪ`a^&ɂ\v㋪'#tIGR.^-:٩V2VU)ux82kj&5Fjk)̩,@Th6fYfe5*]qؤ)oX70ݚ"e),\ކ5Zխja2,E!R 9E2Ľe*(CpY1qecRW5uN㚲'tWcV FԱ ʺ'OEY5i|aa|SDȽ,&qsa,C Ȍ#nFSMntP޸O^fRϏU荣WSܡ$:fXr:ѱlXԗ: !}+L*mf1TZ&۽_is2AEқM׺_Hׇ͘hadfD>؜wRyS*Rb$쭦yOGPn**Wlu>c2Z o;^{󢯭j|9/Ztxc߮-:'&h n̹Ȝ"7](-기TWD'{|A VRI~oh%˶mM[bb38N,hueМّ1!i߁:;knڵv.ɹUDZix KJbʳ2||L93{Qihb"٥uUۼݾ0ֱ֘u_Bͮ դ_Vw$^CDUF8ʺeDO5Vj222221bVAjըZ ajV GQzfZjVMڪcҵVjadji-k'yULTujڣ(©M(:)]I6R abaUeryJ4ɲ|GjV"ʜl7s$4){t ۸zo.vŕ{ ]h6~a:gH}=XfypXd䞫L#%eYijbeYK2M,Tbf6FDjLF:1n_G<[\U*w-]vs<_TQܲfiflٙ,0!;<ጳjKj{jFMJz.9Mդ7 m{{FZftj=ҝ{Pܬ(ϟz$\MդSLx$>c6%URdyXY'f$>9cr/Fu8iN+%YWanyrpXp̘>L3f. FfN'Y_gm(}QiTSEXYJq:Y~@  !BQ(1 !1Lh46Ć8+,iZ2 &3uF里jm[}(<2;ـdEyexVK^HZ…63fl)%abD DjuoUXNVseNO :aŮ_ yi'yTy½[Z1 CL6p_& *V$һ0)m1,R`2M q)DƠ0:zx4&0+Zw{]ל9)7XɒʰcʲwE*9FjʴAZ.]Q6ĦR&)YVVO}wla [s.^K(V0c-/xIZ0;L Qɢ?'Xh<+)ifN>TqJjAWi>|&+RW!r=G+N*vlጧB|jjDSNXުo̰#{$C~y&$WŐ#ҞѓsPTׁ^!ڇ/*-ZbiUk/A)AҖ}n14Ɩ+)aZ^ h&+euߥt!K AM*vP2<.RұwTG XaYRți^2񲬮JLIm6+LLbNׯyGhKK z$ueK7t %ZuoT *8jiU,Xj\OXuJjK U˜02>ArAB̒ebe&+ X&,%bb *ba^_ Qwԍ[uYWjmn٥X#I%p{~ޙkr{ jvmñg`7s-|qhKlmyQSƆ2Rcb 2c^9χv<'^CT|SZQ38*zReFJ{jy*߁0eeGIY 1;]Veh2buUOpVNp5iu|k0JKyV|1}֫ ~ 1V/;V &60|O?FOнjdcW#Ԝ'5LֹܙMZqi7c1ahK+[Ubi)tV!]vJ5)jBGX:BS~(cS̄DŽJWQQcu-j&DZ;;ݬzyTd9IJpϹ!0e#($}γܞhUGꭏY폧`[:Mix kصQoˀxXwe} NQ'Z!.+%0S~u]&ۻwyR6T$YeI kk1552&4f1X) s}O7kZ!P/dG%颂@G>eVMt/\A^&Nn]|o#]%>p}J_}Wv3bۛ0lI`/,RO`毎K}yMҾMl/?gL~Jꅃ U9U{~" d/j]8_/f[ք)}3ZѫSSZզ1dP5q QC0D7 !a !}Wց;U=OKlQ ߢMRP'|J}%U}t}CT+JTJNJm BQC?y@KQO8|#!U@T$MC{UxUbQV%//I{u#r'a 'Uҽ) Gt#(.~k'nP\ddRTF`!yQIL6ҶWnDD&LFآe-6 DQ&HbM2c3FJib)LjdDDDD$R,f#SeF(u$mN#'+dW\JM:FEb$V`0ȃtuPUP.P!UBG5W0S.˝g全U3$:0[#KIJFԼ p)P%˰/zZvx찏 ; NWxKu)d8Xh9˩8RWc]UX(Zp@7m- g&X1XĪaGr7*9b2a1a55+1҂ʣ%SiVLXʖL,`1$2,H "U Z )1dPZc#2KDbaXiS#)SAM0Q&D,1ʣT#FUd2Y!ʆQa+ d+%`2K%XJb` 2R,#K"d^ aT1K,aYeMTMԖSVTƭZQJŊ2XH " Ū**f֕hRYlԖKiKMlf3Vf2S5%IdImTԖkI2ZI*iVkm$5-fY6R+R)YM-Y 1d*ba8#stY Ya*Kmɴ3L$)L ##4ġ%$4MI5&K%! A6R V6iT.Iw3eثvuB.$^*)}BRBJREҷ2JM.#̓E ZT ("|OZ":Pʞ>=)T>~5IPMUeMޣڥNx"3G66eiE4o*Vdٜ'[. ̱W'7$C{sInmں)+Lai3'ܯؐS)x'>EF5 eGU)hTbv-P~{+킰QQ(|vW4޸(IQeb'QE *)pQIsr!wnXOq@j]k;'Ow-{$MiR~EBWԨ=(WU oL,$K*Y6S4J( $M2eEcFōF63%,dڈ¥`zJP!}Tu^եjEo{N*|QV]d؋_h"Gçg5Zm)%$mSe*+(F2dI(S<3 HDbPj@bRx#u7 ~b*a/^T4hGWzn Mį&]jRN7^.'\VQ0Ș2Rc±@Ń d@YUYYXLɅ ʬS ad`ʕb"b, bȰdfKfAXLUb1QS)N#Sء<1uT*ޠO'N~k3淅TߐEVH+BT+'QC+<#e2w"ܴ£sGؓT-G&  Ez !g(/w8qPP4NQ aGZIUGTT8]O:sB68٭7V ]ώ(Hٺ4kj QE,U$5 šJN*WE]r%[\b ?*TQvl?bVەMJh&RH<*5 E-Ql`2b;Rs-E%&nB\ 8y~CﲙqD~^.~ ~% ?E~0GjU;%2UD LC\HQCJ/DzML 'B RhоUuP/J!?uI] "쒖 m_\{S JR >BrtjEyPjM6ZH0ԑf(QD2%)C]E-&_21C{"ueh|)9Fԫ>6toZIJ4aO8j A 1 XnG2D1KBISojكcܨlLɴ ^gJL9^ 5'EC+gJ9VjjYɔ*L$S 2+Qo͵@Z9#{ X])-̂25+HFbUYD bK ɡ00aLi&LbMV UGD|)_ATT*GOn_ a}lua܆ q%"?*9(UA@۾A%U C cChM૽m,2I(Ta@*Ԅ1CHi,࢜(%!00bn d5Y h&}j}?; PM@C%Da uYc0;.\& gBпBi0|Zp z@F-Bd.$`KhA륔?іS9MTkdzL6d.#권6n(ԊjjDD[6Nܜf1LaM毂_ݱkm~pbNBVT}7RXu q)wi*N^Bf]x&k.:_$7FG4$|s:GpxOI˕yYm H4.jwٜ-m"*+]'mqW70usm\\Qmg(< !ZB ;Zt Qc4H',]Eְ[*|_nҌ_x^^^^ލ__܋{Rܜ=϶W+>YmyNiQٶ =nGe\ge1GE;^]^ޜx(&"UT$8HͻL * nBLKuyEy2W 0,u'Uc8 8He t 뢽FGȡn4# p]$#9S`6ؚIX%cc=@YH<|g#]=v{XUQV{=w(Ho$ʎp 1Nݽ DNw(G~⒟G`EO!jnmQo!5t[p%XH "#|$6o ^i!VѠe5/cyWZ!4R3B 6uvm 1 *;7jֻV3`.Mg~ٶN64b"6&EƱx~_*]3<ֳ4G&t:<Dk[Q4h ᢺEuZ6XU>bM0_ jKӊ4kSbt3+&JUU^I$ձYAS)/B555muеpVs9f] Di5 )NR[e &ۥ I ]Pau%†ф$-8faRC 0TUG*RsoE X AlR06 ¸ZU0lۻZLY5)5,dDžQu1"ޅúmI:dL2%R0 tۡHgLqf,58heHu;stά[pFM\K) mM2Qx.`Z*QPhՇc3 Y.;@H}QRʥ{/*ϡGu>L単=2뷭+ѫۦ״nޖǸvh溦:UKMVoM:+u`D X.#.0&\ ,*]XNn&{4As؈hBm{/ ‘{~d$Odx^Vff=YzX1\lb~+uqK{t12G}n4ażl q^̑hq}$8OHsIv>v+1UnsfQ_4VYyKKfqmB:9zT]أvo$H m#5NoVgGy^)Õs.5ڶvmi6qYVI㘬K#ãܕI}|KcZ\5m#cWܬlbdV\Xa#j[c\ۗbm+13l+U&dKc"?R`Uv3п6LxKG ˦GY9-8fXn&߮ѢnujHN1[)ޝ'ڷZk/ +n.AˇsIiҨۮgZmil[鶘@MQIY8D,yf/+XO&df&o*Hbs1yKh9LZ7nU떤hM8uv""Q U]*w*7a)øygmk++té؞'u[Cjg.H-nbGvG;G^m;Wu0$sVZrTpCAAcd߽x$g+ӡTZV}ZyIļR!JR(Ƌ,ɇ-FˢbۮJsc>_ۣTF|r8owZ^哆 *7.R\c~73Ttާ.~km^C]9MuquhQtWEȷb\m b&T[Q]Dzc/G)Y#j2,0pt|97bu>߽]@Wzy& L_Δ 87Bh% (MC&SlIllĸbĴ̬[%u=d钙*zI _yRL3k闚V2WUekNՈz童hʼ5Ɵ/mq[P&-[A R @L4BApt[o],ﶉa,?Ne[+eG*aihm48wR͗\O7e_E 98gŏ[Js=OY*5ӼyW\ykZ֚hM8'e=7{#Q.^~No we*͍ˢ%~2Ƽn֚\[n.4iH5FiV%&/0;B:uAb<Xd 2XRMSbYʞ6y_毁=]fI^am|QjFOFS%gfurE(,kVe6^$JN/Vg*I]zO<ҲrKpJ22UGuZgRקNm֦[gzgtJ*G_c1Fc[6-$ ,# Lc*dE'̰:4 ݂4`I,wr8ÊXK欠s @aETTETTCe e*icEO6q$:^Q3F/:m[ ;>/wؾ`v˪_,U|s|fm1Q X 5%6>;hQ&yC+\yj<Ūg^jw&#)y+QXKgy[M)nMzKѮU_Mzj܏LN.XtiWR+“݆3խZZ՚)k"Ե1_'*.V/Q+*25vqףjpE^qcu4T.vHʮkMl2bplluոRt,Suni[c 08k]I7ԏm 6dzmO+c"+Uul=u&.n[޻ WSX4Ml 79՛N2Kˊ/+$qeUpsmS-L*eTl6chXj^QudÂ%Mnjt\B iwY#5,`:.>mgBN_U-+;jny%(;EmY7貰L,ŵ}S3/D %thR$;3!!b,b0+CUff '{̞U .+L.4#f֭Ziw3aqOr!h"H-s2 Z`XK.J|׉[" )&L-%-jǥ]%Jc -5ƫ,93um[Z7{'7H'ȴ.JC7tڶ 6_6D_紶~5~(i<., *9d}'0puQd|n1bs'Zֹ\RSl e%&l1$uǧ~kDDDD@DDCnCovZ xޑqN+B飄s6^wӵ|#Q:WefjxnXK(_l-]p}_D?& ;132jS*= ?a%?a%?l)+m(NH|ށKQk P[0jxw>E^mlDP+-͕0sN/l*4f]OԞg{ezy짨)~Tk­s\rI3E)蠯wt$(0S L£Ry=;Oz0a*u['|Ol%<]uuxUO;Ci)23kRS2$(QG"GJs$ȓNKLb4US(=d7y\آڑG!QtRp`,IcRi.Ԝ@E=P|N-ѧWĪ]H/J{thhΕfLZV͗s WSe(=ʌYHc3LJ)ٙ8R_wy,bꢭQ V,DbYH&g utuEQӔ>G7S\D#c+l\ƸZXJnܶ""*XN(>`a+Uea1jɌK%CL 1U2b& a1LT2փELMXcVhjUU543 0Ř b,L&b)j5CQ*f@ƭ6Uc5 K0#*2յek)D4kYď,d`e0\jKV5A̐MI)3 Ylmu"t*a2$TQJvSYJr>ʞ,:ȣ[?*L"bpU>/Tҏ~O@wQ%3RS.P8ĝ_>>cF)2]UF+6OTҿuNYezNN{ݒֱC2̝J?a-iz;G7r!Gؒ7qzUQ85yGMuцfZ:ؕe(l&e%5wkviU5ۺ#D9Ue7w&=m7 5 1:p٭[5$8$ԓ&2<"Gy9iL+tf3>vTw(-M|zgۋ[qT{Q̩,(+qU=h.jI=Ԋm{[:su$"}^~[M~fUw)NoF9Z{hE/zEHhZTI(LEEo|}.-y'b3̈́ȋi9/HRO\]SaXe)j++6ͲJZ*k6lZj)L1T! ҲM3 ^ uEЮ:MFAGE"bqNrS%^i&eGJ7~/HB.DAq ͠h|q?'aMhpXVJD0šj.+RKu.zp>֋ Jz&C g@|~gcl(Fs]@RUzIAktطxe~$'z[mI$MݷYmI$m8ܶM"-hI s3$Znhm[m[mwI${grsuyNI!$ޛ93;n!$H{{$[39ۙZnC3~Oڿdٻ&mzyC@$IwwuI$ s3;I$www@$I߾[mM{$,1$Imnff -I~?@330A$H7s-7 rIm@$I$I;nm{~f~?lI}k;Pm$O&̷k܅-730-ngn]Գu4E>s:ƤvɋmI7QmnL$yý{I$m׉$ ffI-ր8fwv$bm{ۏxw{330{w[s33 @[3ewK7rIrNjm{Im' &g9ݽʹ̒H'߃Tr)cFXk9AhXĉGq=<%֜lqI$nq^m޽J.fgmIV~9L 'zn|33333$I$I$I$I$̱[նv߭ܶom̳f}'em,Ȼw-\g>_;[k}nۖ{پmmml[mmomr9wEon[ymmܶm{n-m-o.Iwn;<>yYo˻{+J7{{^\砷m[}2M绻_4}~I|[m۶IӞy}w~moݛRKww^[bfwwwwM_;%ayr!@]mڍN;tDwmݷs{m[mo%=[/m0%Z|鐥ZB_X_ߜGO >T1O<@JT-(oxq޾ZcWDFykw׉Ey_ATI[=.]r񛨷EFzy &u-a š_myv(oF.<6y-"'*;0oO, @\ѠHг,+^GjX?" ۤ2e^q9r|Fiu%vc<\]~'%Q%J {cL1_x|F=!E=xj")KI$I$|VI$I$I$TUUUUU*Ҫ*Ҫ*Ҫ*UU2)I$I$I$TURJĉI$I$I$TUUUUUUUR*Ҫ*Ҫ8~P @J7=LCC쌽ys匽f~ N|1˶:J=3#{t nyqMW[Hu*tZטKgo_פkFE[c{~Qy[2R.EZQ[(W֭b(.F\?XOJ]Hr}4v b?;K_9^m琔|˳֌kV>߻n]~M`qemwu=Yu%cN|7oΡZY- ? 3ߊy? Ño@Tzyw´~C$%ӳGJbfy˽{::p*2}P>8oIom*9D)a䈁""S^(ښeFļ}-4]UtUP|?ö& *g˟_/\WPG2͞Zw׌GܸL {cSUU>tjQKR,6D@˕5 V8gpM.mG Kŧ^S(4m>t>ҎJ5xA8wVv@2RgƏ+,]qY:d,gC S-jWf_^c_@)N1 csl--va˻Wh^u׭iJ8k\JzKBP xZUe\@0RDDb[Ƽ|{v1؏}g0B  =:!ʔ3+ $I$=f1$I$־m@e(iF*r2./Pp]!0ඐHi(!F;momd.M,$|]8U$L2> #6rЈ*ThزrƤr8qUOUUUGUUUUڪvEUUU\~:&UUUzHEUUU^*1$I$L~La7-n<1A|A?rS%x-̹˚๏s J= 5BjY[[+U TQ+qCi^97ubKy,$I$i_2[H_7bO$9~Z|i)R,}/,x'|U`Nde ]E*:gg75Qe] ҄$۷4 <`mZKm<)KVbBaxT$J6q~?ϱ}UUUV~7j7jv9v$I$jkpf ̱.vlƛE7~YxR#Qa reωiHV5H>dߝE/ߓx蹉<=o v|a H~g߼sp @ @"QgRr0ǭJ,oUT2{x΂ ^j>c!8RBrXU9gL9uyF=:zM<4")HMhfJҐN1$J&"ҪvHjۻ. @Sg~F3.!Orao\m@ 98|z?zǵl4 P)AfdPРt 5"QkK˭+=sr(">(՗~iwHO^QwL.B ,L_2TJڵxYeLޮ?o^{O/]w۳*BzĢ fvv6ÀXֵObo.=ҒȠpESGcG)VBI$ROGF~{kV P1GD*/uZvcM^n'}M`D 6(Pd/^] =MD< x0`l/K}ArU6F}8͚,߼5JgGS^rʪBDYfE1v r "Cx(whGkc{x SV)HQǧE2y.[wt:O|B`UP=ym) `A 伺V)JPۦuG;"QVO°b;<9vuN98l,'wwwwww&`Ws[cmJeɾo)GU؇B(Q/ P&$"|#^Ik P`Z?CהP "0+pGBS1)0ݲ~GV!B!u錬Ao{@'B B29V ܇FyoorY +;T VǢPk ɗө@|-:7 ʃ 4X р9a% k9o4]9"V@BD0 93A1 ʻpeH#i|d\g,BrcY`c;<8.+&.>5iZTW v( @WX" !(Yg7 >X}uY p>R^=B rՊYd4?0A@ԎHoIq;ҔҔ)iv짾C#c^J'B$ +ˎ8I2uUpe s•$}L>U}YRR=A2f*<@9Kc e+e,St ^te( mga2bWx^{G[ "jDXwEPAQR 'ԪWBH!E^T!JB#QEdFWY0Q^h A W0ppfVu>ɗM׿)h*$]A UUYDFap$5hA (w׵\ 8!*|m~ k7 K!.nI!FǏߤ@?'0R2N@U,MHUS@}YK}sdKI$]D d낮eRj耘Xjy&A,L4+䳚9.K?gxu7hed~k8gr"B11&]vl[8gkH9ʐ-j3FLU_/(<#_fPJW ࠑMD[( ?Ę(UTŴITۭ ="0Z8^@AM1! 4+V0஡m7 ?+mmgK;C&>eGd胥jre.DYX IEb2bQ30m↉wD/5/ژm$CB/_q{}8mzV1 eH\|yf^354!?Uwl2j-5އ^:wu/,N]cR\̂gՓG?Kd42v̉Om)ϳZ z?[_55+H‘;d2͖u'!eUgOȪ7OLR{saQG[}6b@'-R>g,rVM&򼴎k[gۻ=GTDZ)$NEbQop5%!Rl6,6AImSm:m[qK0L`"**7$$ UQ maamh$,7aѥz6U$U$RI%UUUU UUUU/ &mfnO u ްl60ۤaammH+tnaml1eUW[r**bAUTÌwڪxb)2AIsX1c1` .-~ūT$I%mX)QZwEP_m9ʣ*2BHI ߽~ֳrT![4ffsHiEnj$I$I!'ȢΟ3@3㯊%44-"R@9w9}M^{$,'O7(e-7iUYGQdg{&Z(5tYjIR.*6⊓=mu!GמR -Iu*vUfsڮI$I"I$H*ß9E)$TI$TI&jI$I"RHD IrʒG*I<7HIWr9RHI#) HjH(z.[$T*H$I$MXH&Ci$T}7\D ^k%~יWBHDI#H}1@_-wF0HZcF0RF^3R,ցc E 1s?./63HI !$$ !$BIH$d1w/F2o>|]i7%4RPP$>إ{ZyU`e)J Q^ACk@mpI"?F"Z~:""Mܗ.Ȍ` `H.Kn屻2d*P\H~{1+ceIv7im2`15HPc)jC9~dp;Ю<ʪ3?H(nc1B y2 1KJCm(`(e-7rF2ȄXQ}U?sZc LΒk:H-8/+ "QTBIAE I΃*W̳z%1`DoYvQ+n|F֍zm}yZVY 5a7n_n՛QeLz̨}5$ǵ2֕@IEY $*Ѹ_F(M?eK[,+}@**"ƟFǬ~w1QEPRFRR+sR/,\ɒHnhsf6577*;wy*ۜ͘$тmͮ1&͙rOs}p]To7y|7YRt{v"*kFl;[%UsL )^2h իU E1Њ0fY[ڇ+/[ fvIwٺ$NRJs;EF![WKRGf )XI!U!Su'f<uU˹aQR&sAAy ޹`$ nqCu*R Ҕǚs,[7eίӽ[˿3nO|E~)b<oմ+˾{4i}4(}w5iI9Y7.ɲR&m"ZM$vi&MIe)F2uB!WfeYZ1 ru7^Yl31ȆC͓v{۽W}mI*͈-~[ȶp1BwyAf?o˽n`&oܿR`Mbt8-Zc)iF2g8-;w0y  8ZquD2Q)KQ# 5.M[yn =`sa )()s ) (`0X00ZDEЋb(B\PE إhB8XC 2PC6lR!f PD[=mwbAzCb7e;`sD7ne}P +ۈ$banoި.d-\eѭ+$( # ?.BB]͔o4$t3Q gt5oM6 !4l0(X*0E5"HjSpB_3onJaz({}9ILDK10 ">Mi77痌qPoɹ !nI!!{xu' m̼Uߴ zEuH!@XEFHmMAHE0@Tdr:JOyJp W J^H7A]O1'33⺚MTdk @^,@< K0QVZb%j%@ " )(5!"* +^ U~q !"x1<|Kw(! A,@0"ZUs( "L$)Gp(@%0QT(P HAa!Kr_0 =Dj.0[Ȃ\R=Dn2 3 C鱐^ֺ2jOd` "m{]骦"k삘5jBQ$c֭R{(.e=}ղU2Ew)f0b* GIUW9U!$ԩ#pTHTQ:v[_GD;aM(ׇ Ĉ–|8wJOq ^1ΌxtrNrHW :eږ*Űm)7;`ge5<~bbA,4j.fC<B+f".wE T B$$$*QEiiDTAA)B0#0#,R,F,R$F,B0c0"ċ,"U" "#TR4 %44M%50AFH@BBH_wH2wKcS |ω օpw׏oʥҺe}y%VnKOҥ>M6_kZt]F9Sz┐MCR0-SBe8F v[=To#՚ZFepG1hrcS$ 0]plz&^B P!4qE E?p` .d +~WHtBi6.n %?? r~?[$%H V{xn_nS!ۘo rgZOMGy!, 6.^"K*n>km߮{ Q̀/N3'ӴZ@; -<$&F4 5_(^X?n xq*"HO*4 Vq$eK> iX㪢RhBhH\,HĽ/& )yۏ S71fqP!>`Ct< h\>JF BG; 0dzi@\qo;#}8mp ?`أM!qH4'!<1Y3=x(+LUbK-"s0@@i+H¨lP#AW'`t~$"+2'o]gNˢl:|d-C1%&e*C>`bB)2O? ت4*#JHO~4^ZkZrAQlq ~p. c܇nY*@ˌUǖrA;\%cJ꫅ XpԣeJƓNqZu'|PBP|@E pX+@NHA\{M?P<, PwmwL! `H$??{G{ϷIs#G!P'/_>om%?O_}#rg i19;t@5=9|&F$%_q<Ҟ RL[1>TMO7:yhmB)nv{C?:s^!ڜג>>,NCLQ1W$ގfB#Pi Q)RTJTeDYQ(߿} j~2eWCNq$S:N*ߌL?jK)jK)-jK!jK*%b8r` /|T.ܫ1`ˎiH*C8C$@ H cbEN(9T1y %|j~R|VOgkSE\,m[rѠ,khCHsbk{ļm(UUT pQ'\m&[ۅ/{{|p [eDdT'OF {kd*ÑN{Yg\2uv'tfCrGm[m2w9ԜD^cso-[@w铽7wvNȴI$-Hmwww@}mOmՑjnffD;sw m-nI ;[$IY^st}hyrۛ&-*fdopvȫ[M뻒 ({}뙟fg}_dKIo̒e-b]l>Y32֦NwqCfgwo+3 emm333os7we7@f}{}t@m$s.ͶmI$x_s~<=N#˚S@ys{z_-@-h_>;)OQ+c|b*E+l 1@2F(P" T`r1pX8p^E @Dsq܅Sz7Y^gL5k<^Es8:ۄF fn%m*Ro{WU@XY$mi3S<Omjmޭgy&l}jh˼-wZRwww3+aلVd"eժO(nۯ wwݺZz9Q/iϣW>kEjlAu7]_׼?AJh^kRIAT8M睻9}ݒI7wwwwm{Y';33339wA'{www{w;Ispswffffss$+ ' ,{mUS{Tyk]I.{j{کǜֻrHQ&y;۶$r%w8?A|dm{ۖwK:#̞&Svm{ߞ$dqըȳr16Z(?oxQ]bX,@[mwi;` i*kf0QEQE s wsI'/1MnrI$kvTI'9] 90tsF)J((ġP\I %$@89ydQ݀prI$*J;Q1aCALBŪ/m4<~wfy33;sˍfl;q?-{7H㜭d#T'? " kQ.3Ff}?ʻm:IUwyiл{wU]U$Rnۻ&NK˾0mB\ޓ[wA u&6m:հmCvLɬ׿{~~?.`?Km$*frNnvM}{PtW{][w.(w:(DbHBFrL1Q&*YE40I0ˬ-'JɖBppJ!RDl&K]BErTj%dBswYwd*HUn+kjw[&eU;;޶&sfM'y.y'9<{΢A;@ OgQ7ॲI"dϾ@n-O:z32$I$/&tɠq&f{32;!&2do۸N7;z7DxGIQwf:0tvB_[%NM" UCe]$_on8`|m m1]&!?6յҷ$Bb{$Cp$@"U@>lχg.Λ8I$ =!"hƱwJ֫dX,Wr̅a 2~n-#UU989 O`ywH$%oŻREs"k .ʓ;= θ t6"Um۳1oMwvlܝ꺦o_}>&){x^ٽ}#}w~_{}I$PI s̶ۜ733?y<X礙 (:ǙɄG''gI-v>;&f?<']<q$䝝tkJ {yJ?lbI;eQs䪋r;ݚ..om[myϼ̼'?>j4F|~\xߑ$2?+ySgM_bI tII}jkI &Li$HQyFF9叠8{;rs][ݹ*8ܹ@yUU@ݟ%&kn Hp G(d s+JRz='<|^F $h0g.7n]6E\?,JC2v*}_M(E BZwbUG*CֹuXQ3o(+ ZT|33QEUUseW$L0#>WA mvݷP偏oo:=K=;^w}>m\ϧ{i;mȷ-nm mRg92.ff4$H[$-www@]mH[ssTEݙH;mh$ n{9wwwbDI ߜ/ۖ%-$ɰ}I.7<;&{#X;#uM4K]wѵzuu$Av9RB-pZ֛Z{L{܍1ܹ+$swveJ%Vfܰy}̑ʒE!"ns3,\A$B{ff`C|mm7gH33-mI$=IPX \ ,ns3RUvf*&kƠDPY:Ωj ꍜꪓ'yͤO%:l.(7~1¸;Kݭ1_LzvUJ07H4t17-sfn I 3HI !$$!ԉ-)Ee*бZ 2!qSWv`Â&h {ASxoۼfwsvI2vv4.zUtC!rϾ$n9mnff }!3\0-W"l 5冹D+K'!R\#V 4>$P+, F%%ULٳz[Is\B4}Y20VbBD&S860J\c0Qnnں @bpcBA`:[[vQ{jJ}J3{a;bWl-)Ü96B5k> Bsgw T+zZ$Snݞu#T#T#wM4ӑ  36M4o$!v.uZcU77_$"M.kZ3U!D ΐUpc!Qt }_s9Vspa 6m$m8sHI$_fcxJ$m6 I$Rhޙ$I%mI$Ia:{ԒI$mI$I${I$DI$I"I/|n&d$I%$I$HOy=KmI$DI"]wZ6mCZk8 q" yc-\mܒ$Hwr3-8ٱQ}%p|ݾq!m??~o?OwW}${ddP݁1y$>{oXʦG|Qw $LUHu=I6$I$J6$I~~p;S8C$I &mp$4JI@?S 7KX%LXIG$I"RbVf&I$I$I$Iy&XsSl$I$I$PNVhIHI%$$8H>Umi$I$I$I%nTuORDI%$D'3I$UVc<;WHHyכ^ԐUVTUwߛߘUUUU_ nܿ33;w;w7rs{s|*}I$B7w\3- oy{sp A(_\ evygYT+dI$I$I9>|~߄P$I*I$I$yd$̻)/Sl$I%{~I$Ԓ$%I*IRJd=Q^zѿ=CBI$I$I$J<04$I$I$I$'`Uy3UUU\^7kR*{kٽJ$*5ɽ3UW E^7ww_gow/~{Ln8ېB э:  -M0bUTo$I$IĒH]Tı$I$I A$IN {;] E$IHI$I={ލI$Nw$I$xN^ISm$IRE$3bI/:I$I$I$)"M~_~}}{LI$$I&~y($@I $B!DHQ<޳I UIPI $IPcg<($J$J !DHQ$(Pv>A9Us~nwww{mv{j;TU^M{7Ev~w: 4K!ᢪH$$H A@Lcy̕U,.!%qDI"Onәװ^-ٜ䙒^LI7RI-^m &f;q&I$ mI$$@57wl9 -kwwfw-s@-z椁$H.;37wRٿ^^vdLe]Kdy ̻֮쵍n]kzsV*8H۾kYza]M9[nczG--䓾/;Ǎ K SY\+^[Ϧk(.B!DHQ.W{k $B!DE̙/u(.B!DHQ.p"Is^*S~g8 B!DE^k^guc ( $B!D st;zQrI $Bwe%H]7$UW9EUUUW^h5T$Bn|>|ۼm˵fZ<_@>H msjfX~,g&%a ⯭f6_m$I$hmCߍ$IiHD<^y ^II$I$Ig2_zvݲ+E\wy\9rHܴI5UR9暭*nC<ĉ&W$5<|;rHܻE\{z&Hjn\*.WZs9wEUI$wR-y95{HI"1sֹ$UY$W{\d*HwsI"CT{^Imf7&7sO;|;%\^0(ތ׀ܠTg.}-yY%41>35P*s^{9jwswcI($J @$*9MkyUUUU[YXUUUU[kS.9UUUWX13jwLc9UUUU[o9UUUVoַj*@#zZ=33ĒI._doy\;l*I$3d!rv\)% &1<&Rf3_}n33[$-www@_/{[l^̲I$mݛ}B\32*h8a&g=9s33R@wwKE\w~3;jI7ynDslj67k2wdg{܍ėr6D TL6<.:ޕ *owն/mg7ur3-_y{s%wӻݽݑ$A$ʒM7w}̱wm$F}>ܻmlI%={WΦs x7[ƛba֋f2kbx[bCFs.&ZF?_g;ӯVs}6A=>c)=_qwT0==ٮc!D'TB2'>x(8C-ZiUF)!(@y*{u5WUU\[-f;5Hwl_twfмjKw^Pʪs^o˿s3sw.c>l{7gכ=`̿?=靯"fY.Gϛ2́@H]{vUVH9S|dUU*1=S|dUU1^F|hP;cG[Bߝsеڠ;=MsUV0D&ȁb)Z.'82733NMϧI7zgܼ` LK6ԒI$jVh h{'2:wnw܍߮aUqsUTK28 PK2L@(rw~_37y7{OC}$wwwz330 ~};LgV}3? Q3;mmI$' l$U KW$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$̧U7TaFUyxJG.A@)6W `!cS5s!ómX5p:0jn.ڹ>\~ŗw&ǘiU bk|&s w3@/i$RQ#dy֝]-m Q."j LVk 4")z̷oyk -EVrV/s=w-{7s{G3'z{UݮJΝ0ּ>wU}I$ =ms30[siyu:!ApoBbCTX* J<zr79oQE^ez$Y<8WvÃh *#:Cn+keW/ + ӜwT,$$'+lcM)JN,]cJ$w_.~7gܷ_/|mm矾֣{!W&J~QE?\E\<\xAwk$Y"HE,dQEQE_{ɝ&tTZfgr7~P]xyA@\c hL_ktjc3ߣ<PBmm4Mu9rgdm֒[}VUBeI2/Ö@sq E0a{WMK7?~! Vs*swg%@/N[|U&jR! ֵRHfFƆsos:ϏP??nnYlI1_㹿_翶}I vfgfP SrQߟC]u]+-v&RIdXV7nw]kyKM,ɝ$[wpf<)n4^I}<#I.wl^DxI3eEUH,$6 Pz̧߿џfvI&{k!N߽}I$wwwuUvfdB(QGP Ybyc2hY3Vdt ).]U2333;WI {}; a$npp$/\cɉ@YOLJRޔ)G n;mٜz%߇,メH&@{sf`|7]==A\蓮Ѥ:6@'Rƙ^>OUXM%?]{6 ̂X܌88 wDjK%f'.pp2 c,s߳?n9)ƺϕ)9{O9sw9JN(ɸ4U*}ҷv\iQwzRҔ)JQY:AL(!J`O*+S&)A(C 2@)s%T&nnI|7vw%ww%aF U9Jd)JRyq]$-n9޷;ޜyw>Y!2xJR(fgf 'Rn2w#wI$HNk\⪪jwfsjw|GwUUUUq*9g;ڪ{s;$IUTI$@Szf0JFrI!$[j&k{q3U{wwww|ėR_U\ 1 ޹eX2dfܠ3R0{ޗ:i{I$I$,f1^1JZ2:*1sζ{5UUUkar*KT}NNP3eҔm1!9&&Vɺ9I#ᄇyJR(fgf>E&H\kLLW'UbJf$Uʧ$LKi1L8 79d$GQϑ@z˅Z-xkة:(Q?s P.r"z(R6u"gg&5u!h+sN$*[mݱvnЂ BsϿOI$&-[oHZ"mVI<"[n7MmƕdEI$Ykw=n&cvnIHI${[-y[ۼpILwt[ȘY$'~wbI {9y:jI2}o330$3&K6+n˘ '*i{#a{2vndջг۞sy̳{ج$wGm!)*II&ػڼI$I$I$;{&$I$I$I$I$I$I$I$I$OA-h7gD m[m||=|tL{w&r<Ǩ>3:dxD)R,놃stREXBd"T]o+sw5&oEP&*ո["o5h&kw.EmݒHVHH1@s UEu;&c|ߖ~U7k$I'D(E-kZъZ䐸Kwvsj]'% H%ww3jhKsT8 ̂X7wpJ${|#CkKr˽nIY{y}t߯<9;7*&C@M3I p=HMbӛN"2HR7u2 ((&wQEQE5' dY|̡Uٛ Z֯+Z䐴 ?j{8EUVsn*⪪Ȫ7{犓UUUQ hfsw6:}rkk+uシ̒EngSܐ_y;Q!ԠE R R[6 ???UIA5cNFYOe4LֵҪ]ZMk92Tfha.d`Fh΢k̳cUC 2 UCUC"ϧN]#-U }⺫tL"T~I}5"m (ҢqЕ1-E?|Q!'a6۰E$Vz DE /1H [Te凕U6}yə m[lwwvhWIɨնؒnKoމ3͔M~[L[$lH$-W3,$7wwvI' &{e͒-s0]wwvh{-,ϭ6O};ވzޏ~kZ5Ē@-hw2qZ_|}m-|F {}pO, Yps>Ȯ$1wAy33ۿ߷-IκݽgyYWs|*V*",8KnNsΒ&RF2^U]ۼ$8n]A$r`p]ͩwx8nU8wbԆIr "xwwc2dImκddsl 2/J;o]B4O>|Oww23GKwpmffor "IIms1ܒdWvWrJ2ݶۻIyyضImImo̶o~7-PI s̶ۜ733_|.{ utuU8dWvWr틻ybp\{߃I痣k34:CUPffUT(r&LP9=ws̒IewZ{w2̻.m7zR75~_DD^´w}kwz8ow|烂I<3Gm̫yןm?;{$I@ge{;۹$du)7K/nΏkVwwwwwUWfI0TDP1W>w?"I,Ē˻P{~p]^~x $y{ބ_Ī'єg}3n I39į00:%69s$yC}웩RZ wvmeY\C_?s6W'?{ 8wwy8{og0 ēVp#-%a[5-%0vzꪪMg㭽nFd.M}WxnwdI%3/nw߻2FCF!JԽm{a|1n`P@i1j^׳6s[m(}XB.Yް zABԛ $Pu/PMLơڙh }ބsk6#3K1fcPMj/c&u}C쒔-U_Cw>~>1f}no$f߼~} Pe-||\s7e_v֎ou4p&tjf2qa9yS>jLyvs-{-$H-n9&_N=Ksz<@{Je&]\LrDLHPD˵wi\E>PBUS-$vϕU^{RO}ϭ߮2N7s7{rwJɷa4%m~oo|˻V)2ȠfCh^M ow+m]i;v6 +31hM$i\JXm(L$iR'wwwwwwzNsf[w&yo>PhRHH(mi1_Ko^wFzij3\),YSv^Ys3YV4OC ?1*>wwvn㜝X?'쿿뮺ۋ=ZH^׋B c'q<[8-ymCcIe 3n˽w"8)0<)$rAdAdwhg[ľņq!fz5c9̱*` AT[mTZ*TC: Uv! a˶05#$4*#mQjJ@L1uDpUV_|uc$ZI$,~ߗlԭgi'V}x3;r%egDÎ\7{{|g-nZo77XI$!"$v{{"33I$$[hI"sw3o9Jj-mI7vAm7uI${mԊ7wwtnsws-kϳ3s3J&-osndjK666a;ܒ7Qvqt끺޽i:<$xo7*Zm mMۖ$[m:gUu&R덂h]DY49W Bb` baԫCKZJx73pR/ )b5Da8!UD"ft(Bj&dt:˷#T MRW]Gs FL\@e N)q)F+8B^&v9Rƪ% Z ;sOba Tgd3a &K˻۹'Z]ǹy׻wzggy| I$Pe+VkZ>Q1aka-n2”  wtR'!:׻ֵ}Z[JҢ((s;sw&f\ $sȷofř78ݻwoݻw{$?oګs3?i߯fdՏ%K;#zu=-\]a+wq3|7~g+$k[h;^|]ZF yr>pݗ ݴj?ӈLz T&}yz+ wG+wvmfڪ{kaÊz~ {}̾]{ĔOEc3C_.1K^wwwwusLY>1xH2  P gw2]bdQH)Wk:ւz: ֕@wwU խkZ$T V# $nz֮*@iTDjp`/BP }?$#\u^q_׹_>vs&"L[\ټ,a)БTh!J 3e%R8(%Vvs=<#9D hÃ/:VZӭ#3uE&*$P NV\;⤾WqY"{[HWg;A>U4>ywwws;37uZ$^ Bq/ җwwwwwusL.3;oYxQ-UY8C$ @h׻n&*}!CI@]shL1KIn`Pc<$y9jx{ $& Yt;{9a=O);gxc!Hǒa/qUULB"- KҾ;{{{{6:GUFit qZ:Bh@8Ǔ9 g&[8Yj5ُkva̘9.ꀷ|囱QD#gV~nf~u˶ndϵ[swh{Z%$2"2&M` 0T%`:4T喔̦DT(iPRրyV\8efPڨMrׂvlg(p52&[6ss3$RnF9י?<k""[m"ν)KF2S[k;޷]˻¹\ђ.+߯^z]H7h>Q%;ck>n/q7-#RCG{LdMο1dH~,y矟7ml$$orfnff\wdHgyg&XI"ngKVI$M89דlff\˻̹#Epv׻]$y9~@vT˪ċ1lqhʝ@IAr^&b9$9kj$I$аM &[.WD!dTLѪx^W P*I$I$\B |TMn1V5k<{<)kyn݀n@aO*HĄBpunz< ]*)T7%7'?&dB&w^ |k'cvb}`6-K( `` #]4 6#SD5&j4qڔpIX Z+!$I bڙXsvX Ϸ 尀DE<><!%-Eftڥ$lvNV*Wp#|Ke."TITyOĨg76vwm}>..xIhƏhAƁNCvġ]|hί;;&^zHD)E4bCf/V^#$ b;h/r5f!|}]c[P"Uwj@E,D؋`!O~W-OrgIGP:e~\zO'ӓZah>@PxJ?)0COU/uB)t=m)"yIITUEQQef}7}CS ]pjuCI _>/?'~usw"ivױ2&8ǟ} lu !0Q@R:\r2    ~J,D>`Dq٣kǿ%A'&foK6v=i9;<=04c͐|eRK QxZJ" ;"n<-FM3݌!*mv(,A0I ;~k%{݂֠~_:hnfJ-WJ֢O9!9nuZ6= ںV# #$ xE*&(PhX0"mqh`|niF@`AxP4"cJA *cL1#0fE,EkoܥOI=mi@̃ HNЯʟI$Ϸ [p{D۸qqRϟ?ou5yi o,W]"O7bx %DCÏd:=d3/*Cv~߮bBLMSd}lEv fox^ \U|M zSngꣲCC0[*9 N [Gp}j> :!Hǘ8s\ ;}pKjzw68o6c[ǯ굿?Zϵ]uPԒIt#GfIl*BHI !$$BHI !$$BHI ?HI !'aM(O ?^}8O$h "FJ, 81EK  dSwdISꪺUV Cl<^#V܃$ IR'𯇿ߗ˖acdd]"Й=m==m:N曆!QX! >RU;>z Pgo("v?w-ݪG>pCERg>Ưָ Z]BM\5a ¤ֱ^s%m9<a&f*Ɵ|m6JNljͬ7Xտ @grJ檪bʰ4;pK~$fnӭU ,r- U8 @l!''YN˙f6,:'h$["l\חNʕ}~_8~f(l;0#rQEQ%E)BD/0q \}w5$TZ_>I Oʪ> _7?A}3t};__G%NQ:&+h~_(HHTN>Qw2]Lh AP";C0GtuXgI$I$I$jh0'߻kcX̚ R LE ;;l_xle%A*x7tkoCTuidN'E}`Ss/*b*RM 4n\mrEdYooS)W'\jxdUUUG/sHF'^J`7`X'/  (vB&!bnh c -QJ"@-P-XHI-}3Yv q Lپ?Ķgso&-^m-듨4~CD D-VB?p|u ҃oӼ`oN+itd$*S$$$kʅH0U8j(-PW2'1e X g}AAeMb - k!C_hl w=a:rmO?#oɴmmQڀt4UF%Z֐ZEepZ`kq!E!#['.zOS;0S0QsͺtOu}n<_wDŽ jn:# FFFE##;Gsl۸Lې%DB Bgb(@4@P/-x|ӗvI7pRB~H,@Gοf&1fo9sD0@`=?4D) \Jѓ= %Dfxc RE:iOZֳfFɓŅHH<nH,u>~_.t<rk8K?+Y9x87S{M%̷]m,TԵ>> 3P}=ȦV7݆MܶI_ӆ3ygXF'?aN[[>9 wIie΅P""!qy<>W $#|?x)wFj{?QOֿ7=[8iq@ 1G@I!&RlD[8Oyqǹ۷nrqtcN /?D6Z B*^p5tf/R@{<Ϥ^&$] t9r@Æ;_?˛I6x5ة!C?U=Y-NHEE$ (h" PB@ KAh(J)\58T<`2HuV$p`8đBDP^aLDtK-o o\})}Ҋp\heķ_sݧi{o-nuӈɇo{ڹn.wN-cxc%rtyރ2 XԸ;|d䢎w>z7Y87P5Nb%zG}{&&Sv7̡}y)?o\0evݭ&UUW}[7@T|/ʐIsWÁCᭀВ}e>*=p_ d<^H9g ׅy@M[ǵ&]8:h]Pɂ0!$ >Q4}<`eg i$>oSn4!{SNw p6ɣFr^fvO?a-{I${nooy[{;|ޜ9I;${sgy<M.wmmmemmI&[%ilJ^%_\wU35D$Iw˅]<9{{ΩJW|9|{Vmp:˻_My\-f^y4ݺUUVYfkZֻmmom}mmm73rO#}$gݲI?@{ۻ/ϻ}mmmwDwֲi8NUpה]]ʠ7~Sgs<-i~΍wmh[dI$==ϳlZ:3vfc1"`~{To=H0\CyتJ6 EL:b ~wDT1Fxê5 /P*)͑`h$! 77/OJO:`' I{J<,bZ&u߿yL3LՑ1]?Ͽ=h:<ɢ˩;> - Lǯ9}2jS(s_f+K1$I$q }uU{AUUUUUUUmRU_?]4my~씤/yfiJJk *(UWq!XBs1*NR6YnzMp9*~>{y_oO=G~yU_.;G \iUU]Ug8}UV~ ͓uU?.םǝx1N( Hl|DžUn~o響W)JR$ǹ*&@ hLJI$3Izu@k s$'󵌺uٗM.Lw)G>h/?/z{zNdaILcJf7~%n8͚Rv9#["x@IDfI/3Lh0mUmo5Sk d~4ϡ:apVD$Ծ}<|VOy.5m),{`T^{#{?+<|L&=X~}k Ff'韶ۏ35I r'do7yHR1Ρz4{}|{ܛ/?wzf4$<:ZD] CҌx);22U&p>|)i_O3vC9-}c\Zz~ }:ndL@218xi;?PJ܎!⠁,}w >~?u^{ӭy^mi ݽ-kb!?']U>ӓ|?O[|d!ΟOݬQZs**Bqs$ϡ'ǫrQű'bT= '"'\x)x@B!ѱD\`$ P+R^N[8hOOWJO eS{B0 A܂O`{Kj. ?G(. ʁב!; >,kl>x3jh~4/MtxNx΂!hL=v`@!Yʱ`/ׯR~A|$ ?|$'L eρ WW<4r\up~0<]UY)ѷSFn Y06"6HVMTm]Y}Пկn)La#m2Û$N,\ܯaߤD.o0 ;: l ;HH'&h_Ƅ$I=}O:|Uhq_UUI@1ҩt,T5Ny;;wv~jw]Nך%X $ʪI$IG_W7ޛ߹OėS߉$ʤ?_#} X;A @:09ߡ1b2(e0VV?>IG۟}"WTޢ4dِF))_߰AVу (bmzkI5k^fHM*{.r {UYV8Bٺ} Ǥ?8]{mmmmmmmmmysnuy{-mm k̙кc*O|rIӤr>ap{W:uT~5g>S(c!","ל"*dDE*@J@D)JTTJ`X !X"& j(D`;pVb EU#R @ +A#~O/gOǫ_>ɰ/%#Ln;9j6Z^J{wAW;y)L$ȁ{/ uRcIT.b&qMpy{{;!o՜h; Q)w>(ỳ!{u_(=/T mY}UwEƠT?/M V>nB܄aF>3C;̤g;8_s쿇@8dn ;_eq7~2DҪD@= EwN|pmu`_٭ w|<A5$I$I$spnqNw@)8H"6^6"ګ./𗉇 :y?H _kfvYܟƯV&x.?޽ć(qac_8c[ C ;w-fL)%DO:j!{ tX+.[[[UİG{OC?w{' n.a\()0@/Oh<-迷qǚHrI$d Oğ]M+9 &/y$y$'9e+w|e޿ mo rD 줪cYRNJ y`ώ5ѕI5G8?H^I;a( {%/!F멱_uo?ohmaŲm09?QbHݾٗnC}c!/?v|:rfu FHI![{mpuN06HC0t|(%@6~h ~RZֵڞI||-/U m}_yNglV_UKz{z[}^5ݣ %†G9!!!!9@'A!!!!.dA{Xc;|o77c\tek\klsmw͞ y qrpʹSk"T4kmB~~?3$*-)Ш$ ܦư(wwϸOSy>.C:M @$* ;vTF, PCBA l/P]pU? "qp)}ţw65'#箇߁^ <$d$R _?'償\ (!E0'*Xjt?@w'+ֿM~vNALJxa $P@0#$ MZ"UaU^QiA˻JAJ(F ŭOabX0pv"/ G7_2#kAe1kBv0h~#&zb% BcJ/!?|kroW슺?ڬ C oBS\$46ha=RuDL`Q_|vC=Ǩû)Zַ`mb |Q}S߸rs4=HC 0avckwo'\Sb|ʟvb=`*H040S!/ =} PQǎ\lUs/)I>f'[pY? uDA/ɲmDiJYEҙvZIFnI$B7G8/a yBN\~ҸsAFCI*=>;s=טX'TɃC̫2}7Y˝g?=zH  $r $ %E"2(,Q$BAb)Q*5KQ I B"@R)B"4@JPA)hQjҪЂ"FXB)#TEAh(H ) " 2$Ah""2$bUPFj Q R1bQU B#LQԌZAJȲX)M " *(KE4S ,h(44((ҋTH-$cTDZPQE)hZRQUZUjIB%Ih"!D$Rdj$"L@Q,j5JB(UBP -("д*5R RBHBd(R KKH*H*KQJЂT`#A$@ T$FTAiФV(RHhQd F**S B2B@TTY B$dEd $ $Z QQ$E$Y*H$dEdBTVARI@$P+PdD T%@dJ((FJZ22#"I $d$AEP @D$AB5 !$$BER@J(Td@QIPPAZ @DdDmO݆? 8׶Pf ,Ԥm@1aB%%Gx(.{>12.s(`GVNJ yorͷML\X} 1@C0@ M@{cT驐7[a/T2Q{D')p>?II(.;a!!軸v04KdD[vJ_1BYM3[0 t.C/{+ZWI>h".3#b EV1 dM QACP6plss |džOWǥ)d ˻o  w w/b,-w4Ƈ'c& k Te섐h Ń] _*[b%/6{B }ĬxV-jk_~ooyV4VVEbEP%+Tƅh>pmGzs}uNT̩UJmx, X 'XL"0m0N2R4Uv;}9#yMx:_[_tS|θD3ep15h -,>uCe:ո@湘l-SO1SA?Y|^΁7"+eC ޝk<ԉP~gqɫ}v<(m\zQߍ wDQSVń r7,24أیmkb˅i@lCD m3ﴠܚ^x\ـ}B4uEܖA=G :8Mr,'l<㯴:/2أ3#dq 9 mc >=%.`7ܥ0`l'n1c!|S< o0}sn@nm7M"T@/&6}&xAn$#FDdBE,Rvt(~t&Ƨc^5oչć i@ϒ'ָG9nP=5rt 8F$I !$I! %;S](4yr!}r;h~44Zuϸ{Crn/saOZx ?P |F"ü|J9t}gg+}+c`E@>KMs+:|J38BC<9}L= X.q+11Z\/G~txwwFGxI$$$DQiiDF1_v!v"':6<h HH(zw vXϗ $"PB@<.!PJG{3McW6ǐ2!jMaAq׉8 .\n~a"CW*#kvnp8kd]z'0Cχ#vb}}Cphv>yx32vpDZlQ7 ,H`DzcB˚ 2@H!Ωdh1QЈV*!0B&Qj.4Ld{2`DYF,-b % X6nq"  vj&gbUQ+BAW,`\n?4 MB8i`x@4|ƾAFXXX@&Լ !PLMWqt(8; 39 RB)~j)*5z/!hH #"HFڠB4P *J +5JU|]Vݣ!.U[T[epUW m[ӕȕm(k'jw3,.&Dp^ k(:G& (KDf38g tkk "2Ȣ-#J-P(IM4(T(--M"-RRЈ*%*"T#IJ-5B " SM R SBд--P PCB")JR%( JСKTR5B*QB4HB"*Ҋ**ҋHЊ4#@R** A@-  H"!"+# 2 H (*2 BUTFt B*= $HRDVDD1*"1(@̼;dIKjD hbD%Z?WWf`Tt!aP+B!dJ6T0CA  &2JLB 8Hwx\ݨ>?mWȠOHAy#wX8{%p^rp4:(]^2{tUV6Qӑ{./<֊¼O36wskv1C4u4|%3P`x @uB\؆555\ 왰}j @QphFÔfb cР +skw,\|- еBJN8q{LWw]W\GqP2ߝ!{ke;vh` ME4aeòˆu 恑s60d\0|K I %P$c+\& # ,9(!߯X\*%ԡ(8"'`a4p7+s$b@<@c{I0Ȇa?bUnB=cFƎA`h̐agLq'w"sM36_yo< ({NIۏVBfe(_X<˗bC<;@2!""w)G+8AoSCj) CchxPzԴKRn X.ٮijz̋?m sBq n'\=s}ǧmDvd_N4 wS0w$$i^ k @&7vM7FqO1{_{B|\I 3 +\(d%z*hw匤3o2'lRdIQj Wj*,(9BWL 5BJD 륂KPM3|sҴk8eH&l"`wG Z%UFB AQ$I!8 %0BҀoբQL0@2?yI>*.C H$ Š` E  ,"  xrmӧT{J{n}R10c(&hIj-> a,f*7v `(v2IodleBE'`'eǠAvN˟PFGI(! !ߞx(p ᠎m %;?1^]L[%1y=~!"aq0wU-r5;ͅw7=(rMo #6_?Sz`{ӳJ>`@S&=9aQEP  ;<̈@d/m] ( [eP0"!fh UZ,BXXD.."& h E ,@/HRCא2 CMLoE3޴H 4. nWHـ( 1J A0cy\ 3 SՐD@&!f=>!NK]#u5*5)h~j城0XcZ ݒ 7Zڔ>( }A@B;9I!.`23?ԼtGqd3M;L EQ"8M#Pߗ!9Ԗ }R!׹2hY>B&Qxs*VQ 7Q#1t^Ht \й`a} 0DHi(7(–,`fIֵGhNyLKءq:u+ 4OE CjX(.&Q+ nTsf*@vXZ ]؈ F, (ZpYd-Lsf~֖[ b1bLů/uU͢ +O h@ڨ 78T @H6U7F7B!de%"xq!hq5f@8M痤%F@04EAģƪ"eZ!B%9RaE"4yHf%.0T A(VHFHYEP " TU5H)J$dQ &U*$(-)qVX Z[$)Jt4PL"(CU)jIb5R^,m,@*6PzPz& p)A-C"jUؐTa,-Un\ 00X26-Ϋ)UU*Z -䈠HA@EBZ)Teq@ H`(A (J2 TCPD.få 03-`F݌ȊF!HPʂ @ۄbPKZ%@P E@ID."R"4- "**!X047NLLL x@fػA` JTHDRV4fqoE 0)b| RNUTb-Blc4QIՂfVfXAAQL.&ǚ"}TL!##* E_CJ?e0"** 4 *"".#5,1JD[ɉDixp$P-l.HcypwpmR=9՘qT0&ˊZ@Nၰi"i2W{Ț5\'?`4~-Ib4Ce@!=`\̌?`;t'v5XfOԟ{c0b򖉌rA1)Mfm3ᣀ1,!p^F!DI!&(Xgp\(3 BmhohێÀ N0 F $" ""Td@Q"r|NST{h>'SrNAd\il2/5>H2 &^7}ώCQzx{>em8zH‹1;} '8\@$0ءѐ6Q!Ǹz=]cK \`px98xTMx ڲŽ/}q o~/Z $IZEhaJO՝jְsv02k ~3q4lqpSnps@釯P:;|^yNCühX(;`P;g`gS(Yl$#By[)wc Hd#rYgS3q?t 20,9EBxbq9"̬H.!,UkFJZQQvFD-QuM!d!L TP \UJ4 QQF~ʮWL {Zedr3/jFH,,(+qt>3-e_}jo3>Hw) EnSxL445F|Y!0Km.8`.|z.BCg$F {亻2E\)&=257B*u+Y006\ {YD1n FRJ"F%=!))hiDUU0b,UJp8аm' 7MMw'uOfBg܀PR4)&*) %kT,bPBJ!`" Gj`Hn3.@TU S3cǕ9eNl:;4`kgP؁df p eD  B-B *PB@$h$iaQe- PDk'][xdݽDX%se]@P@Go1c+eS}gu#%  kD%ZrF&Cp3(b(`R.>.O1d$-UWR!Ihb$7m02bZeloʚ6e:Ajf)6r("d ܅SjRD ]-  ɰss tE+$k.kpK(\T#4nM /Q2e ZUtp\Tzf\`1ެ#%hUFH((ҴJJ*ҊR**H**J *"-"Ң("*(Ҫ(-* Ъ*H5TSM"JRI$ Dzn _8R@A@ZMX4V!2&W(q0 @֪P H/(%D7TEI"BeBD%Z(^b2RJec% (ÉF V*nېD Q5tyD5MK ^ u5[3(Xn4Sir, 4*U45dj M墛` #E%US"*JGh-*ZbU !"* 3z@0Ѭhz?2wuGH2D% 2UϥQNd lRZaFD E.I$#T $E#QBDJ6 ,K #c2SA*|%QIv륕 s,H@ ZQ .`LsFBH@lHZ` Ck~l q3B֪\ ]TU RhhBAR  C+i2"$TEEťi0DQbDAURT$" JUXUUUQUUUI, JETUEFJUhTTiTVTDEFiUZQPPVQUUQUZiUQZUUUU$qtCH*4 F(9Ef;c{; T u?\2,P)1!ZgՅX|5Azb1?0@N`X(,UԣxvnAFBh*{)!2ki֙"y/ɰ-htC >7 n -'3?`v$6F b$LKPV =`]ZSh(۹LPw("=" 5CĵˠE{};?g4(cm ş#v>A045n'#z نAx݄;b@@X!@m?y H\F!`u2pmO>́6:uCNI]D:tREAb*UC7 )zPc'#RX CQ26S9Jagv"2yCsw|k (P&/#TGv(1b3?0w2BZKQ3c4D8N-i^ojg8"yp]pg M 0') 24^BL$LDPD@@OJ{}Eڞ{u ]wy>;@ؖ|,E|pjzϻQ}W{D`oT:ɄtWl]}ϓ^MuQ"|Y\`#UaZTv5t`{ooF 2hk! ݹ*{omϫ{}{ lt@ݹ3z͕<Gw5;%2ZWL32Sp\ t가mZG@9{ئ5{W✍x SF @e+34M @ 2i&i"hɴ4mM1 @ zjdzh4RQm(m@hIꤐ&OM7O( ! j`jz)McS=M4 AJ F&@Lѣ& @zh6SMhA @E$"HJJHȤLP4 HX4"@ "5XdQA5PCvڰUQ jQHI-P (~CEF"D?(B"#TMV4XUAPD"HAdVUjK[m)jmj-mEJbjƚVRSJEfMFٵTͭWҀ?⨂ AAU~OiHPy|/K c$~u5zr<_\fێǒW0k4|{UO! UUDdVMmq-qc`qmn..63-)!n+qcUrW51@. 4Rr\RmFZqlk1 AqE'9c9ĀIR&LIB ?7_]wwwS^&ªn)cH: =XIVkW2j8 hQn9,cqbkqqcQchj6-Z6mصA Q * (-D* TFںV6j"ZQmqk\XZ,iL4kF3-&&#V&n6b[\U6 HHFpTn-Dj wHp" F"lZ4Qh䫋%mӈdSN\3pFVAUQvFKPa6Ug}{>/oēBd ɊA=8Μr\]7%]73H,VbDp(љ IW%[WM&ps7.rr湜[\&$J F BFױeO}DEj殗#jl oR8QCM0g=q!] |jwǗ)AI'$3 daUft]8sG\梺[6*F,K\ӗr5.$X` "+U[l[mUIXsq\\M8!:mAF-8QK6z4ȗ _%t Z*?m5O؄>Hg xH):i>ntN6 (da27DbW:D 9"zm  EpX1%jxAr1=K,O88Tk!2wA!V0S31-FXYU>BB}g^Z2G-l [44=EilTĵkr`U1%JU`}25u9 a̔*iA4C(ZhbRM! _'EϨMP<ǁ3E F,(aS2SO 2cLn[I;>8pVGR)Ád}= wd=G=g^2 kiBK7yy<ߓw~fffg<Ên#B<@yI$v[ 6L>8L%HӷS=_g9yљ""1kZ 8Cz^a7O:fIלU@ 9ϗ] ]uIbֵ$͡8wzhj>vGE*niJ閍AKU>`wvɑDofO#5v Ag/p)|ϋrJYuMIf&,xo4s  ףputPWz6d> 99 Q $/ LZO0I2”6t|ֺn[+0f q68Ԡ[%};c0dSIMjJQ"aArs҃&*LY^<Χ>whT'*x =V˶f3?w\4rݨd$dCw ;FZ:tt A3{i-$4n)! 1SN5,,\)% ƮZ)ھÓ$\WW7k˾>~=:n{.s|6(Ta!I ֥$ksr5Ƣ6:hC|BC $gm7t;v:,Yߩf&>Ǭu!W#]aSI7n{x5B<} A$D/$ܠTxw`۹"4 !z$~4*#ꎈbRJr{};{;sC]|@..1@זh{|z|ę&kXǧ<JI==:.sWT@sO8+~@m ލt4|=n\ /kd "`K׸`-!T\''Sw[0&M -BXh<$9۴Hjr ^k9b.Mq'm%X$߆Z0u~R5*#l~yɈ{ h<jXu=NA۵Ӹ %ސD(!ȍ$ wb AȒBa-rn C 9fD0$d X I`fD9rfuw6w(&۵gwh1hf X%1$%G#\0"AL! !LK՝/vtKM* ]rFUR|QI$ $ ەJm˳ipIhpR`EE EU[Ǡ;]sx7h,Q^+x:S0 @V"Nul;o[7 %& s.:뫴3\XΚ1j/kP(̼@p HELJfOsdp.EG(qU@QvkR`։0 L,ĉ"` F"K n4@#@CYPig2O:yaႚ<ʠ+e̾C "(~cQT*X'+},?,jrUu!y%fAt(!36Gs{2cԨ!Wԅ?ɂv+xBJ#uIAk9`|``rz-X9@L3F&=6P_6 I|@&\ _iʦ#so8ڕ?{n }fҢ4{CPib5P s}nͯ~ӝ)ӗ[FѷڼUۋk6V-t%Ũ,"\GxSi-.Mb \@.$K*b魮mmWlWUmkW󐨠*?_~4'V?o/KV?sjjZYرbC7KӭAVFȓ"8* p[,Xy,X#{uz#)Kd"YijǐxO,zO͠Rŋn!PA)$ȾĵMeSrB*`49rzC&u dad_VZʽ -,X!mx!Ԛ(,# ؍ !kAeOIjZYرb<r^j%26Dո8 Lʓ " ,T'Ul%jYAms,\mzBgpvy}oo@|'Z -,X A/NY"LKXu<},$~mZv,Xq ܗZ L&En%:sA>2 ?6-KK;,CunKӭAVFȓ"u9 vYA!C7%֠S+#dI}[kǐzO,zO͠Rŋn!PA)$Ⱦĵ]Nc='ݖFA='eigbňnB zu d_VZî1# h2Դb7[x!:Jedl2/pq-aSIeOIjZYرb<r^j%26Dո8*r-KK;,npA8&/ jE76#DtpvZsm=dd~mZv,Xq ܗZ L&En%:sA>2 ?6-KK;,CunKӭAVFȓ"ué}b" DCxhw!D<b" DCx1"!D<Դy e:xՖ2.ݠ\h҇Lq3l DCh)%iD8vrʹËh)L8vrʹËh)L8vrʹËh)L8vrʹËh)̰q3l DCh)L8vr?'@?COSxsO=SB4Q+r16I.m3 h&6~KЙd0+I?7_n*.haS!Q cʕkmmvQQE"[F6*ЄmѴ!Ea UQU"5bѵQhB6UEڱU#jTZQhB6UEڱU#jTZTZ*Bb-ElVV@]0,lFsUOS?hzhJRZSMjI ,ɍ(Og`_+ZJ'H#Cou(' yp RrO&UוVZn{~ $I%^_nxP"%Q%kI1(1 #!pTd@dI "$^0(w  _E{CF (0 \,v9Mg9WQUZĄ(=ʥrdS;+dyD5dGh!̥;ls(ڈ^8eecpɑ*'&PnhOT:HWΫS7rV-eᐱS.;:YWKf9Jؖ%@$ҽ1 5f^>9Cɨ>>jy p) :a%,42 (9<,0Tz"+Cte +BY9 FA!;u2r8NC?\>p@@=`w;Jn2x9tNOD1W'U""*iΙq(Рv?0 tUiZ(`@PfI.vu1't%<Ȁ|ȥldJHlV89;zZle1&g(0ޅUUUUU*=Nk7E^ \nA֡z#71MP"03U[Ь wr@f+k}J•yeK Op (yǴ ulN欶kۧ:y +%m8BH "Bqx(-N{Ά.3RJ9%U=eSt^!bDC 14"jX>Y$$0!5)CП>84]OZ6E{_UZJ(UyåU| P>UȪJwgj"R"!߃lG] Tn~Ou}.BxgNh$LbQI)PIh.1%0\뮰WfdJѓrpBs[Xa,Műc$%qPj#W9XQDX3̃AK3@Ҁ7 HC2$3.\E h$lLM&FH2IjF"1AKTh@F"5b髂g.w_3h D"qH&|)BPJUVۊ>* <ُS,SF2I' ѭk5- 2h4V]]nɨVJm$DG4.3O[5[1Zy0g݋I ҹ4Ep|ri|i8Z0a^im3o2Ș J}tDEpqhxxꃤ39sipLpѪFcċgF7owA؂3fYwLBr%ͼ9 va$Lf&Fݹ ,CZdsRԚ0ד2Q.!4 4$q:dDDDc5U0'\sߠu#(cUc 0{hd0IC# Wٶ]$1JVZ7֟{]]wYun_ 7tte]vI&I$̓$d8UU@2Y8̓tUUOZwrs$ mUUUUa8k{ u֪4"u]A$ @DR٬\rZY2-jbKF4֙liMiie_ܭST؃}aO>eI zLĆ)|S@5I$ba~ӯ{|ݎ csrCњ뛎Hk Zs!,zޛH0 $$ `+bݘVr^GC'Ċ|ar3Œ$9rzt5;@y"0t m9YW'LT<8[ eZc3d ǶB>C:I M=m%iR W$)6I pD!x΅3$+&0ti5(Tn:]U$27S"&pAP+$.[ܵH u"ǻ ƨ18֧̃1H3݃@$gR,1e c&(:!܎H,뙝zn5kYf.KՖb*ZRjyr K%إ&+z@4-J(j%v肂B"DWUsndQJ.UW\ mQ Ia `.5=\̼p>Q5=`"ԕvtۻAW{=;]ACP> .ܑ̻7-sgn\93ql;ǻ+pEqdfL6ۚq}I!$,=zl>o[hN8G4I?N*scŠt AB i)*JJ)JT AP˽_7x=4"C:Hɓj1@j4Xm4lhJE.KLBHCq=b(@C6 7o$Z|ڑsz0ivC!b'VZ7`1b0 `rVz;v-Ue"ѤtϜ J5~iii颐OC"^ɧK&fEc»-Vt٧L@h#'wрţk˭wojVFkZֵloD|)T| ΉD$Yd@f 0 VƊcDb`*ډckhȨ"$2VlEQM &B1b4M`l[mVM+}(/}ߩG)$#oﺜ@ntg-oq$ː@07b (7Q @".]<ӵūwt"OL=E,-Dֵk[$cB- rvEP-t6(54"ӖR. k`EťϛV>W?n*qDb'lOySC^^5P$UA9e Tљ ٛxq(7(bG0\[}By} KOϛr* $sss 5@#(? 塈 ܍Q ԇ)j!aN~ >(kb=fQک; HBe"9b@jp 9Jp&|>idq~8V~yO=iB/7"!HӹuL Lh}7"Ѿx=> tt@O76LZuVHn/` Bĵ.H)!&tFRAрZXeJ;#lD> p*be3@$J$X8hZBq)A "*As_/J; ~ nUhQ+@Pm{Yo7ג+g>l~?hg}lsL9_ڑ?0ߚ_]WeTy7ʦJzw_?JP2OЄ Ĉ{ܢ iȧ3R\;;6"cnbq )"h434>\kr|y=G#|,=돎cnYWh_'̖/pp#SEJޜv-d[ގ-CzE'/D̝hb"蟛{E?|x0gLh|;(@u{#X܋#!-O҂ 9-B.",=@+NYp G7ziH~Zl Ce C"exby0WvkqKMdSpP'~߯EqLp$>Y]hFOuk׽FmEffffffffDDDfffffffffefffffffffdDDFffffffffflo{ffffff33;;f^fffffff33;ww;:A$D~.qT1>iOXu~ @WOLNO'7ZT!đcѧ>Zg"7rц^aN!E gDLc4hQ~`NޗCETPi6Ŝf0JF 6UTh~~4R3#TH|, ?CMjyߙ~0҇Ĕyo}Z5@H~D?Y!'Oxȿ["|O6 mJZaY_K~}x4O:s@ 4'C V8P?D€w y_# ,Lb="!qd7dmֻxE24N!1QܑTV>S|^Mp$^o/`E1c#"U]nnFzvx 9>;KczfB-`hűN:wo  8gB>*lUu[n[B-a8 {KnUZL 24WN߆g~Y6`E1c#"dRHW Ap l@Pd>ߖsiɓoͫ^my5p"9`X@ɔNH ?x K`PhQ;P(` *z՟΂;~}}>Ϗ&.E4=`E1c#"!$Wj0oW?KIMj"a)e|bYtOLZ7S"yA084P >}y}ȭ 8zX>5$ 0 q ?$ ?Xu%a6i$I4DQI9T*2E z") )$ @WAD2v#>;wEy1#tu/r?DN gg A/_/G7==~y~@N6]X._G`E1c#"ţuk@~;AhC_@ˢi'q~|bwzSyyݝ['e=,O-Zo@X|4gӺ鉛;5σ|nπ߰y8o9x%\0!& ى& bjvLT{G߇'od{8ǎ ߿~}f$d9={&|1'uHwMy|>㷨 %ݘ̥{U/wRrJ .PݘI$hT)4Bh,I!E4kfRHRkfWuW.=oKbeMzb@n(gLf?O\&=2Q/CԷ=~ @AL~͎p$vuLj>U<0GTbD=O~(*[T= F RpJE.p* Q(XC Ir!EHZ|Kv:q#2p\E(K/d|Cd9KyHh R ,D B Wv=}~...7VpN|iWGVwɩ >wk:b,1rc͑c1FJK=墹 - @%+A;('mߓ&VތpDDaSuTE>>ihh=?!߹s>0c=|e. "1EC`Ojs #Cߔz'_|/O7VB""lקI[+Mv*?WvxzbPYzyZF̗|&[-  Kjnd܃r͍{tZu$Qm#u afj,uegQ@EA AS9s۷>/۴ 4@%* %Kljl"Ѿhh) DHsWD-ǪZiLBaC5ruyF,Ac EO߅sh5.k[e!)l7Zadl>iS4d$1DINr*G4CV i* j?%DJ~_xY}'VM?,+y,DN|NꞋu?#WE3I$I'Y~ ,6) u8:|qz GG{l?̘ ]RǛwAh<8=$ ߈q>Q"@O,EǦq)_^ķۅ|:ЭyDhQ6[bl \(܄@K:$`}^9={..xzVGhKO,.~F{=EuS@]\SӚ"ѠZ9F`<dnb<[ U$# OA>`2!E(?_J![챩"G % B@`u8ER WR !l&4тpn,!~а{~ź(FlN`ĉ̓!//j lQٛ@M$A\eX?)uڪm’)fYrdZ─A* D$ E"MϧH2l&m%(iDHI"E6LB ,z{?D5/ZqC̑sSswXh1cdW|]߿nwV ւw,1h֖2A AQ~s}^lW}ߔ}CI"{ԯޞY[ (.Ho< " իVZjճoPE@@y0E%}|ڵj+e~Mȁ D$,e~sBFH9 HD>}HDq1^Xbd rnĊRe)b&E &[ϨJ #i/R;PExl)Cc]?!(2ylGZ7ĞvI=Q4!3urs-8yPX"+멺zma"ӯ'_-.؎j-r)hQ&m4M4M3OMZ5ˣ+XN4"̪e1Ǒӹ"`hV)$]ϋ8Nʀ|^dN@4EܕQOU/a2# n% ƪ5bkVڡȠz E!>`i]T=E@5y G/ gpOP"qw,޽b%O$C^(> RBAv;elqskZ+XЋF) SDC֞Oj0p=! ^^drraDs\ܜp߀@e2bDNG(50fៃ"Ѡ^ /wVz_լW'#/TLw? Wc|D"(XOH jϾf{A[_c}kMzz1G>4ɧ "BB +0+:0$K!jƈБ&5 BP,V-mhE%)Op%@$[P}E1 RS+(c (7PE9H?Ԓ?Δ Cx6qO1}KE{݆-d=d!0r]*Q=!ë9voS =QfTþxLtMFr=KCl` ր93&a0-ykUpHJP2\ԲԖ&4/kJѢZK $?/}^Gk;,z$I800>^ջsԻܙ|\;o #[W?a{u\E^i}:~`7sk{}th_3[> yn^k/_vs|wD]sN4FT[D<1]oD`te n߂g풪_3$33K -wAv3Lh;NeKrn|$|%vٮ#Z lܧb .ō 6B4~ŽGt#i_]E~"](:rr*0['}-JU;,[T9Ke9^}}[v͗!<d: =PfW,Pb$&;~2|D44Ҝ)D:Xirn6u')9ͦ `$D++O[T s{ 1fSS#PMUUVy6xwkt#Usns7:}Pb/޿=fڕQgRfAգDA^Oƿ9IJ.~zQ_QYc(Wnv2GJU\V+bXV*+%,C Cȅ@ 1n tZj5gš:}}N']ЯBJQ/>cӄqM ykӟᥧ.xw;cLfg9ӸlMPF; [X1ڀЯMsr4ukZSDDhC#cd5SdRA2Eua1RD|ށW7] d/T.`ZD92qbppJhʂ`!5 䄀P:..|r6ABy\ˆr(tmq%ڀEiųçV(@6Ln.c9%H\/O( b`PtZ rՉAJ'Pgd"NZ'] {w%=HPNs$ ?0SCK)CD>7l ++·lD TD7&ns4І8(7Ws^HvAu:B* A95N'R&$nI$ҚGU<)s# z8;;9Խ6+'/Bu/}?黏enOZ=IrW**𨬉_xA4)HkMDw{P@Vv+K1$Ҭ..A 94\==5c^h*˩`PUU JA""e ~Q ]G"MIJI.~k`\Cg1;$t@ꙟ3aD@NGR^KŔ(mj[Tmhڋ%L&+224j1I+ DI*AB2M F!"KQM,# $lCb XZ*kmV[UmըU[bl[V5mFQUZ6XH!XEF'lF6,ʄTmĶ)!aa7*q9UTQA*5fs2MEd :` g[[\gRU7.E4(cfkd!h I0$wfK#ihÖ[3>9 d$ y /_@!`[yHBHwֶ޻W;.-r|#CCpmpzT"i- {<jUq[ *\ M,^ ?)_x3`R ]}4_ )u˯f3N{PWx'|ޞi' I馒za4jY==eDaU.yUΑj50y9n6J.~ |0S&RD$dF~2eUC7 i!GaWɮÒ˖P%i6}hm^[C)gʮ $dfLthP:@Mq֊BB&2# "T5"d⪍b(@1_T!U:dM@2E !|p >B* j7"""""(ﯻz^w[kүk)MQÔ%d3;7v"U>4O%ʼnp\5K^]pUAJ#=ʀT`e灌b)y $ pPd0)(EUAy#r!AM(hr[BBAP hU5 Wl߻$ ʴ(e_C&~* Qo8qgَKT>_0m"#DDDDE/Vݯ}{h[]sѐP:l:@ 4@#!$o"t @L@2'k`ۙRэs؇a2 0L6e$i Kİ,,UA#%+\.|W " "`8 " " " " #$ͷRmΧd4 @xm;n$Oġ?aCG'ZE亩 0uΝ`UIZ"ŮXhc6lV4PQ(kJ+,D@dƒEcPQEbb* F*5cIZ"Ej#d 6Tj +ƒEEƱch@Fi+DX4PQ(kJ6Tj +ƒEEƱch@Fi+DX4PQ(kJ-AFE 5(V5%hF*5cIZ d* (DWaM ~qFI9fPD۳+1m}׾{P.Em-={(}^Є)Bw& pm ڊxS[ޡS|몜ThT` ͸U}r ys(PCAJAUa $Xu `QN r!~BA09NB%H]S5k)boK׏ > DwL mGͮHT9zA;!m!q*P!TW`D1WPwrxlŲ>IT8 q RX(îilHq+fvU1)Yb\bdvrz`$=G`5P{BuF @h!r3jx ~CǘYHD8qQ[ʈ*#>pW.FD;q+ 3;I3Ijt \ha %vJ 9ME~7D峪G4eFEr*T I N5m[[km"+no$Ix "!zv"NmB=@y '8L]W CΕlRQVCqh *I!'dz B@mڙKpD&SĪ8DȆcB\}~WGW۲器s]]ܕZT5jy\VJE+%Z_<:pP¬BNKÃ%8mІvֳ#$]C }W)٠ѣ {A17VhH_ LUHpR&uP=pP1 6- Pj!DfaB}0cyCʷvwi [M~졜q w&)[UZW ZB:K;J)%0mL*,mIR[S%mL2V[S%mL2 ,mL2Y2Cl$* E"RKDhZM>[$ѻ@pC091痓dU!FQTXO%&X)5xIGoƷ)8&h R(a D3irI!B x)]-x]sQ2lχ]zꃼH' 96*gX؄;WR]5p$I$t Akг]Bmˬ34n(*1@ * 1ARP!M&بHnЪU2&JJK h"TP*] T$E4KdI8DUEO>DveZaG&@O2APi ¤W@rPJJ@؜;e B& Hi@PIP*Pwp[5Li%T0m2%I@C!A&@1աW qXj!AD D MZ. A1Rnە[gk&2ɓ;>uOi5)k)kIJR%)JR)JR)JR)JRd䊚yf;;E0Je,5h}߆:SBbBfe:4#Nth64a0j۶y'%>Ȩ0Ϣ_LˢHcsе,=*֖Utj_r Eb xzŠ8W@{@ET8hB*N&Ebә^r!rH ) Di=CCl0\@;wwpݜww;wwpûww|@-- 4ր !Hx+oV3}!h tâRh8O,&`+IfHĆ9DM[~ōw K,KH mv؅6yƕJ%en;a!ȔC"m*ph9!`TS:4PLWUK ʸ>T @}'UvQMfMꦀUh^s!ݜ!%% rP!AKx* UUUUUTQ)J(R J^^RCJ$'0eCYmxN 8cS.QP.k.Gu7 SȯFozQӥ-KS!# iWB2(rE $\q|{|(~e>IF AF\2a6y)9;w-Ȩ q qQEP7*$ /˜ bNQ<9@Trk|mq--bU-)$\V܊q2lQlXصX1$ b6*9mUk3B$ +$#0z5mmlmmmmmm2SsxI9bޭяQ-G!Bn"WDT8LE>մ*VB\|1PHԢ,P(Plf .H-% D 7bQYaneq'W$CD"3⡛nUk`Bкr5h;]K^| onW^5Vz¾ p95HD$Jکh4qjX$3T{FTJm74c@BIG6uUB,)ܲI.!k]+NtC*bPMGe 3Esr暶mfSUWm[W,V\n*NkZ5SUQMV(,@AZJP!M`2;F{($Gњ Ϫd%ƕ$)V,Qm+N5-љ## |`quҫ&rDwP*$Ifa(P1LD%&K%"UT1,Œ%& ;h3.P&bJT9"LS0 @$Ifa(P1LŒ%&)QUC$3 0 rDaFUH%FIaFUH(9xxwv.w]\[lK[ml QZZ֖-ijP$! ~yւm@s9& U[U_b﮽u'LWLM,̼BiY䩔M%&:k5=i0~s̙<0 ̒gx.2`40ky ;oWDp.I S4ha2a1%UUW׶\ɨ;.a9(lw@Ԇ%P r <ܘ1we΂PVC0f5`I$m@$ usjnˬfiiiiii"Z( ACBB mC=ovmӗ!U_'"T5(b(x_V)k@ r(A*  @ C@>NjGBD rBu :s}Y9tAoY8qʼ|}H)Ao?.>8z* \Ph  3t`9"Dd 냠"2&_p*(s<:&JU\!Q =@I  i^ʊ*$0 wTq DtxCGjiZ-&ƚi4M4M4M4,M4L4M4ٳM4M4.sLji&%^W@@5}Zm=݀ m t LZK]CݐNq@Nםڵ֖#""" m^{auֹwm<>^VnLg8LQ/% ~I&(RЇ`n1Zۨ}#$~JhUp'&Ǹh!`[" +"@Cme 2<@" ^CkI_9ҔO9ou>SáCHvks`s' UALȘiO|7CszDVM0~t`}jYs9 UPeU *q0PCW$F*16fԚ'`Zʣ| xmax} or \code{ymin > ymax} which will flip the underlying data and return an object with a normalized bounding box and data.} \item{nx, ny, dx, dy}{Either a number of cells in the x- and y- directions or delta in the x- and y-directions (in which case \code{bbox} must be specified).} \item{type}{Use "polygons" to return a grid whose objects can be represented using an \code{\link[=rct]{rct()}}; use "centers" to return a grid whose objects are the center of the \code{\link[=rct]{rct()}} grid; use "corners" to return a grid along the corners of \code{bbox}.} \item{data}{An object with two or more dimensions. Most usefully, a matrix.} \item{x}{An object to convert to a grid} \item{...}{Passed to S3 methods} } \value{ \itemize{ \item \code{grd()} returns a \code{grd_rct()} for \verb{type == "polygons} or a \code{grd_xy()} otherwise. \item \code{grd_rct()} returns an object of class "wk_grd_rct". \item \code{grd_xy()} returns an object of class "wk_grd_xy". } } \description{ \code{\link[=grd]{grd()}} objects are just an array (any object with more than two \code{\link[=dim]{dim()}}s) and a bounding box (a \code{\link[=rct]{rct()}}, which may or may not have a \code{\link[=wk_crs]{wk_crs()}} attached). The ordering of the dimensions is y (indices increasing downwards), x (indices increasing to the right). This follows the ordering of \code{\link[=as.raster]{as.raster()}}/\code{\link[=rasterImage]{rasterImage()}} and aligns with the printing of matrices. } \examples{ # create a grid with no data (just for coordinates) (grid <- grd(nx = 2, ny = 2)) as_rct(grid) as_xy(grid) plot(grid, border = "black") # more usefully, wraps a matrix or nd array + bbox # approx volcano in New Zealand Transverse Mercator bbox <- rct( 5917000, 1757000 + 870, 5917000 + 610, 1757000, crs = "EPSG:2193" ) (grid <- grd_rct(volcano, bbox)) # these come with a reasonable default plot method for matrix data plot(grid) # you can set the data or the bounding box after creation grid$bbox <- rct(0, 0, 1, 1) # subset by indices or rct plot(grid[1:2, 1:2]) plot(grid[c(start = NA, stop = NA, step = 2), c(start = NA, stop = NA, step = 2)]) plot(grid[rct(0, 0, 0.5, 0.5)]) } wk/man/wk_crs_proj_definition.Rd0000644000176200001440000000375314311405267016502 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wk-crs.R \name{wk_crs_proj_definition} \alias{wk_crs_proj_definition} \alias{wk_crs_projjson} \alias{wk_crs_proj_definition.NULL} \alias{wk_crs_proj_definition.wk_crs_inherit} \alias{wk_crs_proj_definition.character} \alias{wk_crs_proj_definition.double} \alias{wk_crs_proj_definition.integer} \title{CRS object generic methods} \usage{ wk_crs_proj_definition(crs, proj_version = NULL, verbose = FALSE) wk_crs_projjson(crs) \method{wk_crs_proj_definition}{`NULL`}(crs, proj_version = NULL, verbose = FALSE) \method{wk_crs_proj_definition}{wk_crs_inherit}(crs, proj_version = NULL, verbose = FALSE) \method{wk_crs_proj_definition}{character}(crs, proj_version = NULL, verbose = FALSE) \method{wk_crs_proj_definition}{double}(crs, proj_version = NULL, verbose = FALSE) \method{wk_crs_proj_definition}{integer}(crs, proj_version = NULL, verbose = FALSE) } \arguments{ \item{crs}{An arbitrary R object} \item{proj_version}{A \code{\link[=package_version]{package_version()}} of the PROJ version, or \code{NULL} if the PROJ version is unknown.} \item{verbose}{Use \code{TRUE} to request a more verbose version of the PROJ definition (e.g., PROJJSON). The default of \code{FALSE} should return the most compact version that completely describes the CRS. An authority:code string (e.g., "OGC:CRS84") is the recommended way to represent a CRS when \code{verbose} is \code{FALSE}, if possible, falling back to the most recent version of WKT2 or PROJJSON.} } \value{ \itemize{ \item \code{wk_crs_proj_definition()} Returns a string used to represent the CRS in PROJ. For recent PROJ version you'll want to return PROJJSON; however you should check \code{proj_version} if you want this to work with older versions of PROJ. \item \code{wk_crs_projjson()} Returns a PROJJSON string or NA_character_ if this representation is unknown or can't be calculated. } } \description{ CRS object generic methods } \examples{ wk_crs_proj_definition("EPSG:4326") } wk/man/wk_writer.Rd0000644000176200001440000000460414507375520013766 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg-sf.R, R/sfc-writer.R, R/wkb-writer.R, % R/wkt-writer.R, R/writer.R, R/xy-writer.R \name{wk_writer.sfc} \alias{wk_writer.sfc} \alias{wk_writer.sf} \alias{sfc_writer} \alias{wkb_writer} \alias{wkt_writer} \alias{wk_writer} \alias{wk_writer.default} \alias{wk_writer.wk_wkt} \alias{wk_writer.wk_wkb} \alias{wk_writer.wk_xy} \alias{xy_writer} \title{Write geometry vectors} \usage{ \method{wk_writer}{sfc}(handleable, ...) \method{wk_writer}{sf}(handleable, ...) sfc_writer(promote_multi = FALSE) wkb_writer(buffer_size = 2048L, endian = NA_integer_) wkt_writer(precision = 16L, trim = TRUE) wk_writer(handleable, ..., generic = FALSE) \method{wk_writer}{default}(handleable, ...) \method{wk_writer}{wk_wkt}(handleable, ..., precision = 16, trim = TRUE) \method{wk_writer}{wk_wkb}(handleable, ...) \method{wk_writer}{wk_xy}(handleable, ..., generic = FALSE) xy_writer() } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{...}{Passed to the writer constructor.} \item{promote_multi}{Use TRUE to promote all simple geometries to a multi type when reading to sfc. This is useful to increase the likelihood that the sfc will contain a single geometry type.} \item{buffer_size}{Control the initial buffer size used when writing WKB.} \item{endian}{Use 1 for little endian, 0 for big endian, or NA for system endian.} \item{precision}{If \code{trim} is \code{TRUE}, the total number of significant digits to keep for each result or the number of digits after the decimal place otherwise.} \item{trim}{Use \code{FALSE} to keep trailing zeroes after the decimal place.} \item{generic}{Use \code{TRUE} to obtain a writer that can write all geometry types.} } \value{ A \link[=wk_handle]{wk_handler}. } \description{ When writing transformation functions, it is often useful to know which handler should be used to create a (potentially modified) version of an object. Some transformers (e.g., \code{\link[=wk_vertices]{wk_vertices()}}) modify the geometry type of an object, in which case a generic writer is needed. This defaults to \code{\link[=wkb_writer]{wkb_writer()}} because it is fast and can handle all geometry types. } wk/man/wk_crs_equal.Rd0000644000176200001440000000154214106220314014407 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wk-crs.R \name{wk_crs_equal} \alias{wk_crs_equal} \alias{wk_crs_equal_generic} \title{Compare CRS objects} \usage{ wk_crs_equal(x, y) wk_crs_equal_generic(x, y, ...) } \arguments{ \item{x, y}{Objects stored in the \code{crs} attribute of a vector.} \item{...}{Unused} } \value{ \code{TRUE} if \code{x} and \code{y} can be considered equal, \code{FALSE} otherwise. } \description{ The \code{\link[=wk_crs_equal]{wk_crs_equal()}} function uses special S3 dispatch on \code{\link[=wk_crs_equal_generic]{wk_crs_equal_generic()}} to evaluate whether or not two CRS values can be considered equal. When implementing \code{\link[=wk_crs_equal_generic]{wk_crs_equal_generic()}}, every attempt should be made to make \code{wk_crs_equal(x, y)} and \code{wk_crs_equal(y, x)} return identically. } wk/man/vctrs-methods.Rd0000644000176200001440000000227314155244415014550 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg-vctrs.R \name{vctrs-methods} \alias{vctrs-methods} \alias{vec_cast.wk_wkb} \alias{vec_ptype2.wk_wkb} \alias{vec_cast.wk_wkt} \alias{vec_ptype2.wk_wkt} \alias{vec_cast.wk_xy} \alias{vec_ptype2.wk_xy} \alias{vec_cast.wk_xyz} \alias{vec_ptype2.wk_xyz} \alias{vec_cast.wk_xym} \alias{vec_ptype2.wk_xym} \alias{vec_cast.wk_xyzm} \alias{vec_ptype2.wk_xyzm} \alias{vec_cast.wk_rct} \alias{vec_ptype2.wk_rct} \alias{vec_cast.wk_crc} \alias{vec_ptype2.wk_crc} \title{Vctrs methods} \usage{ vec_cast.wk_wkb(x, to, ...) vec_ptype2.wk_wkb(x, y, ...) vec_cast.wk_wkt(x, to, ...) vec_ptype2.wk_wkt(x, y, ...) vec_cast.wk_xy(x, to, ...) vec_ptype2.wk_xy(x, y, ...) vec_cast.wk_xyz(x, to, ...) vec_ptype2.wk_xyz(x, y, ...) vec_cast.wk_xym(x, to, ...) vec_ptype2.wk_xym(x, y, ...) vec_cast.wk_xyzm(x, to, ...) vec_ptype2.wk_xyzm(x, y, ...) vec_cast.wk_rct(x, to, ...) vec_ptype2.wk_rct(x, y, ...) vec_cast.wk_crc(x, to, ...) vec_ptype2.wk_crc(x, y, ...) } \arguments{ \item{x, y, to, ...}{See \code{\link[vctrs:vec_cast]{vctrs::vec_cast()}} and \code{\link[vctrs:vec_ptype2]{vctrs::vec_ptype2()}}.} } \description{ Vctrs methods } wk/man/wk_trans_explicit.Rd0000644000176200001440000000221714471771474015510 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/trans-explicit.R \name{wk_trans_explicit} \alias{wk_trans_explicit} \title{Transform using explicit coordinate values} \usage{ wk_trans_explicit(value, use_z = NA, use_m = NA) } \arguments{ \item{value}{An \code{\link[=xy]{xy()}}, \code{\link[=xyz]{xyz()}}, \code{\link[=xym]{xym()}}, or \code{\link[=xyzm]{xyzm()}} of coordinates used to replace values in the input. Use \code{NA} to keep the existing value.} \item{use_z, use_m}{Used to declare the output type. Use \code{TRUE} to ensure the output has that dimension, \code{FALSE} to ensure it does not, and \code{NA} to leave the dimension unchanged.} } \description{ A \link[=wk_transform]{wk_trans} implementation that replaces coordinate values using a vector of pre-calculated coordinates. This is used to perform generic transforms using R functions and system calls that are impossible or impractical to implement at the C level. } \examples{ trans <- wk_trans_explicit(xy(1:5, 1:5)) wk_transform(rep(xy(0, 0), 5), trans) } \seealso{ \code{\link[=wk_coords]{wk_coords()}} which has a replacement version "\verb{wk_coords<-}" } wk/man/wk_handle.wk_grd_xy.Rd0000644000176200001440000000245414315177334015702 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd-handle.R \name{wk_handle.wk_grd_xy} \alias{wk_handle.wk_grd_xy} \alias{wk_handle.wk_grd_rct} \title{Handler interface for grid objects} \usage{ \method{wk_handle}{wk_grd_xy}(handleable, handler, ..., data_order = c("y", "x")) \method{wk_handle}{wk_grd_rct}(handleable, handler, ..., data_order = c("y", "x")) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{handler}{A \link[=wk_handle]{wk_handler} object.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} \item{data_order}{A vector of length 2 describing the order in which values should appear. The default, \code{c("y", "x")}, will output values in the same order as the default matrix storage in R (column-major). You can prefix a dimension with \code{-} to reverse the order of a dimension (e.g., \code{c("-y", "x")}).} } \value{ The result of the \code{handler}. } \description{ Handler interface for grid objects } \examples{ wk_handle(grd(nx = 3, ny = 3), wkt_writer()) wk_handle(grd(nx = 3, ny = 3, type = "centers"), wkt_writer()) } wk/man/rct_xmin.Rd0000644000176200001440000000172214321145376013570 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rct.R \name{rct_xmin} \alias{rct_xmin} \alias{rct_ymin} \alias{rct_xmax} \alias{rct_ymax} \alias{rct_width} \alias{rct_height} \alias{rct_intersects} \alias{rct_contains} \alias{rct_intersection} \title{Rectangle accessors and operators} \usage{ rct_xmin(x) rct_ymin(x) rct_xmax(x) rct_ymax(x) rct_width(x) rct_height(x) rct_intersects(x, y) rct_contains(x, y) rct_intersection(x, y) } \arguments{ \item{x, y}{\code{\link[=rct]{rct()}} vectors} } \value{ \itemize{ \item \code{rct_xmin()}, \code{rct_xmax()}, \code{rct_ymin()}, and \code{rct_ymax()} return the components of the \code{\link[=rct]{rct()}}. } } \description{ Rectangle accessors and operators } \examples{ x <- rct(0, 0, 10, 10) y <- rct(5, 5, 15, 15) rct_xmin(x) rct_ymin(x) rct_xmax(x) rct_ymax(x) rct_height(x) rct_width(x) rct_intersects(x, y) rct_intersection(x, y) rct_contains(x, y) rct_contains(x, rct(4, 4, 6, 6)) } wk/man/plot.wk_grd_xy.Rd0000644000176200001440000000343114315177334014720 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd-plot.R \name{plot.wk_grd_xy} \alias{plot.wk_grd_xy} \alias{plot.wk_grd_rct} \title{Plot grid objects} \usage{ \method{plot}{wk_grd_xy}(x, ...) \method{plot}{wk_grd_rct}( x, ..., image = NULL, interpolate = FALSE, oversample = 4, border = NA, asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE ) } \arguments{ \item{x}{A \code{\link[=wkb]{wkb()}} or \code{\link[=wkt]{wkt()}}} \item{...}{Passed to plotting functions for features: \code{\link[graphics:points]{graphics::points()}} for point and multipoint geometries, \code{\link[graphics:lines]{graphics::lines()}} for linestring and multilinestring geometries, and \code{\link[graphics:polypath]{graphics::polypath()}} for polygon and multipolygon geometries.} \item{image}{A raster or nativeRaster to pass to \code{\link[graphics:rasterImage]{graphics::rasterImage()}}. use \code{NULL} to do a quick-and-dirty rescale of the data such that the low value is black and the high value is white.} \item{interpolate}{Use \code{TRUE} to perform interpolation between color values.} \item{oversample}{A scale on the number of pixels on the device to use for sampling estimation of large raster values. Use \code{Inf} to disable.} \item{border}{Color to use for polygon borders. Use \code{NULL} for the default and \code{NA} to skip plotting borders.} \item{asp, xlab, ylab}{Passed to \code{\link[graphics:plot.default]{graphics::plot()}}} \item{bbox}{The limits of the plot as a \code{\link[=rct]{rct()}} or compatible object} \item{add}{Should a new plot be created, or should \code{handleable} be added to the existing plot?} } \value{ \code{x}, invisibly. } \description{ Plot grid objects } \examples{ plot(grd_rct(volcano)) plot(grd_xy(volcano)) } wk/man/wk_format.Rd0000644000176200001440000000307714106220314013726 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/format.R \name{wk_format} \alias{wk_format} \alias{wkt_format_handler} \title{Format well-known geometry for printing} \usage{ wk_format(handleable, precision = 7, trim = TRUE, max_coords = 6, ...) wkt_format_handler(precision = 7, trim = TRUE, max_coords = 6) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{precision}{If \code{trim} is \code{TRUE}, the total number of significant digits to keep for each result or the number of digits after the decimal place otherwise.} \item{trim}{Use \code{FALSE} to keep trailing zeroes after the decimal place.} \item{max_coords}{The maximum number of coordinates to include in the output.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} } \value{ A character vector of abbreviated well-known text. } \description{ Provides an abbreviated version of the well-known text representation of a geometry. This returns a constant number of coordinates for each geometry, so is safe to use for geometry vectors with many (potentially large) features. Parse errors are passed on to the format string and do not cause this handler to error. } \examples{ wk_format(wkt("MULTIPOLYGON (((0 0, 10 0, 0 10, 0 0)))")) wk_format(new_wk_wkt("POINT ENTPY")) wk_handle( wkt("MULTIPOLYGON (((0 0, 10 0, 0 10, 0 0)))"), wkt_format_handler() ) } wk/man/wk_identity.Rd0000644000176200001440000000173514106220314014266 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/filter.R \name{wk_identity} \alias{wk_identity} \alias{wk_identity_filter} \alias{wk_restore} \alias{wk_restore.default} \title{Copy a geometry vector} \usage{ wk_identity(handleable, ...) wk_identity_filter(handler) wk_restore(handleable, result, ...) \method{wk_restore}{default}(handleable, result, ...) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} \item{handler}{A \link[=wk_handle]{wk_handler} object.} \item{result}{The result of a filter operation intended to be a transformation.} } \value{ A copy of \code{handleable}. } \description{ Copy a geometry vector } \examples{ wk_identity(wkt("POINT (1 2)")) } wk/man/wk_proj_crs_view.Rd0000644000176200001440000000124314510374414015314 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{wk_proj_crs_view} \alias{wk_proj_crs_view} \alias{wk_proj_crs_json} \title{Common CRS Representations} \format{ An object of class \code{data.frame} with 13387 rows and 7 columns. An object of class \code{data.frame} with 13387 rows and 3 columns. } \usage{ wk_proj_crs_view wk_proj_crs_json } \description{ These fixtures are calculated from PROJ version 9.1.0 and the database built from its source. They are used internally to transform and inspect coordinate reference systems. } \examples{ head(wk_proj_crs_view) colnames(wk_proj_crs_json) } \keyword{datasets} wk/man/grd_snap_next.Rd0000644000176200001440000000153614315177334014605 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd-subset.R \name{grd_snap_next} \alias{grd_snap_next} \alias{grd_snap_previous} \title{Index snap functions} \usage{ grd_snap_next(x) grd_snap_previous(x) } \arguments{ \item{x}{A vector of rescaled but non-integer indices} } \value{ A vector of integer indices } \description{ These functions can be used in \code{\link[=grd_cell]{grd_cell()}} and \code{\link[=grd_cell_range]{grd_cell_range()}}. These functions differ in the way they round 0.5: \code{\link[=grd_snap_next]{grd_snap_next()}} always rounds up and \code{\link[=grd_snap_previous]{grd_snap_previous()}} always rounds down. You can also use \code{\link[=floor]{floor()}} and \code{\link[=ceiling]{ceiling()}} as index snap functions. } \examples{ grd_snap_next(seq(0, 2, 0.25)) grd_snap_previous(seq(0, 2, 0.25)) } wk/man/new_wk_xy.Rd0000644000176200001440000000155114106220314013742 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xyzm.R \name{new_wk_xy} \alias{new_wk_xy} \alias{new_wk_xyz} \alias{new_wk_xym} \alias{new_wk_xyzm} \alias{validate_wk_xy} \alias{validate_wk_xyz} \alias{validate_wk_xym} \alias{validate_wk_xyzm} \title{S3 details for xy objects} \usage{ new_wk_xy(x = list(x = double(), y = double()), crs = NULL) new_wk_xyz(x = list(x = double(), y = double(), z = double()), crs = NULL) new_wk_xym(x = list(x = double(), y = double(), m = double()), crs = NULL) new_wk_xyzm( x = list(x = double(), y = double(), z = double(), m = double()), crs = NULL ) validate_wk_xy(x) validate_wk_xyz(x) validate_wk_xym(x) validate_wk_xyzm(x) } \arguments{ \item{x}{A \code{\link[=xy]{xy()}} object.} \item{crs}{A value to be propagated as the CRS for this vector.} } \description{ S3 details for xy objects } wk/man/wk_set_z.Rd0000644000176200001440000000272714106220314013563 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/set.R \name{wk_set_z} \alias{wk_set_z} \alias{wk_set_m} \alias{wk_drop_z} \alias{wk_drop_m} \alias{wk_trans_set} \title{Set coordinate values} \usage{ wk_set_z(handleable, z, ...) wk_set_m(handleable, m, ...) wk_drop_z(handleable, ...) wk_drop_m(handleable, ...) wk_trans_set(value, use_z = NA, use_m = NA) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{z, m}{A vector of Z or M values applied feature-wise and recycled along \code{handleable}. Use \code{NA} to keep the existing value of a given feature.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} \item{value}{An \code{\link[=xy]{xy()}}, \code{\link[=xyz]{xyz()}}, \code{\link[=xym]{xym()}}, or \code{\link[=xyzm]{xyzm()}} of coordinates used to replace values in the input. Use \code{NA} to keep the existing value.} \item{use_z, use_m}{Used to declare the output type. Use \code{TRUE} to ensure the output has that dimension, \code{FALSE} to ensure it does not, and \code{NA} to leave the dimension unchanged.} } \description{ Set coordinate values } \examples{ wk_set_z(wkt("POINT (0 1)"), 2) wk_set_m(wkt("POINT (0 1)"), 2) wk_drop_z(wkt("POINT ZM (0 1 2 3)")) wk_drop_m(wkt("POINT ZM (0 1 2 3)")) } wk/man/handle_wkt_without_vector_size.Rd0000644000176200001440000000136214160220603020250 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/handle-wkt.R \name{handle_wkt_without_vector_size} \alias{handle_wkt_without_vector_size} \title{Test handlers for handling of unknown size vectors} \usage{ handle_wkt_without_vector_size(handleable, handler) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{handler}{A \link[=wk_handle]{wk_handler} object.} } \description{ Test handlers for handling of unknown size vectors } \examples{ handle_wkt_without_vector_size(wkt(), wk_vector_meta_handler()) } wk/man/grd_tile.Rd0000644000176200001440000000362614315177334013545 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd-tile.R \name{grd_tile} \alias{grd_tile} \alias{grd_tile.wk_grd_rct} \alias{grd_tile.wk_grd_xy} \title{Extract normalized grid tiles} \usage{ grd_tile(grid, level, i, j = NULL) \method{grd_tile}{wk_grd_rct}(grid, level, i, j = NULL) \method{grd_tile}{wk_grd_xy}(grid, level, i, j = NULL) } \arguments{ \item{grid}{A \code{\link[=grd_xy]{grd_xy()}}, \code{\link[=grd_rct]{grd_rct()}}, or other object implementing \verb{grd_*()} methods.} \item{level}{An integer describing the overview level. This is related to the \code{step} value by a power of 2 (i.e., a level of \code{1} indicates a step of \code{2}, a level of \code{2} indicates a step of \code{4}, etc.).} \item{i, j}{1-based index values. \code{i} indices correspond to decreasing \code{y} values; \code{j} indices correspond to increasing \code{x} values. Values outside the range \code{1:nrow|ncol(data)} will be censored to \code{NA} including 0 and negative values.} } \value{ A \code{\link[=grd_subset]{grd_subset()}}ed version } \description{ Unlike \code{\link[=grd_tile_template]{grd_tile_template()}}, which returns a \code{\link[=grd]{grd()}} whose elements are the boundaries of the specified tiles with no data attached, \code{\link[=grd_tile]{grd_tile()}} returns the actual tile with the data. } \examples{ grid <- grd_rct(volcano) plot(grd_tile(grid, 4, 1, 1)) plot(grd_tile(grid, 3, 1, 1), add = TRUE) plot(grd_tile(grid, 3, 1, 2), add = TRUE) plot(grd_tile(grid, 3, 2, 1), add = TRUE) plot(grd_tile(grid, 3, 2, 2), add = TRUE) grid <- as_grd_xy(grd_tile(grid, 4, 1, 1)) plot(grid, add = TRUE, pch = ".") plot(grd_tile(grid, 3, 1, 1), add = TRUE, col = "green", pch = ".") plot(grd_tile(grid, 3, 1, 2), add = TRUE, col = "red", pch = ".") plot(grd_tile(grid, 3, 2, 1), add = TRUE, col = "blue", pch = ".") plot(grd_tile(grid, 3, 2, 2), add = TRUE, col = "magenta", pch = ".") } wk/man/wk_flatten.Rd0000644000176200001440000000235614106220314014072 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/flatten.R \name{wk_flatten} \alias{wk_flatten} \alias{wk_flatten_filter} \title{Extract simple geometries} \usage{ wk_flatten(handleable, ..., max_depth = 1) wk_flatten_filter(handler, max_depth = 1L, add_details = FALSE) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} \item{max_depth}{The maximum (outer) depth to remove.} \item{handler}{A \link[=wk_handle]{wk_handler} object.} \item{add_details}{Use \code{TRUE} to add a "wk_details" attribute, which contains columns \code{feature_id}, \code{part_id}, and \code{ring_id}.} } \value{ \code{handleable} transformed such that collections have been expanded and only simple geometries (point, linestring, polygon) remain. } \description{ Extract simple geometries } \examples{ wk_flatten(wkt("MULTIPOINT (1 1, 2 2, 3 3)")) wk_flatten( wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1))))"), max_depth = 2 ) } wk/man/deprecated.Rd0000644000176200001440000000202614163201750014034 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/deprecated.R \name{wkb_translate_wkt} \alias{wkb_translate_wkt} \alias{wkb_translate_wkb} \alias{wkt_translate_wkt} \alias{wkt_translate_wkb} \title{Deprecated functions} \usage{ wkb_translate_wkt(wkb, ..., precision = 16, trim = TRUE) wkb_translate_wkb(wkb, ..., endian = NA_integer_) wkt_translate_wkt(wkt, ..., precision = 16, trim = TRUE) wkt_translate_wkb(wkt, ..., endian = NA_integer_) } \arguments{ \item{wkb}{A \code{list()} of \code{\link[=raw]{raw()}} vectors, such as that returned by \code{sf::st_as_binary()}.} \item{...}{Used to keep backward compatibility with previous versions of these functions.} \item{precision}{The rounding precision to use when writing (number of decimal places).} \item{trim}{Trim unnecessary zeroes in the output?} \item{endian}{Force the endian of the resulting WKB.} \item{wkt}{A character vector containing well-known text.} } \description{ These functions are deprecated and will be removed in a future version. } wk/man/grd_extract.Rd0000644000176200001440000000237514315177334014262 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd-extract.R \name{grd_extract} \alias{grd_extract} \alias{grd_extract_nearest} \alias{grd_data_extract} \title{Extract values from a grid} \usage{ grd_extract(grid, i = NULL, j = NULL) grd_extract_nearest(grid, point, out_of_bounds = c("censor", "squish")) grd_data_extract(grid_data, i = NULL, j = NULL) } \arguments{ \item{grid}{A \code{\link[=grd_xy]{grd_xy()}}, \code{\link[=grd_rct]{grd_rct()}}, or other object implementing \verb{grd_*()} methods.} \item{i, j}{Index values as in \code{\link[=grd_subset]{grd_subset()}} except recycled to a common size.} \item{point}{A \link[=wk_handle]{handleable} of points.} \item{out_of_bounds}{One of 'keep', 'censor', 'discard', or 'squish'} \item{grid_data}{The \code{data} member of a \code{\link[=grd]{grd()}}. This is typically an array but can also be an S3 object with an array-like subset method. The \link[grDevices:as.raster]{native raster} is special-cased as its subset method requires non-standard handling.} } \value{ A matrix or vector with two fewer dimensions than the input. } \description{ Unlike \code{\link[=grd_subset]{grd_subset()}}, which subsets like a matrix, \code{\link[=grd_extract]{grd_extract()}} returns values. } wk/man/new_wk_wkb.Rd0000644000176200001440000000112014305515351014066 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wkb.R \name{new_wk_wkb} \alias{new_wk_wkb} \alias{validate_wk_wkb} \alias{is_wk_wkb} \title{S3 Details for wk_wkb} \usage{ new_wk_wkb(x = list(), crs = NULL, geodesic = NULL) validate_wk_wkb(x) is_wk_wkb(x) } \arguments{ \item{x}{A (possibly) \code{\link[=wkb]{wkb()}} vector} \item{crs}{A value to be propagated as the CRS for this vector.} \item{geodesic}{\code{TRUE} if edges must be interpolated as geodesics when coordinates are spherical, \code{FALSE} otherwise.} } \description{ S3 Details for wk_wkb } wk/man/wk_translate.Rd0000644000176200001440000000143114155244415014437 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkg-sf.R, R/translate.R \name{wk_translate.sfc} \alias{wk_translate.sfc} \alias{wk_translate} \alias{wk_translate.default} \title{Translate geometry vectors} \usage{ \method{wk_translate}{sfc}(handleable, to, ...) wk_translate(handleable, to, ...) \method{wk_translate}{default}(handleable, to, ...) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{to}{A prototype object.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} } \description{ Translate geometry vectors } wk/man/wk_handle.Rd0000644000176200001440000000551614156165566013717 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/handle-crc.R, R/handle-rct.R, R/handle-sfc.R, % R/handle-wkb.R, R/handle-wkt.R, R/handle-xy.R, R/handler.R, R/pkg-sf.R \name{wk_handle.wk_crc} \alias{wk_handle.wk_crc} \alias{wk_handle.wk_rct} \alias{wk_handle.sfc} \alias{wk_handle.wk_wkb} \alias{wk_handle.wk_wkt} \alias{wk_handle.wk_xy} \alias{wk_handle} \alias{is_handleable} \alias{new_wk_handler} \alias{is_wk_handler} \alias{as_wk_handler} \alias{wk_handle.sfg} \alias{wk_handle.sf} \alias{wk_handle.bbox} \title{Read geometry vectors} \usage{ \method{wk_handle}{wk_crc}( handleable, handler, ..., n_segments = getOption("wk.crc_n_segments", NULL), resolution = getOption("wk.crc_resolution", NULL) ) \method{wk_handle}{wk_rct}(handleable, handler, ...) \method{wk_handle}{sfc}(handleable, handler, ...) \method{wk_handle}{wk_wkb}(handleable, handler, ...) \method{wk_handle}{wk_wkt}(handleable, handler, ...) \method{wk_handle}{wk_xy}(handleable, handler, ...) wk_handle(handleable, handler, ...) is_handleable(handleable) new_wk_handler(handler_ptr, subclass = character()) is_wk_handler(handler) as_wk_handler(handler, ...) \method{wk_handle}{sfg}(handleable, handler, ...) \method{wk_handle}{sf}(handleable, handler, ...) \method{wk_handle}{bbox}(handleable, handler, ...) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{handler}{A \link[=wk_handle]{wk_handler} object.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} \item{n_segments, resolution}{The number of segments to use when approximating a circle. The default uses \code{getOption("wk.crc_n_segments")} so that this value can be set for implicit conversions (e.g., \code{as_wkb()}). Alternatively, set the minimum distance between points on the circle (used to estimate \code{n_segments}). The default is obtained using \code{getOption("wk.crc_resolution")}.} \item{handler_ptr}{An external pointer to a newly created WK handler} \item{subclass}{The handler subclass} } \value{ A WK handler. } \description{ The handler is the basic building block of the wk package. In particular, the \code{\link[=wk_handle]{wk_handle()}} generic allows operations written as handlers to "just work" with many different input types. The wk package provides the \code{\link[=wk_void]{wk_void()}} handler, the \code{\link[=wk_format]{wk_format()}} handler, the \code{\link[=wk_debug]{wk_debug()}} handler, the \code{\link[=wk_problems]{wk_problems()}} handler, and \code{\link[=wk_writer]{wk_writer()}}s for \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, and \code{\link[sf:sfc]{sf::st_sfc()}}) vectors. } wk/man/wkb.Rd0000644000176200001440000000230014305515351012515 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wkb.R \name{wkb} \alias{wkb} \alias{parse_wkb} \alias{wk_platform_endian} \alias{as_wkb} \alias{as_wkb.default} \alias{as_wkb.character} \alias{as_wkb.wk_wkb} \alias{as_wkb.blob} \alias{as_wkb.WKB} \title{Mark lists of raw vectors as well-known binary} \usage{ wkb(x = list(), crs = wk_crs_auto(), geodesic = FALSE) parse_wkb(x, crs = wk_crs_auto(), geodesic = FALSE) wk_platform_endian() as_wkb(x, ...) \method{as_wkb}{default}(x, ...) \method{as_wkb}{character}(x, ..., crs = NULL, geodesic = FALSE) \method{as_wkb}{wk_wkb}(x, ...) \method{as_wkb}{blob}(x, ..., crs = NULL, geodesic = FALSE) \method{as_wkb}{WKB}(x, ..., crs = NULL, geodesic = FALSE) } \arguments{ \item{x}{A \code{\link[=list]{list()}} of \code{\link[=raw]{raw()}} vectors or \code{NULL}.} \item{crs}{A value to be propagated as the CRS for this vector.} \item{geodesic}{\code{TRUE} if edges must be interpolated as geodesics when coordinates are spherical, \code{FALSE} otherwise.} \item{...}{Unused} } \value{ A \code{\link[=new_wk_wkb]{new_wk_wkb()}} } \description{ Mark lists of raw vectors as well-known binary } \examples{ as_wkb("POINT (20 10)") } wk/man/crc_x.Rd0000644000176200001440000000072314321145376013043 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/crc.R \name{crc_x} \alias{crc_x} \alias{crc_y} \alias{crc_center} \alias{crc_r} \title{Circle accessors} \usage{ crc_x(x) crc_y(x) crc_center(x) crc_r(x) } \arguments{ \item{x}{A \code{\link[=crc]{crc()}} vector} } \value{ Components of the \code{\link[=crc]{crc()}} vector } \description{ Circle accessors } \examples{ x <- crc(1, 2, r = 3) crc_x(x) crc_y(x) crc_r(x) crc_center(x) } wk/man/rct.Rd0000644000176200001440000000153014163110540012517 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rct.R \name{rct} \alias{rct} \alias{as_rct} \alias{as_rct.wk_rct} \alias{as_rct.matrix} \alias{as_rct.data.frame} \title{2D rectangle vectors} \usage{ rct( xmin = double(), ymin = double(), xmax = double(), ymax = double(), crs = wk_crs_auto() ) as_rct(x, ...) \method{as_rct}{wk_rct}(x, ...) \method{as_rct}{matrix}(x, ..., crs = NULL) \method{as_rct}{data.frame}(x, ..., crs = NULL) } \arguments{ \item{xmin, ymin, xmax, ymax}{Rectangle bounds.} \item{crs}{A value to be propagated as the CRS for this vector.} \item{x}{An object to be converted to a \code{\link[=rct]{rct()}}.} \item{...}{Extra arguments passed to \code{as_rct()}.} } \value{ A vector along the recycled length of bounds. } \description{ 2D rectangle vectors } \examples{ rct(1, 2, 3, 4) } wk/man/wk_handle.data.frame.Rd0000644000176200001440000000272714305771446015715 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/class-data-frame.R, R/pkg-sf.R \name{wk_handle.data.frame} \alias{wk_handle.data.frame} \alias{wk_restore.data.frame} \alias{wk_restore.tbl_df} \alias{wk_translate.data.frame} \alias{wk_translate.tbl_df} \alias{wk_translate.sf} \alias{wk_restore.sf} \title{Use data.frame with wk} \usage{ \method{wk_handle}{data.frame}(handleable, handler, ...) \method{wk_restore}{data.frame}(handleable, result, ...) \method{wk_restore}{tbl_df}(handleable, result, ...) \method{wk_translate}{data.frame}(handleable, to, ...) \method{wk_translate}{tbl_df}(handleable, to, ...) \method{wk_translate}{sf}(handleable, to, ...) \method{wk_restore}{sf}(handleable, result, ...) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{handler}{A \link[=wk_handle]{wk_handler} object.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} \item{result}{The result of a filter operation intended to be a transformation.} \item{to}{A prototype object.} } \description{ Use data.frame with wk } \examples{ wk_handle(data.frame(a = wkt("POINT (0 1)")), wkb_writer()) wk_translate(wkt("POINT (0 1)"), data.frame(col_name = wkb())) wk_translate(data.frame(a = wkt("POINT (0 1)")), data.frame(wkb())) } wk/man/wk_meta.Rd0000644000176200001440000000572014510124347013371 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/meta.R \name{wk_meta} \alias{wk_meta} \alias{wk_meta.default} \alias{wk_vector_meta} \alias{wk_vector_meta.default} \alias{wk_meta_handler} \alias{wk_vector_meta_handler} \alias{wk_geometry_type_label} \alias{wk_geometry_type} \title{Extract feature-level meta} \usage{ wk_meta(handleable, ...) \method{wk_meta}{default}(handleable, ...) wk_vector_meta(handleable, ...) \method{wk_vector_meta}{default}(handleable, ...) wk_meta_handler() wk_vector_meta_handler() wk_geometry_type_label(geometry_type) wk_geometry_type(geometry_type_label) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} \item{geometry_type}{An integer code for the geometry type. These integers follow the WKB specification (e.g., 1 for point, 7 for geometrycollection).} \item{geometry_type_label}{A character vector of (lowercase) geometry type labels as would be found in WKT (e.g., point, geometrycollection).} } \value{ A data.frame with columns: \itemize{ \item \code{geometry_type}: An integer identifying the geometry type. A value of 0 indicates that the types of geometry in the vector are not known without parsing the entire vector. \item \code{size}: For points and linestrings, the number of coordinates; for polygons, the number of rings; for collections, the number of child geometries. A value of zero indicates an EMPTY geometry. A value of \code{NA} means this value is unknown without parsing the entire geometry. \item \code{has_z}: \code{TRUE} if coordinates contain a Z value. A value of \code{NA} means this value is unknown without parsing the entire vector. \item \code{has_m}: \code{TRUE} if coordinates contain an M value. A value of \code{NA} means this value is unknown without parsing the entire vector. \item \code{srid}: An integer identifying a CRS or NA if this value was not provided. \item \code{precision}: A grid size or 0.0 if a grid size was not provided. Note that coordinate values may not have been rounded; the grid size only refers to the level of detail with which they should be interpreted. \item \code{is_empty}: \code{TRUE} if there is at least one non-empty coordinate. For the purposes of this value, a non-empty coordinate is one that contains at least one value that is not \code{NA} or \code{NaN}. } } \description{ These functions return the non-coordinate information of a geometry and/or vector. They do not parse an entire geometry/vector and are intended to be very fast even for large vectors. } \examples{ wk_vector_meta(as_wkt("LINESTRING (0 0, 1 1)")) wk_meta(as_wkt("LINESTRING (0 0, 1 1)")) wk_meta(as_wkb("LINESTRING (0 0, 1 1)")) wk_geometry_type_label(1:7) wk_geometry_type(c("point", "geometrycollection")) } wk/man/wk_handle_slice.Rd0000644000176200001440000000254514145575672015076 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/class-data-frame.R, R/handle-slice.R \name{wk_handle_slice.data.frame} \alias{wk_handle_slice.data.frame} \alias{wk_handle_slice} \alias{wk_handle_slice.default} \title{Handle specific regions of objects} \usage{ \method{wk_handle_slice}{data.frame}(handleable, handler, from = NULL, to = NULL, ...) wk_handle_slice( handleable, handler = wk_writer(handleable), from = NULL, to = NULL, ... ) \method{wk_handle_slice}{default}( handleable, handler = wk_writer(handleable), from = NULL, to = NULL, ... ) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{handler}{A \link[=wk_handle]{wk_handler} object.} \item{from}{1-based index of the feature to start from} \item{to}{1-based index of the feature to end at} \item{...}{Passed to the \code{\link[=wk_handle]{wk_handle()}} method.} } \value{ A subset of \code{handleable} } \description{ Handle specific regions of objects } \examples{ wk_handle_slice(xy(1:5, 1:5), wkt_writer(), from = 3, to = 5) wk_handle_slice( data.frame(let = letters[1:5], geom = xy(1:5, 1:5)), wkt_writer(), from = 3, to = 5 ) } wk/man/grd_tile_template.Rd0000644000176200001440000000233114315177334015430 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd-tile.R \name{grd_tile_template} \alias{grd_tile_template} \alias{grd_tile_summary} \title{Compute overview grid tile} \usage{ grd_tile_template(grid, level) grd_tile_summary(grid, levels = NULL) } \arguments{ \item{grid}{A \code{\link[=grd_xy]{grd_xy()}}, \code{\link[=grd_rct]{grd_rct()}}, or other object implementing \verb{grd_*()} methods.} \item{level}{An integer describing the overview level. This is related to the \code{step} value by a power of 2 (i.e., a level of \code{1} indicates a step of \code{2}, a level of \code{2} indicates a step of \code{4}, etc.).} \item{levels}{A vector of \code{level} values or \code{NULL} to use a sequence from 0 to the level that would result in a 1 x 1 grid.} } \value{ A \code{\link[=grd]{grd()}} } \description{ A useful workflow for raster data in a memory bounded environment is to chunk a grid into sections or tiles. These functions compute tiles suitable for such processing. Use \code{\link[=grd_tile_summary]{grd_tile_summary()}} to generate statistics for \code{level} values to choose for your application. } \examples{ grid <- grd_rct(volcano) grd_tile_summary(grid) grd_tile_template(grid, 3) } wk/man/grd_cell.Rd0000644000176200001440000000523114315177334013521 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd-subset.R \name{grd_cell} \alias{grd_cell} \alias{grd_cell_range} \alias{grd_cell_rct} \alias{grd_cell_rct.wk_grd_rct} \alias{grd_cell_rct.wk_grd_xy} \alias{grd_cell_xy} \alias{grd_cell_xy.wk_grd_rct} \alias{grd_cell_xy.wk_grd_xy} \title{Grid cell operators} \usage{ grd_cell(grid, point, ..., snap = grd_snap_next) grd_cell_range( grid, bbox = wk_bbox(grid), ..., step = 1L, snap = grd_snap_next ) grd_cell_rct(grid, i, j = NULL, ...) \method{grd_cell_rct}{wk_grd_rct}(grid, i, j = NULL, ..., out_of_bounds = "keep") \method{grd_cell_rct}{wk_grd_xy}(grid, i, j = NULL, ..., out_of_bounds = "keep") grd_cell_xy(grid, i, j = NULL, ...) \method{grd_cell_xy}{wk_grd_rct}(grid, i, j = NULL, ..., out_of_bounds = "keep") \method{grd_cell_xy}{wk_grd_xy}(grid, i, j = NULL, ..., out_of_bounds = "keep") } \arguments{ \item{grid}{A \code{\link[=grd_xy]{grd_xy()}}, \code{\link[=grd_rct]{grd_rct()}}, or other object implementing \verb{grd_*()} methods.} \item{point}{A \link[=wk_handle]{handleable} of points.} \item{...}{Unused} \item{snap}{A function that transforms real-valued indices to integer indices (e.g., \code{\link[=floor]{floor()}}, \code{\link[=ceiling]{ceiling()}}, or \code{\link[=round]{round()}}). For \code{\link[=grd_cell_range]{grd_cell_range()}}, a \code{list()} with exactly two elements to be called for the minimum and maximum index values, respectively.} \item{bbox}{An \code{\link[=rct]{rct()}} object.} \item{step}{The difference between adjascent indices in the output} \item{i, j}{1-based index values. \code{i} indices correspond to decreasing \code{y} values; \code{j} indices correspond to increasing \code{x} values. Values outside the range \code{1:nrow|ncol(data)} will be censored to \code{NA} including 0 and negative values.} \item{out_of_bounds}{One of 'keep', 'censor', 'discard', or 'squish'} } \value{ \itemize{ \item \code{grd_cell()}: returns a \code{list(i, j)} of index values corresponding to the input points and adjusted according to \code{snap}. Index values will be outside \code{dim(grid)} for points outside \code{wk_bbox(grid)} including negative values. \item \code{grd_cell_range()} returns a slice describing the range of indices in the \code{i} and \code{j} directions. \item \code{grd_cell_rct()} returns a \code{\link[=rct]{rct()}} of the cell extent at \verb{i, j}. \item \code{grd_cell_xy()} returns a \code{\link[=xy]{xy()}} of the cell center at \verb{i, j}. } } \description{ Grid cell operators } \examples{ grid <- grd(nx = 3, ny = 2) grd_cell(grid, xy(0.5, 0.5)) grd_cell_range(grid, grid$bbox) grd_cell_rct(grid, 1, 1) grd_cell_xy(grid, 1, 1) } wk/man/new_wk_wkt.Rd0000644000176200001440000000112514305515351014115 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wkt.R \name{new_wk_wkt} \alias{new_wk_wkt} \alias{is_wk_wkt} \alias{validate_wk_wkt} \title{S3 Details for wk_wkt} \usage{ new_wk_wkt(x = character(), crs = NULL, geodesic = NULL) is_wk_wkt(x) validate_wk_wkt(x) } \arguments{ \item{x}{A (possibly) \code{\link[=wkt]{wkt()}} vector} \item{crs}{A value to be propagated as the CRS for this vector.} \item{geodesic}{\code{TRUE} if edges must be interpolated as geodesics when coordinates are spherical, \code{FALSE} otherwise.} } \description{ S3 Details for wk_wkt } wk/man/grd_summary.Rd0000644000176200001440000000116014315177334014274 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/grd.R \name{grd_summary} \alias{grd_summary} \title{Grid information} \usage{ grd_summary(grid) } \arguments{ \item{grid}{A \code{\link[=grd_xy]{grd_xy()}}, \code{\link[=grd_rct]{grd_rct()}}, or other object implementing \verb{grd_*()} methods.} } \value{ \itemize{ \item \code{grd_summary()} returns a \code{list()} with components \code{xmin}, \code{ymin}, \code{xmax}, \code{ymax}, \code{nx}, \code{ny}, \code{dx}, \code{dy}, \code{width}, and \code{height}. } } \description{ Grid information } \examples{ grd_summary(grd(nx = 3, ny = 2)) } wk/man/wk_plot.Rd0000644000176200001440000000435614163210157013424 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/plot.R \name{wk_plot} \alias{wk_plot} \alias{wk_plot.default} \alias{plot.wk_wkt} \alias{plot.wk_wkb} \alias{plot.wk_xy} \alias{plot.wk_rct} \alias{plot.wk_crc} \title{Plot well-known geometry vectors} \usage{ wk_plot( handleable, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE ) \method{wk_plot}{default}( handleable, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE ) \method{plot}{wk_wkt}( x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE ) \method{plot}{wk_wkb}( x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE ) \method{plot}{wk_xy}(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE) \method{plot}{wk_rct}(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE) \method{plot}{wk_crc}(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[=wkb]{wkb()}}, \code{\link[=wkt]{wkt()}}, \code{\link[=xy]{xy()}}, \code{\link[=rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[=wk_handle]{wk_handle()}} is defined.} \item{...}{Passed to plotting functions for features: \code{\link[graphics:points]{graphics::points()}} for point and multipoint geometries, \code{\link[graphics:lines]{graphics::lines()}} for linestring and multilinestring geometries, and \code{\link[graphics:polypath]{graphics::polypath()}} for polygon and multipolygon geometries.} \item{asp, xlab, ylab}{Passed to \code{\link[graphics:plot.default]{graphics::plot()}}} \item{bbox}{The limits of the plot as a \code{\link[=rct]{rct()}} or compatible object} \item{rule}{The rule to use for filling polygons (see \code{\link[graphics:polypath]{graphics::polypath()}})} \item{add}{Should a new plot be created, or should \code{handleable} be added to the existing plot?} \item{x}{A \code{\link[=wkb]{wkb()}} or \code{\link[=wkt]{wkt()}}} } \value{ The input, invisibly. } \description{ Plot well-known geometry vectors } \examples{ plot(as_wkt("LINESTRING (0 0, 1 1)")) plot(as_wkb("LINESTRING (0 0, 1 1)")) } wk/DESCRIPTION0000644000176200001440000000336014531546132012407 0ustar liggesusersPackage: wk Title: Lightweight Well-Known Geometry Parsing Version: 0.9.1 Authors@R: c( person(given = "Dewey", family = "Dunnington", role = c("aut", "cre"), email = "dewey@fishandwhistle.net", comment = c(ORCID = "0000-0002-9415-4582")), person(given = "Edzer", family = "Pebesma", role = c("aut"), email = "edzer.pebesma@uni-muenster.de", comment = c(ORCID = "0000-0001-8049-7069")), person(given = "Anthony", family = "North", email = "anthony.jl.north@gmail.com", role = c("ctb")) ) Maintainer: Dewey Dunnington Description: Provides a minimal R and C++ API for parsing well-known binary and well-known text representation of geometries to and from R-native formats. Well-known binary is compact and fast to parse; well-known text is human-readable and is useful for writing tests. These formats are useful in R only if the information they contain can be accessed in R, for which high-performance functions are provided here. License: MIT + file LICENSE Encoding: UTF-8 RoxygenNote: 7.2.3 Suggests: testthat (>= 3.0.0), vctrs (>= 0.3.0), sf, tibble, readr URL: https://paleolimbot.github.io/wk/, https://github.com/paleolimbot/wk BugReports: https://github.com/paleolimbot/wk/issues Config/testthat/edition: 3 Depends: R (>= 2.10) LazyData: true NeedsCompilation: yes Packaged: 2023-11-29 02:12:31 UTC; deweydunnington Author: Dewey Dunnington [aut, cre] (), Edzer Pebesma [aut] (), Anthony North [ctb] Repository: CRAN Date/Publication: 2023-11-29 05:30:02 UTC wk/tests/0000755000176200001440000000000014106220314012026 5ustar liggesuserswk/tests/testthat/0000755000176200001440000000000014531546132013701 5ustar liggesuserswk/tests/testthat/test-bbox.R0000644000176200001440000000514114163110540015723 0ustar liggesusers test_that("wk_bbox() works", { expect_identical( wk_bbox(wkt("LINESTRING (1 2, 3 4)")), rct(1, 2, 3, 4) ) expect_identical( wk_bbox(wkt(crs = NULL)), rct(Inf, Inf, -Inf, -Inf) ) expect_identical( wk_bbox(wkt(crs = 1234)), rct(Inf, Inf, -Inf, -Inf, crs = 1234) ) }) test_that("wk_bbox() works when vector has cached bbox", { skip_if_not_installed("sf") sf_linestring <- sf::st_sfc(sf::st_linestring(rbind(c(1, 2), c(3, 4)))) expect_identical( wk_bbox(sf_linestring), rct(1, 2, 3, 4, crs = sf::NA_crs_) ) }) test_that("wk_bbox() works when geometry has cached bbox", { expect_identical(wk_bbox(xy(1:3, 2:4)), rct(1, 2, 3, 4)) expect_identical(wk_bbox(crc(2, 3, r = 1)), rct(1, 2, 3, 4)) expect_identical(wk_bbox(rct(1, 2, 3, 4)), rct(1, 2, 3, 4)) }) test_that("wk_envelope() works", { expect_identical( wk_envelope(wkt("LINESTRING (1 2, 3 4)")), rct(1, 2, 3, 4) ) expect_identical( wk_envelope(wkt(NA_character_)), rct(NA_real_, NA_real_, NA_real_, NA_real_) ) expect_identical( wk_envelope(wkt(crs = NULL)), rct(crs = NULL) ) expect_identical( wk_envelope(wkt(crs = 1234)), rct(crs = 1234) ) }) test_that("wk_envelope() works when geometry has cached bbox", { expect_identical( wk_handle(crc(2, 3, r = 1), wk_envelope_handler()), rct(1, 2, 3, 4) ) expect_identical( wk_handle(rct(1, 2, 3, 4), wk_envelope_handler()), rct(1, 2, 3, 4) ) }) test_that("wk_envelope() works when vector size is unknown", { expect_identical( handle_wkt_without_vector_size( wkt("POINT (0 1)"), wk_envelope_handler() ), rct(0, 1, 0, 1) ) expect_identical( handle_wkt_without_vector_size( wkt(rep("POINT (0 1)", 1025)), wk_envelope_handler() ), rep(rct(0, 1, 0, 1), 1025) ) }) test_that("wk_envelope() optimization works for xy()", { expect_identical( wk_envelope(xy(1, 2, crs = 1234)), rct(1, 2, 1, 2, crs = 1234) ) }) test_that("wk_envelope() optimization works for rct()", { expect_identical( wk_envelope(rct(1, 2, 3, 4, crs = 1234)), rct(1, 2, 3, 4, crs = 1234) ) }) test_that("wk_envelope() optimization works for crc()", { expect_identical( wk_envelope(crc(2, 3, r = 1, crs = 1234)), rct(1, 2, 3, 4, crs = 1234) ) }) test_that("wk_bbox() and wk_envelope() fail for geodesic objects", { expect_error( wk_bbox(wkt("LINESTRING (0 1)", geodesic = TRUE)), "Can't compute bbox for geodesic object" ) expect_error( wk_envelope(wkt("LINESTRING (0 1)", geodesic = TRUE)), "Can't compute envelope for geodesic object" ) }) wk/tests/testthat/test-vertex-filter.R0000644000176200001440000000760714515070354017613 0ustar liggesusers test_that("wk_vertices() works", { expect_identical( wk_vertices(wkt(c("POINT (0 0)", "POINT (1 1)", NA))), wkt(c("POINT (0 0)", "POINT (1 1)")) ) expect_identical( wk_vertices(wkt(c("LINESTRING (0 0, 1 1)", NA))), wkt(c("POINT (0 0)", "POINT (1 1)")) ) expect_error(wk_vertices(new_wk_wkt("POINT ENTPY")), "ENTPY") # we need this one to trigger a realloc on the details list xy_copy <- wk_handle( as_wkt(xy(1:1025, 1)), wk_vertex_filter(xy_writer(), add_details = TRUE) ) expect_identical( attr(xy_copy, "wk_details"), list(feature_id = 1:1025, part_id = 1:1025, ring_id = rep(0L, 1025)) ) attr(xy_copy, "wk_details") <- NULL expect_identical(xy_copy, xy(1:1025, 1)) }) test_that("wk_vertices() works for data.frame", { expect_identical( wk_vertices(data.frame(geom = wkt(c("POINT (0 0)", "POINT (1 1)")))), data.frame(geom = wkt(c("POINT (0 0)", "POINT (1 1)"))) ) }) test_that("wk_coords() works", { # point expect_identical( wk_coords(wkt("POINT (30 10)")), data.frame( feature_id = 1L, part_id = 1L, ring_id = 0L, x = 30, y = 10 ) ) # point zm expect_identical( wk_coords(wkt("POINT ZM (30 10 1 2)")), data.frame( feature_id = 1L, part_id = 1L, ring_id = 0L, x = 30, y = 10, z = 1, m = 2 ) ) # linestring expect_identical( wk_coords(wkt("LINESTRING (30 10, 20 11)")), data.frame( feature_id = c(1L, 1L), part_id = c(1L, 1L), ring_id = c(0L, 0L), x = c(30, 20), y = c(10, 11) ) ) # polygon expect_identical( wk_coords(wkt("POLYGON ((30 10, 20 11, 0 0, 30 10))")), data.frame( feature_id = c(1L, 1L, 1L, 1L), part_id = c(1L, 1L, 1L, 1L), ring_id = c(1L, 1L, 1L, 1L), x = c(30, 20, 0, 30), y = c(10, 11, 0, 10) ) ) # multipoint expect_identical( wk_coords(wkt("MULTIPOINT ((30 10), (20 11))")), data.frame( feature_id = c(1L, 1L), part_id = c(2L, 3L), ring_id = c(0L, 0L), x = c(30, 20), y = c(10, 11) ) ) # collection # point expect_identical( wk_coords(wkt("GEOMETRYCOLLECTION (POINT (30 10))")), data.frame( feature_id = 1L, part_id = 2L, ring_id = 0L, x = 30, y = 10 ) ) }) test_that("wk_vertices() communicates correct size and type", { expect_identical( wk_handle(wkt("POINT (0 0)"), wk_vertex_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = NA_real_, has_z = NA, has_m = NA) ) skip_if_not_installed("sf") # need sf because these objects carry vector-level types expect_identical( wk_handle(sf::st_as_sfc("POINT (0 0)"), wk_vertex_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = 1, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("MULTIPOINT EMPTY"), wk_vertex_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("MULTILINESTRING EMPTY"), wk_vertex_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("MULTIPOLYGON EMPTY"), wk_vertex_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("GEOMETRYCOLLECTION EMPTY"), wk_vertex_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) }) test_that("optimized wk_coords() for xy() works", { xys <- xy(1:5, 6:10) expect_identical(wk_coords(xys), wk_coords.default(xys)) xys_with_empty_and_null <- c(xys, xy(NA, NA), xy(NaN, NaN)) expect_identical( wk_coords(xys_with_empty_and_null), wk_coords.default(xys_with_empty_and_null) ) }) wk/tests/testthat/test-handle-wkb.R0000644000176200001440000004125714106220314017013 0ustar liggesusers test_that("wkb_translate_wkt() works with missing values", { point <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical(wkb_translate_wkt(list(NULL)), NA_character_) expect_identical(wkb_translate_wkt(list(point, NULL)), c("POINT (30 10)", NA)) expect_identical(wkb_translate_wkt(list(NULL, point)), c(NA, "POINT (30 10)")) }) test_that("wkb_translate_wkt() works with multiple endians", { point_be <- as.raw(c(0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)) point_le <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical(wkb_translate_wkt(list(point_be)), "POINT (30 10)") expect_identical(wkb_translate_wkt(list(point_le)), "POINT (30 10)") expect_error( wkb_translate_wkt(list(point_le[1:5])), "Unexpected end of buffer" ) }) test_that("wkb_translate_wkt() works with ND points and SRID", { point_xy <- as.raw(c(0x01, # 0x01, 0x00, 0x00, 0x00, # type 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, # x 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) # y point_z <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x80, # type 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, # x 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, # y 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40)) # z point_m <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x40, # type 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, # x 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, # y 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40)) # m point_zm <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0xc0, # type 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, # x 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, # y 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, # z 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f)) # m point_s <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x20, # type 0xc7, 0x00, 0x00, 0x00, # srid 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, # x 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) # y point_zms <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0xe0, 0xe6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x40)) expect_identical(wkb_translate_wkt(list(point_xy)), "POINT (30 10)") expect_identical(wkb_translate_wkt(list(point_z)), "POINT Z (30 10 2)") expect_identical(wkb_translate_wkt(list(point_m)), "POINT M (30 10 2)") expect_identical(wkb_translate_wkt(list(point_zm)), "POINT ZM (30 10 2 1)") expect_identical(wkb_translate_wkt(list(point_s)), "SRID=199;POINT (30 10)") expect_identical(wkb_translate_wkt(list(point_zms)), "SRID=4326;POINT ZM (30 10 12 14)") }) test_that("wkb_translate_wkt() works simple geometries", { # POINT (30 10) point <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) # LINESTRING (30 10, 12 42) linestring <- as.raw(c(0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40)) # POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30)) polygon <- as.raw(c(0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40)) expect_identical(wkb_translate_wkt(list(point)), "POINT (30 10)") expect_identical( wkb_translate_wkt(list(linestring)), "LINESTRING (30 10, 12 42)" ) expect_identical( wkb_translate_wkt(list(polygon)), "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))" ) }) test_that("wkb_translate_wkt() works with multi geometries", { # MULTIPOINT ((10 40), (40 30), (20 20), (30 10)) multipoint <- as.raw(c(0x01, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) # technically these could exist without the redundant parentheses, but # that's pretty inconsistent with how the other multi* geoms are # rendered expect_identical( wkb_translate_wkt(list(multipoint)), "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))" ) }) test_that("wkb_translate_wkt() works with nested collections", { wkt <- "GEOMETRYCOLLECTION ( POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)), GEOMETRYCOLLECTION ( POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)) ), GEOMETRYCOLLECTION EMPTY, POINT (30 10) )" collection <- as.raw(c(0x01, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x01, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x01, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical( wkb_translate_wkt(list(collection)), paste0( "GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10 40), ", "POLYGON ((40 40, 20 45, 45 30, 40 40)), GEOMETRYCOLLECTION ", "(POINT (40 10), LINESTRING (10 10, 20 20, 10 40), ", "POLYGON ((40 40, 20 45, 45 30, 40 40))), ", "GEOMETRYCOLLECTION EMPTY, POINT (30 10))" ) ) }) test_that("wkb_translate_wkb() works with missing values", { point <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical(wkb_translate_wkb(list(NULL)), list(NULL)) expect_identical(wkb_translate_wkb(list(point, NULL), endian = 1), list(point, NULL)) expect_identical(wkb_translate_wkb(list(NULL, point), endian = 1), list(NULL, point)) }) test_that("wkb_translate_wkt() respects trim and rounding options", { # POINT (30 10) point <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) # POINT (30.3333333 10.3333333) point_repeating <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3e, 0x40, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x24, 0x40)) expect_identical( wkb_translate_wkt(list(point), precision = 5, trim = TRUE), "POINT (30 10)" ) expect_identical( wkb_translate_wkt(list(point), precision = 5, trim = FALSE), "POINT (30.00000 10.00000)" ) expect_identical( wkb_translate_wkt(list(point_repeating), precision = 5, trim = TRUE), "POINT (30.333 10.333)" ) expect_identical( wkb_translate_wkt(list(point_repeating), precision = 5, trim = FALSE), "POINT (30.33333 10.33333)" ) }) test_that("wkb writer only includes SRID for top-level geometry", { expect_length(wkt_translate_wkb("SRID=4326;MULTIPOINT (0 0, 1 1)")[[1]], 55) }) test_that("wkb--wkb translation works for nested collections", { collection <- as.raw(c(0x01, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x01, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x01, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical(wkb_translate_wkb(list(collection), endian = 1), list(collection)) }) test_that("wkb_translate_* doesn't segfault on other inputs", { expect_error(wkb_translate_wkt("POINT (30 10)"), "can only be applied to a 'list'") }) test_that("wkb reader can read 1000-3000 style WKB input", { # note that this is what sf outputs for Z, M, and ZM points even when EWKB # is TRUE wkb_xyz <- as.raw(c(0x01, 0xe9, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40)) wkb_xym <- as.raw(c(0x01, 0xd1, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40)) wkb_xyzm <- as.raw(c(0x01, 0xb9, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40)) expect_identical(wkb_translate_wkt(list(wkb_xyz)), "POINT Z (1 2 3)") expect_identical(wkb_translate_wkt(list(wkb_xym)), "POINT M (1 2 3)") expect_identical(wkb_translate_wkt(list(wkb_xyzm)), "POINT ZM (1 2 3 4)") }) wk/tests/testthat/test-grd-plot.R0000644000176200001440000000614314315177334016541 0ustar liggesusers test_that("grd_xy() plot method works", { grid <- grd(nx = 3, ny = 2, type = "centers") expect_identical( plot( grid, col = rgb(0:5, 0:5, 0:5, maxColorValue = 5), pch = 16, cex = 5 ), grid ) }) test_that("grd_rct() plot method works", { grid_empty <- grd(nx = 0, ny = 0) expect_identical(plot(grid_empty, bbox = rct(0, 0, 1, 1)), grid_empty) grid_spec <- grd(nx = 3, ny = 2) expect_identical(plot(grid_spec, border = NULL), grid_spec) grid_numeric <- grd_rct(matrix(0:5, nrow = 2, ncol = 3)) expect_identical(plot(grid_numeric), grid_numeric) grid_raster <- grd_rct(as.raster(grid_numeric$data / 10)) expect_identical(plot(grid_raster), grid_raster) grid_raster_rev_y <- grd_rct( as.raster(grid_numeric$data / 10), bbox = rct(0, 0, 3, -2) ) expect_identical(plot(grid_raster_rev_y), grid_raster_rev_y) grid_raster_rev_x <- grd_rct( as.raster(grid_numeric$data / 10), bbox = rct(0, 0, -3, 2) ) expect_identical(plot(grid_raster_rev_x), grid_raster_rev_x) # can aso check with PNG # col_native <- png::readPNG(system.file("img", "Rlogo.png", package="png"), native = T) col_native <- structure( c(-16777216L, -13421773L, -10066330L, -15066598L, -11711155L, -8355712L), .Dim = 2:3, class = "nativeRaster" ) grid_native <- grd_rct(col_native) expect_identical(plot(grid_native), grid_native) grid_native_rev_y <- grd_rct(col_native, rct(0, 0, 3, -2)) expect_identical(plot(grid_native_rev_y), grid_native_rev_y) grid_native_rev_x <- grd_rct(col_native, rct(0, 0, -3, 2)) expect_identical(plot(grid_native_rev_x), grid_native_rev_x) }) test_that("grd_rct() plot method skips plotting when not relevant", { grid_numeric <- grd_rct(matrix(0:5, nrow = 2, ncol = 3)) # so zoomed out that no pixels would be shown plot(rct(0, 0, 1e6, 1e6)) expect_identical(plot(grid_numeric, add = T), grid_numeric) # viewport area does not intersect grid plot(rct(10, 10, 11, 11)) expect_identical(plot(grid_numeric, add = T), grid_numeric) }) test_that("as.raster() works for grd_rct() objects", { grid_num <- grd_rct(matrix(1:6, nrow = 2, ncol = 3)) expect_identical( as.raster(grid_num), as.raster(matrix(0:5, nrow = 2, ncol = 3) / 5) ) grid_constant <- grd_rct(matrix(0, nrow = 2, ncol = 3)) expect_identical( as.raster(grid_constant), as.raster(matrix(0.5, nrow = 2, ncol = 3)) ) grid_na <- grd_rct(matrix(NA, nrow = 2, ncol = 3)) expect_identical( as.raster(grid_na), as.raster(matrix(NA, nrow = 2, ncol = 3)) ) grid_raster <- grd_rct(as.raster(matrix(0:5, nrow = 2, ncol = 3) / 5)) expect_identical( as.raster(grid_num), as.raster(matrix(0:5, nrow = 2, ncol = 3) / 5) ) grid_cols <- grd_rct(matrix("#1a1a1a", nrow = 2, ncol = 3)) expect_identical( as.raster(grid_cols), as.raster(matrix("#1a1a1a", nrow = 2, ncol = 3)) ) }) test_that("as.raster() errors for grd_rct() with unsupported type", { grid_wut <- grd_rct(array(NA_real_, dim = c(2, 3, 4))) expect_error( as.raster(grid_wut), "Can't convert non-numeric or non-matrix grid to raster image" ) }) wk/tests/testthat/test-pkg-vctrs.R0000644000176200001440000002610314163110540016712 0ustar liggesusers test_that("wk classes are vctrs", { expect_true(vctrs::vec_is(wkt())) expect_true(vctrs::vec_is(wkb())) expect_true(vctrs::vec_is(xy())) expect_true(vctrs::vec_is(xyz())) expect_true(vctrs::vec_is(xym())) expect_true(vctrs::vec_is(xyzm())) expect_true(vctrs::vec_is(rct())) expect_true(vctrs::vec_is(crc())) }) test_that("wk classes can be proxied and restored", { expect_identical(vctrs::vec_restore(vctrs::vec_proxy(wkt()), wkt()), wkt()) expect_identical(vctrs::vec_restore(vctrs::vec_proxy(wkb()), wkb()), wkb()) expect_identical(vctrs::vec_restore(vctrs::vec_proxy(xy()), xy()), xy()) expect_identical(vctrs::vec_restore(vctrs::vec_proxy(xyz()), xyz()), xyz()) expect_identical(vctrs::vec_restore(vctrs::vec_proxy(xym()), xym()), xym()) expect_identical(vctrs::vec_restore(vctrs::vec_proxy(xyzm()), xyzm()), xyzm()) expect_identical(vctrs::vec_restore(vctrs::vec_proxy(rct()), rct()), rct()) expect_identical(vctrs::vec_restore(vctrs::vec_proxy(crc()), crc()), crc()) }) test_that("vctrs wkb implementation works", { expect_true(vctrs::vec_is(wkb())) expect_identical(vctrs::vec_size(wkb()), 0L) expect_identical(vctrs::vec_cast(wkt(), wkb()), wkb()) expect_identical(vctrs::vec_cast(wkb(), wkb()), wkb()) expect_identical(vctrs::vec_cast(xy(), wkb()), wkb()) expect_identical(vctrs::vec_cast(xyz(), wkb()), wkb()) expect_identical(vctrs::vec_cast(xym(), wkb()), wkb()) expect_identical(vctrs::vec_cast(xyzm(), wkb()), wkb()) expect_identical(vctrs::vec_cast(rct(), wkb()), wkb()) expect_identical(vctrs::vec_proxy(wkb(crs = NULL)), list()) expect_identical(vctrs::vec_restore(list(), wkb()), wkb()) expect_identical(vctrs::vec_c(wkb(), wkb()), wkb()) expect_identical(vctrs::vec_c(wkb(), wkt()), wkt()) expect_identical(vctrs::vec_c(wkb(), xy()), wkb()) expect_identical(vctrs::vec_c(wkb(), xyz()), wkb()) expect_identical(vctrs::vec_c(wkb(), xym()), wkb()) expect_identical(vctrs::vec_c(wkb(), xyzm()), wkb()) expect_identical(vctrs::vec_c(wkb(), rct()), wkb()) expect_identical(vctrs::vec_c(wkb(), crc()), wkb()) }) test_that("vctrs wkt implementation works", { expect_true(vctrs::vec_is(wkt())) expect_identical(vctrs::vec_size(wkt()), 0L) expect_identical(vctrs::vec_cast(wkt(), wkt()), wkt()) expect_identical(vctrs::vec_cast(wkb(), wkt()), wkt()) expect_identical(vctrs::vec_cast(xy(), wkt()), wkt()) expect_identical(vctrs::vec_cast(xyz(), wkt()), wkt()) expect_identical(vctrs::vec_cast(xym(), wkt()), wkt()) expect_identical(vctrs::vec_cast(xyzm(), wkt()), wkt()) expect_identical(vctrs::vec_cast(rct(), wkt()), wkt()) expect_identical(vctrs::vec_proxy(wkt(crs = NULL)), character()) expect_identical(vctrs::vec_restore(character(), wkt()), wkt()) expect_identical(vctrs::vec_c(wkt(), wkt()), wkt()) expect_identical(vctrs::vec_c(wkt(), wkb()), wkt()) expect_identical(vctrs::vec_c(wkt(), xy()), wkt()) expect_identical(vctrs::vec_c(wkt(), xyz()), wkt()) expect_identical(vctrs::vec_c(wkt(), xym()), wkt()) expect_identical(vctrs::vec_c(wkt(), xyzm()), wkt()) expect_identical(vctrs::vec_c(wkt(), rct()), wkt()) expect_identical(vctrs::vec_c(wkt(), crc()), wkt()) }) test_that("vctrs xy implementation works", { expect_true(vctrs::vec_is(xy())) expect_identical(vctrs::vec_size(xy()), 0L) expect_identical(vctrs::vec_cast(wkt(), xy()), xy()) expect_identical(vctrs::vec_cast(wkb(), xy()), xy()) expect_identical(vctrs::vec_cast(xy(), xy()), xy()) expect_identical(vctrs::vec_cast(xyz(), xy()), xy()) expect_identical(vctrs::vec_cast(xym(), xy()), xy()) expect_identical(vctrs::vec_cast(xyzm(), xy()), xy()) expect_error(vctrs::vec_cast(rct(), xy()), class = "vctrs_error_incompatible_type") expect_identical(vctrs::vec_proxy(xy(crs = NULL)), data.frame(x = double(), y = double())) expect_identical(vctrs::vec_restore(data.frame(x = double(), y = double()), xy()), xy()) expect_identical(vctrs::vec_c(xy(), wkt()), wkt()) expect_identical(vctrs::vec_c(xy(), wkb()), wkb()) expect_identical(vctrs::vec_c(xy(), xy()), xy()) expect_identical(vctrs::vec_c(xy(), xyz()), xyz()) expect_identical(vctrs::vec_c(xy(), xym()), xym()) expect_identical(vctrs::vec_c(xy(), xyzm()), xyzm()) expect_identical(vctrs::vec_c(xy(), rct()), wkb()) expect_identical(vctrs::vec_c(xy(), crc()), wkb()) }) test_that("vctrs xyz implementation works", { expect_true(vctrs::vec_is(xyz())) expect_identical(vctrs::vec_size(xyz()), 0L) expect_identical(vctrs::vec_cast(wkt(), xyz()), xyz()) expect_identical(vctrs::vec_cast(wkb(), xyz()), xyz()) expect_identical(vctrs::vec_cast(xy(), xyz()), xyz()) expect_identical(vctrs::vec_cast(xyz(), xyz()), xyz()) expect_identical(vctrs::vec_cast(xym(), xyz()), xyz()) expect_identical(vctrs::vec_cast(xyzm(), xyz()), xyz()) expect_error(vctrs::vec_cast(rct(), xyz()), class = "vctrs_error_incompatible_type") expect_identical(vctrs::vec_proxy(xyz(crs = NULL)), data.frame(x = double(), y = double(), z = double())) expect_identical(vctrs::vec_restore(data.frame(x = double(), y = double(), z = double()), xyz()), xyz()) expect_identical(vctrs::vec_c(xyz(), wkt()), wkt()) expect_identical(vctrs::vec_c(xyz(), wkb()), wkb()) expect_identical(vctrs::vec_c(xyz(), xy()), xyz()) expect_identical(vctrs::vec_c(xyz(), xyz()), xyz()) expect_identical(vctrs::vec_c(xyz(), xym()), xyzm()) expect_identical(vctrs::vec_c(xyz(), xyzm()), xyzm()) expect_identical(vctrs::vec_c(xyz(), rct()), wkb()) expect_identical(vctrs::vec_c(xyz(), crc()), wkb()) }) test_that("vctrs xym implementation works", { expect_true(vctrs::vec_is(xym())) expect_identical(vctrs::vec_size(xym()), 0L) expect_identical(vctrs::vec_cast(wkt(), xym()), xym()) expect_identical(vctrs::vec_cast(wkb(), xym()), xym()) expect_identical(vctrs::vec_cast(xy(), xym()), xym()) expect_identical(vctrs::vec_cast(xyz(), xym()), xym()) expect_identical(vctrs::vec_cast(xym(), xym()), xym()) expect_identical(vctrs::vec_cast(xyzm(), xym()), xym()) expect_error(vctrs::vec_cast(rct(), xym()), class = "vctrs_error_incompatible_type") expect_identical(vctrs::vec_proxy(xym(crs = NULL)), data.frame(x = double(), y = double(), m = double())) expect_identical(vctrs::vec_restore(data.frame(x = double(), y = double(), m = double()), xym()), xym()) expect_identical(vctrs::vec_c(xym(), wkt()), wkt()) expect_identical(vctrs::vec_c(xym(), wkb()), wkb()) expect_identical(vctrs::vec_c(xym(), xy()), xym()) expect_identical(vctrs::vec_c(xym(), xyz()), xyzm()) expect_identical(vctrs::vec_c(xym(), xym()), xym()) expect_identical(vctrs::vec_c(xym(), xyzm()), xyzm()) expect_identical(vctrs::vec_c(xym(), rct()), wkb()) expect_identical(vctrs::vec_c(xym(), crc()), wkb()) }) test_that("vctrs xyzm implementation works", { expect_true(vctrs::vec_is(xyzm())) expect_identical(vctrs::vec_size(xyzm()), 0L) expect_identical(vctrs::vec_cast(wkt(), xyzm()), xyzm()) expect_identical(vctrs::vec_cast(wkb(), xyzm()), xyzm()) expect_identical(vctrs::vec_cast(xy(), xyzm()), xyzm()) expect_identical(vctrs::vec_cast(xyz(), xyzm()), xyzm()) expect_identical(vctrs::vec_cast(xym(), xyzm()), xyzm()) expect_identical(vctrs::vec_cast(xyzm(), xyzm()), xyzm()) expect_error(vctrs::vec_cast(rct(), xyzm()), class = "vctrs_error_incompatible_type") expect_identical(vctrs::vec_proxy(xyzm(crs = NULL)), data.frame(x = double(), y = double(), z = double(), m = double())) expect_identical(vctrs::vec_restore(data.frame(x = double(), y = double(), z = double(), m = double()), xyzm()), xyzm()) expect_identical(vctrs::vec_c(xyzm(), wkt()), wkt()) expect_identical(vctrs::vec_c(xyzm(), wkb()), wkb()) expect_identical(vctrs::vec_c(xyzm(), xy()), xyzm()) expect_identical(vctrs::vec_c(xyzm(), xyz()), xyzm()) expect_identical(vctrs::vec_c(xyzm(), xym()), xyzm()) expect_identical(vctrs::vec_c(xyzm(), xyzm()), xyzm()) expect_identical(vctrs::vec_c(xyzm(), rct()), wkb()) expect_identical(vctrs::vec_c(xyzm(), crc()), wkb()) }) test_that("vctrs rct implementation works", { expect_true(vctrs::vec_is(rct())) expect_identical(vctrs::vec_size(rct()), 0L) expect_identical(vctrs::vec_cast(rct(), rct()), rct()) expect_identical( vctrs::vec_proxy(rct(crs = NULL)), data.frame(xmin = double(), ymin = double(), xmax = double(), ymax = double()) ) expect_identical( vctrs::vec_restore(data.frame(xmin = double(), ymin = double(), xmax = double(), ymax = double()), rct()), rct() ) expect_identical(vctrs::vec_c(rct(), wkb()), wkb()) expect_identical(vctrs::vec_c(rct(), wkt()), wkt()) expect_identical(vctrs::vec_c(rct(), xy()), wkb()) expect_identical(vctrs::vec_c(rct(), xyz()), wkb()) expect_identical(vctrs::vec_c(rct(), xym()), wkb()) expect_identical(vctrs::vec_c(rct(), xyzm()), wkb()) expect_identical(vctrs::vec_c(rct(), rct()), rct()) expect_identical(vctrs::vec_c(rct(), crc()), wkb()) }) test_that("vctrs crc implementation works", { expect_true(vctrs::vec_is(crc())) expect_identical(vctrs::vec_size(crc()), 0L) expect_identical(vctrs::vec_cast(crc(), crc()), crc()) expect_identical(vctrs::vec_cast(crc(), wkb()), wkb()) expect_identical(vctrs::vec_cast(crc(), wkt()), wkt()) expect_identical( vctrs::vec_proxy(crc(crs = NULL)), data.frame(x = double(), y = double(), r = double()) ) expect_identical( vctrs::vec_restore(data.frame(x = double(), y = double(), r = double()), crc()), crc() ) expect_identical(vctrs::vec_c(crc(), wkb()), wkb()) expect_identical(vctrs::vec_c(crc(), wkt()), wkt()) expect_identical(vctrs::vec_c(crc(), xy()), wkb()) expect_identical(vctrs::vec_c(crc(), xyz()), wkb()) expect_identical(vctrs::vec_c(crc(), xym()), wkb()) expect_identical(vctrs::vec_c(crc(), xyzm()), wkb()) expect_identical(vctrs::vec_c(crc(), crc()), crc()) }) test_that("vec_c() propagates the crs attribute", { for (constructor in list(wkb, wkt, xy, xyz, xym, xyzm, rct, crc)) { expect_identical( vctrs::vec_c(!!constructor(crs = 1234), !!constructor(crs = 1234)), !!constructor(crs = 1234) ) expect_identical( vctrs::vec_c(!!constructor(crs = 1234), !!constructor()), !!constructor(crs = 1234) ) expect_error( vctrs::vec_c(!!constructor(crs = 1234), !!constructor(crs = NULL)), "are not equal" ) } }) test_that("vec_c() propagates the geodesic attribute", { for (constructor in list(wkb, wkt)) { expect_identical( vctrs::vec_c(!!constructor(geodesic = TRUE), !!constructor(geodesic = TRUE)), !!constructor(geodesic = TRUE) ) expect_identical( vctrs::vec_c(!!constructor(), !!constructor()), !!constructor() ) expect_error( vctrs::vec_c(!!constructor(geodesic = TRUE), !!constructor(crs = NULL)), "have differing" ) } }) test_that("vec_c() propagates the geodesic attribute through points", { for (constructor in list(wkb, wkt)) { for (constructor2 in list(xy, xyz, xym, xyzm)) { expect_identical( vctrs::vec_c(!!constructor(geodesic = TRUE), !!constructor2()), !!constructor(geodesic = TRUE) ) expect_identical( vctrs::vec_c(!!constructor2(), !!constructor(geodesic = TRUE)), !!constructor(geodesic = TRUE) ) } } }) wk/tests/testthat/test-chunk.R0000644000176200001440000000350414320424456016113 0ustar liggesusers test_that("single chunk strategy works", { feat <- c(as_wkt(xy(1:4, 1:4)), wkt("LINESTRING (1 1, 2 2)")) expect_identical( wk_chunk_strategy_single()(list(feat), 5), data.frame(from = 1, to = 5) ) }) test_that("chunk by feature strategy works", { feat <- c(as_wkt(xy(1:4, 1:4)), wkt("LINESTRING (1 1, 2 2)")) expect_identical( wk_chunk_strategy_feature(chunk_size = 2)(list(feat), 5), data.frame(from = c(1, 3, 5), to = c(2, 4, 5)) ) }) test_that("chunk by coordinates strategy works", { n_coord <- c(1, 5, 1, 5, 1) xs <- unlist(lapply(n_coord, seq_len)) ys <- unlist(lapply(n_coord, seq_len)) id <- vctrs::vec_rep_each(seq_along(n_coord), n_coord) feat <- wk_linestring(xy(xs, ys), feature_id = id) expect_identical( wk_chunk_strategy_coordinates(chunk_size = 6)(list(feat), length(n_coord)), data.frame(from = c(1L, 3L, 5L), to = c(2L, 4L, 5L)) ) # for points there's a shortcut for calculating the chunks expect_identical( wk_chunk_strategy_coordinates(chunk_size = 2)(list(xy(1:6, 1:6)), 6), data.frame(from = c(1, 3, 5), to = c(2, 4, 6)) ) }) test_that("chunk_info() works", { expect_identical( chunk_info(5, chunk_size = 2), list(n_chunks = 3, chunk_size = 2) ) expect_identical( chunk_info(5, chunk_size = 5), list(n_chunks = 1, chunk_size = 5) ) expect_identical( chunk_info(0, chunk_size = 5), list(n_chunks = 0, chunk_size = 5) ) expect_identical( chunk_info(5, n_chunks = 3), list(n_chunks = 3, chunk_size = 2) ) expect_identical( chunk_info(5, n_chunks = 1), list(n_chunks = 1, chunk_size = 5) ) expect_identical( chunk_info(0, n_chunks = 5), list(n_chunks = 0L, chunk_size = 1L) ) expect_error(chunk_info(1), "exactly one") expect_error(chunk_info(1, chunk_size = 1, n_chunks = 1), "exactly one") }) wk/tests/testthat/test-flatten.R0000644000176200001440000000772414472006062016445 0ustar liggesusers test_that("wk_flatten() works", { expect_identical( wk_flatten(wkt(c("MULTIPOINT (0 0, 1 1)", NA))), wkt(c("POINT (0 0)", "POINT (1 1)", NA)) ) expect_identical( wk_flatten(wkt(c("POINT (0 0)", "POINT (1 1)", NA))), wkt(c("POINT (0 0)", "POINT (1 1)", NA)) ) expect_error(wk_flatten(new_wk_wkt("POINT ENTPY")), "ENTPY") # we need this one to trigger a realloc on the details list xy_copy <- wk_handle( wkt(c(paste0("MULTIPOINT (", paste(1:1025, 1, collapse = ", ") , ")"), "POINT (0 0)")), wk_flatten_filter(xy_writer(), add_details = TRUE) ) expect_identical( attr(xy_copy, "wk_details"), list(feature_id = c(rep(1L, 1025), 2L)) ) attr(xy_copy, "wk_details") <- NULL expect_identical(xy_copy, c(xy(1:1025, 1), xy(0, 0))) }) test_that("wk_flatten() propagates attributes", { expect_identical( wk_flatten( wkt("LINESTRING ZM (0 0 0 0, 1 0 0 0)", crs = 1234, geodesic = TRUE) ), wkt("LINESTRING ZM (0 0 0 0, 1 0 0 0)", crs = 1234, geodesic = TRUE) ) }) test_that("wk_flatten() works for polygons", { expect_identical( wk_flatten(wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))")), wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))") ) }) test_that("wk_flatten() works for data.frame", { expect_equal( wk_flatten(data.frame(geom = wkt(c("MULTIPOINT (0 0, 1 1)")))), data.frame(geom = wkt(c("POINT (0 0)", "POINT (1 1)"))), ignore_attr = TRUE ) }) test_that("wk_flatten() communicates correct size and type", { expect_identical( wk_handle(wkt("POINT (0 0)"), wk_flatten_filter(wk_vector_meta_handler())), list(geometry_type = 0L, size = NA_real_, has_z = NA, has_m = NA) ) skip_if_not_installed("sf") # need sf because these objects carry vector-level types expect_identical( wk_handle(sf::st_as_sfc("POINT (0 0)"), wk_flatten_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = 1, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("MULTIPOINT EMPTY"), wk_flatten_filter(wk_vector_meta_handler())), list(geometry_type = 1L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("MULTILINESTRING EMPTY"), wk_flatten_filter(wk_vector_meta_handler())), list(geometry_type = 2L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("MULTIPOLYGON EMPTY"), wk_flatten_filter(wk_vector_meta_handler())), list(geometry_type = 3L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) expect_identical( wk_handle(sf::st_as_sfc("GEOMETRYCOLLECTION EMPTY"), wk_flatten_filter(wk_vector_meta_handler())), list(geometry_type = 0L, size = NA_real_, has_z = FALSE, has_m = FALSE) ) }) test_that("wk_flatten() works for nested collections", { expect_identical( wk_flatten( wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1))))"), max_depth = 3 ), wkt("POINT (0 1)") ) expect_identical( wk_flatten( wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1))))"), max_depth = 2 ), wkt("GEOMETRYCOLLECTION (POINT (0 1))") ) expect_identical( wk_flatten( wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1))))"), max_depth = 1 ), wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1)))") ) expect_identical( wk_flatten( wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1))))"), max_depth = 0 ), wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1))))") ) expect_identical( wk_handle( wkt("GEOMETRYCOLLECTION(MULTIPOINT (30 10, 10 10), LINESTRING (0 0, 1 1), GEOMETRYCOLLECTION EMPTY)"), wk_flatten_filter(wkt_writer(), max_depth = 2, add_details = TRUE) ), structure( c("POINT (30 10)", "POINT (10 10)", "LINESTRING (0 0, 1 1)"), class = c("wk_wkt", "wk_vctr"), wk_details = list(feature_id = c(1L, 1L, 1L)) ) ) }) wk/tests/testthat/test-handler.R0000644000176200001440000000121514145575672016431 0ustar liggesusers test_that("wk_handler class works", { expect_true(is_wk_handler(wk_void_handler())) handler <- wk_void_handler() expect_identical(as_wk_handler(handler), handler) expect_output(print(wk_void_handler()), "wk_void_handler") expect_s3_class(as_wk_handler(wk_void_handler), "wk_void_handler") }) test_that("is_handleable works", { expect_true(is_handleable(xy())) expect_false(is_handleable(1:5)) }) test_that("as_handler() works", { handler <- wk_void_handler() expect_identical(as_wk_handler(handler), handler) expect_identical(as_wk_handler(function() handler), handler) expect_error(as_wk_handler(3), "must be a wk handler") }) wk/tests/testthat/test-xy-writer.R0000644000176200001440000000450714515070354016761 0ustar liggesusers test_that("xy_writer() works", { empties <- wkt( c("POINT EMPTY", "LINESTRING EMPTY", "POLYGON EMPTY", "MULTIPOINT EMPTY", "MULTILINESTRING EMPTY", "MULTIPOLYGON EMPTY", "GEOMETRYCOLLECTION EMPTY" ) ) expect_identical( wk_handle(empties, xy_writer()), rep(xy(NaN, NaN), length(empties)) ) expect_identical( wk_handle(wkt("POINT (0 1)"), xy_writer()), xy(0, 1) ) expect_identical( wk_handle(wkt("MULTIPOINT ((0 1))"), xy_writer()), xy(0, 1) ) expect_identical( wk_handle(wkt("GEOMETRYCOLLECTION (MULTIPOINT ((0 1)))"), xy_writer()), xy(0, 1) ) expect_error( wk_handle(wkt("LINESTRING (0 1, 1 2)"), xy_writer()), "Can't convert geometry" ) expect_error( wk_handle(wkt("MULTIPOINT (0 1, 1 2)"), xy_writer()), "contains more than one coordinate" ) }) test_that("xy_writer() works for a vector of indeterminate length", { long_xy <- as_wkt(xy(runif(2048), runif(2048))) expect_identical( handle_wkt_without_vector_size(long_xy, xy_writer()), wk_handle(long_xy, xy_writer()) ) }) test_that("xy_writer() works with zm dimensions", { points_xyzm <- xyzm(1:10, 11:20, 21:30, 31:40) expect_identical( wk_handle(points_xyzm, xy_writer()), points_xyzm ) long_xyzm <- as_wkt(xyzm(runif(2048), runif(2048), runif(2048), runif(2048))) expect_identical( handle_wkt_without_vector_size(long_xyzm, xy_writer()), wk_handle(long_xyzm, xy_writer()) ) }) test_that("xy_writer() fills unused dimensions with NA", { points_xy <- xy(1:10, 11:20) points_xyzm <- xyzm(1:10, 11:20, 21:30, 31:40) expect_identical( wk_handle(c(as_wkb(points_xy), as_wkb(points_xyzm)), xy_writer()), c( xyzm(1:10, 11:20, NA, NA), points_xyzm ) ) }) test_that("xy_writer() can roundtrip point examples", { expect_identical( wk_handle(wk_example_wkt$point, xy_writer()), xy(c(30, NaN, NA), c(10, NaN, NA)) ) expect_identical( wk_handle(wk_example_wkt$point_z, xy_writer()), xyz(c(30, NaN, NA), c(10, NaN, NA), c(40, NaN, NA)) ) expect_identical( wk_handle(wk_example_wkt$point_m, xy_writer()), xym(c(30, NaN, NA), c(10, NaN, NA), c(300, NaN, NA)) ) expect_identical( wk_handle(wk_example_wkt$point_zm, xy_writer()), xyzm(c(30, NaN, NA), c(10, NaN, NA), c(40, NaN, NA), c(300, NaN, NA)) ) }) wk/tests/testthat/test-count.R0000644000176200001440000000075714106220314016127 0ustar liggesusers test_that("wk_count() works", { expect_identical( wk_count(wkt(c("POINT (1 2)", "POLYGON ((0 0, 0 1, 1 1, 0 0))", NA))), data.frame( n_geom = c(1L, 1L, 0L), n_ring = c(0L, 1L, 0L), n_coord = c(1, 4L, 0) ) ) }) test_that("wk_count() works for a vector of indeterminate length", { long_xy <- as_wkt(xy(runif(2048), runif(2048))) expect_identical( new_data_frame(handle_wkt_without_vector_size(long_xy, wk_count_handler())), wk_count(long_xy) ) }) wk/tests/testthat/test-handle-rct.R0000644000176200001440000000060114106220314017004 0ustar liggesusers test_that("wk_handle.wk_rct() works", { expect_identical( wk_handle(rct(c(1, NA, Inf, 0), c(2, NA, 0, Inf), c(3, NA, 1, 1), c(4, NA, 1, 1)), wkt_writer()), wkt(c("POLYGON ((1 2, 3 2, 3 4, 1 4, 1 2))", "POLYGON EMPTY", "POLYGON EMPTY", "POLYGON EMPTY")) ) # check invalid data expect_error(wk_handle.wk_rct("not a rct", wk_void_handler()), "does not inherit from") }) wk/tests/testthat/test-wk-rcrd.R0000644000176200001440000000541014163110540016341 0ustar liggesusers test_that("wk_rcrd works", { xy_rcrd <- structure(list(x = as.numeric(1:3), y = c(2, 2, 2)), class = "wk_rcrd") expect_identical(length(xy_rcrd), 3L) expect_identical( xy_rcrd[2], structure(list(x = 2, y = 2), class = "wk_rcrd") ) expect_identical(xy_rcrd[[2]], xy_rcrd[2]) expect_error(xy_rcrd$x, "is not meaningful") expect_identical(names(xy_rcrd), NULL) expect_identical(is.na(xy_rcrd), c(FALSE, FALSE, FALSE)) expect_identical(is.na(xy_rcrd[NA_integer_]), TRUE) expect_identical(is.na(xy_rcrd[integer(0)]), logical(0)) expect_identical(expect_output(print(xy_rcrd), "wk_rcrd"), xy_rcrd) expect_output(print(xy_rcrd[integer(0)]), "wk_rcrd") expect_output(expect_identical(str(xy_rcrd), xy_rcrd), "wk_rcrd") expect_output(expect_identical(str(xy_rcrd[integer(0)]), xy_rcrd[integer(0)]), "wk_rcrd\\[0\\]") expect_output(print(wk_set_crs(xy_rcrd, 1234)), "CRS=EPSG:1234") expect_length(format(xy_rcrd), 2) expect_length(as.character(xy_rcrd), 2) old_opt <- options(max.print = 1000) expect_output( print(structure(list(x = 1:1001), class = "wk_rcrd")), "Reached max.print" ) options(old_opt) xy_rcrd2 <- xy_rcrd names(xy_rcrd2) <- NULL expect_identical(xy_rcrd2, xy_rcrd) expect_error(names(xy_rcrd) <- "not null", "must be NULL") expect_identical(validate_wk_rcrd(xy_rcrd), xy_rcrd) expect_identical( rep(xy_rcrd, 2), structure(list(x = as.numeric(c(1:3, 1:3)), y = rep(2, 6)), class = "wk_rcrd") ) expect_identical( rep(xy_rcrd, 2), c(xy_rcrd, xy_rcrd) ) expect_error(c(xy_rcrd, 2), "Can't combine") expect_identical( as.matrix(xy_rcrd), matrix(c(1, 2, 3, 2, 2, 2), ncol = 2, dimnames = list(NULL, c("x", "y"))) ) expect_identical( as.data.frame(xy_rcrd), data.frame(x = c(1, 2, 3), y = c(2, 2, 2)) ) expect_identical( data.frame(col_name = xy_rcrd), new_data_frame(list(col_name = xy_rcrd)) ) }) test_that("geodesic gets printed for geodesic rcrd objects", { x_geod <- new_wk_rcrd( list(x = double()), template = structure(list(), class = c("some_wk_rcrd", "wk_rcrd")) ) s3_register("wk::wk_is_geodesic", "some_wk_rcrd", function(x) TRUE) expect_output(print(x_geod), "geodesic some_wk_rcrd") }) test_that("rep_len() works for wk_rcrd", { skip_if_not(packageVersion("base") >= "3.6") xy_rcrd <- structure(list(x = as.numeric(1:3), y = c(2, 2, 2)), class = "wk_rcrd") expect_identical( rep_len(xy_rcrd, 6), structure(list(x = as.numeric(c(1:3, 1:3)), y = rep(2, 6)), class = "wk_rcrd") ) }) test_that("c() for wk_rcrd handles crs attributes", { expect_identical( wk_crs(c(xy(0, 1, crs = wk_crs_inherit()), xy(0, 1, crs = 1234))), 1234 ) expect_error( wk_crs(c(xy(0, 1), xy(0, 1, crs = 1234))), "are not equal" ) }) wk/tests/testthat/test-meta.R0000644000176200001440000000477714513020606015737 0ustar liggesusers test_that("wk_meta() works", { expect_identical( wk_meta(wkt(c("POINT (1 2)", "POINT EMPTY", NA))), data.frame( geometry_type = c(1L, 1L, NA_integer_), size = c(NA_integer_, 0L, NA_integer_), has_z = c(FALSE, FALSE, NA), has_m = c(FALSE, FALSE, NA), srid = c(NA_integer_, NA_integer_, NA_integer_), precision = c(0, 0, NA_integer_), is_empty = c(FALSE, TRUE, NA) ) ) expect_identical( wk_meta(as_wkb(c("POINT (1 2)", "POINT EMPTY", NA))), data.frame( geometry_type = c(1L, 1L, NA_integer_), size = c(1L, 0L, NA_integer_), has_z = c(FALSE, FALSE, NA), has_m = c(FALSE, FALSE, NA), srid = c(NA_integer_, NA_integer_, NA_integer_), precision = c(0, 0, NA_integer_), is_empty = c(FALSE, TRUE, NA) ) ) expect_identical( wk_meta(as_wkb(c("SRID=1234;POINT (1 2)", NA))), data.frame( geometry_type = c(1L, NA_integer_), size = c(1L, NA_integer_), has_z = c(FALSE, NA), has_m = c(FALSE, NA), srid = c(1234L, NA_integer_), precision = c(0, NA_integer_), is_empty = c(FALSE, NA) ) ) }) test_that("wk_vector_meta() works", { expect_identical( wk_vector_meta(wkt(c("POINT (1 2)", NA))), data.frame( geometry_type = 0L, size = 2, has_z = NA, has_m = NA ) ) # only sf reader has vector meta with embedded dimensions skip_if_not_installed("sf") expect_identical( wk_vector_meta(sf::st_as_sfc("POINT (30 10)")), data.frame( geometry_type = 1L, size = 1, has_z = FALSE, has_m = FALSE ) ) expect_identical( wk_vector_meta(sf::st_as_sfc("POINT M (30 10 12)")), data.frame( geometry_type = 1L, size = 1, has_z = FALSE, has_m = TRUE ) ) expect_identical( wk_vector_meta(sf::st_as_sfc("POINT Z (30 10 12)")), data.frame( geometry_type = 1L, size = 1, has_z = TRUE, has_m = FALSE ) ) }) test_that("wk_meta() works for a vector of indeterminate length", { long_xy <- as_wkt(xy(runif(2048), runif(2048))) expect_identical( new_data_frame(handle_wkt_without_vector_size(long_xy, wk_meta_handler())), wk_meta(long_xy) ) }) test_that("geometry type converters work", { types_str <- c( "point", "linestring", "polygon", "multipoint", "multilinestring", "multipolygon", "geometrycollection" ) expect_identical(wk_geometry_type(types_str), 1:7) expect_identical(wk_geometry_type_label(7:1), rev(types_str)) }) wk/tests/testthat/test-grd-subset.R0000644000176200001440000003626114425340015017062 0ustar liggesusers test_that("subset works for grd_rct", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical(grd_subset(empty), empty) grid <- grd_rct(volcano) # ways to identity subset expect_identical(grd_subset(grid), grid) expect_identical(grd_subset(grid, 1:87, 1:61), grid) expect_identical(grd_subset(grid, NULL, 1:61), grid) expect_identical(grd_subset(grid, 1:87, NULL), grid) # bad args expect_error(grd_subset(grid, raw(), NULL), "must be NULL, numeric, or") expect_error(grd_subset(grid, NULL, raw()), "must be NULL, numeric, or") # check small subsets for exactness grid_00 <- grd_subset(grid, integer(), integer()) expect_identical(grid_00$data, volcano[integer(), integer()]) expect_identical(wk_bbox(grid_00), rct(Inf, Inf, -Inf, -Inf)) grid_11 <- grd_subset(grid, 2, 2) expect_identical(grid_11$data, volcano[2, 2, drop = FALSE]) expect_identical(wk_bbox(grid_11), rct(1, 85, 2, 86)) grid_23 <- grd_subset(grid, 1:2, 1:3) expect_identical(grid_23$data, volcano[1:2, 1:3]) expect_identical(wk_bbox(grid_23), rct(0, 85, 3, 87)) grid_13 <- grd_subset(grid, 1, 1:3) expect_identical(grid_13$data, volcano[1, 1:3, drop = FALSE]) expect_identical(wk_bbox(grid_13), rct(0, 86, 3, 87)) grid_03 <- grd_subset(grid, integer(), 1:3) expect_identical(grid_03$data, volcano[integer(), 1:3, drop = FALSE]) expect_identical(grid_03$bbox, rct(0, Inf, 3, -Inf)) grid_31 <- grd_subset(grid, 1:3, 1) expect_identical(grid_31$data, volcano[1:3, 1, drop = FALSE]) expect_identical(wk_bbox(grid_31), rct(0, 84, 1, 87)) grid_30 <- grd_subset(grid, 1:3, integer()) expect_identical(grid_30$data, volcano[1:3, integer(), drop = FALSE]) expect_identical(grid_30$bbox, rct(Inf, 84, -Inf, 87)) }) test_that("grd_subset() works for a grd_rct backed by nativeRaster", { # can aso check with PNG # col_native <- png::readPNG(system.file("img", "Rlogo.png", package="png"), native = T) # grid_native <- grd_rct(col_native) # plot(grid_native) # plot(grd_subset(grid_native, bbox = rct(20, 40, 60, 60)), border = T) col_native <- structure( c(-16777216L, -13421773L, -10066330L, -15066598L, -11711155L, -8355712L), .Dim = 2:3, class = "nativeRaster" ) grid_native <- grd_rct(col_native) grid_21 <- grd_subset(grid_native, i = 2, j = 2:3) expect_identical( as.integer(grid_21$data), c(-11711155L, -8355712L) ) }) test_that("grd_subset() preserves dimensions for nd arrays", { grid <- grd_rct(array(1:24, dim = c(2, 3, 4))) expect_identical( grd_subset(grid, 1, 1), grd_rct(array(c(1L, 7L, 13L, 19L), dim = c(1, 1, 4)), bbox = rct(0, 1, 1, 2)) ) }) test_that("subset works for grd_xy", { empty <- grd_xy(matrix(nrow = 0, ncol = 0)) expect_identical(grd_subset(empty), empty) grid <- grd_xy(volcano) # ways to identity subset expect_identical(grd_subset(grid), grid) expect_identical(grd_subset(grid, 1:87, 1:61), grid) expect_identical(grd_subset(grid, NULL, 1:61), grid) expect_identical(grd_subset(grid, 1:87, NULL), grid) # check small subsets for exactness grid_00 <- grd_subset(grid, integer(), integer()) expect_identical(grid_00$data, volcano[integer(), integer()]) expect_identical(wk_bbox(grid_00), rct(Inf, Inf, -Inf, -Inf)) grid_11 <- grd_subset(grid, 2, 2) expect_identical(grid_11$data, volcano[2, 2, drop = FALSE]) expect_identical(wk_bbox(grid_11), rct(1, 85, 1, 85)) grid_23 <- grd_subset(grid, 1:2, 1:3) expect_identical(grid_23$data, volcano[1:2, 1:3]) expect_identical(wk_bbox(grid_23), rct(0, 85, 2, 86)) }) test_that("crop works for grd_rct and grd_xy", { grid <- grd(nx = 3, ny = 2) expect_identical(grd_crop(grid, grid$bbox), grid) expect_identical( grd_crop(grid, rct(0, 0, 2, 2)), grd_subset(grid, 1:2, 1:2) ) expect_identical( grd_crop(grid, rct(-1, -1, 2, 2)), grd_subset(grid, 1:2, 1:2) ) grid <- grd(nx = 3, ny = 2, type = "corners") expect_identical(grd_crop(grid, grid$bbox), grid) expect_identical( grd_crop(grid, rct(0, 0, 2, 2)), grd_subset(grid, 1:3, 1:3) ) expect_identical( grd_crop(grid, rct(-1, -1, 2, 2)), grd_subset(grid, 1:3, 1:3) ) }) test_that("crop/extend works for grd_rct", { grid <- grd(nx = 3, ny = 2) expect_identical(grd_extend(grid, grid$bbox), grid) expect_identical( grd_extend(grid, rct(0, 0, 2, 2)), grd_subset(grid, 1:2, 1:2) ) expect_identical( grd_extend(grid, rct(-1, -1, 2, 2)), grd_subset(grid, 1:3, 0:2) ) grid <- grd(nx = 3, ny = 2, type = "corners") expect_identical(grd_extend(grid, grid$bbox), grid) expect_identical( grd_extend(grid, rct(0, 0, 2, 2)), grd_subset(grid, 1:3, 1:3) ) expect_identical( grd_extend(grid, rct(-1, -1, 2, 2)), grd_subset(grid, 1:4, 0:3) ) }) test_that("crop/extend works for grd_xy", { grid <- grd(nx = 2, ny = 1, type = "corners") expect_identical(grd_crop(grid, grid$bbox), grid) }) test_that("grd_cell() works for grd_rct()", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical(grd_cell(empty, xy(0, 0)), data.frame(i = NA_integer_, j = NA_integer_)) grid <- grd_rct(volcano) expect_identical(grd_cell(grid, xy(NA, NA)), data.frame(i = NA_integer_, j = NA_integer_)) expect_identical(grd_cell(grid, xy(0.5, 0.5)), data.frame(i = 87, j = 1)) expect_identical(grd_cell(grid, xy(0, 87)), data.frame(i = 1, j = 1)) expect_identical(grd_cell(grid, xy(1, 86)), data.frame(i = 2, j = 2)) expect_identical(grd_cell(grid, xy(2, 85)), data.frame(i = 3, j = 3)) }) test_that("grd_cell() works for grd_xy()", { empty <- grd_xy(matrix(nrow = 0, ncol = 0)) expect_identical(grd_cell(empty, xy(0, 0)), data.frame(i = NA_integer_, j = NA_integer_)) grid <- grd_xy(volcano) expect_identical(grd_cell(grid, xy(NA, NA)), data.frame(i = NA_integer_, j = NA_integer_)) expect_identical(grd_cell(grid, xy(0, 0)), data.frame(i = 87, j = 1)) }) test_that("grd_cell_range() works for grd_rct()", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical( grd_cell_range(empty, rct(0, 0, 1, 1)), list(i = integer(), j = integer()) ) grid <- grd_rct(volcano) expect_identical( grd_cell_range(grid, rct(NA, NA, NA, NA)), list(i = integer(), j = integer()) ) expect_identical( grd_cell_range(grid, wk_bbox(grid), snap = list(ceiling, floor)), list( i = c(start = 0, stop = nrow(grid), step = 1L), j = c(start = 0, stop = ncol(grid), step = 1L) ) ) # bbox with exact boundaries expect_identical( grd_cell_range(grid, bbox = rct(0, 86, 3, 87), snap = list(ceiling, floor)), list( i = c(start = 0, stop = 1, step = 1L), j = c(start = 0, stop = 3, step = 1L) ) ) # subset by bbox with non-exact boundaries expect_identical( grd_cell_range(grid, bbox = rct(0.5, 86.1, 2.5, 86.9)), list( i = c(start = 0, stop = 1, step = 1L), j = c(start = 0, stop = 3, step = 1L) ) ) # subset by arbitrary object with non-exact boundaries expect_identical( grd_cell_range(grid, bbox = as_wkb(rct(0.5, 86.1, 2.5, 86.9))), grd_cell_range(grid, bbox = rct(0.5, 86.1, 2.5, 86.9)) ) }) test_that("grd_cell_range() works for grd_xy()", { empty <- grd_xy(matrix(nrow = 0, ncol = 0)) expect_identical( grd_cell_range(empty, rct(0, 0, 1, 1)), list(i = integer(), j = integer()) ) grid <- grd_xy(volcano) expect_identical( grd_cell_range(grid, rct(NA, NA, NA, NA)), list(i = integer(), j = integer()) ) expect_identical( grd_cell_range(grid, wk_bbox(grid)), list( i = c(start = 0, stop = nrow(grid), step = 1L), j = c(start = 0, stop = ncol(grid), step = 1L) ) ) # bbox with exact boundaries expect_identical( grd_cell_range(grid, bbox = rct(0, 85, 3, 86)), list( i = c(start = 0, stop = 2, step = 1L), j = c(start = 0, stop = 4, step = 1L) ) ) # subset by bbox with non-exact boundaries expect_identical( grd_cell_range(grid, bbox = rct(0.6, 85.9, 2.4, 86.1)), list( i = c(start = 0, stop = 1, step = 1L), j = c(start = 1, stop = 3, step = 1L) ) ) # subset by arbitrary object with non-exact boundaries expect_identical( grd_cell_range(grid, bbox = as_wkb(rct(0.5, 86.1, 2.5, 86.9))), grd_cell_range(grid, bbox = rct(0.5, 86.1, 2.5, 86.9)) ) }) test_that("grd_cell_range() can downsample", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical( grd_cell_range(empty, rct(0, 0, 1, 1), step = 2L), list(i = integer(), j = integer()) ) grid <- grd(nx = 15, ny = 10) expect_identical( grd_cell_range(grid, step = 2L), list( i = c(start = 1, stop = 10, step = 2), j = c(start = 1, stop = 15, step = 2) ) ) expect_identical( grd_cell_range(grid, step = 3L), list( i = c(start = 1, stop = 9, step = 3), j = c(start = 1, stop = 14, step = 3) ) ) expect_identical( grd_cell_range(grid, step = c(1L, 3L)), list( i = c(start = 0, stop = 10, step = 1), j = c(start = 1, stop = 14, step = 3) ) ) expect_identical( grd_cell_range(grid, step = c(3L, 1L)), list( i = c(start = 1, stop = 9, step = 3), j = c(start = 0, stop = 15, step = 1) ) ) expect_identical( grd_cell_range(grid, step = c(1L, 1L)), grd_cell_range(grid, step = 1L) ) }) test_that("grd_cell_rct() works for grd_rct()", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical( wk_bbox(grd_cell_rct(empty, 0, 0)), wk_bbox(rct(NA, NA, NA, NA)) ) grid <- grd(nx = 3, ny = 2) expect_identical(grd_cell_rct(grid, 1, 1), rct(0, 1, 1, 2)) expect_identical(grd_cell_rct(grid, 0, 0), rct(-1, 2, 0, 3)) expect_identical( grd_cell_rct(grid, 0, 0, out_of_bounds = "discard"), rct(crs = NULL) ) expect_identical( wk_bbox(grd_cell_rct(grid, 0, 0, out_of_bounds = "censor")), wk_bbox(rct(NA, NA, NA, NA)) ) expect_identical( grd_cell_rct(grid, 0, 0, out_of_bounds = "squish"), grd_cell_rct(grid, 1, 1) ) expect_error(grd_cell_rct(grid, "fish", "fish"), "must be numeric") }) test_that("grd_cell_rct() works for grd_xy()", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical( wk_bbox(grd_cell_rct(empty, 0, 0)), wk_bbox(rct(NA, NA, NA, NA)) ) grid <- grd(nx = 3, ny = 2, type = "centers") expect_identical(grd_cell_rct(grid, 1, 1), rct(0, 1, 1, 2)) expect_identical(grd_cell_rct(grid, 0, 0), rct(-1, 2, 0, 3)) expect_identical( grd_cell_rct(grid, 0, 0, out_of_bounds = "discard"), rct(crs = NULL) ) expect_identical( wk_bbox(grd_cell_rct(grid, 0, 0, out_of_bounds = "censor")), wk_bbox(rct(NA, NA, NA, NA)) ) expect_identical( grd_cell_rct(grid, 0, 0, out_of_bounds = "squish"), grd_cell_rct(grid, 1, 1) ) expect_error(grd_cell_rct(grid, "fish", "fish"), "must be numeric") }) test_that("grd_cell_xy() works for grd_rct()", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical( grd_cell_xy(empty, 0, 0), xy(NaN, NaN) ) grid <- grd(nx = 3, ny = 2) expect_identical(grd_cell_xy(grid, 1, 1), xy(0.5, 1.5)) expect_identical(grd_cell_xy(grid, 0, 0), xy(-0.5, 2.5)) expect_identical( grd_cell_xy(grid, 0, 0, out_of_bounds = "discard"), xy(crs = NULL) ) expect_identical( grd_cell_xy(grid, 0, 0, out_of_bounds = "censor"), xy(NA, NA) ) expect_identical( grd_cell_xy(grid, 0, 0, out_of_bounds = "squish"), grd_cell_xy(grid, 1, 1) ) expect_error(grd_cell_xy(grid, "fish", "fish"), "must be numeric") }) test_that("grd_cell_xy() works for grd_xy()", { empty <- grd_xy(matrix(nrow = 0, ncol = 0)) expect_identical( grd_cell_xy(empty, 0, 0), xy(NaN, NaN) ) grid <- grd(nx = 3, ny = 2, type = "centers") expect_identical(grd_cell_xy(grid, 1, 1), xy(0.5, 1.5)) expect_identical(grd_cell_xy(grid, 0, 0), xy(-0.5, 2.5)) expect_identical( grd_cell_xy(grid, 0, 0, out_of_bounds = "discard"), xy(crs = NULL) ) expect_identical( grd_cell_xy(grid, 0, 0, out_of_bounds = "censor"), xy(NA, NA) ) expect_identical( grd_cell_xy(grid, 0, 0, out_of_bounds = "squish"), grd_cell_xy(grid, 1, 1) ) expect_error(grd_cell_xy(grid, "fish", "fish"), "must be numeric") }) test_that("ij_expand_one works", { expect_identical(ij_expand_one(NULL, 0L), integer()) expect_identical(ij_expand_one(NULL, 2L), 1:2) expect_identical(ij_expand_one(4:8, 10L), 4:8) expect_identical(ij_expand_one(c(start = NA, stop = NA, step = NA), 10L), 1:10) expect_identical(ij_expand_one(c(start = 0L, stop = 4L, step = NA), 10L), 1:4) expect_identical(ij_expand_one(c(start = 0L, stop = 4L, step = 2L), 10L), c(1L, 3L)) expect_identical(ij_expand_one(0:2, 1L, out_of_bounds = "keep"), 0:2) expect_identical(ij_expand_one(0:2, 1L, out_of_bounds = "censor"), c(NA, 1L, NA)) expect_identical(ij_expand_one(0:2, 1L, out_of_bounds = "discard"), 1L) expect_identical(ij_expand_one(0:2, 1L, out_of_bounds = "squish"), c(1L, 1L, 1L)) expect_error(ij_expand_one(0:2, 1L, out_of_bounds = "not an option"), "must be one of") expect_identical(ij_expand_one(c(start = 0, stop = 0, step = 1L), 1L), integer()) expect_error(ij_expand_one(TRUE, 1L), "must be NULL, numeric") }) test_that("ij_handle_out_of_bounds2 works", { # no oob expect_identical( ij_handle_out_of_bounds2(list(i = 1:3, j = 4:6), n = c(3, 6), out_of_bounds = "keep"), list(i = 1:3, j = 4:6) ) ij <- list(i = 0:2, j = 4:6) expect_identical( ij_handle_out_of_bounds2(ij, n = c(2L, 5L), out_of_bounds = "keep"), ij ) expect_identical( ij_handle_out_of_bounds2(ij, n = c(2L, 5L), out_of_bounds = "censor"), list(i = c(NA, 1L, NA), j = c(NA, 5L, NA)) ) expect_identical( ij_handle_out_of_bounds2(ij, n = c(2L, 5L), out_of_bounds = "discard"), list(i = 1L, j = 5L) ) expect_identical( ij_handle_out_of_bounds2(ij, n = c(2L, 5L), out_of_bounds = "squish"), list(i = c(1L, 1L, 2L), j = c(4L, 5L, 5L)) ) expect_error( ij_handle_out_of_bounds2(ij, c(2L, 5L), out_of_bounds = "not an option"), "must be one of" ) }) test_that("ij_to_slice_one works", { expect_identical(ij_to_slice_one(NULL, 0L), integer()) expect_identical(ij_to_slice_one(NULL, 2L), c(start = 0L, stop = 2L, step = 1L)) expect_identical(ij_to_slice_one(integer(), 2L), integer()) expect_identical(ij_to_slice_one(1L, 2L), c(start = 0L, stop = 1L, step = 1L)) expect_identical(ij_to_slice_one(4:8, 10L), c(start = 3L, stop = 8L, step = 1L)) expect_identical( ij_to_slice_one(seq(1L, 9L, by = 2L), 10L), c(start = 0L, stop = 9L, step = 2L) ) expect_identical( ij_to_slice_one(c(start = NA, stop = NA, step = NA), 10L), c(start = 0L, stop = 10L, step = 1L) ) expect_identical( ij_to_slice_one(c(start = 1L, stop = 2L, step = 3L), 10L), c(start = 1L, stop = 2L, step = 3L) ) expect_error( ij_to_slice_one(c(1, 2, 4), 1L), "must be equally spaced" ) expect_error( ij_to_slice_one(c(1, 0, -1), 1L), "must be equally spaced and ascending" ) expect_error( ij_to_slice_one(NA_integer_, 1L), "must be finite" ) expect_error( ij_to_slice_one(logical(), 1L), "must be NULL, numeric, or" ) }) test_that("snap functions work as expected", { expect_identical(grd_snap_next(seq(0, 1, 0.25)), c(0, 0, 1, 1, 1)) expect_identical(grd_snap_previous(seq(0, 1, 0.25)), c(0, 0, 0, 1, 1)) }) wk/tests/testthat/test-grd-handle.R0000644000176200001440000001020414315177334017007 0ustar liggesusers test_that("wk_handle() works for grd_rct", { expect_identical( wk_handle(grd(nx = 1, ny = 1), wkt_writer()), wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") ) }) test_that("wk_handle() works for grd_xy", { expect_identical( wk_handle(grd(nx = 1, ny = 1, type = "centers"), wkt_writer()), wkt("POINT (0.5 0.5)") ) }) test_that("as_xy() works for grd objects", { grid_empty <- grd(nx = 0, ny = 0) expect_identical(as_xy(grid_empty), xy(crs = NULL)) grid1 <- grd(nx = 1, ny = 1) expect_identical(as_xy(grid1), xy(0.5, 0.5)) data <- matrix(0:5, nrow = 2, ncol = 3) grid <- grd_xy(data) # order should match the internal ordering of data # (column major unless specified) expect_identical( as_xy(grd_xy(data)), c( xy(0, 1), xy(0, 0), xy(1, 1), xy(1, 0), xy(2, 1), xy(2, 0) ) ) # order should still be top left -> bottom right # even with flipped initial bbox expect_identical( as_xy(grd_xy(data, rct(0, 0, -2, -1))), c( xy(-2, 0), xy(-2, -1), xy(-1, 0), xy(-1, -1), xy(0, 0), xy(0, -1) ) ) expect_identical(as_xy(as_grd_rct(grid)), as_xy(grid)) }) test_that("as_xy() works for row-major grd objects", { grid_empty <- grd(nx = 0, ny = 0) expect_identical(as_xy(grid_empty, data_order = c("x", "y")), xy(crs = NULL)) data <- matrix(0:5, nrow = 2, ncol = 3) grid <- grd_xy(data) expect_identical( as_xy(grid, data_order = c("x", "y")), c( xy(0, 1), xy(1, 1), xy(2, 1), xy(0, 0), xy(1, 0), xy(2, 0) ) ) }) test_that("as_xy() works for flipped grd objects", { data <- matrix(0:5, nrow = 2, ncol = 3) grid <- grd_xy(data) expect_identical( as_xy(grid, data_order = c("-y", "-x")), c( xy(2, 0), xy(2, 1), xy(1, 0), xy(1, 1), xy(0, 0), xy(0, 1) ) ) expect_identical( as_xy(grid, data_order = c("-x", "-y")), c( xy(2, 0), xy(1, 0), xy(0, 0), xy(2, 1), xy(1, 1), xy(0, 1) ) ) }) test_that("as_rct() works for grd objects", { grid_empty <- grd(nx = 0, ny = 0) expect_identical(as_rct(grid_empty), rct(crs = NULL)) data <- matrix(0:5, nrow = 2, ncol = 3) grid <- grd_rct(data) # order should match the internal ordering of data # (column major unless specified) expect_identical( as_rct(grid), c( rct(0, 1, 1, 2), rct(0, 0, 1, 1), rct(1, 1, 2, 2), rct(1, 0, 2, 1), rct(2, 1, 3, 2), rct(2, 0, 3, 1) ) ) expect_identical( as_rct(grd_rct(data, rct(0, 0, -3, -2))), c( rct(-3, -1, -2, 0), rct(-3, -2, -2, -1), rct(-2, -1, -1, 0), rct(-2, -2, -1, -1), rct(-1, -1, 0, 0), rct(-1, -2, 0, -1) ) ) expect_identical(as_rct(as_grd_xy(grid)), as_rct(grid)) }) test_that("as_rct() works for row-major grd objects", { grid_empty <- grd(nx = 0, ny = 0) expect_identical(as_rct(grid_empty, data_order = c("x", "y")), rct(crs = NULL)) data <- matrix(0:5, nrow = 2, ncol = 3) grid <- grd_rct(data) # order should match the internal ordering of data # (row major unless specified) expect_identical( as_rct(grid, data_order = c("x", "y")), c( rct(0, 1, 1, 2), rct(1, 1, 2, 2), rct(2, 1, 3, 2), rct(0, 0, 1, 1), rct(1, 0, 2, 1), rct(2, 0, 3, 1) ) ) expect_identical(as_rct(as_grd_xy(grid)), as_rct(grid)) }) test_that("as_rct() works for flipped grd objects", { data <- matrix(0:5, nrow = 2, ncol = 3) grid <- grd_rct(data) # order should match the internal ordering of data # (row major unless specified) expect_identical( as_rct(grid, data_order = c("-y", "-x")), c( rct(2, 0, 3, 1), rct(2, 1, 3, 2), rct(1, 0, 2, 1), rct(1, 1, 2, 2), rct(0, 0, 1, 1), rct(0, 1, 1, 2) ) ) # order should match the internal ordering of data # (row major unless specified) expect_identical( as_rct(grid, data_order = c("-x", "-y")), c( rct(2, 0, 3, 1), rct(1, 0, 2, 1), rct(0, 0, 1, 1), rct(2, 1, 3, 2), rct(1, 1, 2, 2), rct(0, 1, 1, 2) ) ) }) wk/tests/testthat/test-pkg-sf.R0000644000176200001440000002621314515070354016174 0ustar liggesusers test_that("sf CRS objects can be compared", { skip_if_not_installed("sf") expect_true(wk_crs_equal(sf::st_crs(4326), 4326)) expect_true(wk_crs_equal(sf::st_crs(4326), 4326L)) expect_true(wk_crs_equal(sf::st_crs(NA), NULL)) expect_true(wk_crs_equal(NULL, sf::st_crs(NA))) }) test_that("wk_crs_proj_definition() works for sf crs objects", { skip_if_not_installed("sf") expect_identical(wk_crs_proj_definition(sf::NA_crs_), NA_character_) epsg4326 <- 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]' expect_identical(wk_crs_proj_definition(sf::st_crs(epsg4326)), "EPSG:4326") expect_identical(wk_crs_proj_definition(sf::st_crs(4326)), "EPSG:4326") expect_match(wk_crs_proj_definition(sf::st_crs(4326), verbose = TRUE), "^GEOGCS") expect_identical(wk_crs_proj_definition("OGC:CRS84"), "OGC:CRS84") expect_identical( wk_crs_proj_definition(sf::st_crs("+proj=merc +lat_ts=56.5 +type=crs")), "+proj=merc +lat_ts=56.5 +type=crs" ) }) test_that("wk_crs_projjson() works for sf crs objects", { skip_if_not_installed("sf") expect_match(wk_crs_projjson(sf::st_crs(4326)), "GeographicCRS") expect_identical(wk_crs_projjson(sf::NA_crs_), NA_character_) }) test_that("wk_crs/set_crs works on sf/sfc", { skip_if_not_installed("sf") sf <- sf::st_as_sf(data.frame(geometry = sf::st_as_sfc("POINT (1 2)"))) expect_identical(wk_crs(sf), sf::st_crs(sf)) expect_identical(sf::st_crs(wk_set_crs(sf, 4326)), sf::st_crs(4326)) sfc <- sf::st_as_sfc("POINT (1 2)") expect_identical(wk_crs(sfc), sf::st_crs(sfc)) expect_identical(sf::st_crs(wk_set_crs(sfc, 4326)), sf::st_crs(4326)) }) test_that("conversion from sf to wkt works", { skip_if_not_installed("sf") sfc <- sf::st_sfc(sf::st_point(), sf::st_point(c(0, 1)), crs = 4326) expect_s3_class(as_wkt(sfc), "wk_wkt") expect_identical( as.character(as_wkt(sfc)), c("POINT EMPTY", "POINT (0 1)") ) expect_identical(wk_crs(as_wkt(sfc)), sf::st_crs(sfc)) sf <- sf::st_as_sf(new_data_frame(list(geometry = sfc))) expect_identical( as.character(as_wkt(sf)), c("POINT EMPTY", "POINT (0 1)") ) expect_identical(wk_crs(as_wkt(sf)), sf::st_crs(sf)) }) test_that("conversion from sf to wkb works", { skip_if_not_installed("sf") sfc <- sf::st_sfc(sf::st_point(), sf::st_point(c(0, 1)), crs = 4326) expect_s3_class(as_wkb(sfc), "wk_wkb") expect_identical( as.character(as_wkt(as_wkb(sfc))), c("POINT EMPTY", "POINT (0 1)") ) expect_identical(wk_crs(as_wkb(sfc)), sf::st_crs(sfc)) sfg <- sf::st_point(c(0, 1)) expect_s3_class(as_wkb(sfg), "wk_wkb") expect_identical( as.character(as_wkt(as_wkb(sfg))), "POINT (0 1)" ) sf <- sf::st_as_sf(new_data_frame(list(geometry = sfc))) expect_identical( as.character(as_wkt(as_wkb(sf))), c("POINT EMPTY", "POINT (0 1)") ) expect_identical(wk_crs(as_wkb(sf)), sf::st_crs(sf)) }) test_that("conversion from sf to xy works", { skip_if_not_installed("sf") sfc <- sf::st_sfc(sf::st_point(), sf::st_point(c(0, 1))) expect_s3_class(as_xy(sfc), "wk_xy") expect_identical(as_xy(sfc), xy(c(NaN, 0), c(NaN, 1))) sf <- sf::st_as_sf(new_data_frame(list(geometry = sfc))) expect_identical(as_xy(sf), xy(c(NaN, 0), c(NaN, 1))) expect_identical(as_xy(sf::st_sfc()), xy(crs = NULL)) expect_identical( as_xy(sf::st_sfc(sf::st_linestring())), xy(NaN, NaN, crs = sf::NA_crs_) ) # check all dimensions expect_identical(as_xy(sf::st_sfc(sf::st_point(c(1, 2, 3, 4), dim = "XYZM"))), xyzm(1, 2, 3, 4)) expect_identical(as_xy(sf::st_sfc(sf::st_point(c(1, 2, 3), dim = "XYZ"))), xyz(1, 2, 3)) expect_identical(as_xy(sf::st_sfc(sf::st_point(c(1, 2, 3), dim = "XYM"))), xym(1, 2, 3)) expect_identical(as_xy(sf::st_sfc(sf::st_point(c(1, 2)))), xy(1, 2)) }) test_that("conversion from bbox to rct works", { skip_if_not_installed("sf") sfc <- sf::st_sfc(sf::st_point(c(2, 3)), sf::st_point(c(0, 1))) expect_identical(as_rct(sf::st_bbox(sfc)), rct(0, 1, 2, 3)) }) test_that("conversion to sf works", { skip_if_not_installed("sf") sfc <- sf::st_sfc(sf::st_point(), sf::st_point(c(0, 1)), NULL, crs = 4326) sf <- sf::st_as_sf(new_data_frame(list(geometry = sfc))) wkb <- as_wkb(c("POINT EMPTY", "POINT (0 1)", NA), crs = 4326) wkt <- as_wkt(c("POINT EMPTY", "POINT (0 1)", NA), crs = 4326) expect_equal(sf::st_as_sf(wkb), sf) expect_equal(sf::st_as_sfc(wkb), sfc) expect_equal(sf::st_as_sf(wkt), sf) expect_equal(sf::st_as_sfc(wkt), sfc) # xy expect_equal( sf::st_as_sf(xy(c(NA, 0, NA), c(NA, 1, NA), crs = 4326)), sf ) expect_equal( sf::st_as_sfc(xy(c(NA, 0, NA), c(NA, 1, NA), crs = 4326)), sfc ) expect_equal( sf::st_as_sfc(xy(c(NaN, 0, NA), c(NaN, 1, NA), crs = 4326)), sfc ) # xy with all !is.na() uses faster sf conversion with coords expect_equal(sf::st_as_sf(xy(0, 1, crs = 4326)), sf[2,, , drop = FALSE]) expect_equal(sf::st_as_sfc(xy(0, 1, crs = 4326)), sfc[2]) # rct can only generate rectangles expect_equal( sf::st_as_sfc(rct(1, 2, 3, 4, crs = 4326)), sf::st_as_sfc(sf::st_bbox(c(xmin = 1, ymin = 2, xmax = 3, ymax = 4), crs = 4326)) ) expect_equal( sf::st_as_sf(rct(1, 2, 3, 4, crs = 4326)), sf::st_as_sf( data.frame( geometry = sf::st_as_sfc( sf::st_bbox(c(xmin = 1, ymin = 2, xmax = 3, ymax = 4), crs = 4326) ) ) ) ) # crc only generates circles expect_equal( as_rct(sf::st_bbox(sf::st_as_sfc(crc(1, 2, 3)))), rct(-2, -1, 4, 5) ) expect_equal( as_rct(sf::st_bbox(sf::st_as_sf(crc(1, 2, 3)))), rct(-2, -1, 4, 5) ) # grid objects grid <- grd(nx = 1, ny = 1, type = "centers") wk_crs(grid) <- sf::st_crs("OGC:CRS84") expect_identical( sf::st_as_sfc(grid), sf::st_sfc(sf::st_point(c(0.5, 0.5)), crs = sf::st_crs("OGC:CRS84")) ) expect_identical( sf::st_as_sf(grid), sf::st_as_sf( data.frame( geometry = sf::st_sfc(sf::st_point(c(0.5, 0.5)), crs = sf::st_crs("OGC:CRS84")) ) ) ) }) test_that("wk_handle.sfg works", { skip_if_not_installed("sf") expect_identical( wk_handle(wkt("POINT (1 2)"), wkb_writer()), wk_handle(sf::st_point(c(1, 2)), wkb_writer()) ) }) test_that("wk_handle.bbox works", { skip_if_not_installed("sf") expect_identical( wk_handle(sf::st_bbox(sf::st_linestring(rbind(c(0, 1), c(2, 3)))), wkb_writer()), wk_handle(rct(0, 1, 2, 3), wkb_writer()) ) }) test_that("wk_translate.sfc() works", { skip_if_not_installed("sf") expect_identical( wk_translate(wkt("POINT (1 2)", crs = 4326), sf::st_sfc(crs = 4326)), sf::st_sfc(sf::st_point(c(1, 2)), crs = 4326) ) }) test_that("wk_translate() works for sf", { skip_if_not_installed("sf") expect_identical( wk_translate( sf::st_as_sf(data.frame(geometry = sf::st_as_sfc("POINT (1 2)"))), sf::st_as_sf(data.frame(a = sf::st_sfc())) ), sf::st_as_sf(data.frame(geometry = sf::st_as_sfc("POINT (1 2)"))) ) expect_identical( wk_translate( data.frame(a = 1, geometry = wkt("POINT (1 2)")), sf::st_as_sf(data.frame(a = sf::st_sfc())) ), sf::st_as_sf(data.frame(a = 1, geometery = sf::st_as_sfc("POINT (1 2)"))) ) expect_identical( wk_translate(as_wkb("POINT (1 2)"), sf::st_as_sf(data.frame(a = sf::st_sfc()))), sf::st_as_sf(data.frame(a = sf::st_as_sfc("POINT (1 2)"))) ) expect_identical( wk_translate( as_wkb("POINT (1 2)", crs = 4326), sf::st_as_sf(data.frame(a = sf::st_sfc(crs = 4326))) ), sf::st_as_sf(data.frame(a = sf::st_as_sfc("POINT (1 2)", crs = 4326))) ) }) test_that("wk_restore() works for sf", { skip_if_not_installed("sf") expect_identical( wk_identity(sf::st_as_sf(data.frame(a = sf::st_as_sfc("POINT (1 2)")))), sf::st_as_sf(data.frame(a = sf::st_as_sfc("POINT (1 2)"))) ) expect_identical( wk_identity(sf::st_as_sf(data.frame(a = sf::st_as_sfc("POINT (1 2)", crs = 4326)))), sf::st_as_sf(data.frame(a = sf::st_as_sfc("POINT (1 2)", crs = 4326))) ) expect_identical( wk_restore( sf::st_as_sf(data.frame(geometry = sf::st_as_sfc("POINT (1 2)"))), sf::st_as_sfc(c("POINT (3 4)", "POINT (5 6)")) ), sf::st_as_sf(data.frame(geometry = sf::st_as_sfc(c("POINT (3 4)", "POINT (5 6)")))) ) expect_error( wk_restore( sf::st_as_sf(data.frame(geometry = sf::st_as_sfc(rep("POINT (1 2)", 3)))), sf::st_as_sfc(c("POINT (3 4)", "POINT (5 6)")) ), "Can't assign" ) }) test_that("st_geometry() methods are defined for wk objects", { skip_if_not_installed("sf") expect_identical(sf::st_geometry(wkb()), sf::st_as_sfc(wkb())) expect_identical(sf::st_geometry(wkt()), sf::st_as_sfc(wkt())) expect_identical(sf::st_geometry(xy()), sf::st_as_sfc(xy())) expect_identical(sf::st_geometry(rct()), sf::st_as_sfc(rct())) expect_identical(sf::st_geometry(crc()), sf::st_as_sfc(crc())) grid <- grd(nx = 1, ny = 1, type = "centers") expect_identical(sf::st_geometry(grid), sf::st_as_sfc(grid)) }) test_that("st_bbox() methods are defined for wk objects", { skip_if_not_installed("sf") sf_obj <- sf::st_as_sfc("LINESTRING (0 1, 2 3)", crs = 32620) bbox_obj <- sf::st_bbox(sf_obj) expect_identical(sf::st_bbox(as_wkb(sf_obj)), bbox_obj) expect_identical(sf::st_bbox(as_wkt(sf_obj)), bbox_obj) expect_identical(sf::st_bbox(as_xy(wk_vertices(sf_obj))), bbox_obj) expect_identical(sf::st_bbox(rct(0, 1, 2, 3, crs = 32620)), bbox_obj) expect_identical(sf::st_bbox(crc(1, 2, 1, crs = 32620)), bbox_obj) grid <- grd(nx = 1, ny = 1, type = "centers") wk_crs(grid) <- sf::st_crs("OGC:CRS84") expect_identical( sf::st_bbox(grid), sf::st_bbox( sf::st_as_sfc(rct(0.5, 0.5, 0.5, 0.5, crs = sf::st_crs("OGC:CRS84"))) ) ) }) test_that("st_crs() methods are defined for wk objects", { skip_if_not_installed("sf") sf_obj <- sf::st_as_sfc("LINESTRING (0 1, 2 3)", crs = 32620) crs_obj <- sf::st_crs(sf_obj) expect_identical(sf::st_crs(as_wkb(sf_obj)), crs_obj) expect_identical(sf::st_crs(as_wkt(sf_obj)), crs_obj) expect_identical(sf::st_crs(as_xy(wk_vertices(sf_obj))), crs_obj) expect_identical(sf::st_crs(rct(0, 1, 2, 3, crs = 32620)), crs_obj) expect_identical(sf::st_crs(crc(1, 2, 1, crs = 32620)), crs_obj) grid <- grd(nx = 1, ny = 1, type = "centers") wk_crs(grid) <- sf::st_crs("OGC:CRS84") expect_identical( sf::st_crs(grid), sf::st_crs("OGC:CRS84") ) }) test_that("st_crs<-() methods are defined for wk objects", { skip_if_not_installed("sf") sf_obj <- sf::st_as_sfc("LINESTRING (0 1, 2 3)") crs_obj <- sf::st_crs(32620) expect_identical(wk_crs(sf::st_set_crs(as_wkb(sf_obj), 32620)), crs_obj) expect_identical(wk_crs(sf::st_set_crs(as_wkt(sf_obj), 32620)), crs_obj) expect_identical(wk_crs(sf::st_set_crs(as_xy(wk_vertices(sf_obj)), 32620)), crs_obj) expect_identical(wk_crs(sf::st_set_crs(rct(0, 1, 2, 3), 32620)), crs_obj) expect_identical(wk_crs(sf::st_set_crs(crc(1, 2, 1), 32620)), crs_obj) grid <- grd(nx = 1, ny = 1, type = "centers") sf::st_crs(grid) <- sf::st_crs("OGC:CRS84") expect_identical( sf::st_crs(grid), sf::st_crs("OGC:CRS84") ) }) wk/tests/testthat/test-debug.R0000644000176200001440000000656714161345517016110 0ustar liggesusers test_that("debug handlers print messages from the wkt handler", { wkt_good <- as_wkt( c( NA, "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ) expect_output( wk_handle(wkt_good, wk_debug_filter()), "null_feature.*?POINT.*?LINESTRING.*?POLYGON.*?MULTIPOINT.*?MULTILINESTRING.*?MULTIPOLYGON.*?GEOMETRYCOLLECTION.*?POINT.*?LINESTRING" ) wkt_bad <- new_wk_wkt("NOT WKT") expect_error( expect_output( wk_handle(wkt_bad, wk_debug_filter()), "Expected geometry type or 'SRID='" ), "Expected geometry type or 'SRID='" ) }) test_that("debug handlers print messages from the wkb handler", { wkb_good <- as_wkb( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ) expect_output( wk_handle(wkb_good, wk_debug_filter()), "POINT.*?LINESTRING.*?POLYGON.*?MULTIPOINT.*?MULTILINESTRING.*?MULTIPOLYGON.*?GEOMETRYCOLLECTION.*?POINT.*?LINESTRING" ) wkb_bad <- unclass(wkb_good[1]) wkb_bad[[1]][2:3] <- as.raw(0xff) expect_error( expect_output( wk_handle(new_wk_wkb(wkb_bad), wk_debug_filter()), "Unrecognized geometry type code" ) ) }) test_that("vector attributes are printed by wk_debug()", { skip_if_not_installed("sf") # sfc is currently the only handler that has vector types, dims, and WK_ABORT expect_output(wk_debug(sf::st_sfc(sf::st_point())), "POINT B\\[1\\]") expect_output(wk_debug(sf::st_sfc(sf::st_linestring())), "LINESTRING B\\[1\\]") expect_output(wk_debug(sf::st_sfc(sf::st_polygon())), "POLYGON B\\[1\\]") expect_output(wk_debug(sf::st_sfc(sf::st_multipoint())), "MULTIPOINT B\\[1\\]") expect_output(wk_debug(sf::st_sfc(sf::st_multilinestring())), "MULTILINESTRING B\\[1\\]") expect_output(wk_debug(sf::st_sfc(sf::st_multipolygon())), "MULTIPOLYGON B\\[1\\]") expect_output(wk_debug(sf::st_sfc(sf::st_geometrycollection())), "GEOMETRYCOLLECTION B\\[1\\]") expect_output(wk_debug(sf::st_sfc(sf::st_point(c(1, 2, 3, 4)))), "POINT ZMB.*?POINT ZM") expect_output(wk_debug(sf::st_sfc()), "\\[EMPTY\\]") obj <- sf::st_sfc(sf::st_point(c( 1 / 3, 1 / 3))) sf::st_precision(obj) <- 0.01 expect_output(wk_debug(obj), "POINT P\\[1\\]") sf::st_precision(obj) <- 1L expect_output(wk_debug(obj), "POINT P\\[1\\]") attr(obj, "precision") <- NULL expect_output(wk_debug(obj), "POINT\\[1\\]") expect_output(wk_debug(sf::st_as_sfc("POINT (1 2)"), wk_bbox_handler()), "WK_ABORT") }) test_that("wk_debug() prints error information", { expect_output( wk_debug(new_wk_wkt("NOT WKT"), wk_problems_handler()), "=> WK_ABORT_FEATURE" ) }) test_that("wk_debug() runs the debug handler", { expect_identical( expect_output( wk_debug(wkt("POINT (1 2)"), handler = wkb_writer()), "POINT" ), as_wkb("POINT (1 2)") ) }) test_that("wk_debug() works for a vector of indeterminate length", { expect_output( handle_wkt_without_vector_size(wkt(), wk_debug_filter()), "\\[UNKNOWN\\]" ) }) wk/tests/testthat/test-wkt.R0000644000176200001440000000635614313206670015616 0ustar liggesusers test_that("wkt class works", { x <- wkt("POINT (40 10)") expect_s3_class(x, "wk_wkt") expect_s3_class(x, "wk_vctr") expect_true(is_wk_wkt(x)) expect_output(print(x), "wk_wkt") expect_identical(as.character(x), unclass(x)) expect_s3_class(wkt(NA), "wk_wkt") expect_error(new_wk_wkt(structure(character(), thing = "stuff")), "must be a character") expect_error(new_wk_wkt(list()), "must be a character") expect_error(wkt("NOPE"), "Encountered 1 parse problem") expect_error(wkt(rep("NOPE", 10)), "Encountered 10 parse problems") expect_error(validate_wk_wkt(list()), "must be of type character") expect_error(validate_wk_wkt(""), "must inherit from") expect_s3_class(x[1], "wk_wkt") expect_identical(x[[1]], x[1]) expect_s3_class(c(x, x), "wk_wkt") expect_identical(rep(x, 2), c(x, x)) expect_identical(rep(wkt(), 1), wkt()) expect_length(c(x, x), 2) x[1] <- as_wkb("POINT (11 12)") expect_identical(x[1], wkt("POINT (11 12)")) skip_if_not(packageVersion("base") >= "3.6") expect_identical(rep_len(x, 2), c(x, x)) }) test_that("wkt() and parse_wkt() strip attributes", { text <- structure("POINT (40 10)", some_attr = "value") expect_identical(wkt(text), wkt("POINT (40 10)")) expect_identical(parse_wkt(text), wkt("POINT (40 10)")) }) test_that("as_wkt() works", { x <- wkt("POINT (40 10)") expect_identical(as_wkt(x), x) expect_identical(as_wkt("POINT (43 44)"), wkt("POINT (43 44)")) expect_identical(as_wkt(wkb(wkt_translate_wkb("POINT (99 100)"))), wkt("POINT (99 100)")) }) test_that("parse_wkt() works", { x <- "POINT (40 10)" expect_silent(parsed <- parse_wkt(x)) expect_false(is.na(parsed)) expect_null(attr(parsed, "problems")) x <- "POINT ENTPY" expect_warning(parsed <- parse_wkt(x), "Encountered 1 parse problem") expect_true(is.na(parsed)) expect_s3_class(attr(parsed, "problems"), "data.frame") expect_identical(nrow(attr(parsed, "problems")), 1L) }) test_that("wkt() propagates CRS", { x <- wkt("POINT (1 2)") wk_crs(x) <- 1234 expect_identical(wk_crs(x[1]), 1234) expect_identical(wk_crs(c(x, x)), 1234) expect_identical(wk_crs(rep(x, 2)), 1234) expect_error(x[1] <- wkt(x, crs = NULL), "are not equal") x[1] <- wkt(x, crs = 1234L) expect_identical(wk_crs(x), 1234) }) test_that("wkt() propagates geodesic", { x <- wkt("POINT (1 2)", geodesic = TRUE) expect_true(wk_is_geodesic(x)) expect_true(wk_is_geodesic(x[1])) expect_true(wk_is_geodesic(c(x, x))) expect_true(wk_is_geodesic(rep(x, 2))) expect_error(x[1] <- wk_set_geodesic(x, FALSE), "objects have differing values") x[1] <- wk_set_geodesic(x, TRUE) expect_true(wk_is_geodesic(x)) }) test_that("as_wkt() propagates CRS", { x <- as_wkt("POINT (1 2)", crs = 1234) expect_identical(wk_crs(x), 1234) expect_identical(wk_crs(as_wkt(as_wkb(wkt("POINT (1 2)", crs = 1234)))), 1234) }) test_that("as_wkt() propagates geodesic", { x <- as_wkt("POINT (1 2)", geodesic = TRUE) expect_true(wk_is_geodesic(x)) expect_true(wk_is_geodesic(as_wkt(as_wkb(wkt("POINT (1 2)", geodesic = TRUE))))) }) test_that("examples as wkt roundtrip", { for (which in names(wk_example_wkt)) { expect_identical( wk_handle(wk_example(!!which, crs = NULL), wkt_writer()), wk_example(!!which, crs = NULL) ) } }) wk/tests/testthat/test-problems.R0000644000176200001440000000457714163200202016623 0ustar liggesusers test_that("wk_problems() reports parsing errors for wkb", { point <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical(wk_problems(new_wk_wkb(list(point))), NA_character_) expect_match(wk_problems(new_wk_wkb(list(point[1:5]))), "Unexpected end of buffer") point_bad_type <- point point_bad_type[2:3] <- as.raw(0xff) expect_match(wk_problems(new_wk_wkb(list(point_bad_type))), "Unrecognized geometry type code") }) test_that("wk_problems() reports parsing errors for wkt", { expect_identical(wk_problems(new_wk_wkt("POINT (30 10)")), NA_character_) expect_match(wk_problems(new_wk_wkt("sss")), "Expected geometry type or") }) test_that("validating handlers return a character vector of problems", { wkb_good <- wk_handle( new_wk_wkt( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ), wkb_writer(endian = 1L) ) expect_identical( wk_handle(wkb_good, wk_problems_handler()), rep(NA_character_, length(wkb_good)) ) wkb_bad <- unclass(wkb_good) wkb_bad[[1]][3:4] <- as.raw(0xff) problems <- wk_handle(new_wk_wkb(wkb_bad), wk_problems_handler()) expect_match(problems[1], "^Unrecognized geometry type code") expect_identical(problems[-1], c(rep(NA_character_, length(wkb_good) - 1))) }) test_that("validating handlers return a character vector of problems for WKT", { wkt_good <- as_wkt( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ) expect_identical( wk_handle(wkt_good, wk_problems_handler()), rep(NA_character_, length(wkt_good)) ) wkt_bad <- unclass(wkt_good) wkt_bad[1] <- "NOT WKT" expect_identical( wk_handle(new_wk_wkt(wkt_bad), wk_problems_handler()), c("Expected geometry type or 'SRID=' but found 'NOT' at byte 0", rep(NA_character_, length(wkt_good) - 1)) ) }) wk/tests/testthat/test-grd.R0000644000176200001440000001161014315177334015560 0ustar liggesusers test_that("grd_rct() works", { grid <- grd_rct(volcano) expect_s3_class(grid, "wk_grd_rct") expect_s3_class(grid, "wk_grd") expect_identical(as_grd_rct(grid), grid) expect_null(wk_crs(grid)) expect_identical(wk_bbox(grid), rct(0, 0, 61, 87)) expect_match(format(grid), "wk_grd_rct") expect_output(print(grid), "wk_grd_rct") grid_crs <- wk_set_crs(grid, 1234) expect_match(format(grid_crs), "wk_grd_rct") expect_output(print(grid_crs), "wk_grd_rct") }) test_that("grd_rct() works for an empty grid", { empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical(wk_bbox(empty), wk_bbox(xy(crs = NULL))) }) test_that("grd_xy() works for an empty grid", { empty <- grd_xy(matrix(nrow = 0, ncol = 0)) expect_identical(wk_bbox(empty), wk_bbox(xy(crs = NULL))) }) test_that("grd_xy() works for h/v lines", { hline <- grd_xy(matrix(nrow = 10, ncol = 1), rct(0, 0, 0, 1)) expect_identical(wk_bbox(hline), rct(0, 0, 0, 1)) vline <- grd_xy(matrix(nrow = 1, ncol = 10), rct(0, 0, 1, 0)) expect_identical(wk_bbox(vline), rct(0, 0, 1, 0)) }) test_that("grd_xy() works", { grid <- grd_xy(volcano) expect_s3_class(grid, "wk_grd_xy") expect_s3_class(grid, "wk_grd") expect_identical(as_grd_xy(grid), grid) expect_null(wk_crs(grid)) expect_identical(wk_bbox(grid), rct(0, 0, 60, 86)) expect_match(format(grid), "wk_grd_xy") expect_output(print(grid), "wk_grd_xy") grid_crs <- wk_set_crs(grid, 1234) expect_match(format(grid_crs), "wk_grd_xy") expect_output(print(grid_crs), "wk_grd_xy") }) test_that("grd_xy <-> grd_rct converters work", { grid <- grd_rct(volcano) expect_identical(as_grd_rct(as_grd_xy(grid)), grid) empty <- grd_rct(matrix(nrow = 0, ncol = 0)) expect_identical(as_grd_rct(as_grd_xy(empty)), empty) # make sure x/y value don't get lost for a single row/col expect_identical( wk_bbox(as_grd_rct(as_grd_xy(grd(nx = 10, ny = 1)))), rct(0, 0.5, 10, 0.5) ) expect_identical( wk_bbox(as_grd_rct(as_grd_xy(grd(nx = 1, ny = 10)))), rct(0.5, 0, 0.5, 10) ) }) test_that("grd() works", { grid_bbox <- grd( bbox = xy(c(0, 10), c(0, 20)), nx = 10, ny = 20, type = "polygons" ) expect_identical(dim(grid_bbox$data), c(20L, 10L, 0L)) expect_identical(wk_bbox(grid_bbox), rct(0, 0, 10, 20)) grid_nxny <- grd(nx = 10, ny = 20, type = "polygons") expect_identical(dim(grid_nxny$data), c(20L, 10L, 0L)) expect_identical(wk_bbox(grid_nxny), rct(0, 0, 10, 20)) grid_dxdy <- grd(rct(0, 0, 10, 20), dx = 1, dy = 4) expect_identical(dim(grid_dxdy$data), c(5L, 10L, 0L)) expect_identical(wk_bbox(grid_dxdy), rct(0, 0, 10, 20)) center_nxny <- grd(nx = 10, ny = 20, type = "centers") expect_identical(dim(center_nxny$data), c(20L, 10L, 0L)) expect_identical(wk_bbox(center_nxny), rct(0.5, 0.5, 9.5, 19.5)) center_dxdy <- grd(rct(0, 0, 10, 20), dx = 1, dy = 4, type = "centers") expect_identical(dim(center_dxdy$data), c(5L, 10L, 0L)) expect_identical(wk_bbox(center_dxdy), rct(0.5, 2, 9.5, 18)) corner_nxny <- grd(nx = 10, ny = 20, type = "corners") expect_identical(dim(corner_nxny$data), c(21L, 11L, 0L)) expect_identical(wk_bbox(corner_nxny), rct(0, 0, 10, 20)) corner_dxdy <- grd(rct(0, 0, 10, 20), dx = 1, dy = 4, type = "corners") expect_identical(dim(corner_dxdy$data), c(6L, 11L, 0L)) expect_identical(wk_bbox(corner_dxdy), rct(0, 0, 10, 20)) expect_error(grd(), "Must specify") }) test_that("grd matrix interface works", { grid <- grd_rct(array(1:24, dim = c(2, 3, 4))) expect_identical(grid[1, 1, ], grd_subset(grid, 1, 1)) expect_identical(grid[, , 1], grd_rct(grid$data[, , 1, drop = FALSE])) expect_identical(dim(grid)[1], 2L) expect_identical(dim(grid)[2], 3L) # rct subsetting expect_identical(grid[rct(0.1, 1.1, 0.9, 1.9), ], grd_subset(grid, 1, 1)) expect_equal(unname(dim(grid[rct(0.1, 1.1, 0.9, 1.9), 1])), c(1L, 1L, 1L)) grid$data <- grid$data[, , 1, drop = TRUE] expect_identical(grid[rct(0.1, 1.1, 0.9, 1.9), ], grd_subset(grid, 1, 1)) }) test_that("grd[[]]<- interface works", { grid <- grd_rct(array(1:24, dim = c(2, 3, 4))) grid[["bbox"]] <- rct(0, 0, 1, 1) expect_identical(wk_bbox(grid), rct(0, 0, 1, 1)) # make sure this is normalized/converted grid[["bbox"]] <- rct(1, 1, 0, 0) expect_identical(wk_bbox(grid), rct(0, 0, 1, 1)) grid[["bbox"]] <- as_wkb(rct(1, 1, 0, 0)) expect_identical(wk_bbox(grid), rct(0, 0, 1, 1)) expect_error(grid[["bbox"]] <- rct(NA, 1, 0, 0), "Can't set missing") grid[["data"]] <- matrix() expect_identical(grid, grd_rct(matrix(), rct(0, 0, 1, 1))) expect_error(grid[["not_data_or_bbox"]] <- NULL, "Can't set element") }) test_that("grd$<- interface works", { grid <- grd_rct(array(1:24, dim = c(2, 3, 4))) grid$bbox <- rct(0, 0, 1, 1) expect_identical(wk_bbox(grid), rct(0, 0, 1, 1)) grid$data <- matrix() expect_identical(grid, grd_rct(matrix(), rct(0, 0, 1, 1))) expect_error(grid$not_data_or_bbox <- NULL, "Can't set element") }) wk/tests/testthat/test-set.R0000644000176200001440000000276614106220314015574 0ustar liggesusers test_that("wk_set_(z|m)() works", { expect_identical(wk_set_z(wkt("POINT (0 1)"), 2), wkt("POINT Z (0 1 2)")) expect_identical(wk_set_m(wkt("POINT (0 1)"), 2), wkt("POINT M (0 1 2)")) expect_identical(wk_set_z(wkt("POINT M (0 1 3)"), 2), wkt("POINT ZM (0 1 2 3)")) expect_identical(wk_set_m(wkt("POINT Z (0 1 3)"), 2), wkt("POINT ZM (0 1 3 2)")) expect_identical(wk_set_z(wkt("POINT ZM (0 1 2 3)"), 7), wkt("POINT ZM (0 1 7 3)")) expect_identical(wk_set_m(wkt("POINT ZM (0 1 2 3)"), 7), wkt("POINT ZM (0 1 2 7)")) }) test_that("wk_drop_(z|m) works", { expect_identical(wk_drop_z(wkt("POINT ZM (0 1 2 3)")), wkt("POINT M (0 1 3)")) expect_identical(wk_drop_m(wkt("POINT ZM (0 1 2 3)")), wkt("POINT Z (0 1 2)")) }) test_that("wk_trans_set() is vectorized", { expect_identical( wk_handle( rep(wkt("POINT Z (0 0 0)"), 4), wk_transform_filter(wkt_writer(), wk_trans_set(xyz(NA, NA, c(1, 2)), use_z = TRUE)) ), rep(wkt(c("POINT Z (0 0 1)", "POINT Z (0 0 2)")), 2) ) }) test_that("wk_trans_set() can set ZM values at the same time", { expect_identical( wk_handle( wkt("POINT (0 0)"), wk_transform_filter( wkt_writer(), wk_trans_set(xyzm(NA, NA, 1, 2), use_z = TRUE, use_m = TRUE) ) ), wkt("POINT ZM (0 0 1 2)") ) }) test_that("wk_trans_set() can set XY values", { expect_identical( wk_handle( wkt("POINT Z (0 0 0)"), wk_transform_filter(wkt_writer(), wk_trans_set(xy(1, 2))) ), wkt("POINT Z (1 2 0)") ) }) wk/tests/testthat/test-handle-sfc.R0000644000176200001440000001012514106220314016771 0ustar liggesusers test_that("wk_handle.sfc() works", { skip_if_not_installed("sf") expect_identical( wk_handle( sf::st_sfc( sf::st_point(), sf::st_linestring(), sf::st_polygon(), sf::st_multipoint(), sf::st_multilinestring(), sf::st_multipolygon(), sf::st_geometrycollection() ), wkt_writer() ), wkt( c("POINT EMPTY", "LINESTRING EMPTY", "POLYGON EMPTY", "MULTIPOINT EMPTY", "MULTILINESTRING EMPTY", "MULTIPOLYGON EMPTY", "GEOMETRYCOLLECTION EMPTY" ) ) ) expect_identical( wk_handle(sf::st_sfc(sf::st_point(c(1, 2))), wkt_writer()), wkt("POINT (1 2)") ) expect_identical( wk_handle(sf::st_sfc(sf::st_point(c(1, 2, 3))), wkt_writer()), wkt("POINT Z (1 2 3)") ) expect_identical( wk_handle(sf::st_sfc(sf::st_point(c(1, 2, 4), "XYM")), wkt_writer()), wkt("POINT M (1 2 4)") ) expect_identical( wk_handle(sf::st_sfc(sf::st_point(c(1, 2, 3, 4))), wkt_writer()), wkt("POINT ZM (1 2 3 4)") ) expect_identical( wk_handle(sf::st_sfc(sf::st_linestring(rbind(c(1, 2), c(2, 3)))), wkt_writer()), wkt("LINESTRING (1 2, 2 3)") ) expect_identical( wk_handle(sf::st_sfc(sf::st_polygon(list(rbind(c(0, 0), c(1, 0), c(0, 1), c(0, 0))))), wkt_writer()), wkt("POLYGON ((0 0, 1 0, 0 1, 0 0))") ) expect_identical( wk_handle(sf::st_sfc(sf::st_multipoint(rbind(c(1, 2), c(2, 3)))), wkt_writer()), wkt("MULTIPOINT ((1 2), (2 3))") ) expect_identical( wk_handle(sf::st_sfc(sf::st_multilinestring(list(rbind(c(1, 2), c(2, 3))))), wkt_writer()), wkt("MULTILINESTRING ((1 2, 2 3))") ) expect_identical( wk_handle( sf::st_sfc(sf::st_multipolygon(list(list(rbind(c(0, 0), c(1, 0), c(0, 1), c(0, 0)))))), wkt_writer() ), wkt("MULTIPOLYGON (((0 0, 1 0, 0 1, 0 0)))") ) expect_identical( wk_handle(sf::st_sfc(sf::st_geometrycollection(list(sf::st_point(c(1, 2))))), wkt_writer()), wkt("GEOMETRYCOLLECTION (POINT (1 2))") ) }) test_that("wk_handle.sfc() generates same WKB as st_as_binary", { skip_if_not_installed("sf") nc_multipolygon <- sf::read_sf(system.file("shape/nc.shp", package = "sf"))$geometry nc_multilines <- sf::st_boundary(nc_multipolygon) nc_multipoints <- sf::st_cast(nc_multilines, "MULTIPOINT") nc_polygon <- sf::st_cast(nc_multipolygon, "POLYGON") nc_lines <- sf::st_cast(nc_multilines, "LINESTRING") nc_points <- sf::st_cast(nc_lines, "POINT") nc_collection <- sf::st_sfc(sf::st_geometrycollection(nc_multipolygon)) expect_identical( unclass(as_xy(sf::st_coordinates(nc_points))), unclass(wk_handle(nc_points, xy_writer())) ) expect_identical( unclass(sf::st_as_binary(nc_points)), unclass(wk_handle(nc_points, wkb_writer())) ) expect_identical( unclass(sf::st_as_binary(nc_lines)), unclass(wk_handle(nc_lines, wkb_writer())) ) expect_identical( unclass(sf::st_as_binary(nc_polygon)), unclass(wk_handle(nc_polygon, wkb_writer())) ) expect_identical( unclass(sf::st_as_binary(nc_multipoints)), unclass(wk_handle(nc_multipoints, wkb_writer())) ) expect_identical( unclass(sf::st_as_binary(nc_multilines)), unclass(wk_handle(nc_multilines, wkb_writer())) ) expect_identical( unclass(sf::st_as_binary(nc_multipolygon)), unclass(wk_handle(nc_multipolygon, wkb_writer())) ) expect_identical( unclass(sf::st_as_binary(nc_collection)), unclass(wk_handle(nc_collection, wkb_writer())) ) }) test_that("wk_handle.sfc() handles malformed input", { skip_if_not_installed("sf") bad_sfc <- unclass(sf::st_sfc(sf::st_point(c(1, 2)))) class(bad_sfc[[1]]) <- "sfg" expect_error( wk_handle.sfc(bad_sfc, wk_void_handler()), "Can't guess dimensions from class of 'sfg'" ) class(bad_sfc[[1]]) <- c("sfg", "XY") expect_error( wk_handle.sfc(bad_sfc, wk_void_handler()), "Unsupported sfg type" ) class(bad_sfc[[1]]) <- c("not_an_sfg", "XY") expect_error( wk_handle.sfc(bad_sfc, wk_void_handler()), "must inherit from 'sfg'" ) bad_sfc[1] <- list(NULL) expect_identical(wk_handle.sfc(bad_sfc, wkt_writer()), wkt(NA)) }) wk/tests/testthat/test-void.R0000644000176200001440000000366014161345517015752 0ustar liggesusers test_that("void handler can be created", { expect_s3_class(wk_void_handler(), "wk_void_handler") expect_s3_class(wk_void_handler(), "wk_handler") }) test_that("wk_void() does nothing", { expect_null(wk_void(wkt("POINT (1 2)"))) }) test_that("void handlers do nothing", { wkb_good <- wk_handle( new_wk_wkt( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ), wkb_writer(endian = 1L) ) expect_null(wk_handle(wkb_good, wk_void_handler())) wkb_bad <- unclass(wkb_good[1]) wkb_bad[[1]][3:4] <- as.raw(0xff) expect_error(wk_handle(new_wk_wkb(wkb_bad), wk_void_handler()), "Unrecognized geometry type code") }) test_that("void handlers cannot be re-used", { handler <- wk_void_handler() expect_null(wk_handle(as_wkb("POINT (1 1)"), handler)) expect_error(wk_handle(as_wkb("POINT (1 1)"), handler), "Can't re-use this wk_handler") }) test_that("void handlers do nothing when passed to the wkt handler", { wkt_good <- as_wkt( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ) expect_null(wk_handle(wkt_good, wk_void_handler())) wkt_bad <- new_wk_wkt("NOT WKT") expect_error(wk_handle(wkt_bad, wk_void_handler()), "Expected geometry type or 'SRID='") }) test_that("void handlers cannot be re-used when called from C++", { handler <- wk_void_handler() expect_null(wk_handle(as_wkt("POINT (1 1)"), handler)) expect_error(wk_handle(as_wkt("POINT (1 1)"), handler), "Can't re-use this wk_handler") }) wk/tests/testthat/test-filter.R0000644000176200001440000000102214472006062016256 0ustar liggesusers test_that("wk_identity() works", { diverse_wkt <- wkt( c( NA, "POINT EMPTY", "POINT (1 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))" ) ) expect_identical(wk_identity(diverse_wkt), diverse_wkt) expect_error(wk_identity(new_wk_wkt("NOT WKT")), "Expected") }) test_that("wk_identity() propagates attributes", { expect_identical( wk_identity( wkt("LINESTRING ZM (0 0 0 0, 1 0 0 0)", crs = 1234, geodesic = TRUE) ), wkt("LINESTRING ZM (0 0 0 0, 1 0 0 0)", crs = 1234, geodesic = TRUE) ) }) wk/tests/testthat/test-wk-vctr.R0000644000176200001440000000541714161345517016410 0ustar liggesusers test_that("wk_vctr class works", { x <- structure(1:5, class = "wk_vctr") expect_s3_class(x, "wk_vctr") expect_s3_class(x[1:2], "wk_vctr") expect_identical( c(x, x), structure(c(1:5, 1:5), class = "wk_vctr") ) expect_output(print(x), "wk_vctr") expect_output(print(stats::setNames(x, as.character(1:5))), "wk_vctr") expect_output(print(x[0]), "wk_vctr") expect_output(print(wk_set_crs(x, 1234)), "CRS=EPSG:1234") expect_output(expect_identical(str(x), x), "wk_vctr") expect_output(expect_identical(str(x[0]), x[0]), "wk_vctr\\[0\\]") old_opt <- options(max.print = 1000) expect_output( print(structure(1:1001, class = "wk_vctr")), "Reached max.print" ) options(old_opt) x[[3]] <- 13L expect_identical(unclass(x), c(1L, 2L, 13L, 4L, 5L)) expect_identical( data.frame(col_name = x), new_data_frame(list(col_name = x)) ) expect_error(as.data.frame(x), "cannot coerce") }) test_that("geodesic gets printed for geodesic objects", { x_geod <- wkt("POINT EMPTY", geodesic = TRUE) expect_output(print(x_geod), "geodesic wk_wkt") }) test_that("rep() works for list wk_vctrs", { expect_identical( rep(structure(list(NULL), class = "wk_vctr"), 3), structure(list(NULL, NULL, NULL), class = "wk_vctr") ) expect_identical( rep(structure(list(), class = "wk_vctr"), 3), structure(list(), class = "wk_vctr") ) }) test_that("rep() works for chr wk_vctrs", { expect_identical( rep(structure(NA_character_, class = "wk_vctr"), 3), structure(rep(NA_character_, 3), class = "wk_vctr") ) expect_identical( rep(structure(character(), class = "wk_vctr"), 3), structure(character(), class = "wk_vctr") ) }) test_that("rep_len() works for wk_vctr objects", { skip_if_not(packageVersion("base") >= "3.6") expect_identical( rep_len(structure(list(NULL), class = "wk_vctr"), 3), structure(list(NULL, NULL, NULL), class = "wk_vctr") ) expect_identical( rep_len(structure(list(), class = "wk_vctr"), 3), structure(list(NULL, NULL, NULL), class = "wk_vctr") ) expect_identical( rep_len(structure(NA_character_, class = "wk_vctr"), 3), structure(rep(NA_character_, 3), class = "wk_vctr") ) expect_identical( rep_len(structure(character(), class = "wk_vctr"), 3), structure(rep(NA_character_, 3), class = "wk_vctr") ) }) test_that("c() for wk_vctr handles crs attributes", { expect_identical( wk_crs(c(wkt("POINT (0 1)", crs = wk_crs_inherit()), wkt("POINT (0 2)", crs = 1234))), 1234 ) expect_error( wk_crs(c(wkt("POINT (0 1)"), wkt("POINT (0 2)", crs = 1234))), "are not equal" ) }) test_that("wk_vctr objects with different subclasses can't be combined", { expect_error( c(as_wkt("POINT EMPTY"), as_wkb("POINT EMPTY")), "Can't combine" ) }) wk/tests/testthat/test-translate.R0000644000176200001440000000151114161345517016777 0ustar liggesusers test_that("wk_translate.wkt works", { expect_identical( wk_translate(as_wkb("POINT (1 2)"), wkt()), wkt("POINT (1 2)") ) }) test_that("wkt writing is vectorized", { expect_identical( wkt_translate_wkt(c("POINT (20 20)", "POINT (30 30)")), c("POINT (20 20)", "POINT (30 30)") ) }) test_that("wkb writing is vectorized", { expect_identical( wkt_translate_wkb(c("POINT (20 20)", "POINT (30 30)"), endian = 1L), list( as.raw( c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40 ) ), as.raw( c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40 ) ) ) ) }) wk/tests/testthat/test-affine.R0000644000176200001440000000760214125354157016241 0ustar liggesusers test_that("wk_trans_affine() works", { expect_s3_class(wk_affine_identity(), "wk_trans_affine") expect_output(print(wk_affine_identity()), "wk_trans_affine") expect_equal(format(as.matrix(wk_affine_identity())), format(wk_affine_identity())) }) test_that("wk_trans_affine() errors for invalid matrix", { expect_error(wk_trans_affine(5), "must be a 3x3 matrix") }) test_that("wk_affine_translate() works", { coords <- matrix(c(0, 0, 1, 1, 2, 2, 3, 3), nrow = 2) expect_equal( as.matrix(wk_affine_translate(2, 3)) %*% rbind(coords, 1), rbind(matrix(c(2, 3, 3, 4, 4, 5, 5, 6), nrow = 2), 1) ) }) test_that("wk_affine_rotate() works", { coords <- matrix(c(0, 0, 1, 1, 2, 2, 3, 3), nrow = 2) expect_equal( as.matrix(wk_affine_rotate(45)) %*% rbind(coords, 1), rbind(matrix(c(0, 0, 0, sqrt(2), 0, 2 * sqrt(2), 0, 3 * sqrt(2)), nrow = 2), 1) ) }) test_that("wk_affine_scale() works", { coords <- matrix(c(0, 0, 1, 1, 2, 2, 3, 3), nrow = 2) expect_equal( as.matrix(wk_affine_scale(2, 3)) %*% rbind(coords, 1), rbind(matrix(c(0, 0, 2, 3, 4, 6, 6, 9), nrow = 2), 1) ) }) test_that("wk_affine_rescale() works", { coords <- matrix(c(0, 0, 1, 1, 2, 2, 3, 3), nrow = 2) # make sure we pick an example where the rescale + translate order matters expect_equal( as.matrix(wk_affine_rescale(rct(1, 1, 2, 2), rct(12, 12, 13, 14))) %*% rbind(coords, 1), rbind(matrix(c(11, 11, 12, 13, 13, 15, 14, 17), nrow = 2), 1) ) }) test_that("wk_affine_fit() works", { src <- xy(c(0, 1, 0), c(0, 0, 1)) dst <- xy(c(0, 2, 0), c(0, 0, 3)) expect_equal( as.matrix(wk_affine_fit(src, dst)), as.matrix(wk_affine_scale(2, 3)) ) expect_equal( wk_transform(src, wk_affine_fit(src, dst)), dst ) }) test_that("wk_affine_compose() works", { expect_identical( as.matrix(wk_affine_compose()), as.matrix(wk_affine_identity()) ) comp <- wk_affine_compose( wk_affine_translate(1, 2), wk_affine_translate(3, 4) ) expect_s3_class(comp, "wk_trans_affine") expect_identical(as.matrix(comp), as.matrix(wk_affine_translate(4, 6))) comp <- wk_affine_compose( wk_affine_rotate(12), wk_affine_rotate(13) ) expect_equal(as.matrix(comp), as.matrix(wk_affine_rotate(25))) }) test_that("wk_affine_compose() can combine translation and rotation", { comp <- wk_affine_compose( wk_affine_translate(1, 0), wk_affine_rotate(90) ) comp_inverse <- wk_affine_compose( wk_affine_rotate(-90), wk_affine_translate(-1, 0) ) expect_equal( as.matrix(wk_affine_invert(comp_inverse)), as.matrix(comp) ) # check with actual coordinates coords <- matrix(c(0, 0, 1, 1, 2, 2, 3, 3), nrow = 2) coords1 <- as.matrix(wk_affine_translate(1, 0)) %*% rbind(coords, 1) coords2 <- as.matrix(wk_affine_rotate(90)) %*% coords1 # the first point will be wrong if the order was backward expect_equal( as.matrix(comp) %*% rbind(coords, 1)[, 1], matrix(c(0, 1, 1), ncol = 1) ) expect_equal( as.matrix(comp) %*% rbind(coords, 1), coords2 ) expect_equal( as.matrix(comp_inverse) %*% coords2, rbind(coords, 1) ) }) test_that("wk_affine_inverse() works", { expect_equal( as.matrix(wk_affine_compose(wk_affine_rotate(12), wk_affine_translate(1, 2))), as.matrix( wk_affine_invert( wk_affine_compose(wk_affine_translate(-1, -2), wk_affine_rotate(-12)) ) ) ) expect_equal( as.matrix(wk_affine_invert(wk_affine_translate(1, 2))), as.matrix(wk_trans_inverse(wk_affine_translate(1, 2))) ) }) test_that("wk_transform() works with an affine transformer", { expect_equal( wk_transform( xy(0:3, 0:3), wk_affine_identity() ), xy(0:3, 0:3) ) expect_equal( wk_transform( xy(0:3, 0:3), wk_affine_compose( wk_affine_translate(1, 0), wk_affine_rotate(90) ) ), c(xy(0, 1), xy(-1, 2), xy(-2, 3), xy(-3, 4)) ) }) wk/tests/testthat/test-class-data-frame.R0000644000176200001440000000634514163110540020104 0ustar liggesusers test_that("wk_handle() works for data.frame", { expect_error(wk_handle(data.frame(a = 1)), "must have at least one") expect_identical( wk_handle(data.frame(a = wkt("POINT (0 1)")), wkb_writer()), wk_handle(wkt("POINT (0 1)"), wkb_writer()) ) }) test_that("wk_writer() works for data.frame", { expect_s3_class(wk_writer(data.frame(wkt())), "wk_wkt_writer") expect_error(wk_writer(data.frame(a = 1)), "must have at least one") }) test_that("wk_restore() works for data.frame", { expect_identical( wk_identity(data.frame(a = wkt("POINT (1 2)"))), data.frame(a = wkt("POINT (1 2)")) ) expect_identical( wk_restore(data.frame(a = wkt("POINT (1 2)")), wkt(c("POINT (1 2)", "POINT (3 4)"))), data.frame(a = wkt(c("POINT (1 2)", "POINT (3 4)")), row.names = c("1", "1.1")) ) expect_error( wk_restore(data.frame(a = wkt(rep(NA, 3))), wkt(c("POINT (1 2)", "POINT (3 4)"))), "Can't assign" ) expect_identical( wk_identity(data.frame(a = wkt("POINT (1 2)", crs = 1234))), data.frame(a = wkt("POINT (1 2)", crs = 1234)) ) }) test_that("wk_restore() works for tibble", { expect_identical( wk_identity(tibble::tibble(a = wkt("POINT (1 2)"))), tibble::tibble(a = wkt("POINT (1 2)")) ) expect_identical( wk_identity(tibble::tibble(a = wkt("POINT (1 2)", crs = 1234))), tibble::tibble(a = wkt("POINT (1 2)", crs = 1234)) ) }) test_that("wk_translate() works for data.frame", { expect_identical( wk_translate(as_wkb("POINT (1 2)"), data.frame(a = wkt())), data.frame(a = wkt("POINT (1 2)")) ) expect_identical( wk_translate( tibble::tibble(a = as_wkb("POINT (1 2)")), data.frame(a = wkt()) ), data.frame(a = wkt("POINT (1 2)")) ) expect_identical( wk_translate( data.frame(a = as_wkb("POINT (1 2)")), data.frame(a = wkt()) ), data.frame(a = wkt("POINT (1 2)")) ) }) test_that("wk_translate() works for tibble::tibble()", { expect_identical( wk_translate(as_wkb("POINT (1 2)"), tibble::tibble(a = wkt())), tibble::tibble(a = wkt("POINT (1 2)")) ) expect_identical( wk_translate( tibble::tibble(a = as_wkb("POINT (1 2)")), tibble::tibble(a = wkt()) ), tibble::tibble(a = wkt("POINT (1 2)")) ) expect_identical( wk_translate( data.frame(a = as_wkb("POINT (1 2)")), tibble::tibble(a = wkt()) ), tibble::tibble(a = wkt("POINT (1 2)")) ) }) test_that("wk_handle_slice() works for data.frame", { expect_identical( wk_handle_slice(data.frame(geom = xy(1:5, 1:5)), xy_writer(), 3, 6), xy(3:5, 3:5) ) expect_identical( wk_handle_slice(data.frame(geom = xy(1:5, 1:5)), xy_writer(), 0, 2), xy(1:2, 1:2) ) expect_identical( wk_handle_slice(data.frame(geom = xy(1:5, 1:5)), xy_writer(), 5, 4), xy(crs = NULL) ) }) test_that("wk_crs() and wk_set_crs() work for data.frame", { df <- data.frame(a = wkt("POINT (1 2)", crs = 1234)) expect_identical(wk_crs(df), 1234) expect_identical(wk_crs(wk_set_crs(df, 5678)), 5678) }) test_that("wk_is_geodesic() and wk_set_geodesic() work for data.frame", { df <- data.frame(a = wkt("POINT (1 2)", geodesic = FALSE)) expect_false(wk_is_geodesic(df)) expect_true(wk_is_geodesic(wk_set_geodesic(df, TRUE))) }) wk/tests/testthat/test-grd-extract.R0000644000176200001440000000251014315177334017227 0ustar liggesusers test_that("grd_extract() works", { grid <- grd_rct(matrix(1:6, ncol = 3)) expect_identical( grd_extract(grid, i = 2, j = 2:3), array(c(4L, 6L)) ) }) test_that("grd_extract_nearest() works", { grid <- grd_rct(matrix(1:6, ncol = 3)) expect_identical( grd_extract_nearest(grid, grd_cell_xy(grid, i = 2, j = 2:3)), array(c(4L, 6L)) ) }) test_that("grd_data_extract() works for nativeRaster", { col_native <- structure( c(-16777216L, -13421773L, -10066330L, -15066598L, -11711155L, -8355712L), .Dim = 2:3, class = "nativeRaster" ) data_21 <- grd_data_extract(col_native, i = 2, j = 2:3) expect_identical( data_21, array(c(-11711155L, -8355712L)) ) }) test_that("grd_data_extract() works for matrix", { grid <- grd_rct(matrix(1:6, ncol = 3)) expect_identical( grd_data_extract(grid$data, i = 2, j = 2:3), array(c(4L, 6L)) ) }) test_that("grd_data_extract() works for matrix-like arrays", { grid <- grd_rct(array(1:6, dim = c(2L, 3L, 1L))) expect_identical( grd_data_extract(grid$data, i = 2, j = 2:3), array(c(4L, 6L), dim = c(2L, 1L)) ) }) test_that("grd_data_extract() errors for bad data", { grid <- grd_rct(array(1:6, dim = c(2L, 3L, 4L))) expect_error( grd_data_extract(grid$data, i = 2, j = 2:3), "not implemented for non-matrix-like data" ) }) wk/tests/testthat/test-handle-slice.R0000644000176200001440000000051214145575672017343 0ustar liggesusers test_that("wk_handle_slice() works", { expect_identical( wk_handle_slice(xy(1:5, 1:5), xy_writer(), 3, 6), xy(3:5, 3:5) ) expect_identical( wk_handle_slice(xy(1:5, 1:5), xy_writer(), 0, 2), xy(1:2, 1:2) ) expect_identical( wk_handle_slice(xy(1:5, 1:5), xy_writer(), 5, 4), xy(crs = NULL) ) }) wk/tests/testthat/test-crc.R0000644000176200001440000000345014321145376015554 0ustar liggesusers test_that("crc class works", { expect_s3_class(crc(), "wk_crc") expect_output(print(crc(1, 2, 3)), "\\[1 2, r = 3\\]") expect_identical(as_crc(crc(1, 2, 3)), crc(1, 2, 3)) expect_identical( as_crc(as.matrix(data.frame(x = 1, y = 2, r = 3))), crc(1, 2, 3) ) expect_identical( as_crc(data.frame(x = 1, y = 2, r = 3)), crc(1, 2, 3) ) expect_identical( as_crc(matrix(1:3, nrow = 1)), crc(1, 2, 3) ) }) test_that("coercion to and from wk* classes works", { expect_s3_class(as_wkt(crc(0, 0, 1)), "wk_wkt") expect_s3_class(as_wkb(crc(0, 0, 1)), "wk_wkb") expect_identical( wk_handle(crc(1, 2, 3), wkt_writer(precision = 2), n_segments = 4), wkt("POLYGON ((4 2, 1 5, -2 2, 1 -1, 4 2))") ) expect_identical( as_wkb(wk_handle(crc(1, 2, 3), wkt_writer(precision = 2), n_segments = 4)), as_wkb("POLYGON ((4 2, 1 5, -2 2, 1 -1, 4 2))") ) # check options for circle resolution + as_wkb/t() prev_opt <- options(wk.crc_n_segments = 4) expect_length( unclass(as_wkb(crc(1, 2, 3)))[[1]], 1 + 4 + 4 + 4 + 5 * 8 * 2 ) options(prev_opt) }) test_that("subset-assign works for crc", { x <- crc(1:2, 2:3, 3:4) x[1] <- crc(NA, NA, NA) expect_identical(x, c(crc(NA, NA, NA), crc(2, 3, 4))) }) test_that("crc() propagates CRS", { x <- crc(1, 2, 3) wk_crs(x) <- 1234 expect_identical(wk_crs(x[1]), 1234) expect_identical(wk_crs(c(x, x)), 1234) expect_identical(wk_crs(rep(x, 2)), 1234) expect_error(x[1] <- wk_set_crs(x, NULL), "are not equal") x[1] <- wk_set_crs(x, 1234L) expect_identical(wk_crs(x), 1234) }) test_that("crc() accessors return the correct components", { x <- crc(1, 2, r = 3) expect_identical(crc_x(x), 1) expect_identical(crc_y(x), 2) expect_identical(crc_r(x), 3) expect_identical(crc_center(x), xy(1, 2)) }) wk/tests/testthat/test-handle-xy.R0000644000176200001440000000173514515070354016700 0ustar liggesusers test_that("wk_handle.wk_xy() works", { expect_identical( wk_handle(xy(c(NA, NaN, 2, 3, NA), c(NA, NaN, NA, 4, 5)), wkt_writer()), wkt(c(NA, "POINT EMPTY", "POINT (2 nan)", "POINT (3 4)", "POINT (nan 5)")) ) expect_identical( wk_handle( xyz(c(NA, NaN, 2, 3, NA), c(NA, NaN, NA, 4, 5), c(NA, NaN, NA, NA, NA)), wkt_writer() ), wkt(c(NA, "POINT Z EMPTY", "POINT Z (2 nan nan)", "POINT Z (3 4 nan)", "POINT Z (nan 5 nan)")) ) expect_identical( wk_handle( xym(c(NA, NaN, 2, 3, NA), c(NA, NaN, NA, 4, 5), c(NA, NaN, NA, NA, NA)), wkt_writer() ), wkt(c(NA, "POINT M EMPTY", "POINT M (2 nan nan)", "POINT M (3 4 nan)", "POINT M (nan 5 nan)")) ) expect_identical( wk_handle( xyzm(c(NA, NaN, 2, 3, NA), c(NA, NaN, NA, 4, 5), c(NA, NaN, NA, NA, NA), c(NA, NaN, rep(1, 3))), wkt_writer() ), wkt(c(NA, "POINT ZM EMPTY", "POINT ZM (2 nan nan 1)", "POINT ZM (3 4 nan 1)", "POINT ZM (nan 5 nan 1)")) ) }) wk/tests/testthat/test-handle-wkt.R0000644000176200001440000003360614513020606017040 0ustar liggesusers test_that("basic translation works on non-empty 2D geoms", { expect_identical( wkt_translate_wkt("POINT (30 10)"), "POINT (30 10)" ) expect_identical( wkt_translate_wkt("LINESTRING (30 10, 0 0)"), "LINESTRING (30 10, 0 0)" ) expect_identical( wkt_translate_wkt("POLYGON ((30 10, 0 0, 10 10, 30 10))"), "POLYGON ((30 10, 0 0, 10 10, 30 10))" ) expect_identical( wkt_translate_wkt("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))"), "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))" ) expect_identical( wkt_translate_wkt("MULTIPOINT (30 10, 0 0, 10 10)"), "MULTIPOINT ((30 10), (0 0), (10 10))" ) expect_identical( wkt_translate_wkt("MULTIPOINT ((30 10), (0 0), (10 10))"), "MULTIPOINT ((30 10), (0 0), (10 10))" ) expect_identical( wkt_translate_wkt("MULTILINESTRING ((30 10, 0 0), (20 20, 0 0))"), "MULTILINESTRING ((30 10, 0 0), (20 20, 0 0))" ) expect_identical( wkt_translate_wkt("MULTIPOLYGON (((30 10, 0 0, 10 10, 30 10)), ((30 10, 0 0, 10 10, 30 10)))"), "MULTIPOLYGON (((30 10, 0 0, 10 10, 30 10)), ((30 10, 0 0, 10 10, 30 10)))" ) expect_identical( wkt_translate_wkt( "GEOMETRYCOLLECTION (POINT (30 10), GEOMETRYCOLLECTION (POINT (12 6)), LINESTRING (1 2, 3 4))" ), "GEOMETRYCOLLECTION (POINT (30 10), GEOMETRYCOLLECTION (POINT (12 6)), LINESTRING (1 2, 3 4))" ) }) test_that("basic translation works on empty geoms", { expect_identical( wkt_translate_wkt("POINT EMPTY"), "POINT EMPTY" ) expect_identical( wkt_translate_wkt("LINESTRING EMPTY"), "LINESTRING EMPTY" ) expect_identical( wkt_translate_wkt("POLYGON EMPTY"), "POLYGON EMPTY" ) expect_identical( wkt_translate_wkt("MULTIPOINT EMPTY"), "MULTIPOINT EMPTY" ) expect_identical( wkt_translate_wkt("MULTILINESTRING EMPTY"), "MULTILINESTRING EMPTY" ) expect_identical( wkt_translate_wkt("MULTIPOLYGON EMPTY"), "MULTIPOLYGON EMPTY" ) expect_identical( wkt_translate_wkt( "GEOMETRYCOLLECTION EMPTY" ), "GEOMETRYCOLLECTION EMPTY" ) }) test_that("mutli* geometries can contain empties", { expect_identical( wkt_translate_wkt("MULTIPOINT (EMPTY)"), "MULTIPOINT (EMPTY)" ) expect_identical( wkt_translate_wkt("MULTIPOINT (1 1, EMPTY)"), "MULTIPOINT ((1 1), EMPTY)" ) expect_identical( wkt_translate_wkt("MULTIPOINT ((1 1), EMPTY)"), "MULTIPOINT ((1 1), EMPTY)" ) expect_identical( wkt_translate_wkt("MULTILINESTRING (EMPTY)"), "MULTILINESTRING (EMPTY)" ) expect_identical( wkt_translate_wkt("MULTILINESTRING ((1 1, 2 4), EMPTY)"), "MULTILINESTRING ((1 1, 2 4), EMPTY)" ) expect_identical( wkt_translate_wkt("MULTIPOLYGON (((1 1, 2 4, 3 6)), EMPTY)"), "MULTIPOLYGON (((1 1, 2 4, 3 6)), EMPTY)" ) expect_identical( wkt_translate_wkt("MULTIPOLYGON (EMPTY)"), "MULTIPOLYGON (EMPTY)" ) }) test_that("Z, ZM, and M prefixes are parsed", { expect_identical( wkt_translate_wkt("POINT (30 10)"), "POINT (30 10)" ) expect_identical( wkt_translate_wkt("POINT Z (30 10 1)"), "POINT Z (30 10 1)" ) expect_identical( wkt_translate_wkt("POINT M (30 10 1)"), "POINT M (30 10 1)" ) expect_identical( wkt_translate_wkt("POINT ZM (30 10 0 1)"), "POINT ZM (30 10 0 1)" ) }) test_that("SRID prefixes are parsed", { expect_identical( wkt_translate_wkt("SRID=218;POINT (30 10)"), "SRID=218;POINT (30 10)" ) }) test_that("correctly formatted ZM geomteries are translated identically", { expect_identical( wkt_translate_wkt("POINT ZM (30 10 0 1)"), "POINT ZM (30 10 0 1)" ) expect_identical( wkt_translate_wkt("LINESTRING ZM (30 10 0 1, 0 0 2 3)"), "LINESTRING ZM (30 10 0 1, 0 0 2 3)" ) expect_identical( wkt_translate_wkt("POLYGON ZM ((30 10 2 1, 0 0 9 10, 10 10 10 8, 30 10 3 8))"), "POLYGON ZM ((30 10 2 1, 0 0 9 10, 10 10 10 8, 30 10 3 8))" ) expect_identical( wkt_translate_wkt("MULTIPOINT ZM (30 10 32 1, 0 0 2 8, 10 10 1 99)"), "MULTIPOINT ZM ((30 10 32 1), (0 0 2 8), (10 10 1 99))" ) expect_identical( wkt_translate_wkt("MULTIPOINT ZM ((30 10 32 1), (0 0 2 8), (10 10 1 99))"), "MULTIPOINT ZM ((30 10 32 1), (0 0 2 8), (10 10 1 99))" ) expect_identical( wkt_translate_wkt("MULTILINESTRING ZM ((30 10 2 1, 0 0 2 8), (20 20 1 1, 0 0 2 2))"), "MULTILINESTRING ZM ((30 10 2 1, 0 0 2 8), (20 20 1 1, 0 0 2 2))" ) expect_identical( wkt_translate_wkt("MULTIPOLYGON ZM (((30 10 1 3, 0 0 9 1, 10 10 5 9, 30 10 1 2)))"), "MULTIPOLYGON ZM (((30 10 1 3, 0 0 9 1, 10 10 5 9, 30 10 1 2)))" ) expect_identical( wkt_translate_wkt("GEOMETRYCOLLECTION (POINT ZM (30 10 1 2))"), "GEOMETRYCOLLECTION (POINT ZM (30 10 1 2))" ) }) test_that("wkt_translate_wkb() works on NA", { expect_identical(wkt_translate_wkb(NA_character_), list(NULL)) }) test_that("wkt_translate_wkb() works on empty points", { expect_identical( wkb_translate_wkt(wkt_translate_wkb("POINT EMPTY")), "POINT EMPTY" ) }) test_that("wkt_translate_wkb() works simple geometries", { # POINT (30 10) point <- as.raw(c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) # LINESTRING (30 10, 12 42) linestring <- as.raw(c(0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40)) # POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30)) polygon <- as.raw(c(0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40)) expect_identical(wkt_translate_wkb("POINT (30 10)", endian = 1L), list(point)) expect_identical( wkt_translate_wkb("LINESTRING (30 10, 12 42)", endian = 1L), list(linestring) ) expect_identical( wkt_translate_wkb( "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))", endian = 1L ), list(polygon) ) }) test_that("wkt_translate_wkb() works with multi geometries", { # MULTIPOINT ((10 40), (40 30), (20 20), (30 10)) multipoint <- as.raw(c(0x01, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical( wkt_translate_wkb("MULTIPOINT ((10 40), (40 30), (20 20), (30 10))", endian = 1L), list(multipoint) ) }) test_that("wkt_translate_wkb() works with nested collections", { wkt <- "GEOMETRYCOLLECTION ( POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)), GEOMETRYCOLLECTION ( POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)) ), GEOMETRYCOLLECTION EMPTY, POINT (30 10) )" collection <- as.raw(c(0x01, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x01, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x01, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40)) expect_identical(wkt_translate_wkb(wkt, endian = 1L), list(collection)) }) test_that("wkt_translate_* has reasonable error messages", { # one or more of these expectations fail on CRAN MacOS for R 3.6.2 # I can't replicate the check failure using a fresh install # of R 3.6.2 on MacOS Mojave, but as all of these functions # are intended to error anyway, I am skipping this check on # CRAN for that platform (with the danger that the errors # that are given are less informative than intended). is_macos <- Sys.info()["sysname"] == "Darwin" is_old_rel <- packageVersion("base") < "4.0.0" is_cran <- !identical(Sys.getenv("NOT_CRAN"), "true") skip_if(is_macos && is_old_rel && is_cran) # close enough to inf to trigger the parse check expect_error(wkt_translate_wkt("MULTIPOINT (iambic 3)"), "^Expected") expect_error(wkt_translate_wkt(""), "^Expected") expect_error(wkt_translate_wkt("SRID=fish;POINT (30 10)"), "^Expected") expect_error(wkt_translate_wkt("SRID="), "^Expected") expect_error(wkt_translate_wkt("POINT (fish fish)"), "^Expected") expect_error(wkt_translate_wkt("POINT ("), "^Expected") expect_error(wkt_translate_wkt("POINT (3"), "^Expected") expect_error(wkt_translate_wkt("POINT"), "^Expected") expect_error(wkt_translate_wkt("POINT "), "^Expected") expect_error(wkt_translate_wkt("POINT (30 10="), "^Expected") expect_error(wkt_translate_wkt("POINT (30 10)P"), "^Expected") expect_error(wkt_translate_wkt("LINESTRING (30 10, 0 0="), "^Expected") expect_error(wkt_translate_wkt("LINESTRING (30A"), "^Expected") expect_error(wkt_translate_wkt("LINESTRING (30,"), "^Expected") expect_error(wkt_translate_wkt("LINESTRING (30"), "^Expected") expect_error(wkt_translate_wkt("SRID=30A"), "^Expected") expect_error(wkt_translate_wkt("SRID"), "^Expected") expect_error( wkt_translate_wkt(strrep("a", 4096)), "Expected a value with fewer than 4096 character" ) }) test_that("wkt_translate_* can handle non-finite values", { expect_identical(wkt_translate_wkt("MULTIPOINT (nan nan)"), "MULTIPOINT ((nan nan))") }) test_that("wkt_translate_* doesn't segfault on other inputs", { expect_error(wkt_translate_wkt(as_wkb("POINT (30 10)")), "must be a character vector") }) wk/tests/testthat/test-format.R0000644000176200001440000000331714163200166016270 0ustar liggesusers test_that("format() works for wkt", { expect_identical( wk_format(wkt("LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)"), max_coords = 3), "LINESTRING (0 1, 2 3, 4 5..." ) expect_identical( wk_format(wkt("LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)"), max_coords = 10), "LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)" ) expect_identical(wk_format(wkt(NA_character_)), "") }) test_that("format() works for wkb", { expect_identical( wk_format(as_wkb("LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)"), max_coords = 3), "LINESTRING (0 1, 2 3, 4 5..." ) expect_identical( wk_format(as_wkb("LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)"), max_coords = 10), "LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)" ) expect_identical(wk_format(wkb(list(NULL))), "") }) test_that("format() handles errors", { bad_wkb <- unclass(wk_handle(new_wk_wkt("POINT (30 10)"), wkb_writer(endian = 1L))) bad_wkb[[1]][3:4] <- as.raw(0xff) expect_match(wk_format(new_wk_wkb(bad_wkb)), "!!!") expect_match(wk_format(new_wk_wkt("POINT ENTPY")), "!!!") }) test_that("format handlers return abbreviated WKT", { expect_identical( wk_handle( new_wk_wkt(c(NA, "LINESTRING (0 1, 1 2)", "LINESTRING (0 1, 2 3, 4 5)", "NOT WKT")), wkt_format_handler(max_coords = 3) ), c( "", "LINESTRING (0 1, 1 2)", "LINESTRING (0 1, 2 3, 4 5...", "!!! Expected geometry type or 'SRID=' but found 'NOT' at byte 0" ) ) }) test_that("wkt_format_handler() works for a vector of indeterminate length", { long_xy <- as_wkt(xy(runif(2048), runif(2048))) expect_identical( handle_wkt_without_vector_size(long_xy, wkt_format_handler()), wk_handle(long_xy, wkt_format_handler()) ) }) wk/tests/testthat/test-wkb-writer.R0000644000176200001440000000732214161345517017105 0ustar liggesusers test_that("wkb_writer() works", { wkb_good <- wk_handle( new_wk_wkt( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ), wkb_writer(endian = 1L) ) expect_identical( wk_handle(wkb_good, wkb_writer(endian = 1L)), wkb_good ) wkb_bad <- unclass(wkb_good[1]) wkb_bad[[1]][3:4] <- as.raw(0xff) expect_error(wk_handle(new_wk_wkb(wkb_bad), wkb_writer()), "Unrecognized geometry type code") }) test_that("wkb_writer() can generate swapped endian", { wkb_system <- wk_handle(wkt("LINESTRING (1 2, 3 4)"), wkb_writer(endian = NA)) wkb_le <- wk_handle(wkt("LINESTRING (1 2, 3 4)"), wkb_writer(endian = 1)) wkb_be <- wk_handle(wkt("LINESTRING (1 2, 3 4)"), wkb_writer(endian = 0)) expect_identical(as_wkt(wkb_system), wkt("LINESTRING (1 2, 3 4)")) expect_identical(as_wkt(wkb_le), wkt("LINESTRING (1 2, 3 4)")) expect_identical(as_wkt(wkb_be), wkt("LINESTRING (1 2, 3 4)")) expect_false(identical(wkb_be, wkb_le)) expect_identical( wkb_be, # dput(geos::geos_write_wkb("LINESTRING (1 2, 3 4)", endian = 0)) structure( list( as.raw( c(0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) ) ), class = c("wk_wkb", "wk_vctr") ) ) expect_identical( wkb_le, # dput(geos::geos_write_wkb("LINESTRING (1 2, 3 4)", endian = 1)) structure( list( as.raw( c(0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40 ) ) ), class = c("wk_wkb", "wk_vctr") ) ) }) test_that("wkb_writer() reallocates its buffer as needed", { expect_identical( wk_handle(wkt("POINT (1 2)"), wkb_writer(buffer_size = 0)), wk_handle(wkt("POINT (1 2)"), wkb_writer(buffer_size = 1024)) ) }) test_that("wkb_writer() works with streaming input", { wkb_good <- as_wkb( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ) expect_identical( wk_handle(as_wkt(wkb_good), wkb_writer()), wkb_good ) }) test_that("wkb_writer() errors when the recursion limit is too high", { make_really_recursive_geom <- function(n) { wkt(paste0( c(rep("GEOMETRYCOLLECTION (", n), "POLYGON ((0 1))", rep(")", n)), collapse = "" )) } # errors in geometry_start expect_error( wk_handle(make_really_recursive_geom(31), wkb_writer()), "Can't write WKB with maximum" ) # errors in ring_start expect_error( wk_handle(make_really_recursive_geom(32), wkb_writer()), "Can't write WKB with maximum" ) }) test_that("wkb_writer() works for a vector of indeterminate length", { long_xy <- as_wkt(xy(runif(2048), runif(2048))) expect_identical( handle_wkt_without_vector_size(long_xy, wkb_writer()), wk_handle(long_xy, wkb_writer()) ) }) wk/tests/testthat/test-sfc-writer.R0000644000176200001440000003521214515070354017071 0ustar liggesusers test_that("sfc_writer() works with fixed-length input", { skip_if_not_installed("sf") # zero-length expect_identical(wk_handle(wkb(), sfc_writer()), sf::st_sfc()) expect_identical( wk_handle( as_wkt( c("POINT EMPTY", "LINESTRING EMPTY", "POLYGON EMPTY", "MULTIPOINT EMPTY", "MULTILINESTRING EMPTY", "MULTIPOLYGON EMPTY", "GEOMETRYCOLLECTION EMPTY" ) ), sfc_writer() ), sf::st_sfc( sf::st_point(), sf::st_linestring(), sf::st_polygon(), sf::st_multipoint(), sf::st_multilinestring(), sf::st_multipolygon(), sf::st_geometrycollection() ) ) expect_identical( wk_handle(as_wkb("POINT (1 1)"), sfc_writer()), sf::st_sfc(sf::st_point(c(1, 1))) ) expect_identical( wk_handle(as_wkb("LINESTRING (1 2, 3 4)"), sfc_writer()), sf::st_sfc(sf::st_linestring(rbind(c(1, 2), c(3, 4)))) ) expect_identical( wk_handle(as_wkb("POLYGON ((0 0, 0 1, 1 0, 0 0))"), sfc_writer()), sf::st_sfc(sf::st_polygon(list(rbind(c(0, 0), c(0, 1), c(1, 0), c(0, 0))))) ) expect_identical( wk_handle(as_wkb("MULTIPOINT ((1 2), (3 4))"), sfc_writer()), sf::st_sfc(sf::st_multipoint(rbind(c(1, 2), c(3, 4)))) ) expect_identical( wk_handle(as_wkb("MULTILINESTRING ((1 1, 2 2), (2 2, 3 4))"), sfc_writer()), sf::st_sfc( sf::st_multilinestring( list(rbind(c(1, 1), c(2, 2)), rbind(c(2, 2), c(3, 4))) ) ) ) expect_identical( wk_handle( as_wkb("MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -2, -1 0, 0 0)))"), sfc_writer() ), sf::st_sfc( sf::st_multipolygon( list( list(rbind(c(0, 0), c(0, 1), c(1, 0), c(0, 0))), list(rbind(c(0, 0), c(0, -2), c(-1, 0), c(0, 0))) ) ) ) ) expect_identical( wk_handle(as_wkb("GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))"), sfc_writer()), sf::st_sfc( sf::st_geometrycollection( list( sf::st_point(c(1, 1)), sf::st_linestring(rbind(c(1, 1), c(2, 2))) ) ) ) ) }) test_that("sfc_writer() works with promote_multi = TRUE", { skip_if_not_installed("sf") expect_identical( wk_handle( as_wkt( c("POINT EMPTY", "LINESTRING EMPTY", "POLYGON EMPTY", "MULTIPOINT EMPTY", "MULTILINESTRING EMPTY", "MULTIPOLYGON EMPTY", "GEOMETRYCOLLECTION EMPTY" ) ), sfc_writer(promote_multi = TRUE) ), sf::st_sfc( sf::st_multipoint(), sf::st_multilinestring(), sf::st_multipolygon(), sf::st_multipoint(), sf::st_multilinestring(), sf::st_multipolygon(), sf::st_geometrycollection() ) ) expect_identical( wk_handle(as_wkb("POINT (1 1)"), sfc_writer(promote_multi = TRUE)), sf::st_sfc(sf::st_multipoint(matrix(c(1, 1), ncol = 2))) ) expect_identical( wk_handle(as_wkb("POINT Z (1 1 2)"), sfc_writer(promote_multi = TRUE)), sf::st_sfc(sf::st_multipoint(matrix(c(1, 1, 2), ncol = 3))) ) expect_identical( wk_handle(as_wkb("POINT M (1 1 2)"), sfc_writer(promote_multi = TRUE)), sf::st_sfc(sf::st_multipoint(matrix(c(1, 1, 2), ncol = 3), dim = "XYM")) ) expect_identical( wk_handle(as_wkb("POINT ZM (1 1 2 3)"), sfc_writer(promote_multi = TRUE)), sf::st_sfc(sf::st_multipoint(matrix(c(1, 1, 2, 3), ncol = 4))) ) expect_identical( wk_handle(as_wkb("LINESTRING (1 2, 3 4)"), sfc_writer(promote_multi = TRUE)), sf::st_sfc(sf::st_multilinestring(list(rbind(c(1, 2), c(3, 4))))) ) expect_identical( wk_handle(as_wkb("POLYGON ((0 0, 0 1, 1 0, 0 0))"), sfc_writer(promote_multi = TRUE)), sf::st_sfc(sf::st_multipolygon(list(list(rbind(c(0, 0), c(0, 1), c(1, 0), c(0, 0)))))) ) expect_identical( wk_handle(as_wkb("MULTIPOINT ((1 2), (3 4))"), sfc_writer(promote_multi = TRUE)), sf::st_sfc(sf::st_multipoint(rbind(c(1, 2), c(3, 4)))) ) expect_identical( wk_handle(as_wkb("MULTILINESTRING ((1 1, 2 2), (2 2, 3 4))"), sfc_writer(promote_multi = TRUE)), sf::st_sfc( sf::st_multilinestring( list(rbind(c(1, 1), c(2, 2)), rbind(c(2, 2), c(3, 4))) ) ) ) expect_identical( wk_handle( as_wkb("MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -2, -1 0, 0 0)))"), sfc_writer(promote_multi = TRUE) ), sf::st_sfc( sf::st_multipolygon( list( list(rbind(c(0, 0), c(0, 1), c(1, 0), c(0, 0))), list(rbind(c(0, 0), c(0, -2), c(-1, 0), c(0, 0))) ) ) ) ) expect_identical( wk_handle( as_wkb("GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))"), sfc_writer(promote_multi = TRUE) ), sf::st_sfc( sf::st_geometrycollection( list( sf::st_point(c(1, 1)), sf::st_linestring(rbind(c(1, 1), c(2, 2))) ) ) ) ) }) test_that("nested points are treated the same as top-level points", { skip_if_not_installed("sf") non_empty_nested <- as_wkt(c("GEOMETRYCOLLECTION (POINT (1 2))", "POINT EMPTY")) empty_nested <- as_wkt(c("GEOMETRYCOLLECTION (POINT EMPTY)", "POINT (1 2)")) expect_identical( sf::st_bbox(wk_handle(non_empty_nested, sfc_writer())), sf::st_bbox(wk_handle(empty_nested, sfc_writer())), ) }) test_that("sfc_writer() turns NULLs into EMPTY", { expect_identical( wk_handle(wkb(list(NULL)), sfc_writer()), wk_handle(wkt("GEOMETRYCOLLECTION EMPTY"), sfc_writer()) ) all_types <- as_wkb( c("POINT EMPTY", "LINESTRING EMPTY", "POLYGON EMPTY", "MULTIPOINT EMPTY", "MULTILINESTRING EMPTY", "MULTIPOLYGON EMPTY", "GEOMETRYCOLLECTION EMPTY" ) ) for (i in seq_along(all_types)) { expect_identical( wk_handle(c(all_types[i], wkb(list(NULL))), sfc_writer()), wk_handle(c(all_types[i], all_types[i]), sfc_writer()) ) } expect_identical( wk_handle(c(all_types[1:2], wkb(list(NULL))), sfc_writer()), wk_handle(c(all_types[1:2], as_wkb("GEOMETRYCOLLECTION EMPTY")), sfc_writer()) ) all_types_non_empty <- as_wkb( c( "POINT (1 2)", "LINESTRING (1 2, 3 4)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 2), (3 4))", "MULTILINESTRING ((1 2, 3 4))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -2, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 2))" ) ) types <- c( "POINT", "LINESTRING", "POLYGON", "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON", "GEOMETRYCOLLECTION" ) for (i in seq_along(all_types)) { vec <- wk_handle(c(all_types_non_empty[i], wkb(list(NULL))), sfc_writer()) expect_identical(vec[[2]], wk_handle(all_types[i], sfc_writer())[[1]]) expect_s3_class(vec, paste0("sfc_", types[i])) } # check at least one Z, M, and ZM geometry zm_types <- as_wkb( c("POINT ZM (1 2 3 4)", "POINT Z (1 2 3)", "POINT M (1 2 3)") ) zm_types_empty <- as_wkb( c("POINT ZM EMPTY", "POINT Z EMPTY", "POINT M EMPTY") ) for (i in seq_along(all_types)) { expect_identical( wk_handle(c(zm_types[i], wkb(list(NULL))), sfc_writer()), wk_handle(c(zm_types[i], zm_types_empty[i]), sfc_writer()) ) } }) test_that("sfc_writer() reproduces all basic geometry types for WKB input", { skip_if_not_installed("sf") nc <- sf::read_sf(system.file("shape/nc.shp", package = "sf")) nc_multipolygon <- sf::st_set_crs(nc$geometry, NA) nc_multilines <- sf::st_boundary(nc_multipolygon) nc_multipoints <- sf::st_cast(nc_multilines, "MULTIPOINT") nc_polygon <- sf::st_cast(nc_multipolygon, "POLYGON") nc_lines <- sf::st_cast(nc_multilines, "LINESTRING") nc_points <- sf::st_cast(nc_lines, "POINT") collection_list <- nc_multipolygon attributes(collection_list) <- NULL nc_collection <- sf::st_sfc(sf::st_geometrycollection(collection_list)) attr(nc_multipoints, "ids") <- NULL attr(nc_polygon, "ids") <- NULL attr(nc_lines, "ids") <- NULL attr(nc_points, "ids") <- NULL expect_identical( wk_handle(as_wkb(nc_multipolygon), sfc_writer()), nc_multipolygon ) expect_identical( wk_handle(as_wkb(nc_multilines), sfc_writer()), nc_multilines ) expect_identical( wk_handle(as_wkb(nc_multipoints), sfc_writer()), nc_multipoints ) expect_identical( wk_handle(as_wkb(nc_polygon), sfc_writer()), nc_polygon ) expect_identical( wk_handle(as_wkb(nc_lines), sfc_writer()), nc_lines ) expect_identical( wk_handle(as_wkb(nc_points), sfc_writer()), nc_points ) expect_identical( wk_handle(as_wkb(nc_collection), sfc_writer()), nc_collection ) }) test_that("sfc_writer() reproduces all basic geometry types for WKT input", { skip_if_not_installed("sf") nc <- sf::read_sf(system.file("shape/nc.shp", package = "sf")) nc_multipolygon <- sf::st_set_crs(nc$geometry, NA) nc_multilines <- sf::st_boundary(nc_multipolygon) nc_multipoints <- sf::st_cast(nc_multilines, "MULTIPOINT") nc_polygon <- sf::st_cast(nc_multipolygon, "POLYGON") nc_lines <- sf::st_cast(nc_multilines, "LINESTRING") nc_points <- sf::st_cast(nc_lines, "POINT") collection_list <- nc_multipolygon attributes(collection_list) <- NULL nc_collection <- sf::st_sfc(sf::st_geometrycollection(collection_list)) attr(nc_multipoints, "ids") <- NULL attr(nc_polygon, "ids") <- NULL attr(nc_lines, "ids") <- NULL attr(nc_points, "ids") <- NULL expect_equal( wk_handle(as_wkt(nc_multipolygon), sfc_writer()), nc_multipolygon ) expect_equal( wk_handle(as_wkt(nc_multilines), sfc_writer()), nc_multilines ) expect_equal( wk_handle(as_wkt(nc_multipoints), sfc_writer()), nc_multipoints ) expect_equal( wk_handle(as_wkt(nc_polygon), sfc_writer()), nc_polygon ) expect_equal( wk_handle(as_wkt(nc_lines), sfc_writer()), nc_lines ) expect_equal( wk_handle(as_wkt(nc_points), sfc_writer()), nc_points ) expect_equal( wk_handle(as_wkt(nc_collection), sfc_writer()), nc_collection ) }) test_that("sfc writer works with ZM dimensions", { skip_if_not_installed("sf") expect_identical( wk_handle(wkt(c("POINT ZM (1 2 3 4)", "POINT ZM EMPTY")), sfc_writer()), sf::st_sfc(sf::st_point(c(1, 2, 3, 4)), sf::st_point(rep(NA_real_, 4), dim = "XYZM")) ) expect_identical( wk_handle(wkt(c("POINT Z (1 2 3)", "POINT Z EMPTY")), sfc_writer()), sf::st_sfc(sf::st_point(c(1, 2, 3)), sf::st_point(rep(NA_real_, 3), dim = "XYZ")) ) expect_identical( wk_handle(wkt(c("POINT M (1 2 3)", "POINT M EMPTY")), sfc_writer()), sf::st_sfc(sf::st_point(c(1, 2, 3), dim = "XYM"), sf::st_point(rep(NA_real_, 3), dim = "XYM")) ) expect_identical( wk_handle(wkt(c("LINESTRING ZM (1 2 3 4, 5 6 7 8)", "LINESTRING ZM EMPTY")), sfc_writer()), sf::st_sfc( sf::st_linestring(rbind(c(1, 2, 3, 4), c(5, 6, 7, 8))), sf::st_linestring(matrix(double(), ncol = 4), dim = "XYZM") ) ) expect_identical( wk_handle(wkt(c("LINESTRING Z (1 2 3, 5 6 7)", "LINESTRING Z EMPTY")), sfc_writer()), sf::st_sfc( sf::st_linestring(rbind(c(1, 2, 3), c(5, 6, 7)), dim = "XYZ"), sf::st_linestring(matrix(double(), ncol = 3), dim = "XYZ") ) ) expect_identical( wk_handle(wkt(c("LINESTRING M (1 2 3, 5 6 7)", "LINESTRING M EMPTY")), sfc_writer()), sf::st_sfc( sf::st_linestring(rbind(c(1, 2, 3), c(5, 6, 7)), dim = "XYM"), sf::st_linestring(matrix(double(), ncol = 3), dim = "XYM") ) ) }) test_that("nested geometries have their dimensions checked", { skip_if_not_installed("sf") expect_identical( wk_handle(wkt("GEOMETRYCOLLECTION Z (POINT Z (1 2 3))"), sfc_writer()), sf::st_sfc(sf::st_geometrycollection(list(sf::st_point(c(1, 2, 3), dim = "XYZ")), dims = "XYZ")) ) expect_identical( wk_handle(wkt("GEOMETRYCOLLECTION Z (LINESTRING Z (1 2 3, 4 5 6))"), sfc_writer()), sf::st_sfc( sf::st_geometrycollection( list(sf::st_linestring(rbind(c(1, 2, 3), c(4, 5, 6)), dim = "XYZ")), dims = "XYZ" ) ) ) # note that this is stricter than sf::st_sfc(), which either drops the missing dimension # on the GEOMETRYCOLLECTION (when creating from R) or assigns 0 to the missing dimension # (when creating from WKT) expect_error( wk_handle(wkt("GEOMETRYCOLLECTION Z (POINT (1 1))"), sfc_writer()), "incompatible dimensions" ) expect_error( wk_handle(wkt("GEOMETRYCOLLECTION Z (POINT (1 1))"), sfc_writer()), "incompatible dimensions" ) }) test_that("nested empties result in NA ranges", { skip_if_not_installed("sf") expect_identical( sf::st_bbox(wk_handle(wkt("GEOMETRYCOLLECTION ZM (POINT EMPTY)"), sfc_writer())), sf::st_bbox(sf::st_as_sfc("POINT ZM EMPTY")) ) expect_identical( sf::st_z_range(wk_handle(wkt("GEOMETRYCOLLECTION ZM (POINT EMPTY)"), sfc_writer())), sf::st_z_range(sf::st_as_sfc("POINT ZM EMPTY")) ) expect_identical( sf::st_m_range(wk_handle(wkt("GEOMETRYCOLLECTION ZM (POINT EMPTY)"), sfc_writer())), sf::st_m_range(sf::st_as_sfc("POINT ZM EMPTY")) ) }) test_that("sfc_writer() errors when the recursion limit is too high", { make_really_recursive_geom <- function(n) { wkt(paste0( c(rep("GEOMETRYCOLLECTION (", n), "POLYGON ((0 1))", rep(")", n)), collapse = "" )) } # errors in geometry_start expect_error( wk_handle(make_really_recursive_geom(32), sfc_writer()), "Invalid recursion depth" ) }) test_that("the polygon container is reallocated according to variable-length input", { # because polygons with many holes are hard to generate in test data, this particular # piece of code, which is similar to that that allows variable-length input to # generate MULTI/COLLECTION geoms, is not fired make_really_holy_polygon <- function(n) { wkt(paste0( "POLYGON (", paste0(rep("(0 0, 0 1, 1 0, 0 0)", n), collapse = ", "), ")" )) } expect_s3_class( wk_handle(make_really_holy_polygon(1), sfc_writer()), "sfc_POLYGON" ) expect_s3_class( # default length is 32, so this should cause one realloc wk_handle(make_really_holy_polygon(40), sfc_writer()), "sfc_POLYGON" ) }) test_that("sfc_writer() works for a vector of indeterminate length", { long_xy <- as_wkt(xy(runif(2048), runif(2048))) expect_identical( handle_wkt_without_vector_size(long_xy, sfc_writer()), wk_handle(long_xy, sfc_writer()) ) }) test_that("sfc_writer() propagates precision", { skip_if_not_installed("sf") sfc_prec <- sf::st_sfc(sf::st_point(c(1/3, 1/3))) sf::st_precision(sfc_prec) <- 0.01 expect_identical(sf::st_precision(wk_handle(sfc_prec, sfc_writer())), 0.01) }) test_that("sfc_writer() can roundtrip examples", { skip_if_not_installed("sf") for (which in names(wk_example_wkt)) { expect_identical( wk_handle(sf::st_as_sfc(wk_example(!!which, crs = NULL)), sfc_writer()), sf::st_as_sfc(wk_example(!!which, crs = NULL)) ) } }) wk/tests/testthat/test-grd-tile.R0000644000176200001440000000641114315177334016516 0ustar liggesusers test_that("grd_tile_summary() works", { grid <- grd_rct(volcano) summary <- grd_tile_summary(grid) # 0th overview is the same as the data expect_identical(summary$nx[1], ncol(grid)) expect_identical(summary$ny[1], nrow(grid)) # Last overview is 1 x 1 expect_identical(summary$nx[8], 1L) expect_identical(summary$ny[8], 1L) }) test_that("grd_tile_template() works for perfectly tileable rct data", { grid <- grd(nx = 8, ny = 8) grid$data <- matrix(1:64, nrow = 8, ncol = 8, byrow = TRUE) expect_identical( grd_tile_template(grid, 1), grd(grid$bbox, nx = 4, ny = 4) ) }) test_that("grd_tile_template() works for imperfectly tileable rct data", { grid <- grd(nx = 9, ny = 9) grid$data <- matrix(1:81, nrow = 9, ncol = 9, byrow = TRUE) expect_identical( grd_tile_template(grid, 1), grd(rct(0, -1, 10, 9), nx = 5, ny = 5) ) }) test_that("grd_tile_template() works for perfectly tileable xy data", { grid <- grd(nx = 8, ny = 8, type = "centers") grid$data <- matrix(1:64, nrow = 8, ncol = 8, byrow = TRUE) expect_identical( grd_tile_template(grid, 1), grd(rct(0, 0, 8, 8), nx = 4, ny = 4) ) }) test_that("grd_tile_template() works for imperfectly tileable xy data", { grid <- grd(nx = 9, ny = 9, type = "centers") grid$data <- matrix(1:81, nrow = 9, ncol = 9, byrow = TRUE) expect_identical( grd_tile_template(grid, 1), grd(rct(0, -1, 10, 9), nx = 5, ny = 5) ) }) test_that("grd_tile() works for perfectly tileable rct data", { grid <- grd(nx = 8, ny = 8) grid$data <- matrix(1:64, nrow = 8, ncol = 8, byrow = TRUE) # top-left tile11 <- grd_tile(grid, 1, 1, 1) expect_identical(tile11$bbox, rct(0, 6, 2, 8)) expect_identical(tile11$data, matrix(c(1L, 9L, 2L, 10L), nrow = 2, ncol = 2)) # bottom right tile44 <- grd_tile(grid, 1, 4, 4) expect_identical(tile44$bbox, rct(6, 0, 8, 2)) expect_identical(tile44$data, matrix(c(55L, 63L, 56L, 64L), nrow = 2, ncol = 2)) }) test_that("grd_tile() works for imperfectly tileable rct data", { grid <- grd(nx = 9, ny = 9) grid$data <- matrix(1:81, nrow = 9, ncol = 9, byrow = TRUE) # top-left tile11 <- grd_tile(grid, 1, 1, 1) expect_identical(tile11$bbox, rct(0, 7, 2, 9)) expect_identical(tile11$data, matrix(c(1L, 10L, 2L, 11L), nrow = 2, ncol = 2)) # almost bottom right tile45 <- grd_tile(grid, 1, 4, 5) expect_identical(tile45$bbox, rct(8, 1, 10, 3)) expect_identical(tile45$data, matrix(c(63L, 72L, NA, NA), nrow = 2, ncol = 2)) # bottom almost right tile54 <- grd_tile(grid, 1, 5, 4) expect_identical(tile54$bbox, rct(6, -1, 8, 1)) expect_identical(tile54$data, matrix(c(79L, NA, 80L, NA), nrow = 2, ncol = 2)) }) test_that("grd_tile() works for perfectly tileable rct data", { grid <- grd(nx = 8, ny = 8, type = "centers") grid$data <- matrix(1:64, nrow = 8, ncol = 8, byrow = TRUE) # top-left tile11 <- grd_tile(grid, 1, 1, 1) expect_s3_class(tile11, "wk_grd_xy") expect_identical(tile11$bbox, rct(0.5, 6.5, 1.5, 7.5)) expect_identical(tile11$data, matrix(c(1L, 9L, 2L, 10L), nrow = 2, ncol = 2)) # bottom right tile44 <- grd_tile(grid, 1, 4, 4) expect_s3_class(tile11, "wk_grd_xy") expect_identical(tile44$bbox, rct(6.5, 0.5, 7.5, 1.5)) expect_identical(tile44$data, matrix(c(55L, 63L, 56L, 64L), nrow = 2, ncol = 2)) }) wk/tests/testthat/test-grd-snap.R0000644000176200001440000000000014315177334016506 0ustar liggesuserswk/tests/testthat/Rplots.pdf0000644000176200001440000016676214515064034015677 0ustar liggesusers%PDF-1.4 %ρ\r 1 0 obj << /CreationDate (D:20231021210856) /ModDate (D:20231021210856) /Title (R Graphics Output) /Producer (R 4.3.1) /Creator (R) >> endobj 2 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 7 0 obj << /Type /Page /Parent 3 0 R /Contents 8 0 R /Resources 4 0 R >> endobj 8 0 obj << /Length 646 /Filter /FlateDecode >> stream xU0+uWH8 Nؕ~IM<}y34=:ӏˇtwޓ]w9Z}Lts’@ﺒ\Tz=R wrvۡxh"6 &&fmDp;N?ݼ|^!`3H#nxI{=ܰώf~F~H-;>w&k]|aXz _&$&VQ+ØYw@wW}ʕ;@\.Ns0X7&['Jű_#=e&s qSa0x8udSbxRuMߦRVU4kf=u,*rjE~lBɵ=@ Tϳ i2UTQ`5ʩCY9eQ«ԸTb,9y)ZzmyVFM#bLaÔ/ Tӈؠ\qHkW71ܢp\UqFć[=ğ0xQ7\FGZzm^bFP##La<Ĕ/ T=Ԉ\HkW71܋C$˙endstream endobj 9 0 obj << /Type /Page /Parent 3 0 R /Contents 10 0 R /Resources 4 0 R >> endobj 10 0 obj << /Length 396 /Filter /FlateDecode >> stream xAO1sԃcg:;۽BԄDdj ߷e[i|v߾- V5sBӡh`Z† `!s{ta,Zk.׶o^ ,|V"VX8TӘ!I; e p֢?Iv0mp0SFs}!h={ +Zic.]7S#n>Pnc[w;%۟gǩ1׾eY(OdN*h\'ϓ,f]@%йإ0pʱKHҦ3PCPU jXU;ƆTU&VR݅wj[U0~+F—pe)"i1f\> endobj 12 0 obj << /Length 470 /Filter /FlateDecode >> stream xMO0 >aYMw݀I U89[Jmv`a{=dm<o.X!|>-jHf+e1u5!/uw6\7a >+~;Uke RhP,%@R #Kv6)iE\WGXwj|Ԗ,j^]&Δ[8S(7lo;vHlZtb;ĶvEuNOXg5jxl.$kQ O,IbV ID=!程۩~vFkw4֒}xt?xPjדt|/:iWoT4V)3J9I>M뭲)QJYs?(r08PG)DO܄ԄH'CbB~SFYogoendstream endobj 13 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 12 /Filter /FlateDecode /SMask 14 0 R >> stream xgendstream endobj 14 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace /DeviceGray /BitsPerComponent 8 /Length 11 /Filter /FlateDecode >> stream xc`endstream endobj 15 0 obj << /Type /Page /Parent 3 0 R /Contents 16 0 R /Resources 4 0 R >> endobj 16 0 obj << /Length 417 /Filter /FlateDecode >> stream xMO0 >aYIz@q 4ѡw:մocAXvj ;(JmNPoR +e1jwC? *7 UpڙWēXIy)FRdHYJ%5Re,YUH̪L뭚z$'i~B3 ]BQf7/1{smnFw^)wzmТ&OJ EHRR@Db:xO:7l9"zvZk݆xS^9`4jGӥgV~p=w|r=\h:Z B'i "0Ԓ?1Dendstream endobj 17 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 27 /Filter /FlateDecode >> stream xc``HKK;s挱̙3>endstream endobj 18 0 obj << /Type /Page /Parent 3 0 R /Contents 19 0 R /Resources 4 0 R >> endobj 19 0 obj << /Length 417 /Filter /FlateDecode >> stream xMO0 >aYIz@q 4ѡw:մoc ة%(!NY 7xmv2w5໡^76ܫ@LP$?VxRka뢌g)4(,%HR d)Sc謪]fUnzVMoRE'i~@LU.ڨa7/cr#!v8&^)wzm Z.ģ |%ҳNp+?;>L.4ykeB'i tTXxW_jɟ_1Eendstream endobj 20 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 26 /Filter /FlateDecode >> stream xc``066NKKmhhendstream endobj 21 0 obj << /Type /Page /Parent 3 0 R /Contents 22 0 R /Resources 4 0 R >> endobj 22 0 obj << /Length 419 /Filter /FlateDecode >> stream xMO0 >aYIz@q 4ѡ:bUocAXvj ;(Jm΁C oR +e1y5໡^‚ *8LP+Q~6g)k_K I d)Wc譪[fUnzG뭚XKhuS,f2U j+n^nύcr[="7Yw1){DnW FABm ~1endstream endobj 23 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 26 /Filter /FlateDecode >> stream xmhh```066NKK'endstream endobj 24 0 obj << /Type /Page /Parent 3 0 R /Contents 25 0 R /Resources 4 0 R >> endobj 25 0 obj << /Length 418 /Filter /FlateDecode >> stream xMO@+ُ^H ăC,9إ E yL&`a+5J4%\#|,7oG0)1 ߍ~ xU&_(^EdJȧuYK2^Р_K K8g$PE `T&4q=_ XwK } \ 6P/u}iбz+sd=uOvgӑGv3}g!f< H8t(RED?>M8ߞNڑQgw1}g/-{N [?=w^py,мQ-tհD;>!RSR2mendstream endobj 26 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 26 /Filter /FlateDecode >> stream xKKK366f``hhh-endstream endobj 27 0 obj << /Type /Page /Parent 3 0 R /Contents 28 0 R /Resources 4 0 R >> endobj 28 0 obj << /Length 417 /Filter /FlateDecode >> stream xMO0 >aY8Mz@q 4ѡw:մoc ة%, `@j|6ƀ]n_F{Hj O`Ջ'Usغ(Y J K sFYJԘ:j)Yޟi~UT`Q@Ie LCPmuuxzn?ȽGv]7bwM5wʝ^FOձ(}HRR@Db:|u,6ްN`ЎՎtYNp+?;>L.4ykeB'i tT Ԓ?1Hendstream endobj 29 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 26 /Filter /FlateDecode >> stream xc``066NKKmhhendstream endobj 30 0 obj << /Type /Page /Parent 3 0 R /Contents 31 0 R /Resources 4 0 R >> endobj 31 0 obj << /Length 419 /Filter /FlateDecode >> stream xMO0 >aYIz@q 4ѡ:bUocAXvj ;(JmD@H:Xpua `@j|6ƀ<|7_BXo^drG ē^Fe> stream xmhh```066NKK'endstream endobj 33 0 obj << /Type /Page /Parent 3 0 R /Contents 34 0 R /Resources 4 0 R >> endobj 34 0 obj << /Length 418 /Filter /FlateDecode >> stream xMO@+ُ^H ăC,9إ E yL&`a+5J4%\#|,7oG0)1 ߍ~ xU&_(^EdJȧuYK2^Р_K K8g$PE `T&4q=_ XwK } \ 6P/u}iбz+sd=uOvgӑGv3}g!f< H8t(RED?>M8ߞNڑQgw1}g/-{N [?=w^py,мQ-tհD;>pm2pendstream endobj 35 0 obj << /Type /XObject /Subtype /Image /Width 3 /Height 2 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 26 /Filter /FlateDecode >> stream xKKK366f``hhh-endstream endobj 36 0 obj << /Type /Page /Parent 3 0 R /Contents 37 0 R /Resources 4 0 R >> endobj 37 0 obj << /Length 465 /Filter /FlateDecode >> stream xTMk0W̱D;#>|MZخfCYe$blyOf E-30M|"<< ɬ?^ƠEDՏ5knܔ{oHtl '`>e޺SBEpD3BE6jBlT *‹E/}h{|ãѬz4.DŽS!'1>!~~o{[NzWzGm<N2tTqOI3ij'3Re Qf*s, jB%W a࣓\̈́ .z&)c._L6EH(Iif'IT!k6p$+͔(aiƬs4sl e5cpQ7Cf],n M\Pp_3e.݊01v0p13rZ2(W߆ ̺\AYI9endstream endobj 38 0 obj << /Type /XObject /Subtype /Image /Width 1 /Height 1 /ColorSpace 5 0 R /BitsPerComponent 8 /Length 11 /Filter /FlateDecode >> stream xkhhendstream endobj 39 0 obj << /Type /Page /Parent 3 0 R /Contents 40 0 R /Resources 4 0 R >> endobj 40 0 obj << /Length 411 /Filter /FlateDecode >> stream xOO1sԃ&$ x0c;nh],=`J* ͷO ^¢ym2"$W?h)kcFaehyDF) A dm0cF7{q}7v{tkN_ûz+nn;v4#;đN0vvu+]',sQ1VJ(3@kF% c57*Qf@tGrE(W֮%z פԯ#TTRjXO~X58zKmt<$ߛ[NmUvޅ4endstream endobj 41 0 obj << /Type /Page /Parent 3 0 R /Contents 42 0 R /Resources 4 0 R >> endobj 42 0 obj << /Length 424 /Filter /FlateDecode >> stream xTN0 +|\;u $$ةEhČsq'3b+F{h/ao uHA,˜"_HbWZ|O@'0 <>A!˰I_Grha.! :B't%:eo,_X]nMTSg5e e_ÏsF:~jւrr:%yX}BnItv^^[0^4wfcPZlj2ɿtVL{7+o> endobj 44 0 obj << /Length 424 /Filter /FlateDecode >> stream xTN0 +|\;u $$ةEhČsq'3b+F{h/ao uHA,˜"_HbWZ|O@'0 <>A!˰I_Grha.! :B't%:eo,_X]nMTSg5e e_ÏsF:~jւrr:%yX}BnItv^^[0^4wfcPZlj2ɿtVL{7+o> endobj 46 0 obj << /Length 452 /Filter /FlateDecode >> stream xTMO0W̑=qvr-+!J8 N*Z zؿdL @<Njk [+=Cns&op Brpv`˿#sw\˵qTeFZa J ){WhA A!4)XBC8詶hZ0?`?US'n\>k3 ~~cb +/a=m> endobj 48 0 obj << /Length 339 /Filter /FlateDecode >> stream xTN0otmH@*Ā URsi=!@Xak"c2m(glj'.9q1})Ǽf <Ė]ΰE/ޠaJN3T/sEP5!CE8+Q&U`;an1;/V os,kNWF2~a1I=u8_#5]W_;VCQ,wbzj7O [vP!CEkBwʾQ0AbC{񡓟oP^pl ְ̇LU َOi~~nUk7H5 endstream endobj 49 0 obj << /Type /Page /Parent 3 0 R /Contents 50 0 R /Resources 4 0 R >> endobj 50 0 obj << /Length 416 /Filter /FlateDecode >> stream xTN0 +|^nzHJB4vIdMġQ_^㾼1C+:! +<:lp}scZ : O3Xx17Z5ش ~RENeR<,8n g-`I LCA8G/mk3zk{E m4_I(B4u15~u?;FxSBjqv m *\E;:Ϩ(Sq\O 0KR&$X:' 8嘥LH 3ҡ–-!lZQUUM`Zr& $.MH%ZU ZraWFM4yivp: ;|a'GZz4G 7endstream endobj 51 0 obj << /Type /Page /Parent 3 0 R /Contents 52 0 R /Resources 4 0 R >> endobj 52 0 obj << /Length 416 /Filter /FlateDecode >> stream xTN0 +|^nzHJB4vIdMġQ_^㾼1C+:! +<:lp}scZ : O3Xx17Z5ش ~RENeR<,8n g-`I LCA8G/mk3zk{E m4_I(B4u15~u?;FxSBjqv m *\E;:Ϩ(Sq\O 0KR&$X:' 8嘥LH 3ҡ–-!lZQUUM`Zr& $.MH%ZU ZraWFM4yivp: ;|a'GZz4G 7endstream endobj 53 0 obj << /Type /Page /Parent 3 0 R /Contents 54 0 R /Resources 4 0 R >> endobj 54 0 obj << /Length 506 /Filter /FlateDecode >> stream xU0+HzƓs Vii$SaJK>v2N84g;; 8³g}@BO1_)G=4w};;:A $ؿHʤy8/T+BJ0Xt@3M/Pvyt8_|xr79>>McQ+HO*^xti:Uj怺U?F٪zuPBުj ~͹}R.2NE0TӼN̒Tkr +BPY*6];U^9\5Ք Vm)gҜF4Քt7Ք spY;pdV 1{T/ 0TU:݈ 7۽$):r3/cB'n/abVmA j9.sOhֶ˴.}y.X|KgBrendstream endobj 55 0 obj << /Type /Page /Parent 3 0 R /Contents 56 0 R /Resources 4 0 R >> endobj 56 0 obj << /Length 532 /Filter /FlateDecode >> stream xT1 +|8$3]J i`*v~gn)Q̄v6qBϐd&`Ȅ1 |}h}{v >^;<lOAӔ+4 A! )XBC8-+XgV_͇ Kj>*Z> H-;~^k_>Q׫LskzOWлxG/KE'p])pBJi#XvD3;3.$U9]: x.?wgMGXU$X7O7O A@0A8CRDZg3A`Rk&h,1ZJ=5[ݨz]ܣ9kp11QoEʞpz>Qiڴ 6EPӵ~ ھ {k^}.ٻ |T'쑌^֘5|-+;mTtvek蠕#1WT[Kwpbendstream endobj 57 0 obj << /Type /Page /Parent 3 0 R /Contents 58 0 R /Resources 4 0 R >> endobj 58 0 obj << /Length 409 /Filter /FlateDecode >> stream xTN0 +|^nzHJB4vIdMġQ_^㾼1C+:! +<:lp}scZ : O3Xx17Z5ش ~RENeR<,8n g-`I LCA8G/mk3zk{E m4_I(B4u15~u?;FxSBjqv m *\E;:Ϩ(Sq\O 0KR&$X:' 8嘥LH 3ҡ–-!lZQUUM`Zr& $.MH%ZU ZraWFM4yivp: ;|acB1endstream endobj 59 0 obj << /Type /Page /Parent 3 0 R /Contents 60 0 R /Resources 4 0 R >> endobj 60 0 obj << /Length 409 /Filter /FlateDecode >> stream xTN0 +|^nzHJB4vIdMġQ_^㾼1C+:! +<:lp}scZ : O3Xx17Z5ش ~RENeR<,8n g-`I LCA8G/mk3zk{E m4_I(B4u15~u?;FxSBjqv m *\E;:Ϩ(Sq\O 0KR&$X:' 8嘥LH 3ҡ–-!lZQUUM`Zr& $.MH%ZU ZraWFM4yivp: ;|acB1endstream endobj 61 0 obj << /Type /Page /Parent 3 0 R /Contents 62 0 R /Resources 4 0 R >> endobj 62 0 obj << /Length 654 /Filter /FlateDecode >> stream xUMo1 ϯqsmH@*iV*=vS-8μ=9[@ wR~Hk-yn>њ+x ܎DKO.yRlG `"Ψ$۸@CBӴh{Y%x><.WX$;:_ UZװ_XV.׆x6զ˵)6[.ҌRDJkLN@Ca !l  A<)Z囚ߘHj]+Z,]'8~VC'w8[.kIe+L)^Wsݥ꿺^7Wz]ҡ@]co1ήj3x.e7$Wx4q<2y)f†dc#TsZŜꋵ\ 17׌O~BI/$V8yujqnfH%?ͅjnF5.NEk}s-:MƎ|qUGh#L^VPZH$?S97k7j~hGIqM1Wt :NDMQ jC#5ξTk}S-w_4endstream endobj 63 0 obj << /Type /Page /Parent 3 0 R /Contents 64 0 R /Resources 4 0 R >> endobj 64 0 obj << /Length 366 /Filter /FlateDecode >> stream xTMO0 Wq>zH@qChbKzTmC}q0l *m:؀ F1Lhc q>yNo>4=+ TŚMɰ^<#50F#  (֑$('5Kq],XX_l聒' CkCkꕺc]&OtMnW_9U.wn4yFl{qԱE"iD@A4 B`IBPjj Di;aB?Fϝrr?dw3Q+8-BNW>+9?+7 R5U_n endstream endobj 65 0 obj << /Type /Page /Parent 3 0 R /Contents 66 0 R /Resources 4 0 R >> endobj 66 0 obj << /Length 6831 /Filter /FlateDecode >> stream x\I5qݿ_q,Mrk# `  "I` 8^[%d^~?}؟fգj3>G}G\??>g)wxǿǫ#^7zow?]>ݿ>.%?a>/~qA +0|\|\i_?ǚ~Cxq|>{>?~͵ 3gygՂ=|3>uou+UɃ%X\=~Yvna>/=`|+ Ϗ|nc~̞zVlOHOo771>'onβͳe}s,{|WۤS{+mqʺn߫㳅J+~>2?D3*5 ә=/fcx_~2~l* p*BGՌߟRpp~XK<=sE9xZƹ_2-Jn43&ܟj(VW⠻z# &pi22U8їĹ-.j|z>nƼj-86a/lL\O8V͠l,4=,Ɓ)F'NGm+xW"yɅgp/N}m)&8}!륢TF:u5x>Ʀ 8Ēi OHz6 <{ÿ'=ASZ)([X_>\HJ_D=c?G` =d}ZO|jLߗ}j$ L."q{R`eOuLg*&{,Eac"7jN%HσaYҐ\Z-}oO`WkO|(ڗswz|ຈ?!m5ɠ_ƐMwwaNG{ OGh){Զ_5ih`6'JoS-;7Qx{V8 gK9t?z~b\pA _/ߗxye]8a[گXkg5~[c+?_?()^ozy_o)vExQ_KS)a H1@1ҰC /PT`E5^\`/^ϋm\ߠ> fT{>? ɵah~4L_AC ,1Atd!KGW<ænG܉5kKßMj.b}_zy^@Qvk3.A,}Eఈuޕ98{ \olxw:6W5bٯO"t[!Jc!ߛQ܄H_ƒJ#쒽Oy'iUsWWa/7tE&-̇F}$0À_ pOŇu*n:4,$Ua 3?D[$&}Sr πy3W@H n9Lؘ{_4|Vo ޏdTM{APw=o`c%_iоJ7bc50i8# o>"?<1eo_ . |Kc?O=/kUw|G Pz!dk3>eʹylڍ?K_m. G%0[(>V|&Oc’O9#pEtucCCIAEEy$ _noܟTl`)zbxDpw86?(թWy^!烁|b|2{qɾ&kk鯐?X1GĵÌ;3øNC? 8{yC!M2f>g( TZ_m?C*F~G%]? ,sLCڲiXo}Zއg_ [{8=.uyoH#p _#_<5xFHH~gɟWas(ŁN1gDhog!X7K} 2|0WvI/ѐVπV5t} p9F"X*ȷ_M~,*W `W@`0Ճ!OO$wJ}A>Č_O')yWJh LهJtt?!V*hOf$@+` F",ڟ>{#pS"A44$AbXë˾o ]לCT/İO1Y3A,T Ai~e|ӑ~p_h}E5? j  O$)hcOXzL1@;~@;h'Ahw!AHŠh }I &{VߔD:J{|&0)(u~G Q:_a`_:Vz0??;q~8hqޠ8YNH˗0[7yIrnr`:73z<3B(/-* .>^ww^xŽH׉lj#7{?WtH'*nG)G/@q?IyAz +@/yPށ_篼鿟ϼ%N8}{{Nq07p^t_y.˘p}?y%:z>JFy#q+Sމt_qR`[k"'~KFWX^w|[Ax.&@^Thn>yx3x:}OuݼxrPkx8ټ̂yC~0#_/ i>qyG1%a-xP0=I ?ě,%!-^@+A,̾x[`W.6NC,.^nH)XWC[MX'^nUC7xqiO \:ܼ[;[</X.^X axLBu9QWpXFIUKD(3 {51pv݃a$ aGeĈBiY ]ae;uMo]T`+#կ,L׃6=E&fHդ}bǫZv+KU{Jֶs *H}CΣY&wՌU}ܶ}kpۮ%_Ww" |Ɏ'wypWobju=nK *\¹v){,Bu/ 溶D%YD.𩠻/z.POVx)Z>P՚:ft;\^K҅kνO_ee*2"F.yhB@'t},[^ P/"= z:kݙp !n\XӒf=,{GwEx\4M {ϪԀ2Ϫ$^?pUIYU{4,ḓ&̤;Qܨ&,nsYU^ )嚚{h[lrlj:pi(Pi-Sg[e6?kY5 ޡhݝE4_n,b$_$dmnkZΞPogjbŖ]N;Pܐ~۹rw{%Tv(u<2&T:^jc#ڮOܧM-a{tX5=rnc ݾ;a> ijjNcܾO/B7p_*ztn</< ufw5nnz$DnܸeLظ?IёIGO#AVGgw|"Q:BuܒpQGR8-EV/"O:Iø*2.TG1g1"_$h6rw<#q#XߧPtT#&co[|w|c2:Ɓu(GfhuG-}H'acSS=>b:⑩yV:i?3nUSG>2KϗTG?W=1D2[r|_ q:=Tv&izN<D291L^D iH_5t[ ,hb@`d5Br;+,3RŌ3uLk:5/[5C˯&jH<&n@x3q  1D'4C^0riH8aHᡉ9M,k"SM @4!'DTOL`D>Vs[xbtC[ <&8£>&Hx=FClt$<e <8#_D MرgmM|i".Ns &4|O989 'Apx MjGݯ{"'&%OD#A <]<̻1sprOdƉp= M3OQD)Ok>8T 8x 83 }Wg򵭟rZuO܂h@ݺ.8{bEr&v5 Z#t(xbF9oaMXL>ʮ3RD3ȎQfDt [=1 Cz&rO>&3r=1Uơt}ŀ;_ƨw3/1䡟UŃ>~=*U'}ҟ6/6zM____׿81+v^>~I@endstream endobj 67 0 obj << /Type /Page /Parent 3 0 R /Contents 68 0 R /Resources 4 0 R >> endobj 68 0 obj << /Length 11414 /Filter /FlateDecode >> stream x}K$qŷckc غ,Yif h7`UZ۷3+ D+~O?O_u~w)_%r5}?h7??o~#|ouk_?W??# _c N[Nc>|!'ne|ɷ1=v?,ݟ?Ͽ㏿i1ӷ_ze*on?_*nb:ɼ5#7$nH~fNuɷ1Om^6{[Jyb_5_4֔{29ǯǹ8?˺uv͛h&E, 3J6?nﯲ\-g+DĹw kF%]@ݿC5z4Swjw\ e=}\)d(>KctNntݾo);3x|i01zDt?φflyd+yѭ8]|F?x~GYR2LJ9*GC屮QZ<5lsN 2^#C*ޗ*g2T)xR9_o ƻ筁OMy`=au(э\Ag/yAFꌉuh!a3x19Kt:PNޙMrTA7a9-\O!9Qp+l5XkTAs΋A7a5qrz.:6 ~H\ITF.d',ѱ9m>5 =@Lݷ9~@CZ98:D[hrA5cr[Y'cFsN7|qPޖœI;%SzX@H9\|u1ڦc̀e\t.t4hd&yY:ſ .pE0FKaO\䠍@Fr5]Rq kL796c.l)9hSH|c0HjX+Ksz&u% n./W"Bl]mCHgs&=ie;j)>>9U3Fw_$ ']*~_ߟ 䦎MSW(Ӳ1 1RV Y!PL\E.ݯX5VA-1~\GϷ5xFU߀C/i| [itg+m{dd cτM}}NYtLx:́0FgڢDgt_NaNhMtPaqҞS~̞7~ݬA39S:]}\]- 7ocZ>vƒ@8"}fr_V8,2)2ŘV]Co?` <6ǵa?e[33>tdC[nTTT.Un Jr+3fI|ljC:8zc9w ޱle@ lkMyZ4dmjZ;#u$q^Xcui?^M';]*d&qaDbN^#'p_rb!#ES0xݣvFc? i𲛍82y|guĕ7Oxb 6kmϵyp80/E&|U=`*/++Nc"ӊܼ ;V\ 7іر4k 4n0떇ZN@۪ h%5V[VN4mүqbőoYq>qlŹW\ptD/T1U\UV1]|VX1e/k^iľdF;џ X6['NQt;w>ZLL%O^卶9w)4m~ b KBx7v#?>Вc_c9#Sp&밵 [6OC%t]me+Ԗ9utךu/B{~ )_ mk׮ I]:lef5-yIMLkfWMMknZӚ'5AJ03B|љ,[1ɓzv@cȯF:l9E[7S,G]!qF9 8r @s+YrhŮ.lK/<v{~}ƚRm}SŅ3.إf45ܖu۱QɌ5▃lDΌ I`_pr-rqɍʕ%*\ 9̹ ?4?<\Txc1re UrD |xƧwc{83s<Dώ0ۿ:f!F^a41*+6f`>t zF7ɳLCcױLܟ^~u$E~L 5g*el|aR7|/P=jZ;P;Z7ɬ2OCo5u7ot'3˯ s.3(=zF{(zpɭGذb6&]1W ĸn8.wy7c)[~בx8uNG4Zo4rp-f\$MfD/|[U UlW_ņ;VlYgŦhp\zBn=yOpkMisHs5r4MEs}47 CQ1 T S1N@%OI}!ѬlܨWks'mW_sخ鯂+J䜣Jܶ1N ~9s\SĕG^;}Ed'_Eԗe.6A[JZPذYa- \鯃93nb.iQ7S{y+1[&a"GzD[ߣmzBo{b6/>7m[{_VE>:@!z Ja?֗jik=F5긶c&Cx[6n5i}R9YU= 1AǤcV;Wl]w8{ց_Yk53vt1iv{?b+ߣߏ{b ƿ}b~z]zyzAx]Lg/K5b#X_z5^[/NQ:<EF\a4~>?ۤjDE'؂eҽXOx-rr~ɭGi MjlZjtR֟}o:j(se}cKbxSNw ZckRU_1CA1 81 8>a 0bݏT-'^GSь)xL  u$н8-IS&^:֠ޘ +_8O#]zs#qgQ#wmtOSM"w'=?v- =v9_?lA Wwc1dk<>{,wFk!uNkYD$ɮ,5A7Ɩ;Ny1:AtףzԳ~=1kv}uON켕{zGr;{'z}{LCs.iyk%6a[kUPcu: .Գ6׾.4Xp\ݏ5}ɰ:͸Y@<Eg {cabd=/ZmžW֥uujck~g+3RG~( YW~ۍM֩!y}Djnk<|*FI HkDZCzWNz4ƺb04;cRg1tqp{e=q/۩8 OZzxDW}zs5JsLܸ%ƌFsbMjĞ);%<1Fk@ -䎿zasï[κLh'1?3n}xD ;~aEba=ڵľر%( |ϯ90ocYC;kdw i NAjGΪmO;`My_eM)Eū|bѧPoDԟRur6u_#XXR/WntupLZnu:f\#w_{Kjƪޱ+k,}җ}*k{4Pb}==>`ӑdthgںAj#0T¢^fdѯ%<) G4zr5_Ib͠GhQJs|dzX1nH "S^=~ ߙuۘO]k~R#uX,nqBas'm -lU WmygdiԘ;ΔR-? b$t ^ K"y0"Fޛb ۑSGvhE}>)yިa-SLptV fHb n(=H3t̍hVA PCKLQcFܙ4o)ЏA37Ǥe}L]jLAw }ʠ L|F3cs餺;8a+G(9X~|YoopVX|>iOp[?{>!(7eYhDEdthoð Q`mSfz49GM.5T eZH mRBUBY蹋Z"]"ji R|`shݶpEH:C|КOMڲ:AhLjɨ$^ɮ&Ú,kA@ ZPPp8mv~ھ VԶ71me%@S>hpBh݋b4 "ŌaϹ~`GlmL aid<1n[-f1M7><=^ɑzgy! O6fʩ4{\ b#+kk,}dOp/`!=}`r}@A: .@)u7p&f#Q\3dsvuC7`k\Τy]|za7:pm7¸@s!|FOmSwF@1h 76?*0|*k9&BWíAOGG#\T mLC5C=jFm4FmTFhF kR,ƪN,+'50zR^2SGuU]7+_}ٸA Lz2X8{a%md6B6,WÀ-pd3C[ ysV̫yHF8gGK@wHKHZb%Z]xǗ'ŧ{WlW_kZ;ڂڄ6i6XǛXcE\5,~ n.Nޔd |f6h(9朚~YϜVs^͉ ;Է$| !Ba4],ĠJ&?\pgxݍGxY; O$N+?QK?gx$>5B>>P}CǪzԣ^uճuC l0K?kz`FT问 6p ta˔As!^쾷^"̳$\.H4.>xQ TRM?UTU_o9ƽXOzgfބ}Y\dieG8%sxj4hvg9Ϟ*&ro˻$gk|֜q6*;H,U[}nQdR8);64& o+콑{r[#ڿz$d%M)e_o%u]*N}a=L<9{?)o.(>@|+cȻ#+P)sv St}T2zxKtivxY\bUqwD/=F{AUly`} ̃:M&}4YP"o³ދصI9ئTGgR 4ALn$H&IΔ6jռ_Ü);X8D,UdS{|M W蹳EW| {3Y.hlxlk֟ 5I۸km7*< %d#ޢ\0zxy؊}0{9wm;{~G-jml4!lD >.'U w C@ G)(WmNE}6]'!## O)x0Ϛz^mwP8gTtNg$ݹ!ItNtL;R;$R;,LNTFx>b$h<;*-`nhSS-Z\jբW C=z_UsK]W^RH;HEd0zGztnA^竱_/c};exA"} e"gIɡXJyCx*""*"8_yoy|@O]QEEoNki'J<kHĕhh.E7-iNO?t=]DO97lT;&; ^bBU7$S;5:Vdƭ~yb*n'n%X\e$n#rҐWBb 1Vv`m# 47G뼨ww;y)_@ )\B Y(ˊ~ d:m'gOj, ek^継lw+L-,J^^Ƈ޸-y~P7Η|STS(PB#m;[ x7LYJ} {_LJuWK0J;ڳޝuϽqwxGBWe=N[H{ۻÞ"mh%/=Τm:av`ѾlU4Y_ϋiL3hZ; _{9j iJU/7j!-9T7T y_nndb|"i$ޒ|H +(D4^HTj%S+w%j%U+w%Ɨׯ2,yjNq^뻫|ZE+ a]4=-;1?ᘣVjKpV}z(nz|I؇A6HnOo'FZcL/YC[%Kj/2,s=*hDeM+.擢RvsdUtwy vlUx{Lq#V.jGΨ;vLAqա!s|ao(d`*>?n;f_K҉cM_45+6l*Y; X1i6#Ŷ723z"s_7*( '4qYxD-*'tXƫ@QmM 5N::^r,r~52222wˤ;M&,[ ZdhG}tsɿ/Vendstream endobj 69 0 obj << /Type /Page /Parent 3 0 R /Contents 70 0 R /Resources 4 0 R >> endobj 70 0 obj << /Length 449 /Filter /FlateDecode >> stream xTMO0 W0q$HHTÊ"1HuwjCynLp+xv?RDd& uOr'_pp`ߋ2[?#g'wW=b'^Q4L۸BCm !` ]N S =lkwr(bR=b>nλay5IBpyH^%x<&A3rk /Wđz8'lFd$3qd09YBCМFM:`}l`’4O?̓|oga>>h{?gj~zGoT #dfl`-o9}r1@%3|z[)$ AzoS3=V93@)ZM3[$]:SV2i*=5SڼN Bendstream endobj 71 0 obj << /Type /Page /Parent 3 0 R /Contents 72 0 R /Resources 4 0 R >> endobj 72 0 obj << /Length 480 /Filter /FlateDecode >> stream xUMo0 WeۀEO]5h?<:Ȅ= % a;xr_ Cg';Br/j!}n>˯9w};|v >=Ux?=O<gBC )䙠((4HJ(BCXd5'ip۽@Җ y5L Ĺtf|5;~|/r8$Pa3ce e gH7(H'T_}dGWdS LPh eBJ(V#4JI 3d`}(צ31m!yD5#􎲃43r6F~<ˋ0uW2C'X_/ix޼ˆe%#]wkI#~s}Mגny=z cRzz'%pHpendstream endobj 73 0 obj << /Type /Page /Parent 3 0 R /Contents 74 0 R /Resources 4 0 R >> endobj 74 0 obj << /Length 486 /Filter /FlateDecode >> stream xTnAWT"<SX"q> m F{]]5Udn V gO80a?KZ蟛r DE%HȮ,s%L/#p}#NRt˱TxwGxǩgw ȏG/~jȾqkގZ -ub^!( !vbm<,c玝 CE]ġh€ ;F('A ;cF('&Xw$dJٕwoq7];YE1qDzX- ֢ctY>7{drQ-> endobj 76 0 obj << /Length 723 /Filter /FlateDecode >> stream xVN1W5R+DqJ[~w+"n<{g{naX7E4W 4<~z gk-G W`ˀp7|7J;.`h Iz\"%N [eN!PakU}pM> endobj 78 0 obj << /Length 512 /Filter /FlateDecode >> stream xUn0 +xLSEkn@ b`[$tj(2AqG=JH 3=301O/v.߿\B\^}9w{B~k_]Ǟ*lcnGjOjƄ>(@Ph nBh%ZBVB!v5l.{=Cg?ɫ868[aCvӾɇ:;S ;Lg((l:C9bPa398lEAA}@.-GvmEƌ>(! FA!DlBKb5 AwU vͱ}2r ^'1f6;%DNzS钝҃45;额fqj{r,/\!Kv`MO2>ˆhːng9`j=__沣E]SCJ^ZH^ՉU:pUVcaQ8e扁endstream endobj 79 0 obj << /Type /Page /Parent 3 0 R /Contents 80 0 R /Resources 4 0 R >> endobj 80 0 obj << /Length 472 /Filter /FlateDecode >> stream xTMo0 Wm[EOʂ$] h/0V#~("a O3> endobj 82 0 obj << /Length 579 /Filter /FlateDecode >> stream xUMo0 WJdۀEO^m -Jh.=3(-,&ʴ[Y3v޵Ӿm*^k3^^eg8lw~@;;t^h tCOV ^W  b!0 JJ0jV!PxW 7)3(v%)ԦWwN:[u]M4wm&:vϷ줷;#?߳ȖoIo>G~ڵ|=)O/;8CO]TCWRRٺ2j]=?ͪ{]hDj<t|fŇZW5ntchHA#+M_ê>mZW@&MhOک2ri|ӞR>O <= NgMA -3w*endstream endobj 83 0 obj << /Type /Page /Parent 3 0 R /Contents 84 0 R /Resources 4 0 R >> endobj 84 0 obj << /Length 453 /Filter /FlateDecode >> stream xTMo0 WƊ%[@CCa # חM#@$"]#=C L%@,)OuOr'S8[8{W- ݷ[\ȳrFpj&`(g m\!6s*mpک 6˵;P@ WŲ˥ ϫIڕ7C(17p9(_S0x1"8a3"&)+4 !(44lsO &,JM8PY5e{?+t-5s,Q7͂#-ݗƋ%3|Qm]-kYO53cneS {.4SE2ڊڕ3 m)c֮c\3U{$FCendstream endobj 85 0 obj << /Type /Page /Parent 3 0 R /Contents 86 0 R /Resources 4 0 R >> endobj 86 0 obj << /Length 484 /Filter /FlateDecode >> stream xUMOA ϯ190=+EJmV("AB꯯lQ:";9_AX7xgF f~v 38_C`kN. /@/cAc>#jOjƄ>ɮ(H y"(y7 !R -E+fYMIYE(zN? H^ cmb> endobj 88 0 obj << /Length 490 /Filter /FlateDecode >> stream xTnAWL?uMH@ E$g؂UrmWuv\V!b`@~~OH|\.Ek-68e>߀*V&7IQmp 8=r Sb+, bYox ssϖ߁k< mxE +fK= b9A,ѥA]\"_mXA*4]ǎℱ!LQ+ ѡ=* Q0= ˜* zF0xJ> endobj 90 0 obj << /Length 727 /Filter /FlateDecode >> stream xVAOT1_1G8X:BԄDM<a띾Nf†~|mSN 3w5`!!{d V! c@] T 5 )jBBB"l/Rײx+  ԏS,|,'^胱ɒqYie0nv{dvov{dm> |:ѤT7@E ˁ 5Tp)jJ!PdUwPm+wpdK]4i\(euB.)PsdՎ{8]16绋vQ7珫V37 Xo|xv #ǽ=ȰC@ .|}֧ަgKkU $3&d(@&&T(?)iJD:>y Tj)FtfX19y~MLF/GJ=6`/ Q*e &j)Ftk](cQd,1l L\8W?چ'v0Znzj չ%ŗhqm= OI/>ETe=[iʖk\jZ`[Į{\|Uﺖ[r+^nu+Άendstream endobj 91 0 obj << /Type /Page /Parent 3 0 R /Contents 92 0 R /Resources 4 0 R >> endobj 92 0 obj << /Length 517 /Filter /FlateDecode >> stream xUn0 +xLSEkm@ hc`I6KՔGР8~%R$h.bgA &!2ɜ8\^|)_s} ‚kLG(lBn5bƈ6.P- P])T -j 0ոIE8Gq3g_< ǀ۔c3oan|_"E읷Se5 ۟0M'(DӰ p6}lxljP3|Mm[2#a TؐF@M? P6ZB&p* *I}>$>ZU߮ת3dۦRu.Z*[їȏl;pȏlݻI%E[&O%êd_)=@*ƒsp W'C㻞mf6ϫj~"V_7/rSQH*wtrLY.#R*tm;4/DcVendstream endobj 93 0 obj << /Type /Page /Parent 3 0 R /Contents 94 0 R /Resources 4 0 R >> endobj 94 0 obj << /Length 476 /Filter /FlateDecode >> stream xTn1 +xQD&hH =װSח(5 r³;Cr( @{:`l!4h+ #ɜ>_8\MOz6#. ƒ;]nc^Mu jNZjP^L*L̤DLyD}l&աŬ̳!LSjƉ\ogRƭ]vs@_w矷/Xwy$IjH)SvKbC=7m3('hY]A'$5(UI %I%`)qO$Iz>_PE3ZJfendstream endobj 95 0 obj << /Type /Page /Parent 3 0 R /Contents 96 0 R /Resources 4 0 R >> endobj 96 0 obj << /Length 583 /Filter /FlateDecode >> stream xUMo0 Wkm@ b`K@GGCgѠ^zg>QE"a  xN{\D,<. l:}pse1s~f[*Q^{ k. `~dϨe3v` m!@B 8CIH(% `W&$Iܵuұ4V!?2Nd5LLYw~q_AJ:;[c=CRy ¶g(;ð.a ep}Ӱ:NpGvԩ aFup XCJ ఒ %UH0U!M ]lz`tQgtQj`ǽ렱QoltɎz;Abtюz{KUIyz!&=Ua]/x7^ºL%2b^<=Nnբ}|3fօ!񩡻rkŇDj պ6e8dA[[i2دC{?:ê{Y9^%<@}/=4ߍ endstream endobj 97 0 obj << /Type /Page /Parent 3 0 R /Contents 98 0 R /Resources 4 0 R >> endobj 98 0 obj << /Length 452 /Filter /FlateDecode >> stream xTn0 +x\cEkm@hc!K.-:f-?=zHp+xq7EP"2c  a `ӓ\.G=ZfK=xxtW #{a X+xWhA A!,A!i!\tjr{r?1PUl)r}!Ϡ[Oݰ$}ys<$~-<^#˙95+H=ߋ6#2a™BC2BC,A!hN#h0h`’4w?̓`gaީ>h?gj~zGoT #dfl`-}s:r)KgzL6yRZrVSL[$ah9 6oveLB;mʘ+LUk^:)Cendstream endobj 99 0 obj << /Type /Page /Parent 3 0 R /Contents 100 0 R /Resources 4 0 R >> endobj 100 0 obj << /Length 483 /Filter /FlateDecode >> stream xUMo0 We[ECm m )2Aq<~BX7xgF f~n \C`˯kNo O@/cA5'5cBdWh$<Z)Z ТPh$ "qsI$161q:oaa }|hsz33t2x¦#yyvo 2fd)Nv@A y"(2 !bc%Z PhU!$mML5Y*@kcmSغw(Fevd$~4,voWCﺜ>t1׳P9(ewRqendstream endobj 101 0 obj << /Type /Page /Parent 3 0 R /Contents 102 0 R /Resources 4 0 R >> endobj 102 0 obj << /Length 489 /Filter /FlateDecode >> stream xTnAWt&H [@YCz[Wyv5V!b`@~ϰHb%\-Ek-68m| u M`ں$&d_`qR/{ZSW88YL*G[ e7n;Ξ-k< ɶ^} ˕y/= 3b9C,ѥA]\"_mv ޱ:alh(x GBt@(p (z GB @(p 0& GB6P Ńo0xtƬ8EWfkV0>oZx?{{Kp(餭VEOA֠N[4eJG K@LI=ۦYD[ǿkHo}Θj/'E5C* >[ڴ-_pcjendstream endobj 103 0 obj << /Type /Page /Parent 3 0 R /Contents 104 0 R /Resources 4 0 R >> endobj 104 0 obj << /Length 726 /Filter /FlateDecode >> stream xVn1W̱=`m! ?;`Ke@8 o%K3X=W4$Mt=.Pb'2TGN u HE8Y&nlvxXH~Tbc9zx^֢ƎZ.&Ke%2׎m: økEk;ܵ{z_4AWT ~GRa,b'R*Q \*@EU5^Amnz-Y]btѤq`9 @]T;~]V9_jO9\< 0S=Wa74g?aJaGc@ Go~}=Yq;eUɌ ɶ 652*f:LĮCg@֜nDn3T PddUD|9RQY'}\lR1(V0QkNV7^}BkI搟6ZKsmh{bnaP[rK\|Vk!;Gq$⃍_A%qQȻ5klƥŜ M욻Wky/VW| Sendstream endobj 105 0 obj << /Type /Page /Parent 3 0 R /Contents 106 0 R /Resources 4 0 R >> endobj 106 0 obj << /Length 515 /Filter /FlateDecode >> stream xUn0 +xLSEkm@ b`ءq6jʣLhC?Q)a[x2 KdFd.>_8\_})ss ~3,9QBG5bƈ6.P- P])T -j 0մIE'p7<Lj۔c3@~k>K}uvow)gPa3Cy e 8&p6D(&Ȏ-H0*wlHA&R-!PX@E]ޤ>Auli`˪ת3dۦRu.Z*[їOl;pOlݻI%E[&O%㪕d_)=@*ƒ9yy6pb> endobj 108 0 obj << /Length 475 /Filter /FlateDecode >> stream xTMo0 Wum k P=tYҢؿ/UQFb|E l|;:`l!4h+7G99\|>q9>;M |7+m F|1KNR ?um PF)Ԡ91yٛOV>G X[B5bX Ώ-dD_q|=ж*B4!PA;T>(}U9YW̪CY==gCr2$"Ռ -wϼdl.aw߯~k~պyתG2_<0y,&Dϝ2e)6c1 l]|6"JiҎƟZJ 8$8FJ\df.IdN*K]';|u@g<;JWendstream endobj 109 0 obj << /Type /Page /Parent 3 0 R /Contents 110 0 R /Resources 4 0 R >> endobj 110 0 obj << /Length 582 /Filter /FlateDecode >> stream xUMo0 WJdۀEv 6if4azIf`a !xN{z\: <-l:}tse1s~,T#gU{ +t+=f34J I `(]N$qnG4`Q!?2NǮ2&LY~q_6twFPFjAxq;CE? ۝{|^hutB٭N ^ 2ĞPO(0g+)PYCA]ܤ̠HۅȦ VL~u.T4wG:vO쨷;#?ݳU,nQoD~ܵ|=)O/;@O]XWwCWصMݐn'z4 g`ݮVv}=fօ!tӅʩ պ6e8dA#V- fjtUdjdkDW >rd*%yr 9hFa endstream endobj 111 0 obj << /Type /Page /Parent 3 0 R /Contents 112 0 R /Resources 4 0 R >> endobj 112 0 obj << /Length 454 /Filter /FlateDecode >> stream xTn0 +x\cEkm@ b`!mM.K[ț[~{hV. RDd& -MOr_|n>uj> endobj 114 0 obj << /Length 485 /Filter /FlateDecode >> stream xUMkA ϯ>d2hvv mBB!MۘؐP误6j;>O_AX5]EN>49ݙg:Adfa Uo&.N'"LLaS>\^a2`;\2q4,w?._ 1vzͤ^^D夔^I_lr'endstream endobj 115 0 obj << /Type /Page /Parent 3 0 R /Contents 116 0 R /Resources 4 0 R >> endobj 116 0 obj << /Length 491 /Filter /FlateDecode >> stream xTnAWt{^D 8CxD|@x[*\U.k XãGg!Ao=5+|]&pR0>WW[ot ([+x8D4 ^shjA{`;pe?@ n.ޘ `-GgmKtXz^KAlMd=yAtqt-c SꄡL쐥+ #`Xq'T8GB`Np$$; ڍ00Zٶs>)֟5cm"mO"Z4CVYS$V}۱A[{۲+N 7/kkendstream endobj 117 0 obj << /Type /Page /Parent 3 0 R /Contents 118 0 R /Resources 4 0 R >> endobj 118 0 obj << /Length 729 /Filter /FlateDecode >> stream xVn1W̱=zھJ DPUVMg;(zhyE8.B6Ct{M$pM >]! 'ޞbZ sqGcX< ̀썷a ޥ X+GCƀ&dy jV*#SԄ:@EZU_".eW{6au7!.+  ԏS,|,Y1,oiy-`bd\Vb: 3q{m٦2;۶]۶m۾g煫A ]Q3 GRa,b'R*Q ܬ*@EU5^Amnz-YݯbtѤqa93;@U;W9ߴjO9Z?)wp0 ~゗GWQ6>Ȱˣ1_?_=~>yYMkU $3&d(@&(T()]ӬZu\ͬԚSՍհ`r~NLFϪOGJ= f_zlCtAZsJZ{XH2e6| ZM\8?n MsO6oahSsO-%.D}׵#θ_$?)CqhlƥŜ M욻Wnky9/TWb Rendstream endobj 119 0 obj << /Type /Page /Parent 3 0 R /Contents 120 0 R /Resources 4 0 R >> endobj 120 0 obj << /Length 518 /Filter /FlateDecode >> stream xUn0 +xLSEkm@ b`ءq6GՔGР8~%R$ `bgA &!+3\|q9_S?|7+m f|2Yr0?#jŌm ]"x[HA&R-!PZaip!Nno.>xNK[1)D)g`n ּ"E_읷S e5 ۟0g(Dp6M}lxljQ3|Nm[2#a Tؐ&@M? P6ZB&p* *I}ީ$>ZU_ת3dۦRu.Z*[їOl;pOlݻI%E[&O%㪕d_)=@*ƒqxs iniOKQfay *A!%=_̤riJ{/WU2s5ܱ,endstream endobj 121 0 obj << /Type /Page /Parent 3 0 R /Contents 122 0 R /Resources 4 0 R >> endobj 122 0 obj << /Length 476 /Filter /FlateDecode >> stream xTMo0 Wum [ P;tYb~Ri/0V#~("aCYGKdA[yI9\9m*en6AXog0`h,9I1@gXO z!x4A"׍R$ :iA{1y{> endobj 124 0 obj << /Length 583 /Filter /FlateDecode >> stream xUMo0 WJdۀEn 6׏(2A|ԣHJ0 KxP7Ct{q_`ӑ++1 O5n7eaF>koXa ޥ\̏UF6`ut -ĞP](0NJ0dCAߤ \5vw"v.# q]L>vi0f RkKZsx3Ԯx2R{ 3ð.a e0l+B@CΨnu*H:PА!@B 8[I @*$ &eE.D6=ed:먳uQjaǽm=ұD~eGAbtӎz{%Iyz!&zº"//l w;خOSp&jh~n7_w̺؟5^4|]9CU"ZצL =5hJ~LrV݃L͚lѰU* Z|M?&q^\ACoZ-o{ݨ!*endstream endobj 3 0 obj << /Type /Pages /Kids [ 7 0 R 9 0 R 11 0 R 15 0 R 18 0 R 21 0 R 24 0 R 27 0 R 30 0 R 33 0 R 36 0 R 39 0 R 41 0 R 43 0 R 45 0 R 47 0 R 49 0 R 51 0 R 53 0 R 55 0 R 57 0 R 59 0 R 61 0 R 63 0 R 65 0 R 67 0 R 69 0 R 71 0 R 73 0 R 75 0 R 77 0 R 79 0 R 81 0 R 83 0 R 85 0 R 87 0 R 89 0 R 91 0 R 93 0 R 95 0 R 97 0 R 99 0 R 101 0 R 103 0 R 105 0 R 107 0 R 109 0 R 111 0 R 113 0 R 115 0 R 117 0 R 119 0 R 121 0 R 123 0 R ] /Count 54 /MediaBox [0 0 504 504] >> endobj 4 0 obj << /ProcSet [/PDF /Text /ImageC /ImageB] /Font <> /ExtGState << /GSais 127 0 R >> /XObject << /Im0 13 0 R /Mask0 14 0 R /Im1 17 0 R /Im2 20 0 R /Im3 23 0 R /Im4 26 0 R /Im5 29 0 R /Im6 32 0 R /Im7 35 0 R /Im8 38 0 R >> /ColorSpace << /sRGB 5 0 R >> >> endobj 5 0 obj [/ICCBased 6 0 R] endobj 6 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~endstream endobj 125 0 obj << /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [ 45/minus 96/quoteleft 144/dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space] >> endobj 126 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica /Encoding 125 0 R >> endobj 127 0 obj << /Type /ExtGState /AIS false >> endobj xref 0 128 0000000000 65535 f 0000000021 00000 n 0000000163 00000 n 0000054368 00000 n 0000054834 00000 n 0000055135 00000 n 0000055168 00000 n 0000000212 00000 n 0000000292 00000 n 0000001009 00000 n 0000001090 00000 n 0000001558 00000 n 0000001640 00000 n 0000002182 00000 n 0000002387 00000 n 0000002581 00000 n 0000002663 00000 n 0000003152 00000 n 0000003356 00000 n 0000003438 00000 n 0000003927 00000 n 0000004130 00000 n 0000004212 00000 n 0000004703 00000 n 0000004906 00000 n 0000004988 00000 n 0000005478 00000 n 0000005681 00000 n 0000005763 00000 n 0000006252 00000 n 0000006455 00000 n 0000006537 00000 n 0000007028 00000 n 0000007231 00000 n 0000007313 00000 n 0000007803 00000 n 0000008006 00000 n 0000008088 00000 n 0000008625 00000 n 0000008813 00000 n 0000008895 00000 n 0000009378 00000 n 0000009460 00000 n 0000009956 00000 n 0000010038 00000 n 0000010534 00000 n 0000010616 00000 n 0000011140 00000 n 0000011222 00000 n 0000011633 00000 n 0000011715 00000 n 0000012203 00000 n 0000012285 00000 n 0000012773 00000 n 0000012855 00000 n 0000013433 00000 n 0000013515 00000 n 0000014119 00000 n 0000014201 00000 n 0000014682 00000 n 0000014764 00000 n 0000015245 00000 n 0000015327 00000 n 0000016053 00000 n 0000016135 00000 n 0000016573 00000 n 0000016655 00000 n 0000023559 00000 n 0000023641 00000 n 0000035129 00000 n 0000035211 00000 n 0000035732 00000 n 0000035814 00000 n 0000036366 00000 n 0000036448 00000 n 0000037006 00000 n 0000037088 00000 n 0000037883 00000 n 0000037965 00000 n 0000038549 00000 n 0000038631 00000 n 0000039175 00000 n 0000039257 00000 n 0000039908 00000 n 0000039990 00000 n 0000040515 00000 n 0000040597 00000 n 0000041153 00000 n 0000041235 00000 n 0000041797 00000 n 0000041879 00000 n 0000042678 00000 n 0000042760 00000 n 0000043349 00000 n 0000043431 00000 n 0000043979 00000 n 0000044061 00000 n 0000044716 00000 n 0000044798 00000 n 0000045322 00000 n 0000045405 00000 n 0000045961 00000 n 0000046045 00000 n 0000046607 00000 n 0000046691 00000 n 0000047490 00000 n 0000047574 00000 n 0000048162 00000 n 0000048246 00000 n 0000048794 00000 n 0000048878 00000 n 0000049533 00000 n 0000049617 00000 n 0000050144 00000 n 0000050228 00000 n 0000050786 00000 n 0000050870 00000 n 0000051434 00000 n 0000051518 00000 n 0000052320 00000 n 0000052404 00000 n 0000052995 00000 n 0000053079 00000 n 0000053628 00000 n 0000053712 00000 n 0000057863 00000 n 0000058122 00000 n 0000058222 00000 n trailer << /Size 128 /Info 1 0 R /Root 2 0 R >> startxref 58273 %%EOF wk/tests/testthat/test-orient.R0000644000176200001440000001060014471771472016310 0ustar liggesuserstest_that("wk_orient() works", { # ccw to ccw expect_identical( wk_orient( wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0), (0.2 0.2, 0.2 0.8, 0.8 0.8, 0.8 0.2, 0.2 0.2))"), direction = wk_counterclockwise() ), wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0), (0.2 0.2, 0.2 0.8, 0.8 0.8, 0.8 0.2, 0.2 0.2))") ) # ccw to cw expect_identical( wk_orient( wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0), (0.2 0.2, 0.2 0.8, 0.8 0.8, 0.8 0.2, 0.2 0.2))"), direction = wk_clockwise() ), wkt("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0), (0.2 0.2, 0.8 0.2, 0.8 0.8, 0.2 0.8, 0.2 0.2))") ) # inconsistent to ccw expect_identical( wk_orient( wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0), (0.2 0.2, 0.8 0.2, 0.8 0.8, 0.2 0.8, 0.2 0.2))"), direction = wk_counterclockwise() ), wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0), (0.2 0.2, 0.2 0.8, 0.8 0.8, 0.8 0.2, 0.2 0.2))") ) # a collection expect_identical( wk_orient( wkt(c("MULTIPOLYGON (((1 1, 2 1, 1 2, 1 1)), ((0 -1, 0 1, 1 0, 0 -1)))")), direction = wk_clockwise() ), wkt(c("MULTIPOLYGON (((1 1, 1 2, 2 1, 1 1)), ((0 -1, 0 1, 1 0, 0 -1)))")) ) # xyz expect_identical( wk_orient( wkt("POLYGON Z ((1 1 5, 2 4 3, 4 3 2, 1 1 5))"), direction = wk_clockwise() ), wkt("POLYGON Z ((1 1 5, 2 4 3, 4 3 2, 1 1 5))") ) # xyzm expect_identical( wk_orient( wkt("POLYGON ZM ((1 1 5 1, 2 4 3 2, 4 3 2 3, 1 1 5 1))"), direction = wk_counterclockwise() ), wkt("POLYGON ZM ((1 1 5 1, 4 3 2 3, 2 4 3 2, 1 1 5 1))") ) }) test_that("wk_orient() works for nested collections", { expect_identical( wk_orient( wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 1 0, 0 1, 0 0)))))"), direction = wk_clockwise() ), wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)))))") ) }) test_that("wk_orient() skips non-polygons", { expect_identical( wk_orient( wkt(c("MULTIPOINT ((0 1))", "LINESTRING (1 1, 2 2, 3 3)", "POLYGON ((0 0, 0 1, 1 0, 0 0))")), direction = wk_counterclockwise() ), wkt(c("MULTIPOINT ((0 1))", "LINESTRING (1 1, 2 2, 3 3)", "POLYGON ((0 0, 1 0, 0 1, 0 0))")) ) expect_identical( wk_orient( xyzm(1:10, 11:20, -1, -2) ), xyzm(1:10, 11:20, -1, -2) ) }) test_that("wk_orient() skips empty geometries", { # null feature expect_identical( wk_orient( wkt(NA) ), wkt(NA) ) # empty vector expect_identical( wk_orient( wkt() ), wkt() ) expect_identical( wk_orient( wkt(c("POINT EMPTY", "LINESTRING EMPTY", "POLYGON EMPTY")) ), wkt(c("POINT EMPTY", "LINESTRING EMPTY", "POLYGON EMPTY")) ) expect_identical( wk_orient( wkt(c("MULTIPOINT EMPTY", "MULTILINESTRING EMPTY", "MULTIPOLYGON EMPTY", "GEOMETRYCOLLECTION EMPTY")) ), wkt(c("MULTIPOINT EMPTY", "MULTILINESTRING EMPTY", "MULTIPOLYGON EMPTY", "GEOMETRYCOLLECTION EMPTY")) ) }) test_that("wk_orient() skips zero-area polygons", { # zero area expect_identical( wk_orient( wkt("POLYGON ((1 1, 2 2, 3 3, 1 1))"), wk_clockwise() ), wkt("POLYGON ((1 1, 2 2, 3 3, 1 1))") ) expect_identical( wk_orient( wkt("POLYGON ((1 1, 2 2, 3 3, 1 1))"), wk_counterclockwise() ), wkt("POLYGON ((1 1, 2 2, 3 3, 1 1))") ) }) test_that("wk_orient() handles invalid polygons", { # invalid polygon, no coordinates expect_identical( wk_handle( wkt("POLYGON (EMPTY)"), # wkt writer will throw here wk_orient_filter( wkb_writer() ) ), wk_handle( wkt("POLYGON (EMPTY)"), wkb_writer() ) ) # invalid polygon, no area expect_identical( wk_orient( wkt("POLYGON ((0 0, 1 1))") ), wkt("POLYGON ((0 0, 1 1))") ) }) test_that("wk_orient() preserves attributes", { expect_identical( wk_orient( wkt("POLYGON ((0 0, 0 0.5, 0.5 0.5, 0.5 0, 0 0))", crs = wk_crs_longlat(), geodesic = TRUE), direction = wk_clockwise() ), wkt("POLYGON ((0 0, 0 0.5, 0.5 0.5, 0.5 0, 0 0))", crs = wk_crs_longlat(), geodesic = TRUE) ) expect_identical( wk_orient( wkt("POLYGON ((0 0, 0 0.5, 0.5 0.5, 0.5 0, 0 0))", crs = wk_crs_longlat(), geodesic = TRUE), direction = wk_counterclockwise() ), wkt("POLYGON ((0 0, 0.5 0, 0.5 0.5, 0 0.5, 0 0))", crs = wk_crs_longlat(), geodesic = TRUE) ) }) wk/tests/testthat/test-plot.R0000644000176200001440000000353414313206670015762 0ustar liggesusers test_that("wk_plot() works for zero-length vectors", { wk_plot(wkt("POINT (0 1)")) expect_identical(wk_plot(wkt(), add = TRUE), wkt()) }) test_that("wk_plot() works for all points", { x <- wkt("POINT (0 1)") expect_identical(wk_plot(x), x) }) test_that("wk_plot() works for all point/multipoints", { x <- wkt("MULTIPOINT (0 1, 2 2)") expect_identical(wk_plot(x), x) }) test_that("wk_plot() works for all linestrings", { x <- wkt("LINESTRING (0 1, 2 2)") expect_identical(wk_plot(x), x) }) test_that("wk_plot() works for all polygons", { x <- wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))") expect_identical(wk_plot(x), x) }) test_that("wk_plot() works for all collections", { x <- wkt("GEOMETRYCOLLECTION(POLYGON ((0 0, 0 1, 1 0, 0 0)))") expect_identical(wk_plot(x), x) }) test_that("wk_plot() recycles args for each feature", { x <- wkt( c("GEOMETRYCOLLECTION(POLYGON ((0 0, 0 1, 1 0, 0 0)), POINT (1 0.4))", "LINESTRING (0 0, 1 1)" ) ) expect_identical(wk_plot(x, col = c("blue", "red"), lty = 1), x) x <- wkt(c("MULTIPOINT (0 1, 2 2)", "POINT (1 0.4)")) expect_identical(wk_plot(x, col = c("blue", "red"), pch = 16), x) }) test_that("wk_plot() errors for geodesic objects", { expect_error(wk_plot(wkt(geodesic = TRUE)), "can't plot geodesic objects") }) test_that("plot methods work", { x <- "LINESTRING (0 0, 1 1)" expect_identical(plot(as_wkt(x)), as_wkt(x)) expect_identical(plot(as_wkb(x)), as_wkb(x)) }) test_that("xy and rect plot methods work", { expect_identical(plot(xy(1:5, 1:5)), xy(1:5, 1:5)) expect_identical(plot(rct(1, 2, 3, 4)), rct(1, 2, 3, 4)) }) test_that("crc plot method works", { expect_identical(plot(crc(1, 2, 3)), crc(1, 2, 3)) }) test_that("plot can plot all examples", { for (which in names(wk_example_wkt)) { expect_silent(wk_plot(wk_example_wkt[[!!which]], xlab = which)) } }) wk/tests/testthat/test-writer.R0000644000176200001440000000073714106220314016311 0ustar liggesusers test_that("the wk_writer() generic resolves correct handler", { expect_s3_class(wk_writer(wkt()), "wk_wkt_writer") expect_s3_class(wk_writer(wkb()), "wk_wkb_writer") expect_s3_class(wk_writer(xy()), "wk_xy_writer") expect_s3_class(wk_writer(xy(), generic = TRUE), "wk_wkb_writer") expect_s3_class(wk_writer(rct()), "wk_wkb_writer") expect_s3_class(wk_writer(crc()), "wk_wkb_writer") expect_s3_class(wk_writer(structure(list(), class = "sfc")), "wk_sfc_writer") }) wk/tests/testthat/test-wk-crs.R0000644000176200001440000001252114311405267016207 0ustar liggesusers test_that("crs setting and getting works on wk_vctr", { x <- new_wk_wkt() expect_null(wk_crs(x)) x <- wk_set_crs(x, 4326) expect_identical(wk_crs(x), 4326) wk_crs(x) <- 26920 expect_identical(wk_crs(x), 26920) }) test_that("crs setting and getting works on wk_rcrd", { x <- new_wk_xy() expect_null(wk_crs(x)) x <- wk_set_crs(x, 4326) expect_identical(wk_crs(x), 4326) wk_crs(x) <- 26920 expect_identical(wk_crs(x), 26920) }) test_that("geodesic getting and setting works for wkb", { x <- new_wk_wkb() expect_false(wk_is_geodesic(x)) x <- wk_set_geodesic(x, TRUE) expect_true(wk_is_geodesic(x)) wk_is_geodesic(x) <- FALSE expect_false(wk_is_geodesic(x)) expect_null(attr(x, "geodesic")) wk_is_geodesic(x) <- wk_geodesic_inherit() expect_identical(wk_is_geodesic(x), NA) expect_error(wk_set_geodesic(x, "fish"), "must be TRUE, FALSE, or NA") }) test_that("geodesic getting and setting works for wkt", { x <- new_wk_wkt() expect_false(wk_is_geodesic(x)) x <- wk_set_geodesic(x, TRUE) expect_true(wk_is_geodesic(x)) wk_is_geodesic(x) <- FALSE expect_false(wk_is_geodesic(x)) expect_null(attr(x, "geodesic")) wk_is_geodesic(x) <- wk_geodesic_inherit() expect_identical(wk_is_geodesic(x), NA) expect_error(wk_set_geodesic(x, "fish"), "must be TRUE, FALSE, or NA") }) test_that("geodesic setting gives a warning when this isn't supported", { expect_warning(wk_set_geodesic(xy(), TRUE), "for object of class 'wk_xy'") }) test_that("wk_geodesic_output() works", { expect_identical( wk_is_geodesic_output(wkt(geodesic = FALSE), wkt(geodesic = FALSE)), FALSE ) expect_identical( wk_is_geodesic_output(wkt(geodesic = TRUE), wkt(geodesic = TRUE)), TRUE ) expect_identical( wk_is_geodesic_output(wkt(geodesic = wk_geodesic_inherit()), wkt(geodesic = FALSE)), FALSE ) expect_identical( wk_is_geodesic_output(wkt(geodesic = FALSE), wkt(geodesic = wk_geodesic_inherit())), FALSE ) expect_error( wk_is_geodesic_output(wkt(geodesic = TRUE), wkt(geodesic = FALSE)), "differing values" ) expect_error( wk_is_geodesic_output(wkt(geodesic = FALSE), wkt(geodesic = TRUE)), "differing values" ) }) test_that("crs comparison works", { expect_true(wk_crs_equal(NULL, NULL)) expect_false(wk_crs_equal(NULL, "something")) expect_false(wk_crs_equal("something", NULL)) expect_true(wk_crs_equal("something", "something")) expect_false(wk_crs_equal("something", "something_else")) expect_true(wk_crs_equal(1234, 1234L)) expect_true(wk_crs_equal(1234L, 1234)) expect_false(wk_crs_equal(NULL, 1234)) }) test_that("crs output computing works", { x <- wkt("POINT (0 0)", crs = NULL) expect_identical(wk_crs_output(x, x), NULL) expect_identical(wk_crs_output(x, wk_set_crs(x, wk_crs_inherit())), NULL) expect_identical(wk_crs_output(wk_set_crs(x, wk_crs_inherit()), x), NULL) expect_identical( wk_crs_output(wk_set_crs(x, wk_crs_inherit()), wk_set_crs(x, wk_crs_inherit())), wk_crs_inherit() ) expect_identical(wk_crs_output(wk_set_crs(x, 1), wkt()), 1) expect_identical(wk_crs_output(wkt(), wk_set_crs(x, 1)), 1) expect_error(wk_crs_output(wk_set_crs(x, 1), wk_set_crs(x, 2)), "are not equal") }) test_that("crs_proj_definition() works", { expect_identical(wk_crs_proj_definition(NULL), NA_character_) expect_identical(wk_crs_proj_definition(wk_crs_inherit()), NA_character_) expect_identical(wk_crs_proj_definition(1234), "EPSG:1234") expect_identical(wk_crs_proj_definition(NA_real_), NA_character_) expect_identical(wk_crs_proj_definition(1234L), "EPSG:1234") expect_identical(wk_crs_proj_definition(NA_integer_), NA_character_) expect_identical(wk_crs_proj_definition("EPSG:1234"), "EPSG:1234") expect_identical(wk_crs_proj_definition(NA_character_), NA_character_) }) test_that("wk_crs_projjson() works", { expect_identical(wk_crs_projjson(NULL), NA_character_) expect_identical(wk_crs_projjson("{probably json}"), "{probably json}") expect_identical(wk_crs_projjson("probably not json"), NA_character_) expect_identical(wk_crs_projjson("EPSG:1234"), NA_character_) expect_match(wk_crs_projjson("EPSG:4326"), "GeographicCRS") expect_match(wk_crs_projjson(4326), "GeographicCRS") }) test_that("wk_crs_longlat() works for common datums", { expect_identical(wk_crs_longlat(), "OGC:CRS84") expect_identical(wk_crs_longlat(wk_crs_inherit()), "OGC:CRS84") expect_identical(wk_crs_longlat("OGC:CRS84"), "OGC:CRS84") expect_identical(wk_crs_longlat("EPSG:4326"), "OGC:CRS84") expect_identical(wk_crs_longlat("WGS84"), "OGC:CRS84") expect_identical(wk_crs_longlat("OGC:CRS83"), "OGC:CRS83") expect_identical(wk_crs_longlat("EPSG:4269"), "OGC:CRS83") expect_identical(wk_crs_longlat("NAD83"), "OGC:CRS83") expect_identical(wk_crs_longlat("OGC:CRS27"), "OGC:CRS27") expect_identical(wk_crs_longlat("EPSG:4267"), "OGC:CRS27") expect_identical(wk_crs_longlat("NAD27"), "OGC:CRS27") expect_identical(wk_crs_longlat(), "OGC:CRS84") expect_identical(wk_crs_longlat(), "OGC:CRS84") expect_identical(wk_crs_longlat(), "OGC:CRS84") expect_identical(wk_crs_longlat(), "OGC:CRS84") expect_error(wk_crs_longlat("not a crs"), "Can't guess authority-compliant") }) test_that("wk_crs_inherit() prints as expected", { expect_match(format(wk_crs_inherit()), "wk_crs_inherit") expect_output(print(wk_crs_inherit()), "wk_crs_inherit") }) wk/tests/testthat/test-utils.R0000644000176200001440000000107314145575672016156 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("is_vector_class works", { expect_true(is_vector_class(1:5)) expect_true(is_vector_class(xy(1:5, 1:5))) expect_false(is_vector_class(structure(list(), class = "fish"))) }) wk/tests/testthat/test-wkt-writer.R0000644000176200001440000000163214106220314017107 0ustar liggesusers test_that("wkt_writer() works", { wkt_good <- as_wkt( c( "POINT (1 1)", "LINESTRING (1 1, 2 2)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "MULTIPOINT ((1 1))", "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))", "MULTIPOLYGON (((0 0, 0 1, 1 0, 0 0)), ((0 0, 0 -1, -1 0, 0 0)))", "GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))" ) ) expect_identical( wk_handle(wkt_good, wkt_writer()), wkt_good ) expect_error(wk_handle(new_wk_wkt("NOT WKT"), wkt_writer()), "Expected geometry type or 'SRID") expect_identical( wk_handle(new_wk_wkt("POINT (1 1)"), wkt_writer(precision = 1, trim = FALSE)), wkt("POINT (1.0 1.0)") ) }) test_that("wkt_writer() works for a vector of indeterminate length", { long_xy <- as_wkt(xy(runif(2048), runif(2048))) expect_identical( handle_wkt_without_vector_size(long_xy, wkt_writer()), wk_handle(long_xy, wkt_writer()) ) }) wk/tests/testthat/test-handle-crc.R0000644000176200001440000000321514106220314016767 0ustar liggesusers test_that("wk_handle.wk_crc() works", { expect_identical( wk_handle(crc(1, 2, 3), wkt_writer(precision = 2), n_segments = 4), wkt("POLYGON ((4 2, 1 5, -2 2, 1 -1, 4 2))") ) crc_wkb <- wk_handle(crc(1, 2, 3), wkb_writer(), n_segments = 50L) # endian + type + size + ring size + 51 coords expect_length(unclass(crc_wkb)[[1]], 1 + 4 + 4 + 4 + 51 * 8 * 2) # check vectorization of n_segments crc_wkb2 <- wk_handle(crc(1:5, 2, 3), wkb_writer(), n_segments = 50:54) crc_wkb2_lengths <- vapply(unclass(crc_wkb2), length, integer(1)) expect_equal(crc_wkb2_lengths, 1 + 4 + 4 + 4 + ((51:55) * 8 * 2)) # check emptiness of NA circle expect_identical(as_wkt(crc(NA, NA, NA)), wkt("POLYGON EMPTY")) # check options for circle resolution prev_opt <- options(wk.crc_n_segments = 4) expect_identical( wk_handle(crc(1, 2, 3), wkt_writer(precision = 2)), wkt("POLYGON ((4 2, 1 5, -2 2, 1 -1, 4 2))") ) options(prev_opt) prev_opt <- options(wk.crc_resolution = 100) expect_identical( wk_handle(crc(1, 2, 3), wkt_writer(precision = 2)), wkt("POLYGON ((4 2, 1 5, -2 2, 1 -1, 4 2))") ) options(prev_opt) # check invalid options expect_identical( wk_handle(crc(1, 2, 3), wkt_writer(), n_segments = 0), wk_handle(crc(1, 2, 3), wkt_writer(), n_segments = 4) ) expect_identical( wk_handle(crc(1, 2, 3), wkt_writer(), n_segments = NA), wk_handle(crc(1, 2, 3), wkt_writer(), n_segments = 4) ) expect_error(wk_handle(crc(1, 2, 3), wkt_writer(), n_segments = double()), "must be length 1") # check invalid data expect_error(wk_handle.wk_crc("not a crc", wk_void_handler()), "does not inherit from") }) wk/tests/testthat/test-wkb.R0000644000176200001440000001265414513020606015565 0ustar liggesusers test_that("wkb class works", { x <- wkb(wkt_translate_wkb("POINT (40 10)", endian = 1)) expect_s3_class(x, "wk_wkb") expect_true(is_wk_wkb(x)) expect_s3_class(x, "wk_vctr") expect_output(print(x), "wk_wkb") expect_match(as.character(x), "POINT") expect_s3_class(wkb(list(NULL)), "wk_wkb") expect_true(is.na(wkb(list(NULL)))) expect_error(new_wk_wkb(structure(list(), thing = "stuff")), "must be a list") expect_error(new_wk_wkb("char!"), "must be a list") expect_error(wkb(list("not raw()")), "must be raw") expect_error(wkb(list(raw())), "Encountered 1 parse problem") expect_error(wkb(rep(list(raw()), 10)), "Encountered 10 parse problems") expect_error(validate_wk_wkb("char!"), "must be of type list") # See #123 and revert in dev wk after CRAN release # expect_error(validate_wk_wkb(list()), "must inherit from") expect_s3_class(x[1], "wk_wkb") expect_identical(x[[1]], x[1]) expect_s3_class(c(x, x), "wk_wkb") expect_identical(rep(x, 2), c(x, x)) expect_identical(rep(wkb(), 3), wkb()) expect_length(c(x, x), 2) x[1] <- "POINT (11 12)" expect_identical(as_wkt(x[1]), wkt("POINT (11 12)")) skip_if_not(packageVersion("base") >= "3.6") expect_identical(rep_len(x, 2), c(x, x)) }) test_that("as_wkb() works", { x <- wkb(wkt_translate_wkb("POINT (40 10)")) expect_identical(as_wkb(x), x) expect_identical(as_wkb("POINT (40 10)"), x) expect_identical(as_wkb(wkt("POINT (40 10)")), x) # blob and WKB methods expect_identical( as_wkb(structure(wkt_translate_wkb("POINT (11 12)"), class = "blob")), as_wkb("POINT (11 12)") ) expect_identical( as_wkb(structure(wkt_translate_wkb("POINT (11 12)"), class = "WKB")), as_wkb("POINT (11 12)") ) }) test_that("parse_wkb() works", { x <- wkt_translate_wkb("POINT (40 10)", endian = 1) expect_silent(parsed <- parse_wkb(x)) expect_false(is.na(parsed)) expect_null(attr(parsed, "problems")) x[[1]][2:3] <- as.raw(0xff) expect_warning(parsed <- parse_wkb(x), "Encountered 1 parse problem") expect_true(is.na(parsed)) expect_s3_class(attr(parsed, "problems"), "data.frame") expect_identical(nrow(attr(parsed, "problems")), 1L) }) test_that("wkb() propagates CRS", { x <- as_wkb("POINT (1 2)") wk_crs(x) <- 1234 expect_identical(wk_crs(x[1]), 1234) expect_identical(wk_crs(c(x, x)), 1234) expect_identical(wk_crs(rep(x, 2)), 1234) expect_error(x[1] <- wkb(x, crs = NULL), "are not equal") x[1] <- wkb(x, crs = 1234L) expect_identical(wk_crs(x), 1234) }) test_that("wkb() propagates geodesic", { x <- wkb(as_wkb("POINT (1 2)"), geodesic = TRUE) expect_true(wk_is_geodesic(x)) expect_true(wk_is_geodesic(x[1])) expect_true(wk_is_geodesic(c(x, x))) expect_true(wk_is_geodesic(rep(x, 2))) expect_error(x[1] <- wk_set_geodesic(x, FALSE), "objects have differing values") x[1] <- wk_set_geodesic(x, TRUE) expect_true(wk_is_geodesic(x)) }) test_that("as_wkb() propagates CRS", { x <- as_wkb("POINT (1 2)", crs = 1234) expect_identical(wk_crs(x), 1234) expect_identical(wk_crs(as_wkb(wkt("POINT (1 2)", crs = 1234))), 1234) }) test_that("as_wkb() propagates geodesic", { x <- as_wkb("POINT (1 2)", geodesic = TRUE) expect_true(wk_is_geodesic(x)) expect_true(wk_is_geodesic(as_wkb(wkt("POINT (1 2)", geodesic = TRUE)))) }) test_that("examples as wkb roundtrip", { for (which in names(wk_example_wkt)) { expect_identical( wk_handle(as_wkb(wk_example(!!which, crs = NULL)), wkt_writer()), wk_example(!!which, crs = NULL) ) } }) test_that("wk_c_wkb_to_hex works", { list_of_raw <- list(as.raw(0:255), raw(0), NULL) expect_identical( .Call(wk_c_wkb_to_hex, list_of_raw), c(paste(sprintf("%02x", 0:255), collapse = ""), "", NA_character_) ) }) test_that("wkb_to_hex works", { features <- wkt(c("POINT (0 0)", "LINESTRING (1 1, 2 2)", "POLYGON EMPTY", NA)) # little endian wkb_little <- wk_handle(features, wkb_writer(endian = 1)) hex_little <- c( "010100000000000000000000000000000000000000", "010200000002000000000000000000f03f000000000000f03f00000000000000400000000000000040", "010300000000000000", NA_character_ ) expect_equal(wkb_to_hex(wkb_little), hex_little) # big endian wkb_big <- wk_handle(features, wkb_writer(endian = 0)) hex_big <- c( "000000000100000000000000000000000000000000", "0000000002000000023ff00000000000003ff000000000000040000000000000004000000000000000", "000000000300000000", NA_character_ ) expect_equal(wkb_to_hex(wkb_big), hex_big) }) test_that("vec_equal(wkb) works", { points <- wkt(c("POINT (1 1)", "POINT (2 2)", "POINT (3 3)")) # little endian wkb_little <- wk_handle(points, wkb_writer(endian = 1)) hex_little <- c( "0101000000000000000000f03f000000000000f03f", "010100000000000000000000400000000000000040", "010100000000000000000008400000000000000840" ) expect_equal(vctrs::vec_proxy_equal(wkb_little), hex_little) expect_equal(vctrs::vec_equal(wkb_little, wkb_little), c(TRUE, TRUE, TRUE)) expect_equal(vctrs::vec_equal(wkb_little[1], wkb_little[2]), FALSE) # big endian wkb_big <- wk_handle(points, wkb_writer(endian = 0)) hex_big <- c( "00000000013ff00000000000003ff0000000000000", "000000000140000000000000004000000000000000", "000000000140080000000000004008000000000000" ) expect_equal(vctrs::vec_proxy_equal(wkb_big), hex_big) expect_equal(vctrs::vec_equal(wkb_big, wkb_big), c(TRUE, TRUE, TRUE)) expect_equal(vctrs::vec_equal(wkb_big[1], wkb_big[2]), FALSE) }) wk/tests/testthat/test-trans_explicit.R0000644000176200001440000000312414471771474020045 0ustar liggesuserstest_that("wk_trans_explicit works", { expect_identical( wk::wk_transform(rep(wk::xy(0, 0), 5), wk_trans_explicit(wk::xy(1:5, 1:5))), wk::xy(1:5, 1:5) ) # check with ZM values expect_identical( wk::wk_transform( rep(wk::xyzm(0, 0, 0, 0), 5), wk_trans_explicit(wk::xy(1:5, 1:5)) ), wk::xyzm(1:5, 1:5, 0, 0) ) expect_identical( wk::wk_transform( rep(wk::xyzm(0, 0, 0, 0), 5), wk_trans_explicit(wk::xyzm(1:5, 1:5, 1:5, 1:5)) ), wk::xyzm(1:5, 1:5, 1:5, 1:5) ) geoms <- wkt( c( "POLYGON ((0 0, 0 1, 1 0, 0 0))", "POINT (0 0)", "POINT Z (0 0 0)", "POINT M (0 0 0)", "POINT ZM (0 0 0 0)", NA ) ) # explicit transformation cds <- wk_coords(geoms)[c("x", "y", "z", "m")] cds$x <- cds$x * 2 expect_identical( wk_transform(geoms, wk_trans_explicit(cds)), wk_transform(geoms, wk_affine_scale(scale_x = 2)) ) # explicit transformation via replacement function geoms2 <- geoms wk_coords(geoms2) <- cds[c("x", "y", "m")] expect_identical( geoms2, wk_transform(geoms, wk_affine_scale(scale_x = 2)) ) wk_coords(geoms2, use_z = FALSE) <- wk_vertices(geoms) expect_identical( wk::wk_drop_z(geoms), geoms2 ) geoms3 <- geoms wk_coords(geoms3, use_m = FALSE) <- wk_vertices(geoms) expect_identical( wk::wk_drop_m(geoms), geoms3 ) # check that crs is dropped when we clobber the coords pt <- wkt("POINT (0 1)", crs = "EPSG:3976") expect_equal( wk_crs(pt), "EPSG:3976" ) wk_coords(pt) <- xy(1, 2) expect_null( wk_crs(pt) ) }) wk/tests/testthat/test-pkg-readr.R0000644000176200001440000000242614311471710016654 0ustar liggesusers test_that("readr methods work for all vector types", { skip_if_not_installed("readr") vctr_wkb <- as_wkb(c("POINT (1 2)", "POINT Z (3 4 5)", NA)) expect_identical( readr::output_column(vctr_wkb), c("POINT (1 2)", "POINT Z (3 4 5)", NA) ) vctr_wkt <- as_wkt(c("POINT (1 2)", "POINT Z (3 4 5)", NA)) expect_identical( readr::output_column(vctr_wkt), c("POINT (1 2)", "POINT Z (3 4 5)", NA) ) vctr_xy <- as_xy(as_wkt(c("POINT (1 2)", "POINT Z (3 4 5)", NA))) expect_identical( readr::output_column(vctr_xy), c("POINT Z (1 2 nan)", "POINT Z (3 4 5)", NA) ) vctr_rct <- rct(1, 2, 3, 4)[c(1, NA)] expect_identical( readr::output_column(vctr_rct), c("POLYGON ((1 2, 3 2, 3 4, 1 4, 1 2))", NA) ) vctr_crc <- crc(0, 0, 1) expect_match( readr::output_column(vctr_crc), "^POLYGON" ) }) test_that("readr can write files from data frames with wk vectors", { skip_if_not_installed("readr") x_vctr <- as_wkb(c("POINT (1 2)", "POINT Z (3 4 5)", NA)) tf <- tempfile() readr::write_csv(data.frame(x_vctr = x_vctr), tf) expect_identical( as.data.frame(readr::read_csv(tf, show_col_types = FALSE)), data.frame( x_vctr = c("POINT (1 2)", "POINT Z (3 4 5)", NA), stringsAsFactors = FALSE ) ) unlink(tf) }) wk/tests/testthat/test-xyzm.R0000644000176200001440000002034414515070354016013 0ustar liggesusers test_that("wk_xy class works", { expect_s3_class(xy(), "wk_xy") expect_output(print(xy(1, 2)), "\\(1 2\\)") expect_identical(xy_dims(xy()), c("x", "y")) expect_identical(as_xy(xy()), xy()) expect_identical(as_xy(xy(), dims = NULL), xy()) expect_identical(as_xy(xy(), dims = c("x", "y")), xy()) expect_identical(as_xy(xy(), dims = c("x", "y", "z")), xyz()) expect_identical(as_xy(xy(), dims = c("x", "y", "m")), xym()) expect_identical(as_xy(xy(), dims = c("x", "y", "z", "m")), xyzm()) expect_identical(as_xy(xy(1, 2), dims = NULL), xy(1, 2)) expect_identical(as_xy(xy(1, 2), dims = c("x", "y")), xy(1, 2)) expect_identical(as_xy(xy(1, 2), dims = c("x", "y", "z")), xyz(1, 2, NA)) expect_identical(as_xy(xy(1, 2), dims = c("x", "y", "m")), xym(1, 2, NA)) expect_identical(as_xy(xy(1, 2), dims = c("x", "y", "z", "m")), xyzm(1, 2, NA, NA)) }) test_that("wk_xyz class works", { expect_s3_class(xyz(), "wk_xyz") expect_s3_class(xyz(), "wk_xy") expect_output(print(xyz(1, 2, 3)), "Z \\(1 2 3\\)") expect_identical(xy_dims(xyz()), c("x", "y", "z")) expect_identical(as_xy(xyz()), xyz()) expect_identical(as_xy(xyz(), dims = NULL), xyz()) expect_identical(as_xy(xyz(), dims = c("x", "y")), xy()) expect_identical(as_xy(xyz(), dims = c("x", "y", "z")), xyz()) expect_identical(as_xy(xyz(), dims = c("x", "y", "m")), xym()) expect_identical(as_xy(xyz(), dims = c("x", "y", "z", "m")), xyzm()) expect_identical(as_xy(xyz(1, 2, 3), dims = NULL), xyz(1, 2, 3)) expect_identical(as_xy(xyz(1, 2, 3), dims = c("x", "y")), xy(1, 2)) expect_identical(as_xy(xyz(1, 2, 3), dims = c("x", "y", "z")), xyz(1, 2, 3)) expect_identical(as_xy(xyz(1, 2, 3), dims = c("x", "y", "m")), xym(1, 2, NA)) expect_identical(as_xy(xyz(1, 2, 3), dims = c("x", "y", "z", "m")), xyzm(1, 2, 3, NA)) }) test_that("wk_xym class works", { expect_s3_class(xym(), "wk_xym") expect_s3_class(xym(), "wk_xy") expect_output(print(xym(1, 2, 3)), "M \\(1 2 3\\)") expect_identical(xy_dims(xym()), c("x", "y", "m")) expect_identical(as_xy(xym()), xym()) expect_identical(as_xy(xym(), dims = NULL), xym()) expect_identical(as_xy(xym(), dims = c("x", "y")), xy()) expect_identical(as_xy(xym(), dims = c("x", "y", "z")), xyz()) expect_identical(as_xy(xym(), dims = c("x", "y", "m")), xym()) expect_identical(as_xy(xym(), dims = c("x", "y", "z", "m")), xyzm()) expect_identical(as_xy(xym(1, 2, 3), dims = NULL), xym(1, 2, 3)) expect_identical(as_xy(xym(1, 2, 3), dims = c("x", "y")), xy(1, 2)) expect_identical(as_xy(xym(1, 2, 3), dims = c("x", "y", "z")), xyz(1, 2, NA)) expect_identical(as_xy(xym(1, 2, 3), dims = c("x", "y", "m")), xym(1, 2, 3)) expect_identical(as_xy(xym(1, 2, 3), dims = c("x", "y", "z", "m")), xyzm(1, 2, NA, 3)) }) test_that("wk_xyzm class works", { expect_s3_class(xyzm(), "wk_xyzm") expect_s3_class(xyzm(), "wk_xyz") expect_s3_class(xyzm(), "wk_xym") expect_s3_class(xyzm(), "wk_xy") expect_output(print(xyzm(1, 2, 3, 4)), "ZM \\(1 2 3 4\\)") expect_identical(xy_dims(xyzm()), c("x", "y", "z", "m")) expect_identical(as_xy(xyzm()), xyzm()) expect_identical(as_xy(xyzm(), dims = NULL), xyzm()) expect_identical(as_xy(xyzm(), dims = c("x", "y")), xy()) expect_identical(as_xy(xyzm(), dims = c("x", "y", "z")), xyz()) expect_identical(as_xy(xyzm(), dims = c("x", "y", "m")), xym()) expect_identical(as_xy(xyzm(), dims = c("x", "y", "z", "m")), xyzm()) expect_identical(as_xy(xyzm(1, 2, 3, 4), dims = NULL), xyzm(1, 2, 3, 4)) expect_identical(as_xy(xyzm(1, 2, 3, 4), dims = c("x", "y")), xy(1, 2)) expect_identical(as_xy(xyzm(1, 2, 3, 4), dims = c("x", "y", "z")), xyz(1, 2, 3)) expect_identical(as_xy(xyzm(1, 2, 3, 4), dims = c("x", "y", "m")), xym(1, 2, 4)) expect_identical(as_xy(xyzm(1, 2, 3, 4), dims = c("x", "y", "z", "m")), xyzm(1, 2, 3, 4)) }) test_that("wk_xy* are vctrs", { expect_true(vctrs::vec_is(xy())) expect_true(vctrs::vec_is(xyz())) expect_true(vctrs::vec_is(xym())) expect_true(vctrs::vec_is(xyzm())) }) test_that("wk_xy* vectors can be constructed from matrices/data.frames", { expect_identical(as_xy(data.frame(x = 1, y = 2, z = 3, m = 4), dims = NULL), xyzm(1, 2, 3, 4)) expect_identical(as_xy(data.frame(x = 1, y = 2, z = 3, m = 4), dims = c("x", "y")), xy(1, 2)) expect_identical(as_xy(data.frame(x = 1, y = 2, z = 3, m = 4), dims = c("x", "y", "z")), xyz(1, 2, 3)) expect_identical(as_xy(data.frame(x = 1, y = 2, z = 3, m = 4), dims = c("x", "y", "m")), xym(1, 2, 4)) expect_identical(as_xy(data.frame(x = 1, y = 2, z = 3, m = 4), dims = c("x", "y", "z", "m")), xyzm(1, 2, 3, 4)) expect_identical(as_xy(data.frame(x = 1, y = 2), dims = NULL), xy(1, 2)) expect_identical(as_xy(data.frame(x = 1, y = 2), dims = c("x", "y")), xy(1, 2)) expect_identical(as_xy(data.frame(x = 1, y = 2), dims = c("x", "y", "z")), xyz(1, 2, NA)) expect_identical(as_xy(data.frame(x = 1, y = 2), dims = c("x", "y", "m")), xym(1, 2, NA)) expect_identical(as_xy(data.frame(x = 1, y = 2), dims = c("x", "y", "z", "m")), xyzm(1, 2, NA, NA)) expect_error(as_xy(data.frame(x = 1, y = 2), dims = "L"), "Unknown dims") expect_identical( as_xy(as.matrix(data.frame(x = 1, y = 2, z = 3, m = 4))), xyzm(1, 2, 3, 4) ) expect_identical( as_xy(matrix(1:2, nrow = 1)), xy(1, 2) ) expect_identical( as_xy(matrix(1:3, nrow = 1)), xyz(1, 2, 3) ) expect_identical( as_xy(matrix(1:4, nrow = 1)), xyzm(1, 2, 3, 4) ) expect_identical( as_xy(matrix(1:2, nrow = 1, dimnames = list(NULL, c("x", "y")))), xy(1, 2) ) expect_identical( as_xy(matrix(1:3, nrow = 1, dimnames = list(NULL, c("x", "y", "m")))), xym(1, 2, 3) ) expect_error(as_xy(matrix(1:10, nrow = 1)), "Can't guess dimensions") weird_matrix <- matrix(1:9, ncol = 3) colnames(weird_matrix) <- c("tim", "suzie", "bill") expect_error(as_xy(weird_matrix), "Can't guess dimensions") colnames(weird_matrix) <- c("x", "y", "bill") expect_identical(as_xy(weird_matrix), xy(1:3, 4:6)) }) test_that("wk_xy* vectors can be created from data.frames with handleable columns", { expect_identical( as_xy(data.frame(geom = xy(1, 2, crs = 1234))), xy(1, 2, crs = 1234) ) expect_error( as_xy(data.frame(geom = xy(1, 2)), crs = 1234), "missing\\(crs\\) is not TRUE" ) expect_identical( as_xy(data.frame(geom = xy(1, 2, crs = 1234)), dims = c("x", "y", "z")), xyz(1, 2, NA, crs = 1234) ) }) test_that("coercion to wk* vectors works", { expect_identical(as_wkt(xy(1, 2)), wkt("POINT (1 2)")) expect_identical(as_wkb(xy(1, 2)), as_wkb("POINT (1 2)")) }) test_that("coercion from wk* vectors works", { expect_identical(as_xy(wkt("POINT (1 2)")), xy(1, 2)) expect_identical(as_xy(wkt("POINT Z (1 2 3)")), xyz(1, 2, 3)) expect_identical(as_xy(wkt("POINT M (1 2 4)")), xym(1, 2, 4)) expect_identical(as_xy(wkt("POINT ZM (1 2 3 4)")), xyzm(1, 2, 3, 4)) expect_identical(as_xy(wkt("POINT (1 2)"), dims = c("x", "y", "z", "m")), xyzm(1, 2, NA, NA)) expect_identical(as_xy(as_wkb("POINT (1 2)")), xy(1, 2)) expect_error(as_xy(wkt("POINT (1 2)"), dims = "L"), "Unknown dims") }) test_that("subset-assign works for wk_xy", { x <- xyzm(1:2, 2, 3, 4) x[2] <- xy(10, 20) expect_identical(x[2], xyzm(10, 20, NA, NA)) x[2:3] <- xy(11:12, 21:22) expect_identical(x[2:3], xyzm(11:12, 21:22, NA, NA)) x[[2]] <- xy(11, 21) expect_identical(x[2], xyzm(11, 21, NA, NA)) }) test_that("is.na() returns FALSE for EMPTY xy()", { expect_identical( is.na(as_xy(wkt(c("POINT EMPTY", NA, "POINT (0 1)")))), c(FALSE, TRUE, FALSE) ) }) test_that("xy() propagates CRS", { x <- xy(1, 2) wk_crs(x) <- 1234 expect_identical(wk_crs(x[1]), 1234) expect_identical(wk_crs(c(x, x)), 1234) expect_identical(wk_crs(rep(x, 2)), 1234) expect_error(x[1] <- wk_set_crs(x, NULL), "are not equal") x[1] <- wk_set_crs(x, 1234L) expect_identical(wk_crs(x), 1234) }) test_that("as_xy() works for geodesic objects", { expect_identical(as_xy(wkt("POINT (0 1)", geodesic = TRUE)), xy(0, 1)) expect_identical(as_xy(as_wkb(wkt("POINT (0 1)", geodesic = TRUE))), xy(0, 1)) }) test_that("xy_(xyzm)() return the correct components", { x <- xyz(1:5, 6:10, 11:15) expect_identical(xy_x(x), as.double(1:5)) expect_identical(xy_y(x), as.double(6:10)) expect_identical(xy_z(x), as.double(11:15)) expect_null(xy_m(x)) }) wk/tests/testthat/test-rct.R0000644000176200001440000000653014321145376015577 0ustar liggesusers test_that("rct class works", { expect_s3_class(rct(), "wk_rct") expect_output(print(rct(1, 2, 3, 4)), "\\[1 2 3 4\\]") expect_identical(as_rct(rct(1, 2, 3, 4)), rct(1, 2, 3, 4)) expect_identical( as_rct(as.matrix(data.frame(xmin = 1, ymin = 2, xmax = 3, ymax = 4))), rct(1, 2, 3, 4) ) expect_identical( as_rct(data.frame(xmin = 1, ymin = 2, xmax = 3, ymax = 4)), rct(1, 2, 3, 4) ) expect_identical( as_rct(matrix(1:4, nrow = 1)), rct(1, 2, 3, 4) ) }) test_that("coercion to and from wk* classes works", { expect_identical( as_wkt(rct(1, 2, 3, 4)), wkt("POLYGON ((1 2, 3 2, 3 4, 1 4, 1 2))") ) expect_identical( as_wkb(rct(1, 2, 3, 4)), as_wkb("POLYGON ((1 2, 3 2, 3 4, 1 4, 1 2))") ) }) test_that("subset-assign works for rct", { x <- rct(1:2, 2:3, 3:4, 4:5) x[1] <- rct(NA, NA, NA, NA) expect_identical(x, c(rct(NA, NA, NA, NA), rct(2, 3, 4, 5))) }) test_that("rct() propagates CRS", { x <- rct(1, 2, 3, 4) wk_crs(x) <- 1234 expect_identical(wk_crs(x[1]), 1234) expect_identical(wk_crs(c(x, x)), 1234) expect_identical(wk_crs(rep(x, 2)), 1234) expect_error(x[1] <- wk_set_crs(x, NULL), "are not equal") x[1] <- wk_set_crs(x, 1234L) expect_identical(wk_crs(x), 1234) }) test_that("rct() accessors return the correct values", { x <- rct(0, 1, 2, 4) expect_identical(rct_xmin(x), 0) expect_identical(rct_xmax(x), 2) expect_identical(rct_ymin(x), 1) expect_identical(rct_ymax(x), 4) expect_identical(rct_width(x), 2) expect_identical(rct_height(x), 3) }) test_that("rct_intersection() works", { expect_identical( rct_intersection( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), rct(xmin = 5, xmax = 15, ymin = 5, ymax = 15) ), rct(xmin = 5, xmax = 10, ymin = 5, ymax = 10) ) expect_identical( rct_intersection( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), rct(xmin = 15, xmax = 25, ymin = 15, ymax = 25) ), rct(xmin = NA_real_, xmax = NA_real_, ymin = NA_real_, ymax = NA_real_) ) expect_identical( rct_intersection( rct(xmin = NA_real_, xmax = 10, ymin = 0, ymax = 10), rct(xmin = 15, xmax = 25, ymin = 15, ymax = 25) ), rct(xmin = NA_real_, xmax = NA_real_, ymin = NA_real_, ymax = NA_real_) ) }) test_that("rectangle intersector predicate works", { expect_identical( rct_intersects( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), rct(xmin = 5, xmax = 15, ymin = 5, ymax = 15) ), TRUE ) expect_identical( rct_intersects( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), rct(xmin = 15, xmax = 25, ymin = 15, ymax = 25) ), FALSE ) expect_identical( rct_intersects( rct(xmin = NA_real_, xmax = 10, ymin = 0, ymax = 10), rct(xmin = 5, xmax = 15, ymin = 5, ymax = 15) ), NA ) }) test_that("rectangle contains predicate works", { expect_true( rct_contains( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), xy(x = 5, y = 2) ) ) expect_false( rct_contains( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), xy(x = 11, y = 2) ) ) expect_false( rct_contains( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), xy(x = 5, y = 11) ) ) expect_identical( rct_contains( rct(xmin = 0, xmax = 10, ymin = 0, ymax = 10), xy(x = NA_real_, y = 2) ), NA ) }) wk/tests/testthat/test-make.R0000644000176200001440000002451014472006062015715 0ustar liggesusers test_that("wk_linestring() works", { expect_identical(wk_linestring(wkt()), wkt("LINESTRING EMPTY", crs = wk_crs_inherit())) expect_identical( wk_linestring(wkt(NA_character_)), wkt("LINESTRING EMPTY") ) expect_identical( wk_linestring(wkt("POINT EMPTY")), wkt("LINESTRING EMPTY") ) expect_identical( wk_linestring(xy(1:4, 1), feature_id = 1L), as_wkb("LINESTRING (1 1, 2 1, 3 1, 4 1)") ) expect_identical( wk_linestring(xy(1:4, 1), feature_id = c(1L, 1L, 2L, 2L)), as_wkb(c("LINESTRING (1 1, 2 1)", "LINESTRING (3 1, 4 1)")) ) expect_identical( wk_linestring(wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))")), wkt("LINESTRING (0 0, 0 1, 1 0, 0 0)") ) expect_error(wk_linestring(new_wk_wkt("POINT ENTPY")), "EMPTY") }) test_that("wk_linestring() propagates geodesic", { expect_identical( wk_linestring(xy(1:4, 1), geodesic = TRUE), as_wkb(wkt("LINESTRING (1 1, 2 1, 3 1, 4 1)", geodesic = TRUE)) ) expect_identical( wk_linestring(xy(1:4, 1), geodesic = FALSE), as_wkb(wkt("LINESTRING (1 1, 2 1, 3 1, 4 1)", geodesic = FALSE)) ) expect_identical( wk_linestring(wkt(c("POINT (1 1)", "POINT (2 1)"), geodesic = FALSE), geodesic = NULL), wkt("LINESTRING (1 1, 2 1)", geodesic = FALSE) ) expect_identical( wk_linestring(wkt(c("POINT (1 1)", "POINT (2 1)"), geodesic = TRUE), geodesic = NULL), wkt("LINESTRING (1 1, 2 1)", geodesic = TRUE) ) expect_identical( wk_linestring(wkt(c("POINT (1 1)", "POINT (2 1)")), geodesic = TRUE), wkt("LINESTRING (1 1, 2 1)", geodesic = TRUE) ) expect_identical( wk_linestring(wkt(c("POINT (1 1)", "POINT (2 1)")), geodesic = FALSE), wkt("LINESTRING (1 1, 2 1)", geodesic = FALSE) ) }) test_that("wk_linestring() errors for inconsistent dimensions/srid", { expect_error( wk_linestring(wkt(c("POINT (0 1)", "POINT Z (1 2 3)"))), "Can't create linestring" ) expect_error( wk_linestring(wkt(c("POINT (0 1)", "POINT M (1 2 3)"))), "Can't create linestring" ) expect_error( wk_linestring(wkt(c("POINT (0 1)", "POINT ZM (1 2 3 4)"))), "Can't create linestring" ) expect_error( wk_linestring(wkt(c("POINT (0 1)", "SRID=1234;POINT (1 2)"))), "Can't create linestring" ) }) test_that("wk_linestring_filter() errors for handlers that return WK_ABORT_FEATURE", { expect_error( wk_handle(wkt("POINT (0 1)"), wk_linestring_filter(wk_meta_handler())), "does not support WK_ABORT_FEATURE" ) }) test_that("wk_polygon() works", { expect_identical(wk_polygon(xy(double(), double())), as_wkb("POLYGON EMPTY", crs = wk_crs_inherit())) expect_identical( wk_polygon(xy(c(0, 10, 0), c(0, 0, 10))), as_wkb("POLYGON ((0 0, 10 0, 0 10, 0 0))") ) expect_identical( wk_polygon(xy(c(0, 10, 0, 0), c(0, 0, 10, 0))), as_wkb("POLYGON ((0 0, 10 0, 0 10, 0 0))") ) expect_identical( wk_polygon( xy( c(20, 10, 10, 30, 45, 30, 20, 20), c(35, 30, 10, 5, 20, 20, 15, 25) ), ring_id = c(1, 1, 1, 1, 1, 2, 2, 2) ), as_wkb("POLYGON ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))") ) expect_identical( wk_polygon( xy( 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) ), as_wkb( 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))" ) ) ) expect_identical( wk_polygon( xy( 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)), # new ring should be detected on new feature_id ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2) ), as_wkb( 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("wk_polygon() closes rings where appropriate", { rings_closed <- wkt( c( "LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0)", "LINESTRING (0 0, 0 -1, -1 -1, -1 0, 0 0)", "LINESTRING (1 1, 2 1, 2 2, 1 2, 1 1)", "LINESTRING (2 2, 3 2, 3 3, 2 3, 2 2)" ) ) vertices_closed <- wk_vertices(rings_closed) expect_identical( wk_polygon(vertices_closed, rep(1:4, each = 5)), wkt( c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((0 0, 0 -1, -1 -1, -1 0, 0 0))", "POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))", "POLYGON ((2 2, 3 2, 3 3, 2 3, 2 2))" ) ) ) rings_open <- wkt( c( "LINESTRING (0 0, 1 0, 1 1, 0 1)", "LINESTRING (0 0, 0 -1, -1 -1, -1 0)", "LINESTRING (1 1, 2 1, 2 2, 1 2)", "LINESTRING (2 2, 3 2, 3 3, 2 3)" ) ) vertices_open <- wk_vertices(rings_open) expect_identical( wk_polygon(vertices_open, rep(1:4, each = 4)), wkt( c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((0 0, 0 -1, -1 -1, -1 0, 0 0))", "POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))", "POLYGON ((2 2, 3 2, 3 3, 2 3, 2 2))" ) ) ) }) test_that("wk_polygon() propagates geodesic", { expect_identical( wk_polygon(wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))"), geodesic = TRUE), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = TRUE) ) expect_identical( wk_polygon(wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))"), geodesic = FALSE), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = FALSE) ) expect_identical( wk_polygon(wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = FALSE), geodesic = NULL), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = FALSE) ) expect_identical( wk_polygon(wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = TRUE), geodesic = NULL), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = TRUE) ) expect_identical( wk_polygon(wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = FALSE), geodesic = TRUE), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = TRUE) ) expect_identical( wk_polygon(wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = TRUE), geodesic = FALSE), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))", geodesic = FALSE) ) }) test_that("wk_polygon() can use a POLYGON input", { expect_identical( wk_polygon(wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))")), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))") ) }) test_that("wk_polygon passes on errors", { expect_error(wk_polygon(new_wk_wkt("POLYGON ENTPY")), "ENTPY") }) test_that("wk_polygon() treats NA as empty", { expect_identical( wk_polygon(wkt(c("POLYGON ((40 40, 20 45, 45 30, 40 40))", NA))), wkt("POLYGON ((40 40, 20 45, 45 30, 40 40))") ) }) test_that("wk_polygon() requires consistent dimensions within a feature", { expect_error( wk_polygon(wkt(c("POINT (0 1)", "POINT Z (1 2 3)"))), "Can't create polygon" ) }) test_that("wk_polygon_filter() errors for handlers that return WK_ABORT_FEATURE", { expect_error( wk_handle(wkt("POINT (0 1)"), wk_polygon_filter(wk_meta_handler())), "does not support WK_ABORT_FEATURE" ) }) test_that("wk_collection() works", { expect_identical(wk_collection(wkt()), wkt("GEOMETRYCOLLECTION EMPTY", crs = wk_crs_inherit())) expect_identical( wk_collection(wkt(NA_character_)), wkt("GEOMETRYCOLLECTION EMPTY") ) expect_identical( wk_collection(wkt("POINT EMPTY")), wkt("GEOMETRYCOLLECTION (POINT EMPTY)") ) expect_identical( wk_collection(xy(1:4, 1), feature_id = 1L), as_wkb("GEOMETRYCOLLECTION (POINT (1 1), POINT (2 1), POINT (3 1), POINT (4 1))") ) expect_identical( wk_collection(xy(1:4, 1), feature_id = c(1L, 1L, 2L, 2L)), as_wkb( c("GEOMETRYCOLLECTION (POINT (1 1), POINT (2 1))", "GEOMETRYCOLLECTION (POINT (3 1), POINT (4 1))") ) ) expect_identical( wk_collection(wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))")), wkt("GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 0, 0 0)))") ) expect_error(wk_collection(new_wk_wkt("POINT ENTPY")), "EMPTY") }) test_that("wk_collection() propagates attributes", { expect_identical( wk_collection( wkt("LINESTRING ZM (0 0 0 0, 1 0 0 0)", crs = 1234, geodesic = TRUE), wk_geometry_type("multilinestring") ), wkt("MULTILINESTRING ZM ((0 0 0 0, 1 0 0 0))", crs = 1234, geodesic = TRUE) ) }) test_that("wk_collection_filter() errors for handlers that return WK_ABORT_FEATURE", { expect_error( wk_handle(wkt("POINT (0 1)"), wk_collection_filter(wk_meta_handler())), "does not support WK_ABORT_FEATURE" ) }) test_that("wk_collection() works with sfc", { skip_if_not_installed("sf") points_xy <- xy(1:64, 1:64) points_sfc <- wk_handle( points_xy, sfc_writer() ) expect_identical( wk_collection(points_sfc, feature_id = rep(1:2, each = 32)), wk_handle( wk_collection(points_xy, feature_id = rep(1:2, each = 32)), sfc_writer() ) ) expect_identical( wk_collection(points_sfc, wk_geometry_type("multipoint")), wk_handle( wk_collection(points_xy, wk_geometry_type("multipoint")), sfc_writer() ) ) lines_wkb <- as_wkb(wkt(rep("LINESTRING (0 0, 1 1)", 32))) lines_sfc <- wk_handle( lines_wkb, sfc_writer() ) expect_identical( wk_collection(lines_sfc, wk_geometry_type("multilinestring")), wk_handle( wk_collection(lines_wkb, wk_geometry_type("multilinestring")), sfc_writer() ) ) polygons_wkb <- as_wkb(wkt(rep("POLYGON ((0 0, 0 1, 1 0, 0 0))", 32))) polygons_sfc <- wk_handle( polygons_wkb, sfc_writer() ) expect_identical( wk_collection(polygons_sfc, wk_geometry_type("multipolygon")), wk_handle( wk_collection(polygons_wkb, wk_geometry_type("multipolygon")), sfc_writer() ) ) geometries_wkb <- c(as_wkb(points_xy), lines_wkb, polygons_wkb) geometries_sfc <- wk_handle(geometries_wkb, sfc_writer()) expect_identical( wk_collection(geometries_sfc), wk_handle( wk_collection(geometries_wkb), sfc_writer() ) ) # test case from gh #182 multipoint_sfc <- sf::st_sfc( sf::st_multipoint(matrix(runif(10, -90, 90), ncol = 2)) ) expect_no_error( suppressMessages( capture_output(print(wk_collection(multipoint_sfc))) ) ) }) wk/tests/testthat/test-data.R0000644000176200001440000000070614315177334015721 0ustar liggesusers test_that("all examples can be created with default arguments", { for (which in names(wk::wk_example_wkt)) { expect_s3_class(wk_example(!! which), "wk_wkt") } }) test_that("requested example crs is respected", { expect_identical( wk_crs(wk_example(crs = "EPSG:1234")), "EPSG:1234" ) }) test_that("requested example edges field is respected", { spherical <- wk_example(geodesic = TRUE) expect_true(wk_is_geodesic(spherical)) }) wk/tests/testthat/test-transform.R0000644000176200001440000000205014471771474017025 0ustar liggesusers test_that("wk_transform() works", { geoms <- wkt( c( "POLYGON ((0 0, 0 1, 1 0, 0 0))", "POINT (0 0)", "POINT Z (0 0 0)", "POINT M (0 0 0)", "POINT ZM (0 0 0 0)", NA ) ) expect_identical( wk_transform(geoms, wk_affine_translate(1, 2)), wkt( c( "POLYGON ((1 2, 1 3, 2 2, 1 2))", "POINT (1 2)", "POINT Z (1 2 0)", "POINT M (1 2 0)", "POINT ZM (1 2 0 0)", NA ) ) ) # check error propagation expect_error(wk_transform(new_wk_wkt("POINT ENTPY"), wk_affine_identity()), "ENTPY") }) test_that("wk_transform_filter() errors when the recursion limit is too high", { make_really_recursive_geom <- function(n) { wkt(paste0( c(rep("GEOMETRYCOLLECTION (", n), "POINT (0 1)", rep(")", n)), collapse = "" )) } # errors in geometry_start expect_error( wk_handle( make_really_recursive_geom(32), wk_transform_filter(wk_void_handler(), wk_affine_identity()) ), "Too many recursive levels" ) }) wk/tests/testthat.R0000644000176200001440000000006014106220314014005 0ustar liggesuserslibrary(testthat) library(wk) test_check("wk") wk/src/0000755000176200001440000000000014531517017011466 5ustar liggesuserswk/src/problems-handler.c0000644000176200001440000000676614510132141015073 0ustar liggesusers #define R_NO_REMAP #include #include #include #include "wk-v1.h" typedef struct { SEXP problems; R_xlen_t feat_id; } wk_problems_handler_t; int wk_problems_handler_vector_start(const wk_vector_meta_t* meta, void* handler_data) { wk_problems_handler_t* data = (wk_problems_handler_t*)handler_data; if (data->problems != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } R_xlen_t n_features; if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { n_features = 1024; } else { n_features = meta->size; } data->problems = PROTECT(Rf_allocVector(STRSXP, n_features)); R_PreserveObject(data->problems); UNPROTECT(1); data->feat_id = 0; return WK_CONTINUE; } int wk_problems_handler_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { wk_problems_handler_t* data = (wk_problems_handler_t*)handler_data; if (feat_id >= Rf_xlength(data->problems)) { SEXP new_result = PROTECT(Rf_allocVector(STRSXP, Rf_xlength(data->problems) * 2 + 1)); for (R_xlen_t i = 0; i < Rf_xlength(data->problems); i++) { SET_STRING_ELT(new_result, i, STRING_ELT(data->problems, i)); } R_ReleaseObject(data->problems); data->problems = new_result; R_PreserveObject(data->problems); UNPROTECT(1); } SET_STRING_ELT(data->problems, data->feat_id, NA_STRING); data->feat_id++; return WK_CONTINUE; } int wk_problems_handler_error(const char* message, void* handler_data) { wk_problems_handler_t* data = (wk_problems_handler_t*)handler_data; SET_STRING_ELT(data->problems, data->feat_id - 1, Rf_mkCharCE(message, CE_UTF8)); return WK_ABORT_FEATURE; } SEXP wk_problems_handler_vector_end(const wk_vector_meta_t* meta, void* handler_data) { wk_problems_handler_t* data = (wk_problems_handler_t*)handler_data; if (data->feat_id != Rf_xlength(data->problems)) { SEXP new_result = PROTECT(Rf_allocVector(STRSXP, data->feat_id)); for (R_xlen_t i = 0; i < Rf_xlength(new_result); i++) { SET_STRING_ELT(new_result, i, STRING_ELT(data->problems, i)); } R_ReleaseObject(data->problems); data->problems = R_NilValue; UNPROTECT(1); return new_result; } else { return data->problems; } } void wk_problems_handler_deinitialize(void* handler_data) { wk_problems_handler_t* data = (wk_problems_handler_t*)handler_data; if (data->problems != R_NilValue) { R_ReleaseObject(data->problems); data->problems = R_NilValue; } } void wk_problems_handler_finalize(void* handler_data) { wk_problems_handler_t* data = (wk_problems_handler_t*)handler_data; if (data != NULL) { free(data); } } SEXP wk_c_problems_handler_new(void) { wk_handler_t* handler = wk_handler_create(); handler->vector_start = &wk_problems_handler_vector_start; handler->vector_end = &wk_problems_handler_vector_end; handler->feature_start = &wk_problems_handler_feature_start; handler->error = &wk_problems_handler_error; handler->deinitialize = &wk_problems_handler_deinitialize; handler->finalizer = &wk_problems_handler_finalize; wk_problems_handler_t* data = (wk_problems_handler_t*)malloc(sizeof(wk_problems_handler_t)); if (data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } data->feat_id = 0; data->problems = R_NilValue; SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue); handler->handler_data = data; return xptr; } wk/src/handle-crc.c0000644000176200001440000001066514510132141013626 0ustar liggesusers #define R_NO_REMAP #include #include #include #include "altrep.h" #include "wk-v1.h" #define REAL_NA(val) (ISNA(val) || ISNAN(val)) #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result != WK_CONTINUE) return result #define HANDLE_CONTINUE_OR_BREAK(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) \ continue; \ else if (result == WK_ABORT) \ break int wk_crc_handle_single(wk_handler_t* handler, const wk_meta_t* meta, double x, double y, double r, double segs_per_circle) { int result; double coord[4]; double angle; for (int i = 0; i < segs_per_circle; i++) { angle = i / segs_per_circle * PI * 2.0; coord[0] = x + r * cos(angle); coord[1] = y + r * sin(angle); HANDLE_OR_RETURN(handler->coord(meta, coord, i, handler->handler_data)); } // re-export the first coordinate (i = 0) identically // to ensure that the loops are closed with no floating-point error angle = 0 / segs_per_circle * PI * 2.0; coord[0] = x + r * cos(angle); coord[1] = y + r * sin(angle); HANDLE_OR_RETURN(handler->coord(meta, coord, segs_per_circle, handler->handler_data)); return WK_CONTINUE; } SEXP wk_read_crc(SEXP data_coords, wk_handler_t* handler) { SEXP data = VECTOR_ELT(data_coords, 0); int* segs_per_circle = INTEGER(VECTOR_ELT(data_coords, 1)); int segs_per_circle_len = Rf_length(VECTOR_ELT(data_coords, 1)); if (!Rf_inherits(data, "wk_crc")) { Rf_error("Object does not inherit from 'wk_crc'"); } R_xlen_t n_features = Rf_xlength(VECTOR_ELT(data, 0)); double* data_ptr[3]; R_xlen_t data_ptr_i = 0; #ifdef HAS_ALTREP SEXP altrep_buffer = PROTECT(Rf_allocVector(REALSXP, ALTREP_CHUNK_SIZE * 4)); for (int j = 0; j < 3; j++) { data_ptr[j] = REAL(altrep_buffer) + (ALTREP_CHUNK_SIZE * j); } #else for (int j = 0; j < 3; j++) { data_ptr[j] = REAL(VECTOR_ELT(data, j)); } #endif wk_vector_meta_t vector_meta; WK_VECTOR_META_RESET(vector_meta, WK_POLYGON); vector_meta.size = n_features; if (handler->vector_start(&vector_meta, handler->handler_data) == WK_CONTINUE) { int result, n_segs; double cx, cy, radius; wk_meta_t meta; WK_META_RESET(meta, WK_POLYGON); meta.flags = vector_meta.flags | WK_FLAG_HAS_BOUNDS; for (R_xlen_t i = 0; i < n_features; i++) { if (((i + 1) % 1000) == 0) R_CheckUserInterrupt(); HANDLE_CONTINUE_OR_BREAK( handler->feature_start(&vector_meta, i, handler->handler_data)); #ifdef HAS_ALTREP data_ptr_i = i % ALTREP_CHUNK_SIZE; if (data_ptr_i == 0) { for (int j = 0; j < 3; j++) { REAL_GET_REGION(VECTOR_ELT(data, j), i, ALTREP_CHUNK_SIZE, data_ptr[j]); } } #else data_ptr_i = i; #endif cx = data_ptr[0][i]; cy = data_ptr[1][i]; radius = data_ptr[2][i]; n_segs = segs_per_circle[i % segs_per_circle_len]; int circle_empty = REAL_NA(cx) || REAL_NA(cy) || REAL_NA(radius); if (circle_empty) { meta.size = 0; } else { meta.size = 1; } meta.bounds_min[0] = cx - radius; meta.bounds_min[1] = cy - radius; meta.bounds_max[0] = cx + radius; meta.bounds_max[1] = cy + radius; HANDLE_CONTINUE_OR_BREAK( handler->geometry_start(&meta, WK_PART_ID_NONE, handler->handler_data)); if (!circle_empty) { HANDLE_CONTINUE_OR_BREAK( handler->ring_start(&meta, n_segs + 1, 0, handler->handler_data)); HANDLE_CONTINUE_OR_BREAK( wk_crc_handle_single(handler, &meta, cx, cy, radius, n_segs)); HANDLE_CONTINUE_OR_BREAK( handler->ring_end(&meta, n_segs + 1, 0, handler->handler_data)); } HANDLE_CONTINUE_OR_BREAK( handler->geometry_end(&meta, WK_PART_ID_NONE, handler->handler_data)); if (handler->feature_end(&vector_meta, i, handler->handler_data) == WK_ABORT) { break; } } } #ifdef HAS_ALTREP UNPROTECT(1); #endif SEXP result = PROTECT(handler->vector_end(&vector_meta, handler->handler_data)); UNPROTECT(1); return result; } SEXP wk_c_read_crc(SEXP data, SEXP handler_xptr, SEXP n_segs) { SEXP data_coords = PROTECT(Rf_allocVector(VECSXP, 2)); SET_VECTOR_ELT(data_coords, 0, data); SET_VECTOR_ELT(data_coords, 1, n_segs); SEXP result = PROTECT(wk_handler_run_xptr(&wk_read_crc, data_coords, handler_xptr)); UNPROTECT(2); return result; } wk/src/trans-set.c0000644000176200001440000000335614510132141013545 0ustar liggesusers #define R_NO_REMAP #include #include #include #include #include "wk-v1.h" typedef struct { double* xyzm[4]; R_xlen_t n; } wk_trans_set_t; int wk_trans_set_trans(R_xlen_t feature_id, const double* xyzm_in, double* xyzm_out, void* trans_data) { wk_trans_set_t* data = (wk_trans_set_t*)trans_data; R_xlen_t set_id = feature_id % data->n; double set; for (int i = 0; i < 4; i++) { set = data->xyzm[i][set_id]; if (ISNA(set)) { xyzm_out[i] = xyzm_in[i]; } else { xyzm_out[i] = set; } } return WK_CONTINUE; } void wk_trans_set_finalize(void* trans_data) { free(trans_data); } SEXP wk_c_trans_set_new(SEXP xy, SEXP use_z, SEXP use_m) { if (Rf_xlength(xy) != 4 || TYPEOF(xy) != VECSXP) { Rf_error("`xy` must be an xyzm() object"); // # nocov } // prepare data for C struct / validate args int use_z_int = LOGICAL(use_z)[0]; int use_m_int = LOGICAL(use_m)[0]; R_xlen_t n = Rf_xlength(VECTOR_ELT(xy, 0)); double* xyzm[4]; for (int i = 0; i < 4; i++) { xyzm[i] = REAL(VECTOR_ELT(xy, i)); } // create the wk_trans object wk_trans_t* trans = wk_trans_create(); trans->trans = &wk_trans_set_trans; trans->finalizer = &wk_trans_set_finalize; wk_trans_set_t* data = (wk_trans_set_t*)malloc(sizeof(wk_trans_set_t)); if (data == NULL) { free(trans); // # nocov Rf_error("Failed to alloc wk_trans_set_t"); // # nocov } trans->use_z = use_z_int; trans->use_m = use_m_int; memcpy(data->xyzm, xyzm, 4 * sizeof(void*)); data->n = n; trans->trans_data = data; // keep the xy as a tag because we need the pointers to stay valid return wk_trans_create_xptr(trans, xy, R_NilValue); } wk/src/xy-writer.c0000644000176200001440000002336314531511633013611 0ustar liggesusers #define R_NO_REMAP #include #include #include #include #include "wk-v1.h" typedef struct { SEXP result; // caching the underlying pointers results in a slight speedup double* result_ptr[4]; R_xlen_t result_size; R_xlen_t feat_id; int has_coord; uint32_t flags; } xy_writer_t; static inline SEXP xy_writer_alloc_result(R_xlen_t size, int32_t flags) { const char* names[] = {"x", "y", "z", "m", ""}; SEXP result = PROTECT(Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(result, 0, Rf_allocVector(REALSXP, size)); SET_VECTOR_ELT(result, 1, Rf_allocVector(REALSXP, size)); if (flags & WK_FLAG_HAS_Z) { SET_VECTOR_ELT(result, 2, Rf_allocVector(REALSXP, size)); } else { SET_VECTOR_ELT(result, 2, R_NilValue); } if (flags & WK_FLAG_HAS_M) { SET_VECTOR_ELT(result, 3, Rf_allocVector(REALSXP, size)); } else { SET_VECTOR_ELT(result, 3, R_NilValue); } UNPROTECT(1); return result; } static inline SEXP xy_writer_realloc_result(SEXP result, R_xlen_t new_size, int32_t flags) { SEXP new_result = PROTECT(xy_writer_alloc_result(new_size, flags)); R_xlen_t size_cpy; if (Rf_xlength(VECTOR_ELT(result, 0)) < new_size) { size_cpy = Rf_xlength(VECTOR_ELT(result, 0)); } else { size_cpy = new_size; } for (int i = 0; i < 4; i++) { if (VECTOR_ELT(result, i) == R_NilValue) { continue; } memcpy(REAL(VECTOR_ELT(new_result, i)), REAL(VECTOR_ELT(result, i)), sizeof(double) * size_cpy); } UNPROTECT(1); return new_result; } static inline void xy_writer_append_empty(xy_writer_t* writer) { if (writer->feat_id >= writer->result_size) { SEXP new_result = PROTECT(xy_writer_realloc_result( writer->result, writer->result_size * 2 + 1, writer->flags)); R_ReleaseObject(writer->result); writer->result = new_result; R_PreserveObject(writer->result); UNPROTECT(1); writer->result_size = writer->result_size * 2 + 1; for (int i = 0; i < 4; i++) { if (VECTOR_ELT(writer->result, i) == R_NilValue) { writer->result_ptr[i] = NULL; } else { writer->result_ptr[i] = REAL(VECTOR_ELT(writer->result, i)); } } } for (int i = 0; i < 4; i++) { if (writer->result_ptr[i]) { writer->result_ptr[i][writer->feat_id] = R_NaN; } } writer->feat_id++; } int xy_writer_vector_start(const wk_vector_meta_t* meta, void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; if (data->result != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } if (meta->flags & WK_FLAG_HAS_Z) { data->flags |= WK_FLAG_HAS_Z; } if (meta->flags & WK_FLAG_HAS_M) { data->flags |= WK_FLAG_HAS_M; } if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { data->result = PROTECT(xy_writer_alloc_result(1024, data->flags)); data->result_size = 1024; } else { data->result = PROTECT(xy_writer_alloc_result(meta->size, data->flags)); data->result_size = meta->size; } R_PreserveObject(data->result); UNPROTECT(1); for (int i = 0; i < 4; i++) { if (VECTOR_ELT(data->result, i) == R_NilValue) { data->result_ptr[i] = NULL; } else { data->result_ptr[i] = REAL(VECTOR_ELT(data->result, i)); } } data->feat_id = 0; return WK_CONTINUE; } int xy_writer_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; data->has_coord = 0; xy_writer_append_empty(data); return WK_CONTINUE; } int xy_writer_null_feature(void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; for (int i = 0; i < 4; i++) { if (data->result_ptr[i]) { data->result_ptr[i][data->feat_id - 1] = NA_REAL; } } return WK_ABORT_FEATURE; } int xy_writer_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; // EMPTY and any set of features that (could) contain a single point work with this // handler! (error otherwise) if (meta->size != 0 && meta->geometry_type != WK_POINT && meta->geometry_type != WK_MULTIPOINT && meta->geometry_type != WK_GEOMETRYCOLLECTION) { Rf_error("[%ld] Can't convert geometry with type '%d' to coordinate", (long)data->feat_id + 1, (int)meta->geometry_type); } // keep track of zm flags to possibly trim output data->flags |= meta->flags; // make sure we've allocated the output for ZM dimensions if they were just added if (meta->flags & WK_FLAG_HAS_Z && data->result_ptr[2] == NULL) { SET_VECTOR_ELT(data->result, 2, Rf_allocVector(REALSXP, data->result_size)); data->result_ptr[2] = REAL(VECTOR_ELT(data->result, 2)); for (R_xlen_t i = 0; i < data->feat_id; i++) { data->result_ptr[2][i] = NA_REAL; } } if (meta->flags & WK_FLAG_HAS_M && data->result_ptr[3] == NULL) { SET_VECTOR_ELT(data->result, 3, Rf_allocVector(REALSXP, data->result_size)); data->result_ptr[3] = REAL(VECTOR_ELT(data->result, 3)); for (R_xlen_t i = 0; i < data->feat_id; i++) { data->result_ptr[3][i] = NA_REAL; } } return WK_CONTINUE; } int xy_writer_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; if (data->has_coord) { Rf_error("[%ld] Feature contains more than one coordinate.", (long)data->feat_id); } else { data->has_coord = 1; } data->result_ptr[0][data->feat_id - 1] = coord[0]; data->result_ptr[1][data->feat_id - 1] = coord[1]; if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) { data->result_ptr[2][data->feat_id - 1] = coord[2]; data->result_ptr[3][data->feat_id - 1] = coord[3]; } else if (meta->flags & WK_FLAG_HAS_Z) { data->result_ptr[2][data->feat_id - 1] = coord[2]; } else if (meta->flags & WK_FLAG_HAS_M) { data->result_ptr[3][data->feat_id - 1] = coord[2]; } return WK_CONTINUE; } SEXP xy_writer_vector_end(const wk_vector_meta_t* meta, void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; R_xlen_t final_size = data->feat_id; if (final_size != data->result_size) { SEXP new_result = PROTECT(xy_writer_realloc_result(data->result, final_size, data->flags)); R_ReleaseObject(data->result); data->result = new_result; R_PreserveObject(data->result); UNPROTECT(1); } if ((data->flags & WK_FLAG_HAS_Z) && (data->flags & WK_FLAG_HAS_M)) { SEXP xy_class = PROTECT(Rf_allocVector(STRSXP, 5)); SET_STRING_ELT(xy_class, 0, Rf_mkChar("wk_xyzm")); SET_STRING_ELT(xy_class, 1, Rf_mkChar("wk_xyz")); SET_STRING_ELT(xy_class, 2, Rf_mkChar("wk_xym")); SET_STRING_ELT(xy_class, 3, Rf_mkChar("wk_xy")); SET_STRING_ELT(xy_class, 4, Rf_mkChar("wk_rcrd")); Rf_setAttrib(data->result, R_ClassSymbol, xy_class); UNPROTECT(1); return data->result; } else if (data->flags & WK_FLAG_HAS_Z) { const char* xyz_names[] = {"x", "y", "z", ""}; SEXP xyz = PROTECT(Rf_mkNamed(VECSXP, xyz_names)); for (int i = 0; i < 3; i++) { SET_VECTOR_ELT(xyz, i, VECTOR_ELT(data->result, i)); } SEXP xy_class = PROTECT(Rf_allocVector(STRSXP, 3)); SET_STRING_ELT(xy_class, 0, Rf_mkChar("wk_xyz")); SET_STRING_ELT(xy_class, 1, Rf_mkChar("wk_xy")); SET_STRING_ELT(xy_class, 2, Rf_mkChar("wk_rcrd")); Rf_setAttrib(xyz, R_ClassSymbol, xy_class); UNPROTECT(2); return xyz; } else if (data->flags & WK_FLAG_HAS_M) { const char* xym_names[] = {"x", "y", "m", ""}; SEXP xym = PROTECT(Rf_mkNamed(VECSXP, xym_names)); SET_VECTOR_ELT(xym, 0, VECTOR_ELT(data->result, 0)); SET_VECTOR_ELT(xym, 1, VECTOR_ELT(data->result, 1)); SET_VECTOR_ELT(xym, 2, VECTOR_ELT(data->result, 3)); SEXP xy_class = PROTECT(Rf_allocVector(STRSXP, 3)); SET_STRING_ELT(xy_class, 0, Rf_mkChar("wk_xym")); SET_STRING_ELT(xy_class, 1, Rf_mkChar("wk_xy")); SET_STRING_ELT(xy_class, 2, Rf_mkChar("wk_rcrd")); Rf_setAttrib(xym, R_ClassSymbol, xy_class); UNPROTECT(2); return xym; } else { const char* xy_names[] = {"x", "y", ""}; SEXP xy = PROTECT(Rf_mkNamed(VECSXP, xy_names)); for (int i = 0; i < 2; i++) { SET_VECTOR_ELT(xy, i, VECTOR_ELT(data->result, i)); } SEXP xy_class = PROTECT(Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(xy_class, 0, Rf_mkChar("wk_xy")); SET_STRING_ELT(xy_class, 1, Rf_mkChar("wk_rcrd")); Rf_setAttrib(xy, R_ClassSymbol, xy_class); UNPROTECT(2); return xy; } } void xy_writer_deinitialize(void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; if (data->result != R_NilValue) { R_ReleaseObject(data->result); data->result = R_NilValue; } } void xy_writer_finalize(void* handler_data) { xy_writer_t* data = (xy_writer_t*)handler_data; if (data != NULL) { free(data); } } SEXP wk_c_xy_writer_new(void) { wk_handler_t* handler = wk_handler_create(); handler->vector_start = &xy_writer_vector_start; handler->feature_start = &xy_writer_feature_start; handler->null_feature = &xy_writer_null_feature; handler->geometry_start = &xy_writer_geometry_start; handler->coord = &xy_writer_coord; handler->vector_end = &xy_writer_vector_end; handler->deinitialize = &xy_writer_deinitialize; handler->finalizer = &xy_writer_finalize; xy_writer_t* data = (xy_writer_t*)malloc(sizeof(xy_writer_t)); if (data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } data->feat_id = 0; data->has_coord = 0; data->result = R_NilValue; data->flags = 0; handler->handler_data = data; SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue); return xptr; } wk/src/void-handler.c0000644000176200001440000000053214510132141014172 0ustar liggesusers #define R_NO_REMAP #include #include #include "wk-v1.h" SEXP wk_c_handler_void_new(void) { return wk_handler_create_xptr(wk_handler_create(), R_NilValue, R_NilValue); } SEXP wk_c_handler_addr(SEXP xptr) { char buffer[256]; snprintf(buffer, 256, "%p", (void*)R_ExternalPtrAddr(xptr)); return Rf_mkString(buffer); } wk/src/handle-rct.c0000644000176200001440000000745614510132141013653 0ustar liggesusers #define R_NO_REMAP #include #include #include "altrep.h" #include "wk-v1.h" #define REAL_NA(val) (ISNA(val) || ISNAN(val)) #define HANDLE_CONTINUE_OR_BREAK(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) \ continue; \ else if (result == WK_ABORT) \ break SEXP wk_read_rct(SEXP data, wk_handler_t* handler) { if (!Rf_inherits(data, "wk_rct")) { Rf_error("Object does not inherit from 'wk_rct'"); } R_xlen_t n_features = Rf_xlength(VECTOR_ELT(data, 0)); double* data_ptr[4]; R_xlen_t data_ptr_i = 0; #ifdef HAS_ALTREP SEXP altrep_buffer = PROTECT(Rf_allocVector(REALSXP, ALTREP_CHUNK_SIZE * 4)); for (int j = 0; j < 4; j++) { data_ptr[j] = REAL(altrep_buffer) + (ALTREP_CHUNK_SIZE * j); } #else for (int j = 0; j < 4; j++) { data_ptr[j] = REAL(VECTOR_ELT(data, j)); } #endif wk_vector_meta_t vector_meta; WK_VECTOR_META_RESET(vector_meta, WK_POLYGON); vector_meta.size = n_features; if (handler->vector_start(&vector_meta, handler->handler_data) == WK_CONTINUE) { int result; double xmin, ymin, xmax, ymax; double coord[4]; wk_meta_t meta; WK_META_RESET(meta, WK_POLYGON); meta.flags = vector_meta.flags | WK_FLAG_HAS_BOUNDS; for (R_xlen_t i = 0; i < n_features; i++) { if (((i + 1) % 1000) == 0) R_CheckUserInterrupt(); HANDLE_CONTINUE_OR_BREAK( handler->feature_start(&vector_meta, i, handler->handler_data)); #ifdef HAS_ALTREP data_ptr_i = i % ALTREP_CHUNK_SIZE; if (data_ptr_i == 0) { for (int j = 0; j < 4; j++) { REAL_GET_REGION(VECTOR_ELT(data, j), i, ALTREP_CHUNK_SIZE, data_ptr[j]); } } #else data_ptr_i = i; #endif xmin = data_ptr[0][data_ptr_i]; ymin = data_ptr[1][data_ptr_i]; xmax = data_ptr[2][data_ptr_i]; ymax = data_ptr[3][data_ptr_i]; int rect_na = REAL_NA(xmin) && REAL_NA(ymin) && REAL_NA(xmax) && REAL_NA(ymax); int rect_empty = rect_na || ((xmax - xmin) == R_NegInf) || ((ymax - ymin) == R_NegInf); if (rect_empty) { meta.size = 0; } else { meta.size = 1; } meta.bounds_min[0] = xmin; meta.bounds_min[1] = ymin; meta.bounds_max[0] = xmax; meta.bounds_max[1] = ymax; HANDLE_CONTINUE_OR_BREAK( handler->geometry_start(&meta, WK_PART_ID_NONE, handler->handler_data)); if (!rect_empty) { HANDLE_CONTINUE_OR_BREAK(handler->ring_start(&meta, 5, 0, handler->handler_data)); coord[0] = xmin; coord[1] = ymin; HANDLE_CONTINUE_OR_BREAK(handler->coord(&meta, coord, 0, handler->handler_data)); coord[0] = xmax; coord[1] = ymin; HANDLE_CONTINUE_OR_BREAK(handler->coord(&meta, coord, 1, handler->handler_data)); coord[0] = xmax; coord[1] = ymax; HANDLE_CONTINUE_OR_BREAK(handler->coord(&meta, coord, 2, handler->handler_data)); coord[0] = xmin; coord[1] = ymax; HANDLE_CONTINUE_OR_BREAK(handler->coord(&meta, coord, 3, handler->handler_data)); coord[0] = xmin; coord[1] = ymin; HANDLE_CONTINUE_OR_BREAK(handler->coord(&meta, coord, 4, handler->handler_data)); HANDLE_CONTINUE_OR_BREAK(handler->ring_end(&meta, 5, 0, handler->handler_data)); } HANDLE_CONTINUE_OR_BREAK( handler->geometry_end(&meta, WK_PART_ID_NONE, handler->handler_data)); if (handler->feature_end(&vector_meta, i, handler->handler_data) == WK_ABORT) { break; } } } #ifdef HAS_ALTREP UNPROTECT(1); #endif SEXP result = PROTECT(handler->vector_end(&vector_meta, handler->handler_data)); UNPROTECT(1); return result; } SEXP wk_c_read_rct(SEXP data, SEXP handlerXptr) { return wk_handler_run_xptr(&wk_read_rct, data, handlerXptr); } wk/src/handle-wkt.cpp0000644000176200001440000003205414510132141014220 0ustar liggesusers #define R_NO_REMAP #include #include #include "wk-v1.h" #define FASTFLOAT_ASSERT(x) \ { \ if (!(x)) Rf_error("fastfloat assert failed"); \ } #include "internal/buffered-reader.hpp" #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result != WK_CONTINUE) return result #define HANDLE_CONTINUE_OR_BREAK(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) \ continue; \ else if (result == WK_ABORT) \ break // The BufferedWKTParser is the BufferedParser subclass with methods specific // to well-known text. It doesn't know about any particular output format. template class BufferedWKTParser : public BufferedParser { public: BufferedWKTParser() { this->setSeparators(" \r\n\t,();="); } void assertGeometryMeta(wk_meta_t* meta) { std::string geometry_type = this->assertWord(); if (geometry_type == "SRID") { this->assert_('='); meta->srid = this->assertInteger(); this->assert_(';'); geometry_type = this->assertWord(); } meta->geometry_type = this->geometry_typeFromString(geometry_type); if (this->is('Z')) { this->assert_('Z'); meta->flags |= WK_FLAG_HAS_Z; } if (this->is('M')) { this->assert_('M'); meta->flags |= WK_FLAG_HAS_M; } if (this->isEMPTY()) { meta->size = 0; } } int geometry_typeFromString(std::string geometry_type) { if (geometry_type == "POINT") { return WK_POINT; } else if (geometry_type == "LINESTRING") { return WK_LINESTRING; } else if (geometry_type == "POLYGON") { return WK_POLYGON; } else if (geometry_type == "MULTIPOINT") { return WK_MULTIPOINT; } else if (geometry_type == "MULTILINESTRING") { return WK_MULTILINESTRING; } else if (geometry_type == "MULTIPOLYGON") { return WK_MULTIPOLYGON; } else if (geometry_type == "GEOMETRYCOLLECTION") { return WK_GEOMETRYCOLLECTION; } else { this->errorBefore("geometry type or 'SRID='", geometry_type); } } bool isEMPTY() { return this->peekUntilSep() == "EMPTY"; } bool assertEMPTYOrOpen() { if (this->isLetter()) { std::string word = this->assertWord(); if (word != "EMPTY") { this->errorBefore("'(' or 'EMPTY'", word); } return true; } else if (this->is('(')) { this->assert_('('); return false; } else { this->error("'(' or 'EMPTY'"); } } }; // The BufferedWKTReader knows about wk_handler_t and does all the "driving". The // entry point is readFeature(), which does not throw (but may longjmp). // The BufferedWKTReader is carefully designed to (1) avoid any virtual method calls // (via templating) and (2) to avoid using any C++ objects with non-trivial destructors. // The non-trivial destructors bit is important because handler methods can and do longjmp // when used in R. The object itself does not have a non-trivial destructor and it's // expected that the scope in which it is declared uses the proper unwind-protection such // that the object and its members are deleted. template class BufferedWKTReader { public: BufferedWKTReader(handler_t* handler) : handler(handler) { memset(this->error_message, 0, sizeof(this->error_message)); } int readFeature(wk_vector_meta_t* meta, int64_t feat_id, SourceType* source) { try { int result; HANDLE_OR_RETURN( this->handler->feature_start(meta, feat_id, this->handler->handler_data)); if (source == nullptr) { HANDLE_OR_RETURN(this->handler->null_feature(this->handler->handler_data)); } else { s.setSource(source); HANDLE_OR_RETURN(this->readGeometryWithType(WK_PART_ID_NONE)); s.assertFinished(); } return this->handler->feature_end(meta, feat_id, this->handler->handler_data); } catch (std::exception& e) { // can't call a handler method that longjmps here because `e` must be deleted memset(this->error_message, 0, sizeof(this->error_message)); strncpy(this->error_message, e.what(), sizeof(this->error_message) - 1); } return this->handler->error(this->error_message, this->handler->handler_data); } protected: int readGeometryWithType(uint32_t part_id) { wk_meta_t meta; WK_META_RESET(meta, WK_GEOMETRY); s.assertGeometryMeta(&meta); int result; HANDLE_OR_RETURN( this->handler->geometry_start(&meta, part_id, this->handler->handler_data)); switch (meta.geometry_type) { case WK_POINT: HANDLE_OR_RETURN(this->readPoint(&meta)); break; case WK_LINESTRING: HANDLE_OR_RETURN(this->readLineString(&meta)); break; case WK_POLYGON: HANDLE_OR_RETURN(this->readPolygon(&meta)); break; case WK_MULTIPOINT: HANDLE_OR_RETURN(this->readMultiPoint(&meta)); break; case WK_MULTILINESTRING: HANDLE_OR_RETURN(this->readMultiLineString(&meta)); break; case WK_MULTIPOLYGON: HANDLE_OR_RETURN(this->readMultiPolygon(&meta)); break; case WK_GEOMETRYCOLLECTION: HANDLE_OR_RETURN(this->readGeometryCollection(&meta)); break; default: throw std::runtime_error("Unknown geometry type"); // # nocov } return this->handler->geometry_end(&meta, part_id, this->handler->handler_data); } int readPoint(const wk_meta_t* meta) { if (!s.assertEMPTYOrOpen()) { int result; HANDLE_OR_RETURN(this->readPointCoordinate(meta)); s.assert_(')'); } return WK_CONTINUE; } int readLineString(const wk_meta_t* meta) { return this->readCoordinates(meta); } int readPolygon(const wk_meta_t* meta) { return this->readLinearRings(meta); } int readMultiPoint(const wk_meta_t* meta) { if (s.assertEMPTYOrOpen()) { return WK_CONTINUE; } wk_meta_t childMeta; WK_META_RESET(childMeta, WK_POINT); uint32_t part_id = 0; int result; if (s.isNumber()) { // (0 0, 1 1) do { this->readChildMeta(meta, &childMeta); HANDLE_OR_RETURN(this->handler->geometry_start(&childMeta, part_id, this->handler->handler_data)); if (s.isEMPTY()) { s.assertWord(); } else { HANDLE_OR_RETURN(this->readPointCoordinate(&childMeta)); } HANDLE_OR_RETURN(this->handler->geometry_end(&childMeta, part_id, this->handler->handler_data)); part_id++; } while (s.assertOneOf(",)") != ')'); } else { // ((0 0), (1 1)) do { this->readChildMeta(meta, &childMeta); HANDLE_OR_RETURN(this->handler->geometry_start(&childMeta, part_id, this->handler->handler_data)); HANDLE_OR_RETURN(this->readPoint(&childMeta)); HANDLE_OR_RETURN(this->handler->geometry_end(&childMeta, part_id, this->handler->handler_data)); part_id++; } while (s.assertOneOf(",)") != ')'); } return WK_CONTINUE; } int readMultiLineString(const wk_meta_t* meta) { if (s.assertEMPTYOrOpen()) { return WK_CONTINUE; } wk_meta_t childMeta; WK_META_RESET(childMeta, WK_LINESTRING); uint32_t part_id = 0; int result; do { this->readChildMeta(meta, &childMeta); HANDLE_OR_RETURN(this->handler->geometry_start(&childMeta, part_id, this->handler->handler_data)); HANDLE_OR_RETURN(this->readLineString(&childMeta)); HANDLE_OR_RETURN( this->handler->geometry_end(&childMeta, part_id, this->handler->handler_data)); part_id++; } while (s.assertOneOf(",)") != ')'); return WK_CONTINUE; } uint32_t readMultiPolygon(const wk_meta_t* meta) { if (s.assertEMPTYOrOpen()) { return WK_CONTINUE; } wk_meta_t childMeta; WK_META_RESET(childMeta, WK_POLYGON); uint32_t part_id = 0; int result; do { this->readChildMeta(meta, &childMeta); HANDLE_OR_RETURN(this->handler->geometry_start(&childMeta, part_id, this->handler->handler_data)); HANDLE_OR_RETURN(this->readPolygon(&childMeta)); HANDLE_OR_RETURN( this->handler->geometry_end(&childMeta, part_id, this->handler->handler_data)); part_id++; } while (s.assertOneOf(",)") != ')'); return WK_CONTINUE; } int readGeometryCollection(const wk_meta_t* meta) { if (s.assertEMPTYOrOpen()) { return WK_CONTINUE; } uint32_t part_id = 0; int result; do { HANDLE_OR_RETURN(this->readGeometryWithType(part_id)); part_id++; } while (s.assertOneOf(",)") != ')'); return WK_CONTINUE; } uint32_t readLinearRings(const wk_meta_t* meta) { if (s.assertEMPTYOrOpen()) { return WK_CONTINUE; } uint32_t ring_id = 0; int result; do { HANDLE_OR_RETURN(this->handler->ring_start(meta, WK_SIZE_UNKNOWN, ring_id, this->handler->handler_data)); HANDLE_OR_RETURN(this->readCoordinates(meta)); HANDLE_OR_RETURN(this->handler->ring_end(meta, WK_SIZE_UNKNOWN, ring_id, this->handler->handler_data)); ring_id++; } while (s.assertOneOf(",)") != ')'); return WK_CONTINUE; } // Point coordinates are special in that there can only be one // coordinate (and reading more than one might cause errors since // writers are unlikely to expect a point geometry with many coordinates). // This assumes that `s` has already been checked for EMPTY or an opener // since this is different for POINT (...) and MULTIPOINT (.., ...) int readPointCoordinate(const wk_meta_t* meta) { double coord[4]; int result; int coordSize = 2; if (meta->flags & WK_FLAG_HAS_Z) coordSize++; if (meta->flags & WK_FLAG_HAS_M) coordSize++; this->readCoordinate(coord, coordSize); HANDLE_OR_RETURN(handler->coord(meta, coord, 0, this->handler->handler_data)); return WK_CONTINUE; } int readCoordinates(const wk_meta_t* meta) { double coord[4]; int coordSize = 2; if (meta->flags & WK_FLAG_HAS_Z) coordSize++; if (meta->flags & WK_FLAG_HAS_M) coordSize++; if (s.assertEMPTYOrOpen()) { return WK_CONTINUE; } uint32_t coord_id = 0; int result; do { this->readCoordinate(coord, coordSize); HANDLE_OR_RETURN( handler->coord(meta, coord, coord_id, this->handler->handler_data)); coord_id++; } while (s.assertOneOf(",)") != ')'); return WK_CONTINUE; } void readCoordinate(double* coord, int coordSize) { coord[0] = s.assertNumber(); for (int i = 1; i < coordSize; i++) { s.assertWhitespace(); coord[i] = s.assertNumber(); } } void readChildMeta(const wk_meta_t* parent, wk_meta_t* childMeta) { childMeta->flags = parent->flags; childMeta->srid = parent->srid; if (s.isEMPTY()) { childMeta->size = 0; } else { childMeta->size = WK_SIZE_UNKNOWN; } } private: handler_t* handler; BufferedWKTParser s; char error_message[8096]; }; template void finalize_cpp_xptr(SEXP xptr) { T* ptr = (T*)R_ExternalPtrAddr(xptr); if (ptr != nullptr) { delete ptr; } } SEXP wkt_read_wkt(SEXP data, wk_handler_t* handler) { SEXP wkt_sexp = VECTOR_ELT(data, 0); SEXP reveal_size_sexp = VECTOR_ELT(data, 1); int reveal_size = LOGICAL(reveal_size_sexp)[0]; if (TYPEOF(wkt_sexp) != STRSXP) { Rf_error("Input to wkt handler must be a character vector"); } R_xlen_t n_features = Rf_xlength(wkt_sexp); wk_vector_meta_t global_meta; WK_VECTOR_META_RESET(global_meta, WK_GEOMETRY); global_meta.flags |= WK_FLAG_DIMS_UNKNOWN; if (reveal_size) { global_meta.size = n_features; } // These are C++ objects but they are trivially destructible // (so longjmp in this stack is OK). SimpleBufferSource source; BufferedWKTReader reader(handler); int result = handler->vector_start(&global_meta, handler->handler_data); if (result != WK_ABORT) { R_xlen_t n_features = Rf_xlength(wkt_sexp); SEXP item; int result; for (R_xlen_t i = 0; i < n_features; i++) { if (((i + 1) % 1000) == 0) R_CheckUserInterrupt(); item = STRING_ELT(wkt_sexp, i); if (item == NA_STRING) { HANDLE_CONTINUE_OR_BREAK(reader.readFeature(&global_meta, i, nullptr)); } else { const char* chars = CHAR(item); source.set_buffer(chars, strlen(chars)); HANDLE_CONTINUE_OR_BREAK(reader.readFeature(&global_meta, i, &source)); } if (result == WK_ABORT) { break; } } } return handler->vector_end(&global_meta, handler->handler_data); } extern "C" SEXP wk_c_read_wkt(SEXP data, SEXP handler_xptr) { return wk_handler_run_xptr(&wkt_read_wkt, data, handler_xptr); } wk/src/internal/0000755000176200001440000000000014471771472013314 5ustar liggesuserswk/src/internal/fast_float/0000755000176200001440000000000014160220603015412 5ustar liggesuserswk/src/internal/fast_float/fast_float.h0000644000176200001440000032207314160220603017714 0ustar liggesusers// fast_float by Daniel Lemire // fast_float by João Paulo Magalhaes // // with contributions from Eugene Golushkov // with contributions from Maksim Kita // with contributions from Marcin Wojdyr // with contributions from Neal Richardson // with contributions from Tim Paine // with contributions from Fabio Pellacini // // Licensed under the Apache License, Version 2.0, or the // MIT License at your option. This file may not be copied, // modified, or distributed except according to those terms. // // MIT License Notice // // MIT License // // Copyright (c) 2021 The fast_float authors // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the // Software without restriction, including without // limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice // shall be included in all copies or substantial portions // of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // Apache License (Version 2.0) Notice // // Copyright 2021 The fast_float authors // 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 // #ifndef FASTFLOAT_FAST_FLOAT_H #define FASTFLOAT_FAST_FLOAT_H #include namespace fast_float { enum chars_format { scientific = 1<<0, fixed = 1<<2, hex = 1<<3, general = fixed | scientific }; struct from_chars_result { const char *ptr; std::errc ec; }; struct parse_options { constexpr explicit parse_options(chars_format fmt = chars_format::general, char dot = '.') : format(fmt), decimal_point(dot) {} /** Which number formats are accepted */ chars_format format; /** The character used as decimal point */ char decimal_point; }; /** * This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting * a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale. * The resulting floating-point value is the closest floating-point values (using either float or double), * using the "round to even" convention for values that would otherwise fall right in-between two values. * That is, we provide exact parsing according to the IEEE standard. * * Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the * parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned * `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. * * The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). * * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of * the type `fast_float::chars_format`. It is a bitset value: we check whether * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set * to determine whether we allowe the fixed point and scientific notation respectively. * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. */ template from_chars_result from_chars(const char *first, const char *last, T &value, chars_format fmt = chars_format::general) noexcept; /** * Like from_chars, but accepts an `options` argument to govern number parsing. */ template from_chars_result from_chars_advanced(const char *first, const char *last, T &value, parse_options options) noexcept; } #endif // FASTFLOAT_FAST_FLOAT_H #ifndef FASTFLOAT_FLOAT_COMMON_H #define FASTFLOAT_FLOAT_COMMON_H #include #include #include #include #include #if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ || defined(__MINGW64__) \ || defined(__s390x__) \ || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ || defined(__EMSCRIPTEN__)) #define FASTFLOAT_64BIT #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ || defined(__arm__) || defined(_M_ARM) \ || defined(__MINGW32__)) #define FASTFLOAT_32BIT #else // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. // We can never tell the register width, but the SIZE_MAX is a good approximation. // UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability. #if SIZE_MAX == 0xffff #error Unknown platform (16-bit, unsupported) #elif SIZE_MAX == 0xffffffff #define FASTFLOAT_32BIT #elif SIZE_MAX == 0xffffffffffffffff #define FASTFLOAT_64BIT #else #error Unknown platform (not 32-bit, not 64-bit?) #endif #endif #if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) #include #endif #if defined(_MSC_VER) && !defined(__clang__) #define FASTFLOAT_VISUAL_STUDIO 1 #endif #ifdef _WIN32 #define FASTFLOAT_IS_BIG_ENDIAN 0 #else #if defined(__APPLE__) || defined(__FreeBSD__) #include #elif defined(sun) || defined(__sun) #include #else #include #endif # #ifndef __BYTE_ORDER__ // safe choice #define FASTFLOAT_IS_BIG_ENDIAN 0 #endif # #ifndef __ORDER_LITTLE_ENDIAN__ // safe choice #define FASTFLOAT_IS_BIG_ENDIAN 0 #endif # #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define FASTFLOAT_IS_BIG_ENDIAN 0 #else #define FASTFLOAT_IS_BIG_ENDIAN 1 #endif #endif #ifdef FASTFLOAT_VISUAL_STUDIO #define fastfloat_really_inline __forceinline #else #define fastfloat_really_inline inline __attribute__((always_inline)) #endif #ifndef FASTFLOAT_ASSERT #define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); } #endif #ifndef FASTFLOAT_DEBUG_ASSERT #include #define FASTFLOAT_DEBUG_ASSERT(x) assert(x) #endif // rust style `try!()` macro, or `?` operator #define FASTFLOAT_TRY(x) { if (!(x)) return false; } namespace fast_float { // Compares two ASCII strings in a case insensitive manner. inline bool fastfloat_strncasecmp(const char *input1, const char *input2, size_t length) { char running_diff{0}; for (size_t i = 0; i < length; i++) { running_diff |= (input1[i] ^ input2[i]); } return (running_diff == 0) || (running_diff == 32); } #ifndef FLT_EVAL_METHOD #error "FLT_EVAL_METHOD should be defined, please include cfloat." #endif // a pointer and a length to a contiguous block of memory template struct span { const T* ptr; size_t length; span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} span() : ptr(nullptr), length(0) {} constexpr size_t len() const noexcept { return length; } const T& operator[](size_t index) const noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); return ptr[index]; } }; struct value128 { uint64_t low; uint64_t high; value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} value128() : low(0), high(0) {} }; /* result might be undefined when input_num is zero */ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { assert(input_num > 0); #ifdef FASTFLOAT_VISUAL_STUDIO #if defined(_M_X64) || defined(_M_ARM64) unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). _BitScanReverse64(&leading_zero, input_num); return (int)(63 - leading_zero); #else int last_bit = 0; if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32; if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16; if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8; if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4; if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2; if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1; return 63 - last_bit; #endif #else return __builtin_clzll(input_num); #endif } #ifdef FASTFLOAT_32BIT // slow emulation routine for 32-bit fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { return x * (uint64_t)y; } // slow emulation routine for 32-bit #if !defined(__MINGW64__) fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); uint64_t adbc_carry = !!(adbc < ad); uint64_t lo = bd + (adbc << 32); *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + (adbc_carry << 32) + !!(lo < bd); return lo; } #endif // !__MINGW64__ #endif // FASTFLOAT_32BIT // compute 64-bit a*b fastfloat_really_inline value128 full_multiplication(uint64_t a, uint64_t b) { value128 answer; #ifdef _M_ARM64 // ARM64 has native support for 64-bit multiplications, no need to emulate answer.high = __umulh(a, b); answer.low = a * b; #elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 #elif defined(FASTFLOAT_64BIT) __uint128_t r = ((__uint128_t)a) * b; answer.low = uint64_t(r); answer.high = uint64_t(r >> 64); #else #error Not implemented #endif return answer; } struct adjusted_mantissa { uint64_t mantissa{0}; int32_t power2{0}; // a negative value indicates an invalid result adjusted_mantissa() = default; bool operator==(const adjusted_mantissa &o) const { return mantissa == o.mantissa && power2 == o.power2; } bool operator!=(const adjusted_mantissa &o) const { return mantissa != o.mantissa || power2 != o.power2; } }; // Bias so we can get the real exponent with an invalid adjusted_mantissa. constexpr static int32_t invalid_am_bias = -0x8000; constexpr static double powers_of_ten_double[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10}; template struct binary_format { using equiv_uint = typename std::conditional::type; static inline constexpr int mantissa_explicit_bits(); static inline constexpr int minimum_exponent(); static inline constexpr int infinite_power(); static inline constexpr int sign_index(); static inline constexpr int min_exponent_fast_path(); static inline constexpr int max_exponent_fast_path(); static inline constexpr int max_exponent_round_to_even(); static inline constexpr int min_exponent_round_to_even(); static inline constexpr uint64_t max_mantissa_fast_path(); static inline constexpr int largest_power_of_ten(); static inline constexpr int smallest_power_of_ten(); static inline constexpr T exact_power_of_ten(int64_t power); static inline constexpr size_t max_digits(); static inline constexpr equiv_uint exponent_mask(); static inline constexpr equiv_uint mantissa_mask(); static inline constexpr equiv_uint hidden_bit_mask(); }; template <> inline constexpr int binary_format::mantissa_explicit_bits() { return 52; } template <> inline constexpr int binary_format::mantissa_explicit_bits() { return 23; } template <> inline constexpr int binary_format::max_exponent_round_to_even() { return 23; } template <> inline constexpr int binary_format::max_exponent_round_to_even() { return 10; } template <> inline constexpr int binary_format::min_exponent_round_to_even() { return -4; } template <> inline constexpr int binary_format::min_exponent_round_to_even() { return -17; } template <> inline constexpr int binary_format::minimum_exponent() { return -1023; } template <> inline constexpr int binary_format::minimum_exponent() { return -127; } template <> inline constexpr int binary_format::infinite_power() { return 0x7FF; } template <> inline constexpr int binary_format::infinite_power() { return 0xFF; } template <> inline constexpr int binary_format::sign_index() { return 63; } template <> inline constexpr int binary_format::sign_index() { return 31; } template <> inline constexpr int binary_format::min_exponent_fast_path() { #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) return 0; #else return -22; #endif } template <> inline constexpr int binary_format::min_exponent_fast_path() { #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) return 0; #else return -10; #endif } template <> inline constexpr int binary_format::max_exponent_fast_path() { return 22; } template <> inline constexpr int binary_format::max_exponent_fast_path() { return 10; } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } template <> inline constexpr double binary_format::exact_power_of_ten(int64_t power) { return powers_of_ten_double[power]; } template <> inline constexpr float binary_format::exact_power_of_ten(int64_t power) { return powers_of_ten_float[power]; } template <> inline constexpr int binary_format::largest_power_of_ten() { return 308; } template <> inline constexpr int binary_format::largest_power_of_ten() { return 38; } template <> inline constexpr int binary_format::smallest_power_of_ten() { return -342; } template <> inline constexpr int binary_format::smallest_power_of_ten() { return -65; } template <> inline constexpr size_t binary_format::max_digits() { return 769; } template <> inline constexpr size_t binary_format::max_digits() { return 114; } template <> inline constexpr binary_format::equiv_uint binary_format::exponent_mask() { return 0x7F800000; } template <> inline constexpr binary_format::equiv_uint binary_format::exponent_mask() { return 0x7FF0000000000000; } template <> inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() { return 0x007FFFFF; } template <> inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() { return 0x000FFFFFFFFFFFFF; } template <> inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() { return 0x00800000; } template <> inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() { return 0x0010000000000000; } template fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { uint64_t word = am.mantissa; word |= uint64_t(am.power2) << binary_format::mantissa_explicit_bits(); word = negative ? word | (uint64_t(1) << binary_format::sign_index()) : word; #if FASTFLOAT_IS_BIG_ENDIAN == 1 if (std::is_same::value) { ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian } else { ::memcpy(&value, &word, sizeof(T)); } #else // For little-endian systems: ::memcpy(&value, &word, sizeof(T)); #endif } } // namespace fast_float #endif #ifndef FASTFLOAT_ASCII_NUMBER_H #define FASTFLOAT_ASCII_NUMBER_H #include #include #include #include namespace fast_float { // Next function can be micro-optimized, but compilers are entirely // able to optimize it well. fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } fastfloat_really_inline uint64_t byteswap(uint64_t val) { return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 | (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 | (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 | (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56; } fastfloat_really_inline uint64_t read_u64(const char *chars) { uint64_t val; ::memcpy(&val, chars, sizeof(uint64_t)); #if FASTFLOAT_IS_BIG_ENDIAN == 1 // Need to read as-if the number was in little-endian order. val = byteswap(val); #endif return val; } fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { #if FASTFLOAT_IS_BIG_ENDIAN == 1 // Need to read as-if the number was in little-endian order. val = byteswap(val); #endif ::memcpy(chars, &val, sizeof(uint64_t)); } // credit @aqrit fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { const uint64_t mask = 0x000000FF000000FF; const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) val -= 0x3030303030303030; val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; return uint32_t(val); } fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { return parse_eight_digits_unrolled(read_u64(chars)); } // credit @aqrit fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080)); } fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { return is_made_of_eight_digits_fast(read_u64(chars)); } typedef span byte_span; struct parsed_number_string { int64_t exponent{0}; uint64_t mantissa{0}; const char *lastmatch{nullptr}; bool negative{false}; bool valid{false}; bool too_many_digits{false}; // contains the range of the significant digits byte_span integer{}; // non-nullable byte_span fraction{}; // nullable }; // Assuming that you use no more than 19 digits, this will // parse an ASCII string. fastfloat_really_inline parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { const chars_format fmt = options.format; const char decimal_point = options.decimal_point; parsed_number_string answer; answer.valid = false; answer.too_many_digits = false; answer.negative = (*p == '-'); if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here ++p; if (p == pend) { return answer; } if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot return answer; } } const char *const start_digits = p; uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok p += 8; } while ((p != pend) && is_integer(*p)) { // a multiplication by 10 is cheaper than an arbitrary integer // multiplication i = 10 * i + uint64_t(*p - '0'); // might overflow, we will handle the overflow later ++p; } const char *const end_of_integer_part = p; int64_t digit_count = int64_t(end_of_integer_part - start_digits); answer.integer = byte_span(start_digits, size_t(digit_count)); int64_t exponent = 0; if ((p != pend) && (*p == decimal_point)) { ++p; const char* before = p; // can occur at most twice without overflowing, but let it occur more, since // for integers with many digits, digit parsing is the primary bottleneck. while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok p += 8; } while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - '0'); ++p; i = i * 10 + digit; // in rare cases, this will overflow, but that's ok } exponent = before - p; answer.fraction = byte_span(before, size_t(p - before)); digit_count -= exponent; } // we must have encountered at least one integer! if (digit_count == 0) { return answer; } int64_t exp_number = 0; // explicit exponential part if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { const char * location_of_e = p; ++p; bool neg_exp = false; if ((p != pend) && ('-' == *p)) { neg_exp = true; ++p; } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) ++p; } if ((p == pend) || !is_integer(*p)) { if(!(fmt & chars_format::fixed)) { // We are in error. return answer; } // Otherwise, we will be ignoring the 'e'. p = location_of_e; } else { while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - '0'); if (exp_number < 0x10000000) { exp_number = 10 * exp_number + digit; } ++p; } if(neg_exp) { exp_number = - exp_number; } exponent += exp_number; } } else { // If it scientific and not fixed, we have to bail out. if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } } answer.lastmatch = p; answer.valid = true; // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon. // // We can deal with up to 19 digits. if (digit_count > 19) { // this is uncommon // It is possible that the integer had an overflow. // We have to handle the case where we have 0.0000somenumber. // We need to be mindful of the case where we only have zeroes... // E.g., 0.000000000...000. const char *start = start_digits; while ((start != pend) && (*start == '0' || *start == decimal_point)) { if(*start == '0') { digit_count --; } start++; } if (digit_count > 19) { answer.too_many_digits = true; // Let us start again, this time, avoiding overflows. // We don't need to check if is_integer, since we use the // pre-tokenized spans from above. i = 0; p = answer.integer.ptr; const char* int_end = p + answer.integer.len(); const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; while((i < minimal_nineteen_digit_integer) && (p != int_end)) { i = i * 10 + uint64_t(*p - '0'); ++p; } if (i >= minimal_nineteen_digit_integer) { // We have a big integers exponent = end_of_integer_part - p + exp_number; } else { // We have a value with a fractional component. p = answer.fraction.ptr; const char* frac_end = p + answer.fraction.len(); while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { i = i * 10 + uint64_t(*p - '0'); ++p; } exponent = answer.fraction.ptr - p + exp_number; } // We have now corrected both exponent and i, to a truncated value } } answer.exponent = exponent; answer.mantissa = i; return answer; } } // namespace fast_float #endif #ifndef FASTFLOAT_FAST_TABLE_H #define FASTFLOAT_FAST_TABLE_H #include namespace fast_float { /** * When mapping numbers from decimal to binary, * we go from w * 10^q to m * 2^p but we have * 10^q = 5^q * 2^q, so effectively * we are trying to match * w * 2^q * 5^q to m * 2^p. Thus the powers of two * are not a concern since they can be represented * exactly using the binary notation, only the powers of five * affect the binary significand. */ /** * The smallest non-zero float (binary64) is 2^−1074. * We take as input numbers of the form w x 10^q where w < 2^64. * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. * However, we have that * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074. * Thus it is possible for a number of the form w * 10^-342 where * w is a 64-bit value to be a non-zero floating-point number. ********* * Any number of form w * 10^309 where w>= 1 is going to be * infinite in binary64 so we never need to worry about powers * of 5 greater than 308. */ template struct powers_template { constexpr static int smallest_power_of_five = binary_format::smallest_power_of_ten(); constexpr static int largest_power_of_five = binary_format::largest_power_of_ten(); constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1); // Powers of five from 5^-342 all the way to 5^308 rounded toward one. static const uint64_t power_of_five_128[number_of_entries]; }; template const uint64_t powers_template::power_of_five_128[number_of_entries] = { 0xeef453d6923bd65a,0x113faa2906a13b3f, 0x9558b4661b6565f8,0x4ac7ca59a424c507, 0xbaaee17fa23ebf76,0x5d79bcf00d2df649, 0xe95a99df8ace6f53,0xf4d82c2c107973dc, 0x91d8a02bb6c10594,0x79071b9b8a4be869, 0xb64ec836a47146f9,0x9748e2826cdee284, 0xe3e27a444d8d98b7,0xfd1b1b2308169b25, 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7, 0xb208ef855c969f4f,0xbdbd2d335e51a935, 0xde8b2b66b3bc4723,0xad2c788035e61382, 0x8b16fb203055ac76,0x4c3bcb5021afcc31, 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d, 0xd953e8624b85dd78,0xd71d6dad34a2af0d, 0x87d4713d6f33aa6b,0x8672648c40e5ad68, 0xa9c98d8ccb009506,0x680efdaf511f18c2, 0xd43bf0effdc0ba48,0x212bd1b2566def2, 0x84a57695fe98746d,0x14bb630f7604b57, 0xa5ced43b7e3e9188,0x419ea3bd35385e2d, 0xcf42894a5dce35ea,0x52064cac828675b9, 0x818995ce7aa0e1b2,0x7343efebd1940993, 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8, 0xca66fa129f9b60a6,0xd41a26e077774ef6, 0xfd00b897478238d0,0x8920b098955522b4, 0x9e20735e8cb16382,0x55b46e5f5d5535b0, 0xc5a890362fddbc62,0xeb2189f734aa831d, 0xf712b443bbd52b7b,0xa5e9ec7501d523e4, 0x9a6bb0aa55653b2d,0x47b233c92125366e, 0xc1069cd4eabe89f8,0x999ec0bb696e840a, 0xf148440a256e2c76,0xc00670ea43ca250d, 0x96cd2a865764dbca,0x380406926a5e5728, 0xbc807527ed3e12bc,0xc605083704f5ecf2, 0xeba09271e88d976b,0xf7864a44c633682e, 0x93445b8731587ea3,0x7ab3ee6afbe0211d, 0xb8157268fdae9e4c,0x5960ea05bad82964, 0xe61acf033d1a45df,0x6fb92487298e33bd, 0x8fd0c16206306bab,0xa5d3b6d479f8e056, 0xb3c4f1ba87bc8696,0x8f48a4899877186c, 0xe0b62e2929aba83c,0x331acdabfe94de87, 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14, 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9, 0xdb71e91432b1a24a,0xc9e82cd9f69d6150, 0x892731ac9faf056e,0xbe311c083a225cd2, 0xab70fe17c79ac6ca,0x6dbd630a48aaf406, 0xd64d3d9db981787d,0x92cbbccdad5b108, 0x85f0468293f0eb4e,0x25bbf56008c58ea5, 0xa76c582338ed2621,0xaf2af2b80af6f24e, 0xd1476e2c07286faa,0x1af5af660db4aee1, 0x82cca4db847945ca,0x50d98d9fc890ed4d, 0xa37fce126597973c,0xe50ff107bab528a0, 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8, 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a, 0x9faacf3df73609b1,0x77b191618c54e9ac, 0xc795830d75038c1d,0xd59df5b9ef6a2417, 0xf97ae3d0d2446f25,0x4b0573286b44ad1d, 0x9becce62836ac577,0x4ee367f9430aec32, 0xc2e801fb244576d5,0x229c41f793cda73f, 0xf3a20279ed56d48a,0x6b43527578c1110f, 0x9845418c345644d6,0x830a13896b78aaa9, 0xbe5691ef416bd60c,0x23cc986bc656d553, 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8, 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9, 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53, 0xe858ad248f5c22c9,0xd1b3400f8f9cff68, 0x91376c36d99995be,0x23100809b9c21fa1, 0xb58547448ffffb2d,0xabd40a0c2832a78a, 0xe2e69915b3fff9f9,0x16c90c8f323f516c, 0x8dd01fad907ffc3b,0xae3da7d97f6792e3, 0xb1442798f49ffb4a,0x99cd11cfdf41779c, 0xdd95317f31c7fa1d,0x40405643d711d583, 0x8a7d3eef7f1cfc52,0x482835ea666b2572, 0xad1c8eab5ee43b66,0xda3243650005eecf, 0xd863b256369d4a40,0x90bed43e40076a82, 0x873e4f75e2224e68,0x5a7744a6e804a291, 0xa90de3535aaae202,0x711515d0a205cb36, 0xd3515c2831559a83,0xd5a5b44ca873e03, 0x8412d9991ed58091,0xe858790afe9486c2, 0xa5178fff668ae0b6,0x626e974dbe39a872, 0xce5d73ff402d98e3,0xfb0a3d212dc8128f, 0x80fa687f881c7f8e,0x7ce66634bc9d0b99, 0xa139029f6a239f72,0x1c1fffc1ebc44e80, 0xc987434744ac874e,0xa327ffb266b56220, 0xfbe9141915d7a922,0x4bf1ff9f0062baa8, 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9, 0xc4ce17b399107c22,0xcb550fb4384d21d3, 0xf6019da07f549b2b,0x7e2a53a146606a48, 0x99c102844f94e0fb,0x2eda7444cbfc426d, 0xc0314325637a1939,0xfa911155fefb5308, 0xf03d93eebc589f88,0x793555ab7eba27ca, 0x96267c7535b763b5,0x4bc1558b2f3458de, 0xbbb01b9283253ca2,0x9eb1aaedfb016f16, 0xea9c227723ee8bcb,0x465e15a979c1cadc, 0x92a1958a7675175f,0xbfacd89ec191ec9, 0xb749faed14125d36,0xcef980ec671f667b, 0xe51c79a85916f484,0x82b7e12780e7401a, 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810, 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15, 0xdfbdcece67006ac9,0x67a791e093e1d49a, 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0, 0xaecc49914078536d,0x58fae9f773886e18, 0xda7f5bf590966848,0xaf39a475506a899e, 0x888f99797a5e012d,0x6d8406c952429603, 0xaab37fd7d8f58178,0xc8e5087ba6d33b83, 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64, 0x855c3be0a17fcd26,0x5cf2eea09a55067f, 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e, 0xd0601d8efc57b08b,0xf13b94daf124da26, 0x823c12795db6ce57,0x76c53d08d6b70858, 0xa2cb1717b52481ed,0x54768c4b0c64ca6e, 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09, 0xfe5d54150b090b02,0xd3f93b35435d7c4c, 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf, 0xc6b8e9b0709f109a,0x359ab6419ca1091b, 0xf867241c8cc6d4c0,0xc30163d203c94b62, 0x9b407691d7fc44f8,0x79e0de63425dcf1d, 0xc21094364dfb5636,0x985915fc12f542e4, 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d, 0x979cf3ca6cec5b5a,0xa705992ceecf9c42, 0xbd8430bd08277231,0x50c6ff782a838353, 0xece53cec4a314ebd,0xa4f8bf5635246428, 0x940f4613ae5ed136,0x871b7795e136be99, 0xb913179899f68584,0x28e2557b59846e3f, 0xe757dd7ec07426e5,0x331aeada2fe589cf, 0x9096ea6f3848984f,0x3ff0d2c85def7621, 0xb4bca50b065abe63,0xfed077a756b53a9, 0xe1ebce4dc7f16dfb,0xd3e8495912c62894, 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c, 0xb080392cc4349dec,0xbd8d794d96aacfb3, 0xdca04777f541c567,0xecf0d7a0fc5583a0, 0x89e42caaf9491b60,0xf41686c49db57244, 0xac5d37d5b79b6239,0x311c2875c522ced5, 0xd77485cb25823ac7,0x7d633293366b828b, 0x86a8d39ef77164bc,0xae5dff9c02033197, 0xa8530886b54dbdeb,0xd9f57f830283fdfc, 0xd267caa862a12d66,0xd072df63c324fd7b, 0x8380dea93da4bc60,0x4247cb9e59f71e6d, 0xa46116538d0deb78,0x52d9be85f074e608, 0xcd795be870516656,0x67902e276c921f8b, 0x806bd9714632dff6,0xba1cd8a3db53b6, 0xa086cfcd97bf97f3,0x80e8a40eccd228a4, 0xc8a883c0fdaf7df0,0x6122cd128006b2cd, 0xfad2a4b13d1b5d6c,0x796b805720085f81, 0x9cc3a6eec6311a63,0xcbe3303674053bb0, 0xc3f490aa77bd60fc,0xbedbfc4411068a9c, 0xf4f1b4d515acb93b,0xee92fb5515482d44, 0x991711052d8bf3c5,0x751bdd152d4d1c4a, 0xbf5cd54678eef0b6,0xd262d45a78a0635d, 0xef340a98172aace4,0x86fb897116c87c34, 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0, 0xbae0a846d2195712,0x8974836059cca109, 0xe998d258869facd7,0x2bd1a438703fc94b, 0x91ff83775423cc06,0x7b6306a34627ddcf, 0xb67f6455292cbf08,0x1a3bc84c17b1d542, 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93, 0x8e938662882af53e,0x547eb47b7282ee9c, 0xb23867fb2a35b28d,0xe99e619a4f23aa43, 0xdec681f9f4c31f31,0x6405fa00e2ec94d4, 0x8b3c113c38f9f37e,0xde83bc408dd3dd04, 0xae0b158b4738705e,0x9624ab50b148d445, 0xd98ddaee19068c76,0x3badd624dd9b0957, 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6, 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c, 0xd47487cc8470652b,0x7647c3200069671f, 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073, 0xa5fb0a17c777cf09,0xf468107100525890, 0xcf79cc9db955c2cc,0x7182148d4066eeb4, 0x81ac1fe293d599bf,0xc6f14cd848405530, 0xa21727db38cb002f,0xb8ada00e5a506a7c, 0xca9cf1d206fdc03b,0xa6d90811f0e4851c, 0xfd442e4688bd304a,0x908f4a166d1da663, 0x9e4a9cec15763e2e,0x9a598e4e043287fe, 0xc5dd44271ad3cdba,0x40eff1e1853f29fd, 0xf7549530e188c128,0xd12bee59e68ef47c, 0x9a94dd3e8cf578b9,0x82bb74f8301958ce, 0xc13a148e3032d6e7,0xe36a52363c1faf01, 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1, 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9, 0xbcb2b812db11a5de,0x7415d448f6b6f0e7, 0xebdf661791d60f56,0x111b495b3464ad21, 0x936b9fcebb25c995,0xcab10dd900beec34, 0xb84687c269ef3bfb,0x3d5d514f40eea742, 0xe65829b3046b0afa,0xcb4a5a3112a5112, 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab, 0xb3f4e093db73a093,0x59ed216765690f56, 0xe0f218b8d25088b8,0x306869c13ec3532c, 0x8c974f7383725573,0x1e414218c73a13fb, 0xafbd2350644eeacf,0xe5d1929ef90898fa, 0xdbac6c247d62a583,0xdf45f746b74abf39, 0x894bc396ce5da772,0x6b8bba8c328eb783, 0xab9eb47c81f5114f,0x66ea92f3f326564, 0xd686619ba27255a2,0xc80a537b0efefebd, 0x8613fd0145877585,0xbd06742ce95f5f36, 0xa798fc4196e952e7,0x2c48113823b73704, 0xd17f3b51fca3a7a0,0xf75a15862ca504c5, 0x82ef85133de648c4,0x9a984d73dbe722fb, 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba, 0xcc963fee10b7d1b3,0x318df905079926a8, 0xffbbcfe994e5c61f,0xfdf17746497f7052, 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633, 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0, 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0, 0x9c1661a651213e2d,0x6bea10ca65c084e, 0xc31bfa0fe5698db8,0x486e494fcff30a62, 0xf3e2f893dec3f126,0x5a89dba3c3efccfa, 0x986ddb5c6b3a76b7,0xf89629465a75e01c, 0xbe89523386091465,0xf6bbb397f1135823, 0xee2ba6c0678b597f,0x746aa07ded582e2c, 0x94db483840b717ef,0xa8c2a44eb4571cdc, 0xba121a4650e4ddeb,0x92f34d62616ce413, 0xe896a0d7e51e1566,0x77b020baf9c81d17, 0x915e2486ef32cd60,0xace1474dc1d122e, 0xb5b5ada8aaff80b8,0xd819992132456ba, 0xe3231912d5bf60e6,0x10e1fff697ed6c69, 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1, 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2, 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde, 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b, 0xad4ab7112eb3929d,0x86c16c98d2c953c6, 0xd89d64d57a607744,0xe871c7bf077ba8b7, 0x87625f056c7c4a8b,0x11471cd764ad4972, 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf, 0xd389b47879823479,0x4aff1d108d4ec2c3, 0x843610cb4bf160cb,0xcedf722a585139ba, 0xa54394fe1eedb8fe,0xc2974eb4ee658828, 0xce947a3da6a9273e,0x733d226229feea32, 0x811ccc668829b887,0x806357d5a3f525f, 0xa163ff802a3426a8,0xca07c2dcb0cf26f7, 0xc9bcff6034c13052,0xfc89b393dd02f0b5, 0xfc2c3f3841f17c67,0xbbac2078d443ace2, 0x9d9ba7832936edc0,0xd54b944b84aa4c0d, 0xc5029163f384a931,0xa9e795e65d4df11, 0xf64335bcf065d37d,0x4d4617b5ff4a16d5, 0x99ea0196163fa42e,0x504bced1bf8e4e45, 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6, 0xf07da27a82c37088,0x5d767327bb4e5a4c, 0x964e858c91ba2655,0x3a6a07f8d510f86f, 0xbbe226efb628afea,0x890489f70a55368b, 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e, 0x92c8ae6b464fc96f,0x3b0b8bc90012929d, 0xb77ada0617e3bbcb,0x9ce6ebb40173744, 0xe55990879ddcaabd,0xcc420a6a101d0515, 0x8f57fa54c2a9eab6,0x9fa946824a12232d, 0xb32df8e9f3546564,0x47939822dc96abf9, 0xdff9772470297ebd,0x59787e2b93bc56f7, 0x8bfbea76c619ef36,0x57eb4edb3c55b65a, 0xaefae51477a06b03,0xede622920b6b23f1, 0xdab99e59958885c4,0xe95fab368e45eced, 0x88b402f7fd75539b,0x11dbcb0218ebb414, 0xaae103b5fcd2a881,0xd652bdc29f26a119, 0xd59944a37c0752a2,0x4be76d3346f0495f, 0x857fcae62d8493a5,0x6f70a4400c562ddb, 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952, 0xd097ad07a71f26b2,0x7e2000a41346a7a7, 0x825ecc24c873782f,0x8ed400668c0c28c8, 0xa2f67f2dfa90563b,0x728900802f0f32fa, 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9, 0xfea126b7d78186bc,0xe2f610c84987bfa8, 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9, 0xc6ede63fa05d3143,0x91503d1c79720dbb, 0xf8a95fcf88747d94,0x75a44c6397ce912a, 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba, 0xc24452da229b021b,0xfbe85badce996168, 0xf2d56790ab41c2a2,0xfae27299423fb9c3, 0x97c560ba6b0919a5,0xdccd879fc967d41a, 0xbdb6b8e905cb600f,0x5400e987bbc1c920, 0xed246723473e3813,0x290123e9aab23b68, 0x9436c0760c86e30b,0xf9a0b6720aaf6521, 0xb94470938fa89bce,0xf808e40e8d5b3e69, 0xe7958cb87392c2c2,0xb60b1d1230b20e04, 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2, 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3, 0xe2280b6c20dd5232,0x25c6da63c38de1b0, 0x8d590723948a535f,0x579c487e5a38ad0e, 0xb0af48ec79ace837,0x2d835a9df0c6d851, 0xdcdb1b2798182244,0xf8e431456cf88e65, 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff, 0xac8b2d36eed2dac5,0xe272467e3d222f3f, 0xd7adf884aa879177,0x5b0ed81dcc6abb0f, 0x86ccbb52ea94baea,0x98e947129fc2b4e9, 0xa87fea27a539e9a5,0x3f2398d747b36224, 0xd29fe4b18e88640e,0x8eec7f0d19a03aad, 0x83a3eeeef9153e89,0x1953cf68300424ac, 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7, 0xcdb02555653131b6,0x3792f412cb06794d, 0x808e17555f3ebf11,0xe2bbd88bbee40bd0, 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4, 0xc8de047564d20a8b,0xf245825a5a445275, 0xfb158592be068d2e,0xeed6e2f0f0d56712, 0x9ced737bb6c4183d,0x55464dd69685606b, 0xc428d05aa4751e4c,0xaa97e14c3c26b886, 0xf53304714d9265df,0xd53dd99f4b3066a8, 0x993fe2c6d07b7fab,0xe546a8038efe4029, 0xbf8fdb78849a5f96,0xde98520472bdd033, 0xef73d256a5c0f77c,0x963e66858f6d4440, 0x95a8637627989aad,0xdde7001379a44aa8, 0xbb127c53b17ec159,0x5560c018580d5d52, 0xe9d71b689dde71af,0xaab8f01e6e10b4a6, 0x9226712162ab070d,0xcab3961304ca70e8, 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22, 0xe45c10c42a2b3b05,0x8cb89a7db77c506a, 0x8eb98a7a9a5b04e3,0x77f3608e92adb242, 0xb267ed1940f1c61c,0x55f038b237591ed3, 0xdf01e85f912e37a3,0x6b6c46dec52f6688, 0x8b61313bbabce2c6,0x2323ac4b3b3da015, 0xae397d8aa96c1b77,0xabec975e0a0d081a, 0xd9c7dced53c72255,0x96e7bd358c904a21, 0x881cea14545c7575,0x7e50d64177da2e54, 0xaa242499697392d2,0xdde50bd1d5d0b9e9, 0xd4ad2dbfc3d07787,0x955e4ec64b44e864, 0x84ec3c97da624ab4,0xbd5af13bef0b113e, 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e, 0xcfb11ead453994ba,0x67de18eda5814af2, 0x81ceb32c4b43fcf4,0x80eacf948770ced7, 0xa2425ff75e14fc31,0xa1258379a94d028d, 0xcad2f7f5359a3b3e,0x96ee45813a04330, 0xfd87b5f28300ca0d,0x8bca9d6e188853fc, 0x9e74d1b791e07e48,0x775ea264cf55347e, 0xc612062576589dda,0x95364afe032a819e, 0xf79687aed3eec551,0x3a83ddbd83f52205, 0x9abe14cd44753b52,0xc4926a9672793543, 0xc16d9a0095928a27,0x75b7053c0f178294, 0xf1c90080baf72cb1,0x5324c68b12dd6339, 0x971da05074da7bee,0xd3f6fc16ebca5e04, 0xbce5086492111aea,0x88f4bb1ca6bcf585, 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6, 0x9392ee8e921d5d07,0x3aff322e62439fd0, 0xb877aa3236a4b449,0x9befeb9fad487c3, 0xe69594bec44de15b,0x4c2ebe687989a9b4, 0x901d7cf73ab0acd9,0xf9d37014bf60a11, 0xb424dc35095cd80f,0x538484c19ef38c95, 0xe12e13424bb40e13,0x2865a5f206b06fba, 0x8cbccc096f5088cb,0xf93f87b7442e45d4, 0xafebff0bcb24aafe,0xf78f69a51539d749, 0xdbe6fecebdedd5be,0xb573440e5a884d1c, 0x89705f4136b4a597,0x31680a88f8953031, 0xabcc77118461cefc,0xfdc20d2b36ba7c3e, 0xd6bf94d5e57a42bc,0x3d32907604691b4d, 0x8637bd05af6c69b5,0xa63f9a49c2c1b110, 0xa7c5ac471b478423,0xfcf80dc33721d54, 0xd1b71758e219652b,0xd3c36113404ea4a9, 0x83126e978d4fdf3b,0x645a1cac083126ea, 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4, 0xcccccccccccccccc,0xcccccccccccccccd, 0x8000000000000000,0x0, 0xa000000000000000,0x0, 0xc800000000000000,0x0, 0xfa00000000000000,0x0, 0x9c40000000000000,0x0, 0xc350000000000000,0x0, 0xf424000000000000,0x0, 0x9896800000000000,0x0, 0xbebc200000000000,0x0, 0xee6b280000000000,0x0, 0x9502f90000000000,0x0, 0xba43b74000000000,0x0, 0xe8d4a51000000000,0x0, 0x9184e72a00000000,0x0, 0xb5e620f480000000,0x0, 0xe35fa931a0000000,0x0, 0x8e1bc9bf04000000,0x0, 0xb1a2bc2ec5000000,0x0, 0xde0b6b3a76400000,0x0, 0x8ac7230489e80000,0x0, 0xad78ebc5ac620000,0x0, 0xd8d726b7177a8000,0x0, 0x878678326eac9000,0x0, 0xa968163f0a57b400,0x0, 0xd3c21bcecceda100,0x0, 0x84595161401484a0,0x0, 0xa56fa5b99019a5c8,0x0, 0xcecb8f27f4200f3a,0x0, 0x813f3978f8940984,0x4000000000000000, 0xa18f07d736b90be5,0x5000000000000000, 0xc9f2c9cd04674ede,0xa400000000000000, 0xfc6f7c4045812296,0x4d00000000000000, 0x9dc5ada82b70b59d,0xf020000000000000, 0xc5371912364ce305,0x6c28000000000000, 0xf684df56c3e01bc6,0xc732000000000000, 0x9a130b963a6c115c,0x3c7f400000000000, 0xc097ce7bc90715b3,0x4b9f100000000000, 0xf0bdc21abb48db20,0x1e86d40000000000, 0x96769950b50d88f4,0x1314448000000000, 0xbc143fa4e250eb31,0x17d955a000000000, 0xeb194f8e1ae525fd,0x5dcfab0800000000, 0x92efd1b8d0cf37be,0x5aa1cae500000000, 0xb7abc627050305ad,0xf14a3d9e40000000, 0xe596b7b0c643c719,0x6d9ccd05d0000000, 0x8f7e32ce7bea5c6f,0xe4820023a2000000, 0xb35dbf821ae4f38b,0xdda2802c8a800000, 0xe0352f62a19e306e,0xd50b2037ad200000, 0x8c213d9da502de45,0x4526f422cc340000, 0xaf298d050e4395d6,0x9670b12b7f410000, 0xdaf3f04651d47b4c,0x3c0cdd765f114000, 0x88d8762bf324cd0f,0xa5880a69fb6ac800, 0xab0e93b6efee0053,0x8eea0d047a457a00, 0xd5d238a4abe98068,0x72a4904598d6d880, 0x85a36366eb71f041,0x47a6da2b7f864750, 0xa70c3c40a64e6c51,0x999090b65f67d924, 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d, 0x82818f1281ed449f,0xbff8f10e7a8921a4, 0xa321f2d7226895c7,0xaff72d52192b6a0d, 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490, 0xfee50b7025c36a08,0x2f236d04753d5b4, 0x9f4f2726179a2245,0x1d762422c946590, 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5, 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2, 0x9b934c3b330c8577,0x63cc55f49f88eb2f, 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb, 0xf316271c7fc3908a,0x8bef464e3945ef7a, 0x97edd871cfda3a56,0x97758bf0e3cbb5ac, 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317, 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd, 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a, 0xb975d6b6ee39e436,0xb3e2fd538e122b44, 0xe7d34c64a9c85d44,0x60dbbca87196b616, 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd, 0xb51d13aea4a488dd,0x6babab6398bdbe41, 0xe264589a4dcdab14,0xc696963c7eed2dd1, 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2, 0xb0de65388cc8ada8,0x3b25a55f43294bcb, 0xdd15fe86affad912,0x49ef0eb713f39ebe, 0x8a2dbf142dfcc7ab,0x6e3569326c784337, 0xacb92ed9397bf996,0x49c2c37f07965404, 0xd7e77a8f87daf7fb,0xdc33745ec97be906, 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3, 0xa8acd7c0222311bc,0xc40832ea0d68ce0c, 0xd2d80db02aabd62b,0xf50a3fa490c30190, 0x83c7088e1aab65db,0x792667c6da79e0fa, 0xa4b8cab1a1563f52,0x577001b891185938, 0xcde6fd5e09abcf26,0xed4c0226b55e6f86, 0x80b05e5ac60b6178,0x544f8158315b05b4, 0xa0dc75f1778e39d6,0x696361ae3db1c721, 0xc913936dd571c84c,0x3bc3a19cd1e38e9, 0xfb5878494ace3a5f,0x4ab48a04065c723, 0x9d174b2dcec0e47b,0x62eb0d64283f9c76, 0xc45d1df942711d9a,0x3ba5d0bd324f8394, 0xf5746577930d6500,0xca8f44ec7ee36479, 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb, 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e, 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e, 0x95d04aee3b80ece5,0xbba1f1d158724a12, 0xbb445da9ca61281f,0x2a8a6e45ae8edc97, 0xea1575143cf97226,0xf52d09d71a3293bd, 0x924d692ca61be758,0x593c2626705f9c56, 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c, 0xe498f455c38b997a,0xb6dfb9c0f956447, 0x8edf98b59a373fec,0x4724bd4189bd5eac, 0xb2977ee300c50fe7,0x58edec91ec2cb657, 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed, 0x8b865b215899f46c,0xbd79e0d20082ee74, 0xae67f1e9aec07187,0xecd8590680a3aa11, 0xda01ee641a708de9,0xe80e6f4820cc9495, 0x884134fe908658b2,0x3109058d147fdcdd, 0xaa51823e34a7eede,0xbd4b46f0599fd415, 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a, 0x850fadc09923329e,0x3e2cf6bc604ddb0, 0xa6539930bf6bff45,0x84db8346b786151c, 0xcfe87f7cef46ff16,0xe612641865679a63, 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e, 0xa26da3999aef7749,0xe3be5e330f38f09d, 0xcb090c8001ab551c,0x5cadf5bfd3072cc5, 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6, 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa, 0xc646d63501a1511d,0xb281e1fd541501b8, 0xf7d88bc24209a565,0x1f225a7ca91a4226, 0x9ae757596946075f,0x3375788de9b06958, 0xc1a12d2fc3978937,0x52d6b1641c83ae, 0xf209787bb47d6b84,0xc0678c5dbd23a49a, 0x9745eb4d50ce6332,0xf840b7ba963646e0, 0xbd176620a501fbff,0xb650e5a93bc3d898, 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe, 0x93ba47c980e98cdf,0xc66f336c36b10137, 0xb8a8d9bbe123f017,0xb80b0047445d4184, 0xe6d3102ad96cec1d,0xa60dc059157491e5, 0x9043ea1ac7e41392,0x87c89837ad68db2f, 0xb454e4a179dd1877,0x29babe4598c311fb, 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a, 0x8ce2529e2734bb1d,0x1899e4a65f58660c, 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f, 0xdc21a1171d42645d,0x76707543f4fa1f73, 0x899504ae72497eba,0x6a06494a791c53a8, 0xabfa45da0edbde69,0x487db9d17636892, 0xd6f8d7509292d603,0x45a9d2845d3c42b6, 0x865b86925b9bc5c2,0xb8a2392ba45a9b2, 0xa7f26836f282b732,0x8e6cac7768d7141e, 0xd1ef0244af2364ff,0x3207d795430cd926, 0x8335616aed761f1f,0x7f44e6bd49e807b8, 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6, 0xcd036837130890a1,0x36dba887c37a8c0f, 0x802221226be55a64,0xc2494954da2c9789, 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c, 0xc83553c5c8965d3d,0x6f92829494e5acc7, 0xfa42a8b73abbf48c,0xcb772339ba1f17f9, 0x9c69a97284b578d7,0xff2a760414536efb, 0xc38413cf25e2d70d,0xfef5138519684aba, 0xf46518c2ef5b8cd1,0x7eb258665fc25d69, 0x98bf2f79d5993802,0xef2f773ffbd97a61, 0xbeeefb584aff8603,0xaafb550ffacfd8fa, 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38, 0x952ab45cfa97a0b2,0xdd945a747bf26183, 0xba756174393d88df,0x94f971119aeef9e4, 0xe912b9d1478ceb17,0x7a37cd5601aab85d, 0x91abb422ccb812ee,0xac62e055c10ab33a, 0xb616a12b7fe617aa,0x577b986b314d6009, 0xe39c49765fdf9d94,0xed5a7e85fda0b80b, 0x8e41ade9fbebc27d,0x14588f13be847307, 0xb1d219647ae6b31c,0x596eb2d8ae258fc8, 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb, 0x8aec23d680043bee,0x25de7bb9480d5854, 0xada72ccc20054ae9,0xaf561aa79a10ae6a, 0xd910f7ff28069da4,0x1b2ba1518094da04, 0x87aa9aff79042286,0x90fb44d2f05d0842, 0xa99541bf57452b28,0x353a1607ac744a53, 0xd3fa922f2d1675f2,0x42889b8997915ce8, 0x847c9b5d7c2e09b7,0x69956135febada11, 0xa59bc234db398c25,0x43fab9837e699095, 0xcf02b2c21207ef2e,0x94f967e45e03f4bb, 0x8161afb94b44f57d,0x1d1be0eebac278f5, 0xa1ba1ba79e1632dc,0x6462d92a69731732, 0xca28a291859bbf93,0x7d7b8f7503cfdcfe, 0xfcb2cb35e702af78,0x5cda735244c3d43e, 0x9defbf01b061adab,0x3a0888136afa64a7, 0xc56baec21c7a1916,0x88aaa1845b8fdd0, 0xf6c69a72a3989f5b,0x8aad549e57273d45, 0x9a3c2087a63f6399,0x36ac54e2f678864b, 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd, 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5, 0x969eb7c47859e743,0x9f644ae5a4b1b325, 0xbc4665b596706114,0x873d5d9f0dde1fee, 0xeb57ff22fc0c7959,0xa90cb506d155a7ea, 0x9316ff75dd87cbd8,0x9a7f12442d588f2, 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f, 0xe5d3ef282a242e81,0x8f1668c8a86da5fa, 0x8fa475791a569d10,0xf96e017d694487bc, 0xb38d92d760ec4455,0x37c981dcc395a9ac, 0xe070f78d3927556a,0x85bbe253f47b1417, 0x8c469ab843b89562,0x93956d7478ccec8e, 0xaf58416654a6babb,0x387ac8d1970027b2, 0xdb2e51bfe9d0696a,0x6997b05fcc0319e, 0x88fcf317f22241e2,0x441fece3bdf81f03, 0xab3c2fddeeaad25a,0xd527e81cad7626c3, 0xd60b3bd56a5586f1,0x8a71e223d8d3b074, 0x85c7056562757456,0xf6872d5667844e49, 0xa738c6bebb12d16c,0xb428f8ac016561db, 0xd106f86e69d785c7,0xe13336d701beba52, 0x82a45b450226b39c,0xecc0024661173473, 0xa34d721642b06084,0x27f002d7f95d0190, 0xcc20ce9bd35c78a5,0x31ec038df7b441f4, 0xff290242c83396ce,0x7e67047175a15271, 0x9f79a169bd203e41,0xf0062c6e984d386, 0xc75809c42c684dd1,0x52c07b78a3e60868, 0xf92e0c3537826145,0xa7709a56ccdf8a82, 0x9bbcc7a142b17ccb,0x88a66076400bb691, 0xc2abf989935ddbfe,0x6acff893d00ea435, 0xf356f7ebf83552fe,0x583f6b8c4124d43, 0x98165af37b2153de,0xc3727a337a8b704a, 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c, 0xeda2ee1c7064130c,0x1162def06f79df73, 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8, 0xb9a74a0637ce2ee1,0x6d953e2bd7173692, 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437, 0x910ab1d4db9914a0,0x1d9c9892400a22a2, 0xb54d5e4a127f59c8,0x2503beb6d00cab4b, 0xe2a0b5dc971f303a,0x2e44ae64840fd61d, 0x8da471a9de737e24,0x5ceaecfed289e5d2, 0xb10d8e1456105dad,0x7425a83e872c5f47, 0xdd50f1996b947518,0xd12f124e28f77719, 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f, 0xace73cbfdc0bfb7b,0x636cc64d1001550b, 0xd8210befd30efa5a,0x3c47f7e05401aa4e, 0x8714a775e3e95c78,0x65acfaec34810a71, 0xa8d9d1535ce3b396,0x7f1839a741a14d0d, 0xd31045a8341ca07c,0x1ede48111209a050, 0x83ea2b892091e44d,0x934aed0aab460432, 0xa4e4b66b68b65d60,0xf81da84d5617853f, 0xce1de40642e3f4b9,0x36251260ab9d668e, 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019, 0xa1075a24e4421730,0xb24cf65b8612f81f, 0xc94930ae1d529cfc,0xdee033f26797b627, 0xfb9b7cd9a4a7443c,0x169840ef017da3b1, 0x9d412e0806e88aa5,0x8e1f289560ee864e, 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2, 0xf5b5d7ec8acb58a2,0xae10af696774b1db, 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29, 0xbff610b0cc6edd3f,0x17fd090a58d32af3, 0xeff394dcff8a948e,0xddfc4b4cef07f5b0, 0x95f83d0a1fb69cd9,0x4abdaf101564f98e, 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1, 0xea53df5fd18d5513,0x84c86189216dc5ed, 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4, 0xb7118682dbb66a77,0x3fbc8c33221dc2a1, 0xe4d5e82392a40515,0xfabaf3feaa5334a, 0x8f05b1163ba6832d,0x29cb4d87f2a7400e, 0xb2c71d5bca9023f8,0x743e20e9ef511012, 0xdf78e4b2bd342cf6,0x914da9246b255416, 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e, 0xae9672aba3d0c320,0xa184ac2473b529b1, 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e, 0x8865899617fb1871,0x7e2fa67c7a658892, 0xaa7eebfb9df9de8d,0xddbb901b98feeab7, 0xd51ea6fa85785631,0x552a74227f3ea565, 0x8533285c936b35de,0xd53a88958f87275f, 0xa67ff273b8460356,0x8a892abaf368f137, 0xd01fef10a657842c,0x2d2b7569b0432d85, 0x8213f56a67f6b29b,0x9c3b29620e29fc73, 0xa298f2c501f45f42,0x8349f3ba91b47b8f, 0xcb3f2f7642717713,0x241c70a936219a73, 0xfe0efb53d30dd4d7,0xed238cd383aa0110, 0x9ec95d1463e8a506,0xf4363804324a40aa, 0xc67bb4597ce2ce48,0xb143c6053edcd0d5, 0xf81aa16fdc1b81da,0xdd94b7868e94050a, 0x9b10a4e5e9913128,0xca7cf2b4191c8326, 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0, 0xf24a01a73cf2dccf,0xbc633b39673c8cec, 0x976e41088617ca01,0xd5be0503e085d813, 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18, 0xec9c459d51852ba2,0xddf8e7d60ed1219e, 0x93e1ab8252f33b45,0xcabb90e5c942b503, 0xb8da1662e7b00a17,0x3d6a751f3b936243, 0xe7109bfba19c0c9d,0xcc512670a783ad4, 0x906a617d450187e2,0x27fb2b80668b24c5, 0xb484f9dc9641e9da,0xb1f9f660802dedf6, 0xe1a63853bbd26451,0x5e7873f8a0396973, 0x8d07e33455637eb2,0xdb0b487b6423e1e8, 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62, 0xdc5c5301c56b75f7,0x7641a140cc7810fb, 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d, 0xac2820d9623bf429,0x546345fa9fbdcd44, 0xd732290fbacaf133,0xa97c177947ad4095, 0x867f59a9d4bed6c0,0x49ed8eabcccc485d, 0xa81f301449ee8c70,0x5c68f256bfff5a74, 0xd226fc195c6a2f8c,0x73832eec6fff3111, 0x83585d8fd9c25db7,0xc831fd53c5ff7eab, 0xa42e74f3d032f525,0xba3e7ca8b77f5e55, 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb, 0x80444b5e7aa7cf85,0x7980d163cf5b81b3, 0xa0555e361951c366,0xd7e105bcc332621f, 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7, 0xfa856334878fc150,0xb14f98f6f0feb951, 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3, 0xc3b8358109e84f07,0xa862f80ec4700c8, 0xf4a642e14c6262c8,0xcd27bb612758c0fa, 0x98e7e9cccfbd7dbd,0x8038d51cb897789c, 0xbf21e44003acdd2c,0xe0470a63e6bd56c3, 0xeeea5d5004981478,0x1858ccfce06cac74, 0x95527a5202df0ccb,0xf37801e0c43ebc8, 0xbaa718e68396cffd,0xd30560258f54e6ba, 0xe950df20247c83fd,0x47c6b82ef32a2069, 0x91d28b7416cdd27e,0x4cdc331d57fa5441, 0xb6472e511c81471d,0xe0133fe4adf8e952, 0xe3d8f9e563a198e5,0x58180fddd97723a6, 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,}; using powers = powers_template<>; } #endif #ifndef FASTFLOAT_DECIMAL_TO_BINARY_H #define FASTFLOAT_DECIMAL_TO_BINARY_H #include #include #include #include #include #include namespace fast_float { // This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating // the result, with the "high" part corresponding to the most significant bits and the // low part corresponding to the least significant bits. // template fastfloat_really_inline value128 compute_product_approximation(int64_t q, uint64_t w) { const int index = 2 * int(q - powers::smallest_power_of_five); // For small values of q, e.g., q in [0,27], the answer is always exact because // The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]); // gives the exact answer. value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]); static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]"); constexpr uint64_t precision_mask = (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) : uint64_t(0xFFFFFFFFFFFFFFFF); if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower) // regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed. value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]); firstproduct.low += secondproduct.high; if(secondproduct.high > firstproduct.low) { firstproduct.high++; } } return firstproduct; } namespace detail { /** * For q in (0,350), we have that * f = (((152170 + 65536) * q ) >> 16); * is equal to * floor(p) + q * where * p = log(5**q)/log(2) = q * log(5)/log(2) * * For negative values of q in (-400,0), we have that * f = (((152170 + 65536) * q ) >> 16); * is equal to * -ceil(p) + q * where * p = log(5**-q)/log(2) = -q * log(5)/log(2) */ constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { return (((152170 + 65536) * q) >> 16) + 63; } } // namespace detail // create an adjusted mantissa, biased by the invalid power2 // for significant digits already multiplied by 10 ** q. template fastfloat_really_inline adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { int hilz = int(w >> 63) ^ 1; adjusted_mantissa answer; answer.mantissa = w << hilz; int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias); return answer; } // w * 10 ** q, without rounding the representation up. // the power2 in the exponent will be adjusted by invalid_am_bias. template fastfloat_really_inline adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept { int lz = leading_zeroes(w); w <<= lz; value128 product = compute_product_approximation(q, w); return compute_error_scaled(q, product.high, lz); } // w * 10 ** q // The returned value should be a valid ieee64 number that simply need to be packed. // However, in some very rare cases, the computation will fail. In such cases, we // return an adjusted_mantissa with a negative power of 2: the caller should recompute // in such cases. template fastfloat_really_inline adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { adjusted_mantissa answer; if ((w == 0) || (q < binary::smallest_power_of_ten())) { answer.power2 = 0; answer.mantissa = 0; // result should be zero return answer; } if (q > binary::largest_power_of_ten()) { // we want to get infinity: answer.power2 = binary::infinite_power(); answer.mantissa = 0; return answer; } // At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five]. // We want the most significant bit of i to be 1. Shift if needed. int lz = leading_zeroes(w); w <<= lz; // The required precision is binary::mantissa_explicit_bits() + 3 because // 1. We need the implicit bit // 2. We need an extra bit for rounding purposes // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift) value128 product = compute_product_approximation(q, w); if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further // In some very rare cases, this could happen, in which case we might need a more accurate // computation that what we can provide cheaply. This is very, very unlikely. // const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0, // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation. if(!inside_safe_exponent) { return compute_error_scaled(q, product.high, lz); } } // The "compute_product_approximation" function can be slightly slower than a branchless approach: // value128 product = compute_product(q, w); // but in practice, we can win big with the compute_product_approximation if its additional branch // is easily predicted. Which is best is data specific. int upperbit = int(product.high >> 63); answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent()); if (answer.power2 <= 0) { // we have a subnormal? // Here have that answer.power2 <= 0 so -answer.power2 >= 0 if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. answer.power2 = 0; answer.mantissa = 0; // result should be zero return answer; } // next line is safe because -answer.power2 + 1 < 64 answer.mantissa >>= -answer.power2 + 1; // Thankfully, we can't have both "round-to-even" and subnormals because // "round-to-even" only occurs for powers close to 0. answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa >>= 1; // There is a weird scenario where we don't have a subnormal but just. // Suppose we start with 2.2250738585072013e-308, we end up // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer // subnormal, but we can only know this after rounding. // So we only declare a subnormal if we are smaller than the threshold. answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1; return answer; } // usually, we round *up*, but if we fall right in between and and we have an // even basis, we need to round down // We are only concerned with the cases where 5**q fits in single 64-bit word. if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) && ((answer.mantissa & 3) == 1) ) { // we may fall between two floats! // To be in-between two floats we need that in doing // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); // ... we dropped out only zeroes. But if this happened, then we can go back!!! if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) { answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up } } answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa >>= 1; if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); answer.power2++; // undo previous addition } answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); if (answer.power2 >= binary::infinite_power()) { // infinity answer.power2 = binary::infinite_power(); answer.mantissa = 0; } return answer; } } // namespace fast_float #endif #ifndef FASTFLOAT_BIGINT_H #define FASTFLOAT_BIGINT_H #include #include #include #include namespace fast_float { // the limb width: we want efficient multiplication of double the bits in // limb, or for 64-bit limbs, at least 64-bit multiplication where we can // extract the high and low parts efficiently. this is every 64-bit // architecture except for sparc, which emulates 128-bit multiplication. // we might have platforms where `CHAR_BIT` is not 8, so let's avoid // doing `8 * sizeof(limb)`. #if defined(FASTFLOAT_64BIT) && !defined(__sparc) #define FASTFLOAT_64BIT_LIMB typedef uint64_t limb; constexpr size_t limb_bits = 64; #else #define FASTFLOAT_32BIT_LIMB typedef uint32_t limb; constexpr size_t limb_bits = 32; #endif typedef span limb_span; // number of bits in a bigint. this needs to be at least the number // of bits required to store the largest bigint, which is // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or // ~3600 bits, so we round to 4000. constexpr size_t bigint_bits = 4000; constexpr size_t bigint_limbs = bigint_bits / limb_bits; // vector-like type that is allocated on the stack. the entire // buffer is pre-allocated, and only the length changes. template struct stackvec { limb data[size]; // we never need more than 150 limbs uint16_t length{0}; stackvec() = default; stackvec(const stackvec &) = delete; stackvec &operator=(const stackvec &) = delete; stackvec(stackvec &&) = delete; stackvec &operator=(stackvec &&other) = delete; // create stack vector from existing limb span. stackvec(limb_span s) { FASTFLOAT_ASSERT(try_extend(s)); } limb& operator[](size_t index) noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); return data[index]; } const limb& operator[](size_t index) const noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); return data[index]; } // index from the end of the container const limb& rindex(size_t index) const noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); size_t rindex = length - index - 1; return data[rindex]; } // set the length, without bounds checking. void set_len(size_t len) noexcept { length = uint16_t(len); } constexpr size_t len() const noexcept { return length; } constexpr bool is_empty() const noexcept { return length == 0; } constexpr size_t capacity() const noexcept { return size; } // append item to vector, without bounds checking void push_unchecked(limb value) noexcept { data[length] = value; length++; } // append item to vector, returning if item was added bool try_push(limb value) noexcept { if (len() < capacity()) { push_unchecked(value); return true; } else { return false; } } // add items to the vector, from a span, without bounds checking void extend_unchecked(limb_span s) noexcept { limb* ptr = data + length; ::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len()); set_len(len() + s.len()); } // try to add items to the vector, returning if items were added bool try_extend(limb_span s) noexcept { if (len() + s.len() <= capacity()) { extend_unchecked(s); return true; } else { return false; } } // resize the vector, without bounds checking // if the new size is longer than the vector, assign value to each // appended item. void resize_unchecked(size_t new_len, limb value) noexcept { if (new_len > len()) { size_t count = new_len - len(); limb* first = data + len(); limb* last = first + count; ::std::fill(first, last, value); set_len(new_len); } else { set_len(new_len); } } // try to resize the vector, returning if the vector was resized. bool try_resize(size_t new_len, limb value) noexcept { if (new_len > capacity()) { return false; } else { resize_unchecked(new_len, value); return true; } } // check if any limbs are non-zero after the given index. // this needs to be done in reverse order, since the index // is relative to the most significant limbs. bool nonzero(size_t index) const noexcept { while (index < len()) { if (rindex(index) != 0) { return true; } index++; } return false; } // normalize the big integer, so most-significant zero limbs are removed. void normalize() noexcept { while (len() > 0 && rindex(0) == 0) { length--; } } }; fastfloat_really_inline uint64_t empty_hi64(bool& truncated) noexcept { truncated = false; return 0; } fastfloat_really_inline uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept { truncated = false; int shl = leading_zeroes(r0); return r0 << shl; } fastfloat_really_inline uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept { int shl = leading_zeroes(r0); if (shl == 0) { truncated = r1 != 0; return r0; } else { int shr = 64 - shl; truncated = (r1 << shl) != 0; return (r0 << shl) | (r1 >> shr); } } fastfloat_really_inline uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept { return uint64_hi64(r0, truncated); } fastfloat_really_inline uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept { uint64_t x0 = r0; uint64_t x1 = r1; return uint64_hi64((x0 << 32) | x1, truncated); } fastfloat_really_inline uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept { uint64_t x0 = r0; uint64_t x1 = r1; uint64_t x2 = r2; return uint64_hi64(x0, (x1 << 32) | x2, truncated); } // add two small integers, checking for overflow. // we want an efficient operation. for msvc, where // we don't have built-in intrinsics, this is still // pretty fast. fastfloat_really_inline limb scalar_add(limb x, limb y, bool& overflow) noexcept { limb z; // gcc and clang #if defined(__has_builtin) #if __has_builtin(__builtin_add_overflow) overflow = __builtin_add_overflow(x, y, &z); return z; #endif #endif // generic, this still optimizes correctly on MSVC. z = x + y; overflow = z < x; return z; } // multiply two small integers, getting both the high and low bits. fastfloat_really_inline limb scalar_mul(limb x, limb y, limb& carry) noexcept { #ifdef FASTFLOAT_64BIT_LIMB #if defined(__SIZEOF_INT128__) // GCC and clang both define it as an extension. __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); carry = limb(z >> limb_bits); return limb(z); #else // fallback, no native 128-bit integer multiplication with carry. // on msvc, this optimizes identically, somehow. value128 z = full_multiplication(x, y); bool overflow; z.low = scalar_add(z.low, carry, overflow); z.high += uint64_t(overflow); // cannot overflow carry = z.high; return z.low; #endif #else uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); carry = limb(z >> limb_bits); return limb(z); #endif } // add scalar value to bigint starting from offset. // used in grade school multiplication template inline bool small_add_from(stackvec& vec, limb y, size_t start) noexcept { size_t index = start; limb carry = y; bool overflow; while (carry != 0 && index < vec.len()) { vec[index] = scalar_add(vec[index], carry, overflow); carry = limb(overflow); index += 1; } if (carry != 0) { FASTFLOAT_TRY(vec.try_push(carry)); } return true; } // add scalar value to bigint. template fastfloat_really_inline bool small_add(stackvec& vec, limb y) noexcept { return small_add_from(vec, y, 0); } // multiply bigint by scalar value. template inline bool small_mul(stackvec& vec, limb y) noexcept { limb carry = 0; for (size_t index = 0; index < vec.len(); index++) { vec[index] = scalar_mul(vec[index], y, carry); } if (carry != 0) { FASTFLOAT_TRY(vec.try_push(carry)); } return true; } // add bigint to bigint starting from index. // used in grade school multiplication template bool large_add_from(stackvec& x, limb_span y, size_t start) noexcept { // the effective x buffer is from `xstart..x.len()`, so exit early // if we can't get that current range. if (x.len() < start || y.len() > x.len() - start) { FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); } bool carry = false; for (size_t index = 0; index < y.len(); index++) { limb xi = x[index + start]; limb yi = y[index]; bool c1 = false; bool c2 = false; xi = scalar_add(xi, yi, c1); if (carry) { xi = scalar_add(xi, 1, c2); } x[index + start] = xi; carry = c1 | c2; } // handle overflow if (carry) { FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); } return true; } // add bigint to bigint. template fastfloat_really_inline bool large_add_from(stackvec& x, limb_span y) noexcept { return large_add_from(x, y, 0); } // grade-school multiplication algorithm template bool long_mul(stackvec& x, limb_span y) noexcept { limb_span xs = limb_span(x.data, x.len()); stackvec z(xs); limb_span zs = limb_span(z.data, z.len()); if (y.len() != 0) { limb y0 = y[0]; FASTFLOAT_TRY(small_mul(x, y0)); for (size_t index = 1; index < y.len(); index++) { limb yi = y[index]; stackvec zi; if (yi != 0) { // re-use the same buffer throughout zi.set_len(0); FASTFLOAT_TRY(zi.try_extend(zs)); FASTFLOAT_TRY(small_mul(zi, yi)); limb_span zis = limb_span(zi.data, zi.len()); FASTFLOAT_TRY(large_add_from(x, zis, index)); } } } x.normalize(); return true; } // grade-school multiplication algorithm template bool large_mul(stackvec& x, limb_span y) noexcept { if (y.len() == 1) { FASTFLOAT_TRY(small_mul(x, y[0])); } else { FASTFLOAT_TRY(long_mul(x, y)); } return true; } // big integer type. implements a small subset of big integer // arithmetic, using simple algorithms since asymptotically // faster algorithms are slower for a small number of limbs. // all operations assume the big-integer is normalized. struct bigint { // storage of the limbs, in little-endian order. stackvec vec; bigint(): vec() {} bigint(const bigint &) = delete; bigint &operator=(const bigint &) = delete; bigint(bigint &&) = delete; bigint &operator=(bigint &&other) = delete; bigint(uint64_t value): vec() { #ifdef FASTFLOAT_64BIT_LIMB vec.push_unchecked(value); #else vec.push_unchecked(uint32_t(value)); vec.push_unchecked(uint32_t(value >> 32)); #endif vec.normalize(); } // get the high 64 bits from the vector, and if bits were truncated. // this is to get the significant digits for the float. uint64_t hi64(bool& truncated) const noexcept { #ifdef FASTFLOAT_64BIT_LIMB if (vec.len() == 0) { return empty_hi64(truncated); } else if (vec.len() == 1) { return uint64_hi64(vec.rindex(0), truncated); } else { uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); truncated |= vec.nonzero(2); return result; } #else if (vec.len() == 0) { return empty_hi64(truncated); } else if (vec.len() == 1) { return uint32_hi64(vec.rindex(0), truncated); } else if (vec.len() == 2) { return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); } else { uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); truncated |= vec.nonzero(3); return result; } #endif } // compare two big integers, returning the large value. // assumes both are normalized. if the return value is // negative, other is larger, if the return value is // positive, this is larger, otherwise they are equal. // the limbs are stored in little-endian order, so we // must compare the limbs in ever order. int compare(const bigint& other) const noexcept { if (vec.len() > other.vec.len()) { return 1; } else if (vec.len() < other.vec.len()) { return -1; } else { for (size_t index = vec.len(); index > 0; index--) { limb xi = vec[index - 1]; limb yi = other.vec[index - 1]; if (xi > yi) { return 1; } else if (xi < yi) { return -1; } } return 0; } } // shift left each limb n bits, carrying over to the new limb // returns true if we were able to shift all the digits. bool shl_bits(size_t n) noexcept { // Internally, for each item, we shift left by n, and add the previous // right shifted limb-bits. // For example, we transform (for u8) shifted left 2, to: // b10100100 b01000010 // b10 b10010001 b00001000 FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); size_t shl = n; size_t shr = limb_bits - shl; limb prev = 0; for (size_t index = 0; index < vec.len(); index++) { limb xi = vec[index]; vec[index] = (xi << shl) | (prev >> shr); prev = xi; } limb carry = prev >> shr; if (carry != 0) { return vec.try_push(carry); } return true; } // move the limbs left by `n` limbs. bool shl_limbs(size_t n) noexcept { FASTFLOAT_DEBUG_ASSERT(n != 0); if (n + vec.len() > vec.capacity()) { return false; } else if (!vec.is_empty()) { // move limbs limb* dst = vec.data + n; const limb* src = vec.data; ::memmove(dst, src, sizeof(limb) * vec.len()); // fill in empty limbs limb* first = vec.data; limb* last = first + n; ::std::fill(first, last, 0); vec.set_len(n + vec.len()); return true; } else { return true; } } // move the limbs left by `n` bits. bool shl(size_t n) noexcept { size_t rem = n % limb_bits; size_t div = n / limb_bits; if (rem != 0) { FASTFLOAT_TRY(shl_bits(rem)); } if (div != 0) { FASTFLOAT_TRY(shl_limbs(div)); } return true; } // get the number of leading zeros in the bigint. int ctlz() const noexcept { if (vec.is_empty()) { return 0; } else { #ifdef FASTFLOAT_64BIT_LIMB return leading_zeroes(vec.rindex(0)); #else // no use defining a specialized leading_zeroes for a 32-bit type. uint64_t r0 = vec.rindex(0); return leading_zeroes(r0 << 32); #endif } } // get the number of bits in the bigint. int bit_length() const noexcept { int lz = ctlz(); return int(limb_bits * vec.len()) - lz; } bool mul(limb y) noexcept { return small_mul(vec, y); } bool add(limb y) noexcept { return small_add(vec, y); } // multiply as if by 2 raised to a power. bool pow2(uint32_t exp) noexcept { return shl(exp); } // multiply as if by 5 raised to a power. bool pow5(uint32_t exp) noexcept { // multiply by a power of 5 static constexpr uint32_t large_step = 135; static constexpr uint64_t small_power_of_5[] = { 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL, 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL, 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL, 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL, 2384185791015625UL, 11920928955078125UL, 59604644775390625UL, 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL, }; #ifdef FASTFLOAT_64BIT_LIMB constexpr static limb large_power_of_5[] = { 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, 10482974169319127550UL, 198276706040285095UL}; #else constexpr static limb large_power_of_5[] = { 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; #endif size_t large_length = sizeof(large_power_of_5) / sizeof(limb); limb_span large = limb_span(large_power_of_5, large_length); while (exp >= large_step) { FASTFLOAT_TRY(large_mul(vec, large)); exp -= large_step; } #ifdef FASTFLOAT_64BIT_LIMB uint32_t small_step = 27; limb max_native = 7450580596923828125UL; #else uint32_t small_step = 13; limb max_native = 1220703125U; #endif while (exp >= small_step) { FASTFLOAT_TRY(small_mul(vec, max_native)); exp -= small_step; } if (exp != 0) { FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp]))); } return true; } // multiply as if by 10 raised to a power. bool pow10(uint32_t exp) noexcept { FASTFLOAT_TRY(pow5(exp)); return pow2(exp); } }; } // namespace fast_float #endif #ifndef FASTFLOAT_ASCII_NUMBER_H #define FASTFLOAT_ASCII_NUMBER_H #include #include #include #include namespace fast_float { // Next function can be micro-optimized, but compilers are entirely // able to optimize it well. fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } fastfloat_really_inline uint64_t byteswap(uint64_t val) { return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 | (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 | (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 | (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56; } fastfloat_really_inline uint64_t read_u64(const char *chars) { uint64_t val; ::memcpy(&val, chars, sizeof(uint64_t)); #if FASTFLOAT_IS_BIG_ENDIAN == 1 // Need to read as-if the number was in little-endian order. val = byteswap(val); #endif return val; } fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { #if FASTFLOAT_IS_BIG_ENDIAN == 1 // Need to read as-if the number was in little-endian order. val = byteswap(val); #endif ::memcpy(chars, &val, sizeof(uint64_t)); } // credit @aqrit fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { const uint64_t mask = 0x000000FF000000FF; const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) val -= 0x3030303030303030; val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; return uint32_t(val); } fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { return parse_eight_digits_unrolled(read_u64(chars)); } // credit @aqrit fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080)); } fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { return is_made_of_eight_digits_fast(read_u64(chars)); } typedef span byte_span; struct parsed_number_string { int64_t exponent{0}; uint64_t mantissa{0}; const char *lastmatch{nullptr}; bool negative{false}; bool valid{false}; bool too_many_digits{false}; // contains the range of the significant digits byte_span integer{}; // non-nullable byte_span fraction{}; // nullable }; // Assuming that you use no more than 19 digits, this will // parse an ASCII string. fastfloat_really_inline parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { const chars_format fmt = options.format; const char decimal_point = options.decimal_point; parsed_number_string answer; answer.valid = false; answer.too_many_digits = false; answer.negative = (*p == '-'); if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here ++p; if (p == pend) { return answer; } if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot return answer; } } const char *const start_digits = p; uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok p += 8; } while ((p != pend) && is_integer(*p)) { // a multiplication by 10 is cheaper than an arbitrary integer // multiplication i = 10 * i + uint64_t(*p - '0'); // might overflow, we will handle the overflow later ++p; } const char *const end_of_integer_part = p; int64_t digit_count = int64_t(end_of_integer_part - start_digits); answer.integer = byte_span(start_digits, size_t(digit_count)); int64_t exponent = 0; if ((p != pend) && (*p == decimal_point)) { ++p; const char* before = p; // can occur at most twice without overflowing, but let it occur more, since // for integers with many digits, digit parsing is the primary bottleneck. while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok p += 8; } while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - '0'); ++p; i = i * 10 + digit; // in rare cases, this will overflow, but that's ok } exponent = before - p; answer.fraction = byte_span(before, size_t(p - before)); digit_count -= exponent; } // we must have encountered at least one integer! if (digit_count == 0) { return answer; } int64_t exp_number = 0; // explicit exponential part if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { const char * location_of_e = p; ++p; bool neg_exp = false; if ((p != pend) && ('-' == *p)) { neg_exp = true; ++p; } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) ++p; } if ((p == pend) || !is_integer(*p)) { if(!(fmt & chars_format::fixed)) { // We are in error. return answer; } // Otherwise, we will be ignoring the 'e'. p = location_of_e; } else { while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - '0'); if (exp_number < 0x10000000) { exp_number = 10 * exp_number + digit; } ++p; } if(neg_exp) { exp_number = - exp_number; } exponent += exp_number; } } else { // If it scientific and not fixed, we have to bail out. if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } } answer.lastmatch = p; answer.valid = true; // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon. // // We can deal with up to 19 digits. if (digit_count > 19) { // this is uncommon // It is possible that the integer had an overflow. // We have to handle the case where we have 0.0000somenumber. // We need to be mindful of the case where we only have zeroes... // E.g., 0.000000000...000. const char *start = start_digits; while ((start != pend) && (*start == '0' || *start == decimal_point)) { if(*start == '0') { digit_count --; } start++; } if (digit_count > 19) { answer.too_many_digits = true; // Let us start again, this time, avoiding overflows. // We don't need to check if is_integer, since we use the // pre-tokenized spans from above. i = 0; p = answer.integer.ptr; const char* int_end = p + answer.integer.len(); const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; while((i < minimal_nineteen_digit_integer) && (p != int_end)) { i = i * 10 + uint64_t(*p - '0'); ++p; } if (i >= minimal_nineteen_digit_integer) { // We have a big integers exponent = end_of_integer_part - p + exp_number; } else { // We have a value with a fractional component. p = answer.fraction.ptr; const char* frac_end = p + answer.fraction.len(); while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { i = i * 10 + uint64_t(*p - '0'); ++p; } exponent = answer.fraction.ptr - p + exp_number; } // We have now corrected both exponent and i, to a truncated value } } answer.exponent = exponent; answer.mantissa = i; return answer; } } // namespace fast_float #endif #ifndef FASTFLOAT_DIGIT_COMPARISON_H #define FASTFLOAT_DIGIT_COMPARISON_H #include #include #include #include namespace fast_float { // 1e0 to 1e19 constexpr static uint64_t powers_of_ten_uint64[] = { 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, 1000000000000000000UL, 10000000000000000000UL}; // calculate the exponent, in scientific notation, of the number. // this algorithm is not even close to optimized, but it has no practical // effect on performance: in order to have a faster algorithm, we'd need // to slow down performance for faster algorithms, and this is still fast. fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept { uint64_t mantissa = num.mantissa; int32_t exponent = int32_t(num.exponent); while (mantissa >= 10000) { mantissa /= 10000; exponent += 4; } while (mantissa >= 100) { mantissa /= 100; exponent += 2; } while (mantissa >= 10) { mantissa /= 10; exponent += 1; } return exponent; } // this converts a native floating-point number to an extended-precision float. template fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { using equiv_uint = typename binary_format::equiv_uint; constexpr equiv_uint exponent_mask = binary_format::exponent_mask(); constexpr equiv_uint mantissa_mask = binary_format::mantissa_mask(); constexpr equiv_uint hidden_bit_mask = binary_format::hidden_bit_mask(); adjusted_mantissa am; int32_t bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); equiv_uint bits; ::memcpy(&bits, &value, sizeof(T)); if ((bits & exponent_mask) == 0) { // denormal am.power2 = 1 - bias; am.mantissa = bits & mantissa_mask; } else { // normal am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); am.power2 -= bias; am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; } return am; } // get the extended precision value of the halfway point between b and b+u. // we are given a native float that represents b, so we need to adjust it // halfway between b and b+u. template fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept { adjusted_mantissa am = to_extended(value); am.mantissa <<= 1; am.mantissa += 1; am.power2 -= 1; return am; } // round an extended-precision float to the nearest machine float. template fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept { int32_t mantissa_shift = 64 - binary_format::mantissa_explicit_bits() - 1; if (-am.power2 >= mantissa_shift) { // have a denormal float int32_t shift = -am.power2 + 1; cb(am, std::min(shift, 64)); // check for round-up: if rounding-nearest carried us to the hidden bit. am.power2 = (am.mantissa < (uint64_t(1) << binary_format::mantissa_explicit_bits())) ? 0 : 1; return; } // have a normal float, use the default shift. cb(am, mantissa_shift); // check for carry if (am.mantissa >= (uint64_t(2) << binary_format::mantissa_explicit_bits())) { am.mantissa = (uint64_t(1) << binary_format::mantissa_explicit_bits()); am.power2++; } // check for infinite: we could have carried to an infinite power am.mantissa &= ~(uint64_t(1) << binary_format::mantissa_explicit_bits()); if (am.power2 >= binary_format::infinite_power()) { am.power2 = binary_format::infinite_power(); am.mantissa = 0; } } template fastfloat_really_inline void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { uint64_t mask; uint64_t halfway; if (shift == 64) { mask = UINT64_MAX; } else { mask = (uint64_t(1) << shift) - 1; } if (shift == 0) { halfway = 0; } else { halfway = uint64_t(1) << (shift - 1); } uint64_t truncated_bits = am.mantissa & mask; uint64_t is_above = truncated_bits > halfway; uint64_t is_halfway = truncated_bits == halfway; // shift digits into position if (shift == 64) { am.mantissa = 0; } else { am.mantissa >>= shift; } am.power2 += shift; bool is_odd = (am.mantissa & 1) == 1; am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); } fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept { if (shift == 64) { am.mantissa = 0; } else { am.mantissa >>= shift; } am.power2 += shift; } fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept { uint64_t val; while (std::distance(first, last) >= 8) { ::memcpy(&val, first, sizeof(uint64_t)); if (val != 0x3030303030303030) { break; } first += 8; } while (first != last) { if (*first != '0') { break; } first++; } } // determine if any non-zero digits were truncated. // all characters must be valid digits. fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept { // do 8-bit optimizations, can just compare to 8 literal 0s. uint64_t val; while (std::distance(first, last) >= 8) { ::memcpy(&val, first, sizeof(uint64_t)); if (val != 0x3030303030303030) { return true; } first += 8; } while (first != last) { if (*first != '0') { return true; } first++; } return false; } fastfloat_really_inline bool is_truncated(byte_span s) noexcept { return is_truncated(s.ptr, s.ptr + s.len()); } fastfloat_really_inline void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { value = value * 100000000 + parse_eight_digits_unrolled(p); p += 8; counter += 8; count += 8; } fastfloat_really_inline void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { value = value * 10 + limb(*p - '0'); p++; counter++; count++; } fastfloat_really_inline void add_native(bigint& big, limb power, limb value) noexcept { big.mul(power); big.add(value); } fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept { // need to round-up the digits, but need to avoid rounding // ....9999 to ...10000, which could cause a false halfway point. add_native(big, 10, 1); count++; } // parse the significant digits into a big integer inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept { // try to minimize the number of big integer and scalar multiplication. // therefore, try to parse 8 digits at a time, and multiply by the largest // scalar value (9 or 19 digits) for each step. size_t counter = 0; digits = 0; limb value = 0; #ifdef FASTFLOAT_64BIT_LIMB size_t step = 19; #else size_t step = 9; #endif // process all integer digits. const char* p = num.integer.ptr; const char* pend = p + num.integer.len(); skip_zeros(p, pend); // process all digits, in increments of step per loop while (p != pend) { while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { parse_eight_digits(p, value, counter, digits); } while (counter < step && p != pend && digits < max_digits) { parse_one_digit(p, value, counter, digits); } if (digits == max_digits) { // add the temporary value, then check if we've truncated any digits add_native(result, limb(powers_of_ten_uint64[counter]), value); bool truncated = is_truncated(p, pend); if (num.fraction.ptr != nullptr) { truncated |= is_truncated(num.fraction); } if (truncated) { round_up_bigint(result, digits); } return; } else { add_native(result, limb(powers_of_ten_uint64[counter]), value); counter = 0; value = 0; } } // add our fraction digits, if they're available. if (num.fraction.ptr != nullptr) { p = num.fraction.ptr; pend = p + num.fraction.len(); if (digits == 0) { skip_zeros(p, pend); } // process all digits, in increments of step per loop while (p != pend) { while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { parse_eight_digits(p, value, counter, digits); } while (counter < step && p != pend && digits < max_digits) { parse_one_digit(p, value, counter, digits); } if (digits == max_digits) { // add the temporary value, then check if we've truncated any digits add_native(result, limb(powers_of_ten_uint64[counter]), value); bool truncated = is_truncated(p, pend); if (truncated) { round_up_bigint(result, digits); } return; } else { add_native(result, limb(powers_of_ten_uint64[counter]), value); counter = 0; value = 0; } } } if (counter != 0) { add_native(result, limb(powers_of_ten_uint64[counter]), value); } } template inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept { FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); adjusted_mantissa answer; bool truncated; answer.mantissa = bigmant.hi64(truncated); int bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); answer.power2 = bigmant.bit_length() - 64 + bias; round(answer, [truncated](adjusted_mantissa& a, int32_t shift) { round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { return is_above || (is_halfway && truncated) || (is_odd && is_halfway); }); }); return answer; } // the scaling here is quite simple: we have, for the real digits `m * 10^e`, // and for the theoretical digits `n * 2^f`. Since `e` is always negative, // to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. // we then need to scale by `2^(f- e)`, and then the two significant digits // are of the same magnitude. template inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept { bigint& real_digits = bigmant; int32_t real_exp = exponent; // get the value of `b`, rounded down, and get a bigint representation of b+h adjusted_mantissa am_b = am; // gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type. round(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); }); T b; to_float(false, am_b, b); adjusted_mantissa theor = to_extended_halfway(b); bigint theor_digits(theor.mantissa); int32_t theor_exp = theor.power2; // scale real digits and theor digits to be same power. int32_t pow2_exp = theor_exp - real_exp; uint32_t pow5_exp = uint32_t(-real_exp); if (pow5_exp != 0) { FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); } if (pow2_exp > 0) { FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); } else if (pow2_exp < 0) { FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); } // compare digits, and use it to director rounding int ord = real_digits.compare(theor_digits); adjusted_mantissa answer = am; round(answer, [ord](adjusted_mantissa& a, int32_t shift) { round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool { (void)_; // not needed, since we've done our comparison (void)__; // not needed, since we've done our comparison if (ord > 0) { return true; } else if (ord < 0) { return false; } else { return is_odd; } }); }); return answer; } // parse the significant digits as a big integer to unambiguously round the // the significant digits. here, we are trying to determine how to round // an extended float representation close to `b+h`, halfway between `b` // (the float rounded-down) and `b+u`, the next positive float. this // algorithm is always correct, and uses one of two approaches. when // the exponent is positive relative to the significant digits (such as // 1234), we create a big-integer representation, get the high 64-bits, // determine if any lower bits are truncated, and use that to direct // rounding. in case of a negative exponent relative to the significant // digits (such as 1.2345), we create a theoretical representation of // `b` as a big-integer type, scaled to the same binary exponent as // the actual digits. we then compare the big integer representations // of both, and use that to direct rounding. template inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept { // remove the invalid exponent bias am.power2 -= invalid_am_bias; int32_t sci_exp = scientific_exponent(num); size_t max_digits = binary_format::max_digits(); size_t digits = 0; bigint bigmant; parse_mantissa(bigmant, num, max_digits, digits); // can't underflow, since digits is at most max_digits. int32_t exponent = sci_exp + 1 - int32_t(digits); if (exponent >= 0) { return positive_digit_comp(bigmant, exponent); } else { return negative_digit_comp(bigmant, am, exponent); } } } // namespace fast_float #endif #ifndef FASTFLOAT_PARSE_NUMBER_H #define FASTFLOAT_PARSE_NUMBER_H #include #include #include #include namespace fast_float { namespace detail { /** * Special case +inf, -inf, nan, infinity, -infinity. * The case comparisons could be made much faster given that we know that the * strings a null-free and fixed. **/ template from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept { from_chars_result answer; answer.ptr = first; answer.ec = std::errc(); // be optimistic bool minusSign = false; if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here minusSign = true; ++first; } if (last - first >= 3) { if (fastfloat_strncasecmp(first, "nan", 3)) { answer.ptr = (first += 3); value = minusSign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). if(first != last && *first == '(') { for(const char* ptr = first + 1; ptr != last; ++ptr) { if (*ptr == ')') { answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) break; } else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_')) break; // forbidden char, not nan(n-char-seq-opt) } } return answer; } if (fastfloat_strncasecmp(first, "inf", 3)) { if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) { answer.ptr = first + 8; } else { answer.ptr = first + 3; } value = minusSign ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); return answer; } } answer.ec = std::errc::invalid_argument; return answer; } } // namespace detail template from_chars_result from_chars(const char *first, const char *last, T &value, chars_format fmt /*= chars_format::general*/) noexcept { return from_chars_advanced(first, last, value, parse_options{fmt}); } template from_chars_result from_chars_advanced(const char *first, const char *last, T &value, parse_options options) noexcept { static_assert (std::is_same::value || std::is_same::value, "only float and double are supported"); from_chars_result answer; if (first == last) { answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } parsed_number_string pns = parse_number_string(first, last, options); if (!pns.valid) { return detail::parse_infnan(first, last, value); } answer.ec = std::errc(); // be optimistic answer.ptr = pns.lastmatch; // Next is Clinger's fast path. if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path() && !pns.too_many_digits) { value = T(pns.mantissa); if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } else { value = value * binary_format::exact_power_of_ten(pns.exponent); } if (pns.negative) { value = -value; } return answer; } adjusted_mantissa am = compute_float>(pns.exponent, pns.mantissa); if(pns.too_many_digits && am.power2 >= 0) { if(am != compute_float>(pns.exponent, pns.mantissa + 1)) { am = compute_error>(pns.exponent, pns.mantissa); } } // If we called compute_float>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), // then we need to go the long way around again. This is very uncommon. if(am.power2 < 0) { am = digit_comp(pns, am); } to_float(pns.negative, am, value); return answer; } } // namespace fast_float #endif wk/src/internal/buffered-reader.hpp0000644000176200001440000002575314403667457017065 0ustar liggesusers #ifndef WK_BUFFERED_READER_H_INCLUDED #define WK_BUFFERED_READER_H_INCLUDED #include "fast_float/fast_float.h" #include #include #include class BufferedParserException: public std::runtime_error { public: BufferedParserException(std::string expected, std::string found, std::string context): std::runtime_error(makeError(expected, found, context)), expected(expected), found(found), context(context) {} std::string expected; std::string found; std::string context; static std::string makeError(std::string expected, std::string found, std::string context = "") { std::stringstream stream; stream << "Expected " << expected << " but found " << found << context; return stream.str().c_str(); } }; // The SimpleBufferSource is a wrapper around an in-memory buffer of characters. // The BufferedParser classes below template along an object with a fill_buffer() // method with the same signature as this one. class SimpleBufferSource { public: SimpleBufferSource(): str(nullptr), size(0), offset(0) {} void set_buffer(const char* str, int64_t size) { this->str = str; this->size = size; this->offset = 0; } int64_t fill_buffer(char* buffer, int64_t max_size) { int64_t copy_size = std::min(this->size - this->offset, max_size); if (copy_size > 0) { memcpy(buffer, this->str + this->offset, copy_size); this->offset += copy_size; return copy_size; } else { return 0; } } private: const char* str; int64_t size; int64_t offset; }; // The BufferedParser class provides the basic helpers needed to parse simple // text formats like well-known text. It is not intended to be the pinnacle // of speed or elegance, but does a good job at providing reasonable error // messages and has the important feature that it does not need the text // that it's parsing to be fully in-memory. The intended usage is to subclass // the BufferedParser for a particular format. template class BufferedParser { public: BufferedParser(): length(0), offset(0), source_offset(0), whitespace(" \r\n\t"), sep(" \r\n\t"), source(nullptr) {} void setSource(SimpleBufferSource* source) { this->source = source; this->offset = 0; this->length = 0; this->source_offset = 0; } const char* setWhitespace(const char* whitespace) { const char* previous_whitespace = this->whitespace; this->whitespace = whitespace; return previous_whitespace; } const char* setSeparators(const char* separators) { const char* previous_sep = this->sep; this->sep = separators; return previous_sep; } int64_t charsLeftInBuffer() { return this->length - this->offset; } bool checkBuffer(int n_chars) { int64_t chars_to_keep = this->charsLeftInBuffer(); if ((chars_to_keep - n_chars) >= 0) { return true; } if (n_chars >= buffer_length) { std::stringstream stream; stream << "a value with fewer than " << buffer_length << " characters"; throw BufferedParserException(stream.str(), "a longer value", ""); } if (this->source == nullptr) { return false; } if (chars_to_keep > 0) { memmove(this->str, this->str + this->offset, chars_to_keep); } int64_t new_chars = this->source->fill_buffer(this->str + chars_to_keep, buffer_length - chars_to_keep); if (new_chars == 0) { this->source = nullptr; } this->source_offset += new_chars; this->offset = 0; this->length = chars_to_keep + new_chars; return n_chars <= this->length; } bool finished() { return !(this->checkBuffer(1)); } void advance() { if (this->checkBuffer(1)) { this->offset++; } } // Returns the character at the cursor and advances the cursor by one char readChar() { char out = this->peekChar(); this->advance(); return out; } // Returns the character currently ahead of the cursor without advancing the cursor (skips whitespace) char peekChar() { this->skipWhitespace(); if (this->checkBuffer(1)) { return this->str[this->offset]; } else { return '\0'; } } // Returns true if the next character is one of `chars` bool is(char c) { return c == this->peekChar(); } // Returns true if the next character is one of `chars` bool isOneOf(const char* chars) { return strchr(chars, this->peekChar()) != nullptr; } // Returns true if the next character is most likely to be a number bool isNumber() { // complicated by nan and inf if (this->isOneOf("-nNiI.")) { std::string text = this->peekUntilSep(); double out; auto result = fast_float::from_chars(text.data(), text.data() + text.size(), out); return result.ec == std::errc(); } else { return this->isOneOf("-0123456789"); } } // Returns true if the next character is a letter bool isLetter() { char found = this->peekChar(); return (found >= 'a' && found <= 'z') || (found >= 'A' && found <= 'Z'); } std::string assertWord() { std::string text = this->peekUntilSep(); if (!this->isLetter()) { this->error("a word", quote(text)); } this->offset += text.size(); return text; } // Returns the integer currently ahead of the cursor, // throwing an exception if whatever is ahead of the // cursor cannot be parsed into an integer long assertInteger() { std::string text = this->peekUntilSep(); try { long out = std::stol(text); this->offset += text.size(); return out; } catch (std::invalid_argument& e) { this->error("an integer", quote(text)); } } // Returns the double currently ahead of the cursor, // throwing an exception if whatever is ahead of the // cursor cannot be parsed into a double. This will // accept "inf", "-inf", and "nan". double assertNumber() { std::string text = this->peekUntilSep(); double out; auto result = fast_float::from_chars(text.data(), text.data() + text.size(), out); if (result.ec != std::errc()) { this->error("a number", quote(text)); } else { this->offset += text.size(); return out; } } // Asserts that the character at the cursor is whitespace, and // returns a std::string of whitespace characters, advancing the // cursor to the end of the whitespace. void assertWhitespace() { if (!this->checkBuffer(1)) { this->error("whitespace", "end of input"); } char found = this->str[this->offset]; if (strchr(this->whitespace, found) == nullptr) { this->error("whitespace", quote(found)); } this->skipWhitespace(); } void assert_(char c) { char found = this->peekChar(); if (found != c) { this->error(quote(c), quote(found)); } this->advance(); } // Asserts the that the character at the cursor is one of `chars` // and advances the cursor by one (throwing an exception otherwise). char assertOneOf(const char* chars) { char found = this->peekChar(); if ((strlen(chars) > 0) && this->finished()) { this->error(expectedFromChars(chars), "end of input"); } else if (strchr(chars, found) == nullptr) { this->error(expectedFromChars(chars), quote(this->peekUntilSep())); } this->advance(); return found; } // Asserts that the cursor is at the end of the input void assertFinished() { this->assert_('\0'); } // Returns the text between the cursor and the next separator, // which is defined to be whitespace or the following characters: =;,() // advancing the cursor. If we are at the end of the string, this will // return std::string("") std::string readUntilSep() { this->skipWhitespace(); int64_t wordLen = peekUntil(this->sep); bool finished = this->finished(); if (wordLen == 0 && !finished) { wordLen = 1; } std::string out(this->str + this->offset, wordLen); this->offset += wordLen; return out; } // Returns the text between the cursor and the next separator without advancing the cursor. std::string peekUntilSep() { this->skipWhitespace(); int64_t wordLen = peekUntil(this->sep); return std::string(this->str + this->offset, wordLen); } // Advances the cursor past any whitespace, returning the number of characters skipped. int64_t skipWhitespace() { return this->skipChars(this->whitespace); } // Skips all of the characters in `chars`, returning the number of characters skipped. int64_t skipChars(const char* chars) { int64_t n_skipped = 0; bool found = false; while (!found && !this->finished()) { while (this->charsLeftInBuffer() > 0) { if (strchr(chars, this->str[this->offset])) { this->offset++; n_skipped++; } else { found = true; break; } } } return n_skipped; } // Returns the number of characters until one of `chars` is encountered, // which may be 0. int64_t peekUntil(const char* chars) { if (this->finished()) { return 0; } int64_t n_chars = -1; bool found = false; while (!found && this->checkBuffer(n_chars + 2)) { while ((this->offset + n_chars + 1) < this->length) { n_chars++; if (strchr(chars, this->str[this->offset + n_chars])) { found = true; break; } } } if (!found && (this->offset + n_chars + 1) == this->length) { n_chars++; } return n_chars; } [[ noreturn ]] void errorBefore(std::string expected, std::string found) { throw BufferedParserException(expected, quote(found), this->errorContext(this->offset - found.size())); } [[noreturn]] void error(std::string expected, std::string found) { std::stringstream stream; stream << found; throw BufferedParserException(expected, stream.str(), this->errorContext(this->offset)); } [[noreturn]] void error(std::string expected) { throw BufferedParserException(expected, quote(this->peekUntilSep()), this->errorContext(this->offset)); } std::string errorContext(int64_t pos) { std::stringstream stream; stream << " at byte " << (this->source_offset - this->length + pos); return stream.str(); } private: char str[buffer_length]; int64_t length; int64_t offset; int64_t source_offset; const char* whitespace; const char* sep; SimpleBufferSource* source; static std::string expectedFromChars(const char* chars) { int64_t nChars = strlen(chars); std::stringstream stream; for (int64_t i = 0; i < nChars; i++) { if (i > 0) { stream << " or "; } stream << quote(chars[i]); } return stream.str(); } static std::string quote(std::string input) { if (input.empty()) { return "end of input"; } else { std::stringstream stream; stream << "'" << input << "'"; return stream.str(); } } static std::string quote(char input) { if (input == '\0') { return "end of input"; } else { std::stringstream stream; stream << "'" << input << "'"; return stream.str(); } } }; #endif wk/src/internal/wk-v1-handler.hpp0000644000176200001440000002012514471771472016405 0ustar liggesusers #ifndef WK_V1_HANDLER_HPP_INCLUDED #define WK_V1_HANDLER_HPP_INCLUDED #define R_NO_REMAP #include "wk-v1.h" #include #include // This is an internal C++ class for instances where it's easier to use // something in the C++ (>=11) standard library. It is safe to throw // exceptions in handler methods (which are caught and converted to // an Rf_error()); it is safe to longjmp from handler methods provided // that the method has been written in such a way that nothing is // stack-allocated that has a non-trivial destructor. As noted below, // you can get around this by declaring an object with a non-trivial // destructor as a member of the class, which always gets deleted // by the external pointer finalizer. class WKVoidHandler { public: WKVoidHandler() { memset(this->internal_error_message, 0, 8192); } virtual ~WKVoidHandler() {} // # nocov start virtual void initialize(int* dirty) { if (*dirty) { Rf_error("Can't re-use this wk_handler"); } *dirty = 1; } virtual int vector_start(const wk_vector_meta_t* meta) { return WK_CONTINUE; } virtual int feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return WK_CONTINUE; } virtual int null_feature() { return WK_CONTINUE; } virtual int geometry_start(const wk_meta_t* meta, uint32_t part_id) { return WK_CONTINUE; } virtual int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { return WK_CONTINUE; } virtual int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id) { return WK_CONTINUE; } virtual int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { return WK_CONTINUE; } virtual int geometry_end(const wk_meta_t* meta, uint32_t part_id) { return WK_CONTINUE; } virtual int feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return WK_CONTINUE; } virtual SEXP vector_end(const wk_vector_meta_t* meta) { return R_NilValue; } virtual void deinitialize() { } virtual int error(const char* message) { Rf_error("%s", message); } // # nocov end char internal_error_message[8192]; }; // The previous version of this macro also handled cpp11::unwind_exception // throws; however, in this simplified version, we just handle regular // exceptions and require that users consider the longjmp-y-ness of // their handler methods. Because handlers are always cleaned up via // deinitialize/finalize, C++ handlers can declare anything with a // non-trivial destructor as a handler class member rather than // a stack-allocated variable. #define WK_V1_HANDLER_BEGIN_CPP11 \ cpp_handler->internal_error_message[0] = '\0'; \ try { #define WK_V1_HANDLER_END_CPP11(_error_return) \ } catch (std::exception & e) { \ strncpy(cpp_handler->internal_error_message, e.what(), 8192 - 1); \ } catch (...) { \ strncpy(cpp_handler->internal_error_message, "C++ error (unknown cause)", 8192 - 1); \ } \ if (cpp_handler->internal_error_message[0] != '\0') { \ Rf_error("%s", cpp_handler->internal_error_message); \ } \ return _error_return; template class WKHandlerFactory { public: static wk_handler_t* create(HandlerType* handler_data) { wk_handler_t* handler = wk_handler_create(); handler->handler_data = handler_data; handler->initialize = &initialize; handler->vector_start = &vector_start; handler->vector_end = &vector_end; handler->feature_start = &feature_start; handler->null_feature = &null_feature; handler->feature_end = &feature_end; handler->geometry_start = &geometry_start; handler->geometry_end = &geometry_end; handler->ring_start = &ring_start; handler->ring_end = &ring_end; handler->coord = &coord; handler->error = &error; handler->deinitialize = &deinitialize; handler->finalizer = &finalizer; return handler; } static SEXP create_xptr(HandlerType* handler_data, SEXP tag = R_NilValue, SEXP prot = R_NilValue) { wk_handler_t* handler = create(handler_data); return wk_handler_create_xptr(handler, tag, prot); } private: static void finalizer(void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; if (cpp_handler != NULL) { delete cpp_handler; } } static void initialize(int* dirty, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->initialize(dirty); WK_V1_HANDLER_END_CPP11() // # nocov } static int vector_start(const wk_vector_meta_t* meta, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->vector_start(meta); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->feature_start(meta, feat_id); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int null_feature(void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->null_feature(); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int geometry_start(const wk_meta_t* meta, uint32_t partId, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->geometry_start(meta, partId); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ringId, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->ring_start(meta, size, ringId); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->coord(meta, coord, coord_id); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ringId, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->ring_end(meta, size, ringId); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int geometry_end(const wk_meta_t* meta, uint32_t partId, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->geometry_end(meta, partId); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static int feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->feature_end(meta, feat_id); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } static SEXP vector_end(const wk_vector_meta_t* meta, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->vector_end(meta); WK_V1_HANDLER_END_CPP11(R_NilValue) // # nocov } static void deinitialize(void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->deinitialize(); WK_V1_HANDLER_END_CPP11() // # nocov } static int error(const char* message, void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; WK_V1_HANDLER_BEGIN_CPP11 return cpp_handler->error(message); WK_V1_HANDLER_END_CPP11(WK_ABORT) // # nocov } }; #endif wk/src/count-handler.c0000644000176200001440000001252014510132141014361 0ustar liggesusers #define R_NO_REMAP #include #include #include #include #include "wk-v1.h" typedef struct { SEXP result; R_xlen_t result_size; R_xlen_t feat_id; int n_geom; int n_ring; R_xlen_t n_coord; } count_handler_t; SEXP count_handler_alloc_result(R_xlen_t size) { const char* names[] = {"n_geom", "n_ring", "n_coord", ""}; SEXP result = PROTECT(Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(result, 0, Rf_allocVector(INTSXP, size)); SET_VECTOR_ELT(result, 1, Rf_allocVector(INTSXP, size)); SET_VECTOR_ELT(result, 2, Rf_allocVector(REALSXP, size)); UNPROTECT(1); return result; } SEXP count_handler_realloc_result(SEXP result, R_xlen_t new_size) { SEXP new_result = PROTECT(count_handler_alloc_result(new_size)); R_xlen_t size_cpy; if (Rf_xlength(VECTOR_ELT(result, 0)) < new_size) { size_cpy = Rf_xlength(VECTOR_ELT(result, 0)); } else { size_cpy = new_size; } memcpy(INTEGER(VECTOR_ELT(new_result, 0)), INTEGER(VECTOR_ELT(result, 0)), sizeof(int) * size_cpy); memcpy(INTEGER(VECTOR_ELT(new_result, 1)), INTEGER(VECTOR_ELT(result, 1)), sizeof(int) * size_cpy); memcpy(REAL(VECTOR_ELT(new_result, 2)), REAL(VECTOR_ELT(result, 2)), sizeof(double) * size_cpy); UNPROTECT(1); return new_result; } int count_handler_vector_start(const wk_vector_meta_t* meta, void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; if (data->result != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { data->result = PROTECT(count_handler_alloc_result(1024)); data->result_size = 1024; } else { data->result = PROTECT(count_handler_alloc_result(meta->size)); data->result_size = meta->size; } R_PreserveObject(data->result); UNPROTECT(1); data->feat_id = -1; return WK_CONTINUE; } int count_handler_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; data->n_coord = 0; data->n_geom = 0; data->n_ring = 0; data->feat_id++; return WK_CONTINUE; } int count_handler_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; data->n_geom++; return WK_CONTINUE; } int count_handler_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; data->n_ring++; return WK_CONTINUE; } int count_handler_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; data->n_coord++; return WK_CONTINUE; } int count_handler_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; if (data->feat_id >= data->result_size) { SEXP new_result = PROTECT(count_handler_realloc_result(data->result, data->result_size * 2 + 1)); R_ReleaseObject(data->result); data->result = new_result; R_PreserveObject(data->result); UNPROTECT(1); data->result_size = data->result_size * 2 + 1; } INTEGER(VECTOR_ELT(data->result, 0))[data->feat_id] = data->n_geom; INTEGER(VECTOR_ELT(data->result, 1))[data->feat_id] = data->n_ring; REAL(VECTOR_ELT(data->result, 2))[data->feat_id] = data->n_coord; return WK_CONTINUE; } SEXP count_handler_vector_end(const wk_vector_meta_t* meta, void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; R_xlen_t final_size = data->feat_id + 1; if (data->result_size != final_size) { SEXP new_result = PROTECT(count_handler_realloc_result(data->result, final_size)); R_ReleaseObject(data->result); data->result = R_NilValue; UNPROTECT(1); return new_result; } else { return data->result; } } void count_handler_deinitialize(void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; if (data->result != R_NilValue) { R_ReleaseObject(data->result); data->result = R_NilValue; } } void count_handler_finalize(void* handler_data) { count_handler_t* data = (count_handler_t*)handler_data; if (data != NULL) { free(data); } } SEXP wk_c_count_handler_new(void) { wk_handler_t* handler = wk_handler_create(); handler->vector_start = &count_handler_vector_start; handler->feature_start = &count_handler_feature_start; handler->geometry_start = &count_handler_geometry_start; handler->ring_start = &count_handler_ring_start; handler->coord = &count_handler_coord; handler->feature_end = &count_handler_feature_end; handler->vector_end = &count_handler_vector_end; handler->deinitialize = &count_handler_deinitialize; handler->finalizer = &count_handler_finalize; count_handler_t* data = (count_handler_t*)malloc(sizeof(count_handler_t)); if (data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } data->feat_id = -1; data->result = R_NilValue; handler->handler_data = data; SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue); return xptr; } wk/src/handle-wkb.c0000644000176200001440000002702314513020606013643 0ustar liggesusers #define R_NO_REMAP #include #include #include #include #include #include "altrep.h" #include "wk-v1.h" #define WK_DEFAULT_ERROR_CODE 0 #define WK_NO_ERROR_CODE -1 // IS_BIG_ENDIAN, IS_LITTLE_ENDIAN, bswap_32(), bswap_64() #include "port.h" #define EWKB_Z_BIT 0x80000000 #define EWKB_M_BIT 0x40000000 #define EWKB_SRID_BIT 0x20000000 typedef struct { wk_handler_t* handler; R_xlen_t feat_id; SEXP buffer_sexp; R_xlen_t buffer_sexp_i; #ifdef HAS_ALTREP_RAW unsigned char buffer[ALTREP_CHUNK_SIZE]; #else unsigned char* buffer; #endif size_t size; size_t offset; char swap_endian; int error_code; char error_buf[1024]; } wkb_reader_t; int wkb_read_geometry(wkb_reader_t* reader, uint32_t part_id); int wkb_read_endian(wkb_reader_t* reader, unsigned char* value); int wkb_read_geometry_type(wkb_reader_t* reader, wk_meta_t* meta); int wkb_read_uint(wkb_reader_t* reader, uint32_t* value); int wkb_read_point_coordinate(wkb_reader_t* reader, wk_meta_t* meta, uint32_t part_id, int n_dim); int wkb_read_coordinates(wkb_reader_t* reader, const wk_meta_t* meta, uint32_t n_coords, int n_dim); void wkb_read_set_errorf(wkb_reader_t* reader, const char* error_buf, ...); static inline int wkb_read_check_buffer(wkb_reader_t* reader, R_xlen_t bytes) { R_xlen_t bytes_to_keep = reader->size - reader->offset; if ((bytes_to_keep - bytes) >= 0) { return WK_CONTINUE; } #ifdef HAS_ALTREP_RAW // with ALTREP, we try to refill the buffer // We can do this without a memmove() by just issuing slightly overlapping // RAW_GET_REGION() calls, but there are some cases where this might cause // an altrep implementation to seek backwards in a file which is slow. if (bytes_to_keep > 0) { memmove(reader->buffer, reader->buffer + reader->offset, bytes_to_keep); } R_xlen_t new_bytes = RAW_GET_REGION(reader->buffer_sexp, reader->buffer_sexp_i, ALTREP_CHUNK_SIZE - bytes_to_keep, reader->buffer + bytes_to_keep); reader->offset = 0; reader->buffer_sexp_i += new_bytes; reader->size = bytes_to_keep + new_bytes; #else // without ALTREP, reader->size is the full length of the RAW() buffer, so we've // hit the end of it reader->size = 0; reader->buffer_sexp_i += reader->offset; #endif if (reader->size == 0) { wkb_read_set_errorf(reader, "Unexpected end of buffer at %d bytes", reader->buffer_sexp_i); return WK_ABORT_FEATURE; } return WK_CONTINUE; } #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result != WK_CONTINUE) return result #define HANDLE_CONTINUE_OR_BREAK(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) \ continue; \ else if (result == WK_ABORT) \ break int wkb_read_geometry(wkb_reader_t* reader, uint32_t part_id) { int result; unsigned char endian; HANDLE_OR_RETURN(wkb_read_endian(reader, &endian)); #ifdef IS_LITTLE_ENDIAN reader->swap_endian = endian != 1; #else reader->swap_endian = endian != 0; #endif wk_meta_t meta; WK_META_RESET(meta, WK_GEOMETRY); HANDLE_OR_RETURN(wkb_read_geometry_type(reader, &meta)); int n_dim = 2 + ((meta.flags & WK_FLAG_HAS_Z) != 0) + ((meta.flags & WK_FLAG_HAS_M) != 0); // POINT could be size 0, but we don't know until we've read the entire coordinate if (meta.geometry_type != WK_POINT) { HANDLE_OR_RETURN( reader->handler->geometry_start(&meta, part_id, reader->handler->handler_data)); } switch (meta.geometry_type) { case WK_POINT: HANDLE_OR_RETURN(wkb_read_point_coordinate(reader, &meta, part_id, n_dim)); break; case WK_LINESTRING: HANDLE_OR_RETURN(wkb_read_coordinates(reader, &meta, meta.size, n_dim)); break; case WK_POLYGON: for (uint32_t i = 0; i < meta.size; i++) { uint32_t n_coords; HANDLE_OR_RETURN(wkb_read_uint(reader, &n_coords)); HANDLE_OR_RETURN(reader->handler->ring_start(&meta, n_coords, i, reader->handler->handler_data)); HANDLE_OR_RETURN(wkb_read_coordinates(reader, &meta, n_coords, n_dim)); HANDLE_OR_RETURN( reader->handler->ring_end(&meta, n_coords, i, reader->handler->handler_data)); } break; case WK_MULTIPOINT: case WK_MULTILINESTRING: case WK_MULTIPOLYGON: case WK_GEOMETRYCOLLECTION: for (uint32_t i = 0; i < meta.size; i++) { HANDLE_OR_RETURN(wkb_read_geometry(reader, i)); } break; default: wkb_read_set_errorf(reader, "Unrecognized geometry type code '%d'", meta.geometry_type); return WK_ABORT_FEATURE; } return reader->handler->geometry_end(&meta, part_id, reader->handler->handler_data); } int wkb_read_endian(wkb_reader_t* reader, unsigned char* value) { int result; HANDLE_OR_RETURN(wkb_read_check_buffer(reader, sizeof(unsigned char))); memcpy(value, reader->buffer + reader->offset, sizeof(unsigned char)); reader->offset += sizeof(unsigned char); return WK_CONTINUE; } int wkb_read_uint(wkb_reader_t* reader, uint32_t* value) { int result; HANDLE_OR_RETURN(wkb_read_check_buffer(reader, sizeof(uint32_t))); if (reader->swap_endian) { uint32_t swappable; memcpy(&swappable, reader->buffer + reader->offset, sizeof(uint32_t)); reader->offset += sizeof(uint32_t); *value = bswap_32(swappable); } else { memcpy(value, reader->buffer + reader->offset, sizeof(uint32_t)); reader->offset += sizeof(uint32_t); } return WK_CONTINUE; } int wkb_read_geometry_type(wkb_reader_t* reader, wk_meta_t* meta) { int result; uint32_t geometry_type; HANDLE_OR_RETURN(wkb_read_uint(reader, &geometry_type)); if (geometry_type & EWKB_Z_BIT) { meta->flags |= WK_FLAG_HAS_Z; } if (geometry_type & EWKB_M_BIT) { meta->flags |= WK_FLAG_HAS_M; } if (geometry_type & EWKB_SRID_BIT) { HANDLE_OR_RETURN(wkb_read_uint(reader, &(meta->srid))); } geometry_type = geometry_type & 0x0000ffff; if (geometry_type >= 3000) { meta->geometry_type = geometry_type - 3000; meta->flags |= WK_FLAG_HAS_Z; meta->flags |= WK_FLAG_HAS_M; } else if (geometry_type >= 2000) { meta->geometry_type = geometry_type - 2000; meta->flags |= WK_FLAG_HAS_M; } else if (geometry_type >= 1000) { meta->geometry_type = geometry_type - 1000; meta->flags |= WK_FLAG_HAS_Z; } else { meta->geometry_type = geometry_type; } if (meta->geometry_type == WK_POINT) { meta->size = 1; } else { HANDLE_OR_RETURN(wkb_read_uint(reader, &(meta->size))); } return WK_CONTINUE; } int wkb_read_point_coordinate(wkb_reader_t* reader, wk_meta_t* meta, uint32_t part_id, int n_dim) { double coord[4]; int result; if (reader->swap_endian) { uint64_t swappable, swapped; HANDLE_OR_RETURN(wkb_read_check_buffer(reader, sizeof(uint64_t) * n_dim)); for (int j = 0; j < n_dim; j++) { memcpy(&swappable, reader->buffer + reader->offset, sizeof(uint64_t)); reader->offset += sizeof(double); swapped = bswap_64(swappable); memcpy(coord + j, &swapped, sizeof(double)); } } else { uint64_t swappable; HANDLE_OR_RETURN(wkb_read_check_buffer(reader, sizeof(uint64_t) * n_dim)); for (int j = 0; j < n_dim; j++) { memcpy(&swappable, reader->buffer + reader->offset, sizeof(uint64_t)); reader->offset += sizeof(double); memcpy(coord + j, &swappable, sizeof(double)); } } // Emit non-empty at first indication that we have a finite coord for (int j = 0; j < n_dim; j++) { if (!ISNA(coord[j]) && !ISNAN(coord[j])) { HANDLE_OR_RETURN( reader->handler->geometry_start(meta, part_id, reader->handler->handler_data)); HANDLE_OR_RETURN( reader->handler->coord(meta, coord, 0, reader->handler->handler_data)); return WK_CONTINUE; } } // Otherwise, emit EMPTY meta->size = 0; HANDLE_OR_RETURN( reader->handler->geometry_start(meta, part_id, reader->handler->handler_data)); return WK_CONTINUE; } int wkb_read_coordinates(wkb_reader_t* reader, const wk_meta_t* meta, uint32_t n_coords, int n_dim) { double coord[4]; int result; if (reader->swap_endian) { uint64_t swappable, swapped; for (uint32_t i = 0; i < n_coords; i++) { HANDLE_OR_RETURN(wkb_read_check_buffer(reader, sizeof(uint64_t) * n_dim)); for (int j = 0; j < n_dim; j++) { memcpy(&swappable, reader->buffer + reader->offset, sizeof(uint64_t)); reader->offset += sizeof(double); swapped = bswap_64(swappable); memcpy(coord + j, &swapped, sizeof(double)); } HANDLE_OR_RETURN( reader->handler->coord(meta, coord, i, reader->handler->handler_data)); } } else { // seems to be slightly faster than memcpy(coord, ..., coord_size) uint64_t swappable; for (uint32_t i = 0; i < n_coords; i++) { HANDLE_OR_RETURN(wkb_read_check_buffer(reader, sizeof(uint64_t) * n_dim)); for (int j = 0; j < n_dim; j++) { memcpy(&swappable, reader->buffer + reader->offset, sizeof(uint64_t)); reader->offset += sizeof(double); memcpy(coord + j, &swappable, sizeof(double)); } HANDLE_OR_RETURN( reader->handler->coord(meta, coord, i, reader->handler->handler_data)); } } return WK_CONTINUE; } void wkb_read_set_errorf(wkb_reader_t* reader, const char* error_buf, ...) { reader->error_code = WK_DEFAULT_ERROR_CODE; va_list args; va_start(args, error_buf); vsnprintf(reader->error_buf, 1024, error_buf, args); va_end(args); } SEXP wkb_read_wkb(SEXP data, wk_handler_t* handler) { R_xlen_t n_features = Rf_xlength(data); wk_vector_meta_t vector_meta; WK_VECTOR_META_RESET(vector_meta, WK_GEOMETRY); vector_meta.size = n_features; vector_meta.flags |= WK_FLAG_DIMS_UNKNOWN; if (handler->vector_start(&vector_meta, handler->handler_data) == WK_CONTINUE) { int result; SEXP item; wkb_reader_t reader; reader.handler = handler; memset(reader.error_buf, 0, 1024); for (R_xlen_t i = 0; i < n_features; i++) { // each feature could be huge, so check frequently if (((i + 1) % 1000) == 0) R_CheckUserInterrupt(); reader.feat_id = i; item = VECTOR_ELT(data, i); HANDLE_CONTINUE_OR_BREAK( handler->feature_start(&vector_meta, i, handler->handler_data)); if (item == R_NilValue) { HANDLE_CONTINUE_OR_BREAK(handler->null_feature(handler->handler_data)); } else { reader.buffer_sexp = item; reader.buffer_sexp_i = 0; reader.offset = 0; #ifdef HAS_ALTREP_RAW reader.size = 0; #else reader.size = Rf_xlength(item); reader.buffer = RAW(item); #endif reader.error_code = WK_NO_ERROR_CODE; reader.error_buf[0] = '\0'; result = wkb_read_geometry(&reader, WK_PART_ID_NONE); if (result == WK_ABORT_FEATURE && reader.error_code != WK_NO_ERROR_CODE) { result = handler->error(reader.error_buf, handler->handler_data); } if (result == WK_ABORT_FEATURE) { continue; } else if (result == WK_ABORT) { break; } } if (handler->feature_end(&vector_meta, i, handler->handler_data) == WK_ABORT) { break; } } } SEXP result = PROTECT(handler->vector_end(&vector_meta, handler->handler_data)); UNPROTECT(1); return result; } SEXP wk_c_read_wkb(SEXP data, SEXP handler_xptr) { return wk_handler_run_xptr(&wkb_read_wkb, data, handler_xptr); } wk/src/trans-explicit.c0000644000176200001440000000346314510132141014572 0ustar liggesusers#define R_NO_REMAP #include #include #include typedef struct { double* xyzm[4]; R_xlen_t i; R_xlen_t n; } wk_trans_explicit_t; int wk_trans_explicit_trans(R_xlen_t feature_id, const double* xyzm_in, double* xyzm_out, void* trans_data) { wk_trans_explicit_t* data = (wk_trans_explicit_t*)trans_data; data->i++; R_xlen_t set_id = data->i % data->n; double set; for (int i = 0; i < 4; i++) { set = data->xyzm[i][set_id]; if (ISNA(set)) { xyzm_out[i] = xyzm_in[i]; } else { xyzm_out[i] = set; } } return WK_CONTINUE; } void wk_trans_explicit_finalize(void* trans_data) { free(trans_data); } SEXP wk_c_trans_explicit_new(SEXP xy, SEXP use_z, SEXP use_m) { if (Rf_xlength(xy) != 4 || TYPEOF(xy) != VECSXP) { Rf_error("`xy` must be an xyzm() object"); // # nocov } // prepare data for C struct / validate args int use_z_int = LOGICAL(use_z)[0]; int use_m_int = LOGICAL(use_m)[0]; R_xlen_t n = Rf_xlength(VECTOR_ELT(xy, 0)); double* xyzm[4]; for (int i = 0; i < 4; i++) { xyzm[i] = REAL(VECTOR_ELT(xy, i)); } // create the wk_trans object wk_trans_t* trans = wk_trans_create(); trans->trans = &wk_trans_explicit_trans; trans->finalizer = &wk_trans_explicit_finalize; wk_trans_explicit_t* data = (wk_trans_explicit_t*)malloc(sizeof(wk_trans_explicit_t)); if (data == NULL) { free(trans); // # nocov Rf_error("Failed to alloc wk_trans_explicit_t"); // # nocov } trans->use_z = use_z_int; trans->use_m = use_m_int; memcpy(data->xyzm, xyzm, 4 * sizeof(void*)); data->i = -1; data->n = n; trans->trans_data = data; // keep the xy as a tag because we need the pointers to stay valid return wk_trans_create_xptr(trans, xy, R_NilValue); } wk/src/make-polygon-filter.c0000644000176200001440000003065114510132141015510 0ustar liggesusers#define R_NO_REMAP #include #include #include "altrep.h" #include "wk-v1.h" #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) { \ Rf_error("wk_polygon_filter() does not support WK_ABORT_FEATURE"); \ } \ if (result != WK_CONTINUE) return result typedef struct { wk_handler_t* next; R_xlen_t feature_id; SEXP feature_id_sexp; SEXP ring_id_sexp; #ifndef HAS_ALTREP int* feature_id_spec; int* ring_id_spec; #endif R_xlen_t n_feature_id_spec; R_xlen_t n_ring_id_spec; int last_feature_id_spec; int last_ring_id_spec; int is_new_feature; int is_new_ring; R_xlen_t feature_id_out; R_xlen_t ring_id; uint32_t coord_id; double first_coord[4]; double last_coord[4]; int last_coord_size; wk_meta_t meta; wk_vector_meta_t vector_meta; } polygon_filter_t; static inline int wk_polygon_start(polygon_filter_t* polygon_filter) { int result; polygon_filter->feature_id_out++; HANDLE_OR_RETURN(polygon_filter->next->feature_start( &(polygon_filter->vector_meta), polygon_filter->feature_id_out, polygon_filter->next->handler_data)); HANDLE_OR_RETURN(polygon_filter->next->geometry_start( &(polygon_filter->meta), WK_PART_ID_NONE, polygon_filter->next->handler_data)); polygon_filter->ring_id = -1; return WK_CONTINUE; } static inline int wk_ring_start(polygon_filter_t* polygon_filter) { int result; // keep a copy of the first coordinate so that we can check for a closed loop memcpy(polygon_filter->first_coord, polygon_filter->last_coord, 4 * sizeof(double)); polygon_filter->ring_id++; HANDLE_OR_RETURN(polygon_filter->next->ring_start( &(polygon_filter->meta), WK_SIZE_UNKNOWN, polygon_filter->ring_id, polygon_filter->next->handler_data)); polygon_filter->coord_id = 0; return WK_CONTINUE; } static inline int wk_ring_end(polygon_filter_t* polygon_filter) { int result; // close the loop if necessary for (int i = 0; i < polygon_filter->last_coord_size; i++) { if (polygon_filter->last_coord[i] != polygon_filter->first_coord[i]) { HANDLE_OR_RETURN(polygon_filter->next->coord( &(polygon_filter->meta), polygon_filter->first_coord, polygon_filter->coord_id, polygon_filter->next->handler_data)); break; } } HANDLE_OR_RETURN(polygon_filter->next->ring_end( &(polygon_filter->meta), WK_SIZE_UNKNOWN, polygon_filter->ring_id, polygon_filter->next->handler_data)); return WK_CONTINUE; } static inline int wk_polygon_end(polygon_filter_t* polygon_filter) { int result; HANDLE_OR_RETURN(polygon_filter->next->geometry_end( &(polygon_filter->meta), WK_PART_ID_NONE, polygon_filter->next->handler_data)); HANDLE_OR_RETURN(polygon_filter->next->feature_end(&(polygon_filter->vector_meta), polygon_filter->feature_id_out, polygon_filter->next->handler_data)); return WK_CONTINUE; } void wk_polygon_filter_initialize(int* dirty, void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; *dirty = 1; polygon_filter->next->initialize(&polygon_filter->next->dirty, polygon_filter->next->handler_data); } int wk_polygon_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; polygon_filter->feature_id = -1; polygon_filter->feature_id_out = -1; memcpy(&(polygon_filter->vector_meta), meta, sizeof(wk_vector_meta_t)); polygon_filter->vector_meta.geometry_type = WK_POLYGON; polygon_filter->vector_meta.size = WK_VECTOR_SIZE_UNKNOWN; WK_META_RESET(polygon_filter->meta, WK_POLYGON); return polygon_filter->next->vector_start(&(polygon_filter->vector_meta), polygon_filter->next->handler_data); } int wk_polygon_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; polygon_filter->feature_id++; R_xlen_t spec_i = polygon_filter->feature_id % polygon_filter->n_feature_id_spec; #ifdef HAS_ALTREP int feature_id_spec = INTEGER_ELT(polygon_filter->feature_id_sexp, spec_i); #else int feature_id_spec = polygon_filter->feature_id_spec[spec_i]; #endif int feature_id_spec_changed = feature_id_spec != polygon_filter->last_feature_id_spec; polygon_filter->last_feature_id_spec = feature_id_spec; spec_i = polygon_filter->feature_id % polygon_filter->n_ring_id_spec; #ifdef HAS_ALTREP int ring_id_spec = INTEGER_ELT(polygon_filter->ring_id_sexp, spec_i); #else int ring_id_spec = polygon_filter->ring_id_spec[spec_i]; #endif int ring_id_spec_changed = ring_id_spec != polygon_filter->last_ring_id_spec; polygon_filter->last_ring_id_spec = ring_id_spec; polygon_filter->is_new_feature = feature_id_spec_changed || (polygon_filter->feature_id == 0); polygon_filter->is_new_ring = polygon_filter->is_new_feature || ring_id_spec_changed; return WK_CONTINUE; } int wk_polygon_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; int result; // maybe need to close the ring before starting a new feature/ring if (polygon_filter->is_new_ring && polygon_filter->feature_id > 0) { HANDLE_OR_RETURN(wk_ring_end(polygon_filter)); } // We always need to keep a copy of the last coordinate because we // need to check for closed rings and the ring end method gets called // at the *next* coordinate where there's a new feature. polygon_filter->last_coord_size = 2 + ((meta->flags & WK_FLAG_HAS_Z) != 0) + ((meta->flags & WK_FLAG_HAS_M) != 0); memset(polygon_filter->last_coord, 0, 4 * sizeof(double)); memcpy(polygon_filter->last_coord, coord, polygon_filter->last_coord_size * sizeof(double)); if (polygon_filter->is_new_feature) { if (polygon_filter->feature_id > 0) { HANDLE_OR_RETURN(wk_polygon_end(polygon_filter)); } polygon_filter->meta.flags = meta->flags; polygon_filter->meta.flags &= ~WK_FLAG_HAS_BOUNDS; polygon_filter->meta.precision = meta->precision; polygon_filter->meta.srid = meta->srid; HANDLE_OR_RETURN(wk_polygon_start(polygon_filter)); polygon_filter->is_new_feature = 0; } else { // check dimensions against current meta because handlers make the assumption // that all coordinates passed have the same dimension for a single geometry int diff_z = (polygon_filter->meta.flags & WK_FLAG_HAS_Z) ^ (meta->flags & WK_FLAG_HAS_Z); int diff_m = (polygon_filter->meta.flags & WK_FLAG_HAS_M) ^ (meta->flags & WK_FLAG_HAS_M); int diff_srid = polygon_filter->meta.srid != meta->srid; if (diff_z || diff_m || diff_srid) { Rf_error("Can't create polygon using geometries with differing dimensions or SRID"); } } if (polygon_filter->is_new_ring) { HANDLE_OR_RETURN(wk_ring_start(polygon_filter)); polygon_filter->is_new_ring = 0; } HANDLE_OR_RETURN(polygon_filter->next->coord(&(polygon_filter->meta), coord, polygon_filter->coord_id, polygon_filter->next->handler_data)); polygon_filter->coord_id++; return WK_CONTINUE; } SEXP wk_polygon_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; // if there weren't any features we need to start one int result = WK_CONTINUE; if (polygon_filter->feature_id_out == -1) { polygon_filter->meta.size = 0; result = wk_polygon_start(polygon_filter); } if (result != WK_ABORT) { if (polygon_filter->ring_id >= 0) { result = wk_ring_end(polygon_filter); } if (result != WK_ABORT) { wk_polygon_end(polygon_filter); } } return polygon_filter->next->vector_end(&(polygon_filter->vector_meta), polygon_filter->next->handler_data); } int wk_polygon_filter_feature_null(void* handler_data) { return WK_CONTINUE; } int wk_polygon_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { return WK_CONTINUE; } int wk_polygon_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { return WK_CONTINUE; } int wk_polygon_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { return WK_CONTINUE; } int wk_polygon_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { return WK_CONTINUE; } int wk_polygon_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { return WK_CONTINUE; } int wk_polygon_filter_error(const char* message, void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; int result; HANDLE_OR_RETURN( polygon_filter->next->error(message, polygon_filter->next->handler_data)); return WK_CONTINUE; } void wk_polygon_filter_deinitialize(void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; polygon_filter->next->deinitialize(polygon_filter->next->handler_data); } void wk_polygon_filter_finalize(void* handler_data) { polygon_filter_t* polygon_filter = (polygon_filter_t*)handler_data; if (polygon_filter != NULL) { free(polygon_filter); } } SEXP wk_c_polygon_filter_new(SEXP handler_xptr, SEXP feature_id, SEXP ring_id) { #ifndef HAS_ALTREP int* feature_id_spec = INTEGER(feature_id); int* ring_id_spec = INTEGER(ring_id); #endif wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_polygon_filter_initialize; handler->vector_start = &wk_polygon_filter_vector_start; handler->vector_end = &wk_polygon_filter_vector_end; handler->feature_start = &wk_polygon_filter_feature_start; handler->null_feature = &wk_polygon_filter_feature_null; handler->feature_end = &wk_polygon_filter_feature_end; handler->geometry_start = &wk_polygon_filter_geometry_start; handler->geometry_end = &wk_polygon_filter_geometry_end; handler->ring_start = &wk_polygon_filter_ring_start; handler->ring_end = &wk_polygon_filter_ring_end; handler->coord = &wk_polygon_filter_coord; handler->error = &wk_polygon_filter_error; handler->deinitialize = &wk_polygon_filter_deinitialize; handler->finalizer = &wk_polygon_filter_finalize; polygon_filter_t* polygon_filter = (polygon_filter_t*)malloc(sizeof(polygon_filter_t)); if (polygon_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } polygon_filter->next = (wk_handler_t*)R_ExternalPtrAddr(handler_xptr); if (polygon_filter->next->api_version != 1) { wk_handler_destroy(handler); // # nocov free(polygon_filter); // # nocov Rf_error("Invalid API version in polygon_filter"); // # nocov } polygon_filter->coord_id = 0; polygon_filter->ring_id = 0; polygon_filter->feature_id = -1; polygon_filter->feature_id_out = 0; polygon_filter->feature_id_sexp = feature_id; polygon_filter->ring_id_sexp = ring_id; #ifndef HAS_ALTREP polygon_filter->feature_id_spec = feature_id_spec; polygon_filter->ring_id_spec = ring_id_spec; #endif polygon_filter->n_feature_id_spec = Rf_xlength(feature_id); polygon_filter->n_ring_id_spec = Rf_xlength(ring_id); polygon_filter->is_new_feature = 0; polygon_filter->is_new_ring = 0; polygon_filter->last_feature_id_spec = NA_INTEGER; polygon_filter->last_ring_id_spec = NA_INTEGER; handler->handler_data = polygon_filter; // We need the external pointer SEXP, the feature_id SEXP, // and the ring_id SEEXP to be valid for the lifetime of this object SEXP id_spec = PROTECT(Rf_allocVector(VECSXP, 2)); SET_VECTOR_ELT(id_spec, 0, feature_id); SET_VECTOR_ELT(id_spec, 1, ring_id); SEXP filter_xptr = PROTECT(wk_handler_create_xptr(handler, handler_xptr, id_spec)); UNPROTECT(2); return filter_xptr; } wk/src/init.c0000644000176200001440000001076114510132141012566 0ustar liggesusers #define R_NO_REMAP #include #include #include "wk-v1.h" /* generated by data-raw/make_callentries.R */ SEXP wk_c_bbox_handler_new(void); SEXP wk_c_envelope_handler_new(void); SEXP wk_c_count_handler_new(void); SEXP wk_c_debug_filter_new(SEXP handler_xptr); SEXP wk_c_flatten_filter_new(SEXP handler_xptr, SEXP max_depth, SEXP add_details); SEXP wk_c_read_crc(SEXP data, SEXP handler_xptr, SEXP n_segs); SEXP wk_c_read_rct(SEXP data, SEXP handlerXptr); SEXP wk_c_read_sfc_impl(SEXP data, wk_handler_t* handler); SEXP wk_c_read_sfc(SEXP data, SEXP handler_xptr); SEXP wk_c_read_wkb(SEXP data, SEXP handler_xptr); SEXP wk_c_read_wkt(SEXP data, SEXP handler_xptr); SEXP wk_c_read_xy(SEXP data, SEXP handlerXptr); SEXP wk_c_identity_filter_new(SEXP handler_xptr); SEXP wk_c_collection_filter_new(SEXP handler_xptr, SEXP geometry_type, SEXP feature_id); SEXP wk_c_linestring_filter_new(SEXP handler_xptr, SEXP feature_id); SEXP wk_c_polygon_filter_new(SEXP handler_xptr, SEXP feature_id, SEXP ring_id); SEXP wk_c_meta_handler_new(void); SEXP wk_c_vector_meta_handler_new(void); SEXP wk_c_orient_filter_new(SEXP handler_xptr, SEXP direction); SEXP wk_c_problems_handler_new(void); SEXP wk_c_sfc_writer_new(SEXP promote_multi_sexp); SEXP wk_c_trans_affine_new(SEXP trans_matrix); SEXP wk_c_trans_affine_as_matrix(SEXP trans_xptr); SEXP wk_c_trans_explicit_new(SEXP xy, SEXP use_z, SEXP use_m); SEXP wk_c_trans_set_new(SEXP xy, SEXP use_z, SEXP use_m); SEXP wk_c_trans_filter_new(SEXP handler_xptr, SEXP trans_xptr); SEXP wk_c_wkb_is_na(SEXP geom); SEXP wk_c_wkb_is_raw_or_null(SEXP geom); SEXP wk_c_wkb_to_hex(const SEXP geom); SEXP wk_c_vertex_filter_new(SEXP handler_xptr, SEXP add_details); SEXP wk_c_handler_void_new(void); SEXP wk_c_handler_addr(SEXP xptr); SEXP wk_c_wkb_writer_new(SEXP buffer_size_sexp, SEXP endian_sexp); SEXP wk_c_wkt_writer(SEXP precision_sexp, SEXP trim_sexp); SEXP wk_c_wkt_formatter(SEXP precision_sexp, SEXP trim_sexp, SEXP max_coords_sexp); SEXP wk_c_xy_writer_new(void); static const R_CallMethodDef CallEntries[] = { {"wk_c_bbox_handler_new", (DL_FUNC)&wk_c_bbox_handler_new, 0}, {"wk_c_envelope_handler_new", (DL_FUNC)&wk_c_envelope_handler_new, 0}, {"wk_c_count_handler_new", (DL_FUNC)&wk_c_count_handler_new, 0}, {"wk_c_debug_filter_new", (DL_FUNC)&wk_c_debug_filter_new, 1}, {"wk_c_flatten_filter_new", (DL_FUNC)&wk_c_flatten_filter_new, 3}, {"wk_c_read_crc", (DL_FUNC)&wk_c_read_crc, 3}, {"wk_c_read_rct", (DL_FUNC)&wk_c_read_rct, 2}, {"wk_c_read_sfc_impl", (DL_FUNC)&wk_c_read_sfc_impl, 2}, {"wk_c_read_sfc", (DL_FUNC)&wk_c_read_sfc, 2}, {"wk_c_read_wkb", (DL_FUNC)&wk_c_read_wkb, 2}, {"wk_c_read_wkt", (DL_FUNC)&wk_c_read_wkt, 2}, {"wk_c_read_xy", (DL_FUNC)&wk_c_read_xy, 2}, {"wk_c_identity_filter_new", (DL_FUNC)&wk_c_identity_filter_new, 1}, {"wk_c_collection_filter_new", (DL_FUNC)&wk_c_collection_filter_new, 3}, {"wk_c_linestring_filter_new", (DL_FUNC)&wk_c_linestring_filter_new, 2}, {"wk_c_polygon_filter_new", (DL_FUNC)&wk_c_polygon_filter_new, 3}, {"wk_c_meta_handler_new", (DL_FUNC)&wk_c_meta_handler_new, 0}, {"wk_c_vector_meta_handler_new", (DL_FUNC)&wk_c_vector_meta_handler_new, 0}, {"wk_c_orient_filter_new", (DL_FUNC)&wk_c_orient_filter_new, 2}, {"wk_c_problems_handler_new", (DL_FUNC)&wk_c_problems_handler_new, 0}, {"wk_c_sfc_writer_new", (DL_FUNC)&wk_c_sfc_writer_new, 1}, {"wk_c_trans_affine_new", (DL_FUNC)&wk_c_trans_affine_new, 1}, {"wk_c_trans_affine_as_matrix", (DL_FUNC)&wk_c_trans_affine_as_matrix, 1}, {"wk_c_trans_explicit_new", (DL_FUNC)&wk_c_trans_explicit_new, 3}, {"wk_c_trans_set_new", (DL_FUNC)&wk_c_trans_set_new, 3}, {"wk_c_trans_filter_new", (DL_FUNC)&wk_c_trans_filter_new, 2}, {"wk_c_wkb_is_na", (DL_FUNC)&wk_c_wkb_is_na, 1}, {"wk_c_wkb_is_raw_or_null", (DL_FUNC)&wk_c_wkb_is_raw_or_null, 1}, {"wk_c_wkb_to_hex", (DL_FUNC)&wk_c_wkb_to_hex, 1}, {"wk_c_vertex_filter_new", (DL_FUNC)&wk_c_vertex_filter_new, 2}, {"wk_c_handler_void_new", (DL_FUNC)&wk_c_handler_void_new, 0}, {"wk_c_handler_addr", (DL_FUNC)&wk_c_handler_addr, 1}, {"wk_c_wkb_writer_new", (DL_FUNC)&wk_c_wkb_writer_new, 2}, {"wk_c_wkt_writer", (DL_FUNC)&wk_c_wkt_writer, 2}, {"wk_c_wkt_formatter", (DL_FUNC)&wk_c_wkt_formatter, 3}, {"wk_c_xy_writer_new", (DL_FUNC)&wk_c_xy_writer_new, 0}, {NULL, NULL, 0}}; /* end generated by data-raw/make_callentries.R */ void R_init_wk(DllInfo* dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } wk/src/wkb-writer.c0000644000176200001440000002741614531511633013737 0ustar liggesusers #define R_NO_REMAP #include #include #include #include #include "wk-v1.h" // IS_BIG_ENDIAN, IS_LITTLE_ENDIAN, bswap_32(), bswap_64() #include "port.h" #define EWKB_Z_BIT 0x80000000 #define EWKB_M_BIT 0x40000000 #define EWKB_SRID_BIT 0x20000000 #define WKB_MAX_RECURSION_DEPTH 32 typedef struct { SEXP result; int swap_endian; unsigned char endian; unsigned char* buffer; size_t size; size_t offset; size_t current_size_offset[WKB_MAX_RECURSION_DEPTH + 3]; uint32_t current_size[WKB_MAX_RECURSION_DEPTH + 3]; size_t recursion_level; R_xlen_t feat_id; } wkb_writer_t; static inline unsigned char wkb_writer_platform_endian(void) { #ifdef IS_LITTLE_ENDIAN return 1; #else return 0; #endif } static inline uint32_t wkb_writer_encode_type(const wk_meta_t* meta, int recursion_level) { uint32_t out = meta->geometry_type; if (meta->flags & WK_FLAG_HAS_Z) out |= EWKB_Z_BIT; if (meta->flags & WK_FLAG_HAS_M) out |= EWKB_M_BIT; if (recursion_level == 0) { if (meta->srid != WK_SRID_NONE) out |= EWKB_SRID_BIT; } return out; } wkb_writer_t* wkb_writer_new(size_t buffer_size, unsigned char endian) { unsigned char* buffer = malloc(buffer_size); if (buffer == NULL) { return NULL; // # nocov } wkb_writer_t* writer = (wkb_writer_t*)malloc(sizeof(wkb_writer_t)); if (writer == NULL) { free(buffer); // # nocov return NULL; // # nocov } writer->endian = endian; #ifdef IS_LITTLE_ENDIAN writer->swap_endian = endian != 1; #else writer->swap_endian = endian != 0; #endif writer->result = R_NilValue; writer->buffer = buffer; writer->size = buffer_size; writer->offset = 0; writer->recursion_level = 0; writer->feat_id = 0; return writer; } static inline void wkb_writer_ensure_space(wkb_writer_t* writer, size_t item) { if ((writer->offset + item) >= writer->size) { unsigned char* new_buffer = realloc(writer->buffer, writer->size * 2); if (new_buffer == NULL) { Rf_error("Can't reallocate buffer of size %ld", (long)writer->size * 2); // # nocov } writer->buffer = new_buffer; writer->size = writer->size * 2; } } static inline void wkb_write_uchar(wkb_writer_t* writer, const unsigned char value) { wkb_writer_ensure_space(writer, sizeof(unsigned char)); memcpy(writer->buffer + writer->offset, &value, sizeof(unsigned char)); writer->offset += sizeof(unsigned char); } static inline void wkb_write_uint_offset(wkb_writer_t* writer, const uint32_t value, size_t offset) { if (writer->swap_endian) { uint32_t swapped = bswap_32(value); memcpy(writer->buffer + offset, &swapped, sizeof(uint32_t)); } else { memcpy(writer->buffer + offset, &value, sizeof(uint32_t)); } } static inline void wkb_write_uint(wkb_writer_t* writer, const uint32_t value) { wkb_writer_ensure_space(writer, sizeof(uint32_t)); if (writer->swap_endian) { uint32_t swapped = bswap_32(value); memcpy(writer->buffer + writer->offset, &swapped, sizeof(uint32_t)); } else { memcpy(writer->buffer + writer->offset, &value, sizeof(uint32_t)); } writer->offset += sizeof(uint32_t); } static inline void wkb_write_doubles(wkb_writer_t* writer, const double* value, uint32_t n) { wkb_writer_ensure_space(writer, sizeof(double) * n); if (writer->swap_endian) { uint64_t swappable, swapped; for (uint32_t i = 0; i < n; i++) { memcpy(&swappable, value + i, sizeof(double)); swapped = bswap_64(swappable); memcpy(writer->buffer + writer->offset, &swapped, sizeof(double)); writer->offset += sizeof(double); } } else { for (uint32_t i = 0; i < n; i++) { memcpy(writer->buffer + writer->offset, value + i, sizeof(double)); writer->offset += sizeof(double); } } } static inline void wkb_writer_result_append(wkb_writer_t* writer, SEXP value) { R_xlen_t current_size = Rf_xlength(writer->result); if (writer->feat_id >= current_size) { SEXP new_result = PROTECT(Rf_allocVector(VECSXP, current_size * 2 + 1)); for (R_xlen_t i = 0; i < current_size; i++) { SET_VECTOR_ELT(new_result, i, VECTOR_ELT(writer->result, i)); } R_ReleaseObject(writer->result); writer->result = new_result; R_PreserveObject(writer->result); UNPROTECT(1); } SET_VECTOR_ELT(writer->result, writer->feat_id, value); writer->feat_id++; } static inline void wkb_writer_result_finalize(wkb_writer_t* writer) { R_xlen_t current_size = Rf_xlength(writer->result); if (writer->feat_id != current_size) { SEXP new_result = PROTECT(Rf_allocVector(VECSXP, writer->feat_id)); for (R_xlen_t i = 0; i < writer->feat_id; i++) { SET_VECTOR_ELT(new_result, i, VECTOR_ELT(writer->result, i)); } R_ReleaseObject(writer->result); writer->result = new_result; R_PreserveObject(writer->result); UNPROTECT(1); } } int wkb_writer_vector_start(const wk_vector_meta_t* meta, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; if (writer->result != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { writer->result = PROTECT(Rf_allocVector(VECSXP, 1024)); } else { writer->result = PROTECT(Rf_allocVector(VECSXP, meta->size)); } R_PreserveObject(writer->result); UNPROTECT(1); writer->feat_id = 0; return WK_CONTINUE; } int wkb_writer_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; writer->offset = 0; writer->recursion_level = 0; return WK_CONTINUE; } int wkb_writer_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; if (writer->recursion_level > 0) { writer->current_size[writer->recursion_level - 1]++; } wkb_write_uchar(writer, writer->endian); wkb_write_uint(writer, wkb_writer_encode_type(meta, writer->recursion_level)); if (writer->recursion_level == 0 && (meta->srid != WK_SRID_NONE)) { wkb_write_uint(writer, meta->srid); } if (meta->geometry_type != WK_POINT) { if (writer->recursion_level >= WKB_MAX_RECURSION_DEPTH) { Rf_error("Can't write WKB with maximum recursion depth greater than %d", WKB_MAX_RECURSION_DEPTH); } // reserve space for the size and record where it is writer->current_size_offset[writer->recursion_level] = writer->offset; writer->current_size[writer->recursion_level] = 0; wkb_write_uint(writer, 0); } // handle empty point as nan nan here (coord() will not get called) if (meta->geometry_type == WK_POINT && meta->size == 0) { int coord_size = 2; if (meta->flags & WK_FLAG_HAS_Z) coord_size++; if (meta->flags & WK_FLAG_HAS_M) coord_size++; double empty_coord[4]; empty_coord[0] = NAN; empty_coord[1] = NAN; empty_coord[2] = NAN; empty_coord[3] = NAN; wkb_write_doubles(writer, empty_coord, coord_size); } writer->recursion_level++; return WK_CONTINUE; } int wkb_writer_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; writer->recursion_level--; if (meta->geometry_type != WK_POINT) { wkb_write_uint_offset(writer, writer->current_size[writer->recursion_level], writer->current_size_offset[writer->recursion_level]); } return WK_CONTINUE; } int wkb_writer_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; writer->current_size[writer->recursion_level - 1]++; if (writer->recursion_level >= WKB_MAX_RECURSION_DEPTH) { Rf_error("Can't write WKB with maximum recursion depth greater than %d", WKB_MAX_RECURSION_DEPTH); } writer->current_size_offset[writer->recursion_level] = writer->offset; writer->current_size[writer->recursion_level] = 0; wkb_write_uint(writer, 0); writer->recursion_level++; return WK_CONTINUE; } int wkb_writer_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; writer->recursion_level--; wkb_write_uint_offset(writer, writer->current_size[writer->recursion_level], writer->current_size_offset[writer->recursion_level]); return WK_CONTINUE; } int wkb_writer_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; writer->current_size[writer->recursion_level - 1]++; int n_dim = 2 + ((meta->flags & WK_FLAG_HAS_Z) != 0) + ((meta->flags & WK_FLAG_HAS_M) != 0); wkb_write_doubles(writer, coord, n_dim); return WK_CONTINUE; } int wkb_writer_feature_null(void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; wkb_writer_result_append(writer, R_NilValue); return WK_ABORT_FEATURE; } int wkb_writer_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; SEXP item = PROTECT(Rf_allocVector(RAWSXP, writer->offset)); memcpy(RAW(item), writer->buffer, writer->offset); wkb_writer_result_append(writer, item); UNPROTECT(1); return WK_CONTINUE; } SEXP wkb_writer_vector_end(const wk_vector_meta_t* meta, void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; wkb_writer_result_finalize(writer); SEXP wkb_class = PROTECT(Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(wkb_class, 0, Rf_mkChar("wk_wkb")); SET_STRING_ELT(wkb_class, 1, Rf_mkChar("wk_vctr")); Rf_setAttrib(writer->result, R_ClassSymbol, wkb_class); UNPROTECT(1); return writer->result; } void wkb_writer_deinitialize(void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; if (writer->result != R_NilValue) { R_ReleaseObject(writer->result); writer->result = R_NilValue; } } void wkb_writer_finalize(void* handler_data) { wkb_writer_t* writer = (wkb_writer_t*)handler_data; if (writer != NULL) { free(writer->buffer); free(writer); } } SEXP wk_c_wkb_writer_new(SEXP buffer_size_sexp, SEXP endian_sexp) { int endian = INTEGER(endian_sexp)[0]; int buffer_size = INTEGER(buffer_size_sexp)[0]; if (endian == NA_INTEGER) { endian = wkb_writer_platform_endian(); } else if (endian) { endian = 1; } // If the initial buffer is too small, illegal reads can occur // and cause R to crash. The smallest value that doesn't cause a // crash is probably much less than 1024, but since this alloc // only happens once, we set the minimum size to 1024 here. if (buffer_size < 1024) { buffer_size = 1024; } wk_handler_t* handler = wk_handler_create(); handler->vector_start = &wkb_writer_vector_start; handler->feature_start = &wkb_writer_feature_start; handler->geometry_start = &wkb_writer_geometry_start; handler->ring_start = &wkb_writer_ring_start; handler->coord = &wkb_writer_coord; handler->ring_end = &wkb_writer_ring_end; handler->geometry_end = &wkb_writer_geometry_end; handler->null_feature = &wkb_writer_feature_null; handler->feature_end = &wkb_writer_feature_end; handler->vector_end = &wkb_writer_vector_end; handler->deinitialize = &wkb_writer_deinitialize; handler->finalizer = &wkb_writer_finalize; handler->handler_data = wkb_writer_new(buffer_size, endian); if (handler->handler_data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue); return xptr; } wk/src/bbox-handler.c0000644000176200001440000002441014510132141014164 0ustar liggesusers #include #include #include #include "wk-v1.h" #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) typedef struct { // used for bbox and envelope handlers double xmin, ymin, xmax, ymax; int use_geom_meta_bbox; // used for the envelope handler SEXP result; double* result_ptr[4]; R_xlen_t result_size; R_xlen_t feat_id; } wk_bbox_handler_data_t; static inline SEXP wk_bbox_handler_alloc_result(R_xlen_t size) { const char* names[] = {"xmin", "ymin", "xmax", "ymax", ""}; SEXP result = PROTECT(Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(result, 0, Rf_allocVector(REALSXP, size)); SET_VECTOR_ELT(result, 1, Rf_allocVector(REALSXP, size)); SET_VECTOR_ELT(result, 2, Rf_allocVector(REALSXP, size)); SET_VECTOR_ELT(result, 3, Rf_allocVector(REALSXP, size)); UNPROTECT(1); return result; } static inline SEXP wk_bbox_handler_realloc_result(SEXP result, R_xlen_t new_size) { SEXP new_result = PROTECT(wk_bbox_handler_alloc_result(new_size)); R_xlen_t size_cpy; if (Rf_xlength(VECTOR_ELT(result, 0)) < new_size) { size_cpy = Rf_xlength(VECTOR_ELT(result, 0)); } else { size_cpy = new_size; } for (int i = 0; i < 4; i++) { memcpy(REAL(VECTOR_ELT(new_result, i)), REAL(VECTOR_ELT(result, i)), sizeof(double) * size_cpy); } UNPROTECT(1); return new_result; } static inline void wk_bbox_handler_append(wk_bbox_handler_data_t* writer, double xmin, double ymin, double xmax, double ymax) { if (writer->feat_id >= writer->result_size) { SEXP new_result = PROTECT( wk_bbox_handler_realloc_result(writer->result, writer->result_size * 2 + 1)); R_ReleaseObject(writer->result); writer->result = new_result; R_PreserveObject(writer->result); UNPROTECT(1); writer->result_size = writer->result_size * 2 + 1; for (int i = 0; i < 4; i++) { writer->result_ptr[i] = REAL(VECTOR_ELT(writer->result, i)); } } writer->result_ptr[0][writer->feat_id] = xmin; writer->result_ptr[1][writer->feat_id] = ymin; writer->result_ptr[2][writer->feat_id] = xmax; writer->result_ptr[3][writer->feat_id] = ymax; writer->feat_id++; } int wk_bbox_handler_vector_start(const wk_vector_meta_t* vector_meta, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; if (vector_meta->flags & WK_FLAG_HAS_BOUNDS) { data->xmin = vector_meta->bounds_min[0]; data->ymin = vector_meta->bounds_min[1]; data->xmax = vector_meta->bounds_max[0]; data->ymax = vector_meta->bounds_max[1]; return WK_ABORT; } else { return WK_CONTINUE; } } int wk_bbox_handler_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; data->use_geom_meta_bbox = 1; return WK_CONTINUE; } int wk_bbox_handler_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; if (data->use_geom_meta_bbox && (meta->flags & WK_FLAG_HAS_BOUNDS)) { data->xmin = MIN(meta->bounds_min[0], data->xmin); data->ymin = MIN(meta->bounds_min[1], data->ymin); data->xmax = MAX(meta->bounds_max[0], data->xmax); data->ymax = MAX(meta->bounds_max[1], data->ymax); return WK_ABORT_FEATURE; } else { data->use_geom_meta_bbox = 0; return WK_CONTINUE; } } int wk_bbox_handler_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; data->xmin = MIN(coord[0], data->xmin); data->ymin = MIN(coord[1], data->ymin); data->xmax = MAX(coord[0], data->xmax); data->ymax = MAX(coord[1], data->ymax); return WK_CONTINUE; } SEXP wk_bbox_handler_vector_end(const wk_vector_meta_t* meta, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; const char* names[] = {"xmin", "ymin", "xmax", "ymax", ""}; SEXP output = PROTECT(Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(output, 0, Rf_ScalarReal(data->xmin)); SET_VECTOR_ELT(output, 1, Rf_ScalarReal(data->ymin)); SET_VECTOR_ELT(output, 2, Rf_ScalarReal(data->xmax)); SET_VECTOR_ELT(output, 3, Rf_ScalarReal(data->ymax)); SEXP rct_class = PROTECT(Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(rct_class, 0, Rf_mkChar("wk_rct")); SET_STRING_ELT(rct_class, 1, Rf_mkChar("wk_rcrd")); Rf_setAttrib(output, R_ClassSymbol, rct_class); UNPROTECT(1); UNPROTECT(1); return output; } void wk_bbox_handler_finalize(void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; free(data); } SEXP wk_c_bbox_handler_new(void) { wk_handler_t* handler = wk_handler_create(); handler->vector_start = &wk_bbox_handler_vector_start; handler->feature_start = &wk_bbox_handler_feature_start; handler->geometry_start = &wk_bbox_handler_geometry_start; handler->coord = &wk_bbox_handler_coord; handler->vector_end = &wk_bbox_handler_vector_end; handler->finalizer = &wk_bbox_handler_finalize; wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)malloc(sizeof(wk_bbox_handler_data_t)); if (data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } data->xmin = R_PosInf; data->ymin = R_PosInf; data->xmax = R_NegInf; data->ymax = R_NegInf; data->result_size = 0; data->feat_id = 0; data->use_geom_meta_bbox = 1; data->result = R_NilValue; for (int i = 0; i < 4; i++) { data->result_ptr[i] = NULL; } handler->handler_data = data; return wk_handler_create_xptr(handler, R_NilValue, R_NilValue); } int wk_envelope_handler_vector_start(const wk_vector_meta_t* meta, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; if (data->result != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { data->result = PROTECT(wk_bbox_handler_alloc_result(1024)); data->result_size = 1024; } else { data->result = PROTECT(wk_bbox_handler_alloc_result(meta->size)); data->result_size = meta->size; } R_PreserveObject(data->result); UNPROTECT(1); for (int i = 0; i < 4; i++) { data->result_ptr[i] = REAL(VECTOR_ELT(data->result, i)); } data->feat_id = 0; return WK_CONTINUE; } int wk_envelope_handler_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; data->xmin = R_PosInf; data->ymin = R_PosInf; data->xmax = R_NegInf; data->ymax = R_NegInf; data->use_geom_meta_bbox = 1; return WK_CONTINUE; } int wk_envelope_handler_feature_null(void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; data->xmin = NA_REAL; data->ymin = NA_REAL; data->xmax = NA_REAL; data->ymax = NA_REAL; return WK_CONTINUE; } int wk_envelope_handler_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; if (data->use_geom_meta_bbox && (meta->flags & WK_FLAG_HAS_BOUNDS)) { data->xmin = MIN(meta->bounds_min[0], data->xmin); data->ymin = MIN(meta->bounds_min[1], data->ymin); data->xmax = MAX(meta->bounds_max[0], data->xmax); data->ymax = MAX(meta->bounds_max[1], data->ymax); wk_bbox_handler_append(data, data->xmin, data->ymin, data->xmax, data->ymax); return WK_ABORT_FEATURE; } else { data->use_geom_meta_bbox = 0; return WK_CONTINUE; } } int wk_envelope_handler_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; wk_bbox_handler_append(data, data->xmin, data->ymin, data->xmax, data->ymax); return WK_CONTINUE; } SEXP wk_envelope_handler_vector_end(const wk_vector_meta_t* meta, void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; R_xlen_t final_size = data->feat_id; if (final_size != data->result_size) { SEXP new_result = PROTECT(wk_bbox_handler_realloc_result(data->result, final_size)); R_ReleaseObject(data->result); data->result = new_result; R_PreserveObject(data->result); UNPROTECT(1); } SEXP rct_class = PROTECT(Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(rct_class, 0, Rf_mkChar("wk_rct")); SET_STRING_ELT(rct_class, 1, Rf_mkChar("wk_rcrd")); Rf_setAttrib(data->result, R_ClassSymbol, rct_class); UNPROTECT(1); return data->result; } void wk_envelope_handler_deinitialize(void* handler_data) { wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)handler_data; if (data->result != R_NilValue) { R_ReleaseObject(data->result); data->result = R_NilValue; } } SEXP wk_c_envelope_handler_new(void) { wk_handler_t* handler = wk_handler_create(); handler->vector_start = &wk_envelope_handler_vector_start; handler->feature_start = &wk_envelope_handler_feature_start; handler->null_feature = &wk_envelope_handler_feature_null; handler->geometry_start = &wk_envelope_handler_geometry_start; handler->coord = &wk_bbox_handler_coord; handler->feature_end = &wk_envelope_handler_feature_end; handler->vector_end = &wk_envelope_handler_vector_end; handler->finalizer = &wk_bbox_handler_finalize; handler->deinitialize = &wk_envelope_handler_deinitialize; wk_bbox_handler_data_t* data = (wk_bbox_handler_data_t*)malloc(sizeof(wk_bbox_handler_data_t)); if (data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } data->xmin = R_PosInf; data->ymin = R_PosInf; data->xmax = R_NegInf; data->ymax = R_NegInf; data->result_size = 0; data->feat_id = 0; data->use_geom_meta_bbox = 1; data->result = R_NilValue; for (int i = 0; i < 4; i++) { data->result_ptr[i] = NULL; } handler->handler_data = data; return wk_handler_create_xptr(handler, R_NilValue, R_NilValue); } wk/src/transform.c0000644000176200001440000002267014510132141013640 0ustar liggesusers#define R_NO_REMAP #include #include #include #include "wk-v1.h" #define MAX_LEVELS 32 typedef struct { wk_handler_t* next; wk_trans_t* trans; wk_meta_t meta[MAX_LEVELS]; wk_vector_meta_t vector_meta; int recursive_level; R_xlen_t feature_id; double xyzm_in[4]; double xyzm_out[4]; double coord_out[4]; } trans_filter_t; void wk_trans_filter_initialize(int* dirty, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; *dirty = 1; trans_filter->next->initialize(&trans_filter->next->dirty, trans_filter->next->handler_data); } int wk_trans_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; memcpy(&(trans_filter->vector_meta), meta, sizeof(wk_vector_meta_t)); // bounds are no longer valid trans_filter->vector_meta.flags &= ~WK_FLAG_HAS_BOUNDS; // set the output dimensions NA_INTEGER means "leave alone" int dims_maybe_unknown = 0; if (trans_filter->trans->use_z == 1) { trans_filter->vector_meta.flags |= WK_FLAG_HAS_Z; } else if (trans_filter->trans->use_z == 0) { trans_filter->vector_meta.flags &= ~WK_FLAG_HAS_Z; } else { dims_maybe_unknown = 1; } if (trans_filter->trans->use_m == 1) { trans_filter->vector_meta.flags |= WK_FLAG_HAS_M; } else if (trans_filter->trans->use_m == 0) { trans_filter->vector_meta.flags &= ~WK_FLAG_HAS_M; } else { dims_maybe_unknown = 1; } if (!dims_maybe_unknown) { trans_filter->vector_meta.flags &= ~WK_FLAG_DIMS_UNKNOWN; } trans_filter->feature_id = -1; return trans_filter->next->vector_start(&(trans_filter->vector_meta), trans_filter->next->handler_data); } SEXP wk_trans_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; trans_filter->trans->vector_end(trans_filter->trans->trans_data); return trans_filter->next->vector_end(&(trans_filter->vector_meta), trans_filter->next->handler_data); } int wk_trans_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; trans_filter->recursive_level = -1; trans_filter->feature_id++; return trans_filter->next->feature_start(&(trans_filter->vector_meta), feat_id, trans_filter->next->handler_data); } int wk_trans_filter_feature_null(void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; return trans_filter->next->null_feature(trans_filter->next->handler_data); } int wk_trans_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; return trans_filter->next->feature_end(&(trans_filter->vector_meta), feat_id, trans_filter->next->handler_data); } int wk_trans_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; trans_filter->recursive_level++; if (trans_filter->recursive_level >= MAX_LEVELS) { Rf_error("Too many recursive levels for wk_transform_filter()"); } wk_meta_t* new_meta = trans_filter->meta + trans_filter->recursive_level; memcpy(new_meta, meta, sizeof(wk_meta_t)); new_meta->flags &= ~WK_FLAG_HAS_BOUNDS; if (trans_filter->trans->use_z == 1) { new_meta->flags |= WK_FLAG_HAS_Z; } else if (trans_filter->trans->use_z == 0) { new_meta->flags &= ~WK_FLAG_HAS_Z; } if (trans_filter->trans->use_m == 1) { new_meta->flags |= WK_FLAG_HAS_M; } else if (trans_filter->trans->use_m == 0) { new_meta->flags &= ~WK_FLAG_HAS_M; } return trans_filter->next->geometry_start(new_meta, part_id, trans_filter->next->handler_data); } int wk_trans_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; wk_meta_t* new_meta = trans_filter->meta + trans_filter->recursive_level; trans_filter->recursive_level--; return trans_filter->next->geometry_end(new_meta, part_id, trans_filter->next->handler_data); } int wk_trans_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; wk_meta_t* new_meta = trans_filter->meta + trans_filter->recursive_level; return trans_filter->next->ring_start(new_meta, size, ring_id, trans_filter->next->handler_data); } int wk_trans_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; wk_meta_t* new_meta = trans_filter->meta + trans_filter->recursive_level; return trans_filter->next->ring_end(new_meta, size, ring_id, trans_filter->next->handler_data); } int wk_trans_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; wk_meta_t* new_meta = trans_filter->meta + trans_filter->recursive_level; trans_filter->xyzm_in[0] = coord[0]; trans_filter->xyzm_in[1] = coord[1]; if (meta->flags & WK_FLAG_HAS_Z && meta->flags & WK_FLAG_HAS_M) { trans_filter->xyzm_in[2] = coord[2]; trans_filter->xyzm_in[3] = coord[3]; } else if (meta->flags & WK_FLAG_HAS_Z) { trans_filter->xyzm_in[2] = coord[2]; trans_filter->xyzm_in[3] = R_NaN; } else if (new_meta->flags & WK_FLAG_HAS_M) { trans_filter->xyzm_in[2] = R_NaN; trans_filter->xyzm_in[3] = coord[2]; } else { trans_filter->xyzm_in[2] = R_NaN; trans_filter->xyzm_in[3] = R_NaN; } int result = trans_filter->trans->trans(trans_filter->feature_id, trans_filter->xyzm_in, trans_filter->xyzm_out, trans_filter->trans->trans_data); if (result != WK_CONTINUE) { return result; } trans_filter->coord_out[0] = trans_filter->xyzm_out[0]; trans_filter->coord_out[1] = trans_filter->xyzm_out[1]; if (new_meta->flags & WK_FLAG_HAS_Z && new_meta->flags & WK_FLAG_HAS_M) { trans_filter->coord_out[2] = trans_filter->xyzm_out[2]; trans_filter->coord_out[3] = trans_filter->xyzm_out[3]; } else if (new_meta->flags & WK_FLAG_HAS_Z) { trans_filter->coord_out[2] = trans_filter->xyzm_out[2]; } else if (new_meta->flags & WK_FLAG_HAS_M) { trans_filter->coord_out[2] = trans_filter->xyzm_out[3]; } return trans_filter->next->coord(new_meta, trans_filter->coord_out, coord_id, trans_filter->next->handler_data); } int wk_trans_filter_error(const char* message, void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; return trans_filter->next->error(message, trans_filter->next->handler_data); } void wk_trans_filter_deinitialize(void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; trans_filter->next->deinitialize(trans_filter->next->handler_data); } void wk_trans_filter_finalize(void* handler_data) { trans_filter_t* trans_filter = (trans_filter_t*)handler_data; if (trans_filter != NULL) { // finalizer for trans_filter->next is run by the externalptr finalizer // and should not be called here free(trans_filter); } } SEXP wk_c_trans_filter_new(SEXP handler_xptr, SEXP trans_xptr) { wk_trans_t* trans = (wk_trans_t*)R_ExternalPtrAddr(trans_xptr); wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_trans_filter_initialize; handler->vector_start = &wk_trans_filter_vector_start; handler->vector_end = &wk_trans_filter_vector_end; handler->feature_start = &wk_trans_filter_feature_start; handler->null_feature = &wk_trans_filter_feature_null; handler->feature_end = &wk_trans_filter_feature_end; handler->geometry_start = &wk_trans_filter_geometry_start; handler->geometry_end = &wk_trans_filter_geometry_end; handler->ring_start = &wk_trans_filter_ring_start; handler->ring_end = &wk_trans_filter_ring_end; handler->coord = &wk_trans_filter_coord; handler->error = &wk_trans_filter_error; handler->deinitialize = &wk_trans_filter_deinitialize; handler->finalizer = &wk_trans_filter_finalize; trans_filter_t* trans_filter = (trans_filter_t*)malloc(sizeof(trans_filter_t)); if (trans_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } trans_filter->next = R_ExternalPtrAddr(handler_xptr); if (trans_filter->next->api_version != 1) { Rf_error("Can't run a wk_handler with api_version '%d'", trans_filter->next->api_version); // # nocov } trans_filter->trans = trans; handler->handler_data = trans_filter; // include the external pointers as tags 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, trans_xptr); } wk/src/make-collection-filter.c0000644000176200001440000002510114510132141016146 0ustar liggesusers#define R_NO_REMAP #include #include #include "altrep.h" #include "wk-v1.h" #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) { \ Rf_error("wk_collection_filter() does not support WK_ABORT_FEATURE"); \ } \ if (result != WK_CONTINUE) return result typedef struct { wk_handler_t* next; int geometry_type_out; R_xlen_t feature_id; SEXP feature_id_sexp; #ifndef HAS_ALTREP int* feature_id_spec; #endif R_xlen_t n_feature_id_spec; int last_feature_id_spec; int is_new_feature; R_xlen_t feature_id_out; uint32_t part_id; wk_meta_t meta; wk_vector_meta_t vector_meta; } collection_filter_t; static inline int wk_collection_start(collection_filter_t* collection_filter) { int result; collection_filter->feature_id_out++; HANDLE_OR_RETURN(collection_filter->next->feature_start( &(collection_filter->vector_meta), collection_filter->feature_id_out, collection_filter->next->handler_data)); HANDLE_OR_RETURN( collection_filter->next->geometry_start(&(collection_filter->meta), WK_PART_ID_NONE, collection_filter->next->handler_data)); collection_filter->part_id = 0; return WK_CONTINUE; } static inline int wk_collection_end(collection_filter_t* collection_filter) { int result; HANDLE_OR_RETURN( collection_filter->next->geometry_end(&(collection_filter->meta), WK_PART_ID_NONE, collection_filter->next->handler_data)); HANDLE_OR_RETURN(collection_filter->next->feature_end( &(collection_filter->vector_meta), collection_filter->feature_id_out, collection_filter->next->handler_data)); return WK_CONTINUE; } void wk_collection_filter_initialize(int* dirty, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; *dirty = 1; collection_filter->next->initialize(&collection_filter->next->dirty, collection_filter->next->handler_data); } int wk_collection_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; collection_filter->feature_id = -1; collection_filter->feature_id_out = -1; memcpy(&(collection_filter->vector_meta), meta, sizeof(wk_vector_meta_t)); collection_filter->vector_meta.geometry_type = collection_filter->geometry_type_out; collection_filter->vector_meta.size = WK_VECTOR_SIZE_UNKNOWN; WK_META_RESET(collection_filter->meta, collection_filter->geometry_type_out); return collection_filter->next->vector_start(&(collection_filter->vector_meta), collection_filter->next->handler_data); } SEXP wk_collection_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; // if there weren't any features we need to start one int result = WK_CONTINUE; if (collection_filter->feature_id_out == -1) { collection_filter->meta.size = 0; result = wk_collection_start(collection_filter); } if (result != WK_ABORT) { wk_collection_end(collection_filter); } return collection_filter->next->vector_end(&(collection_filter->vector_meta), collection_filter->next->handler_data); } int wk_collection_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; collection_filter->feature_id++; R_xlen_t spec_i = collection_filter->feature_id % collection_filter->n_feature_id_spec; #ifdef HAS_ALTREP int feature_id_spec = INTEGER_ELT(collection_filter->feature_id_sexp, spec_i); #else int feature_id_spec = collection_filter->feature_id_spec[spec_i]; #endif int feature_id_spec_changed = feature_id_spec != collection_filter->last_feature_id_spec; collection_filter->last_feature_id_spec = feature_id_spec; collection_filter->is_new_feature = feature_id_spec_changed || (collection_filter->feature_id == 0); return WK_CONTINUE; } int wk_collection_filter_feature_null(void* handler_data) { return WK_CONTINUE; } int wk_collection_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { return WK_CONTINUE; } int wk_collection_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; int result; // ensure collection_filter->part_id == 0 for is_new_feature & part_id == // WK_PART_ID_NONE int inc_part_id = !collection_filter->is_new_feature && part_id == WK_PART_ID_NONE; if (collection_filter->is_new_feature) { if (collection_filter->feature_id_out >= 0) { HANDLE_OR_RETURN(wk_collection_end(collection_filter)); } collection_filter->meta.flags = meta->flags; collection_filter->meta.flags &= ~WK_FLAG_HAS_BOUNDS; collection_filter->meta.precision = meta->precision; collection_filter->meta.srid = meta->srid; HANDLE_OR_RETURN(wk_collection_start(collection_filter)); collection_filter->is_new_feature = 0; } if (part_id == WK_PART_ID_NONE) { collection_filter->part_id += inc_part_id; part_id = collection_filter->part_id; } return collection_filter->next->geometry_start(meta, part_id, collection_filter->next->handler_data); } int wk_collection_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; if (part_id == WK_PART_ID_NONE) { part_id = collection_filter->part_id; } int result; HANDLE_OR_RETURN(collection_filter->next->geometry_end( meta, part_id, collection_filter->next->handler_data)); return WK_CONTINUE; } int wk_collection_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; int result; HANDLE_OR_RETURN(collection_filter->next->ring_start( meta, size, ring_id, collection_filter->next->handler_data)); return WK_CONTINUE; } int wk_collection_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; int result; HANDLE_OR_RETURN(collection_filter->next->ring_end( meta, size, ring_id, collection_filter->next->handler_data)); return WK_CONTINUE; } int wk_collection_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; int result; HANDLE_OR_RETURN(collection_filter->next->coord(meta, coord, coord_id, collection_filter->next->handler_data)); return WK_CONTINUE; } int wk_collection_filter_error(const char* message, void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; int result; HANDLE_OR_RETURN( collection_filter->next->error(message, collection_filter->next->handler_data)); return WK_CONTINUE; } void wk_collection_filter_deinitialize(void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; collection_filter->next->deinitialize(collection_filter->next->handler_data); } void wk_collection_filter_finalize(void* handler_data) { collection_filter_t* collection_filter = (collection_filter_t*)handler_data; if (collection_filter != NULL) { // finalizer for collection_filter->next is run by the externalptr finalizer // and should not be called here free(collection_filter); } } SEXP wk_c_collection_filter_new(SEXP handler_xptr, SEXP geometry_type, SEXP feature_id) { #ifndef HAS_ALTREP int* feature_id_spec = INTEGER(feature_id); #endif int geometry_type_int = INTEGER(geometry_type)[0]; wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_collection_filter_initialize; handler->vector_start = &wk_collection_filter_vector_start; handler->vector_end = &wk_collection_filter_vector_end; handler->feature_start = &wk_collection_filter_feature_start; handler->null_feature = &wk_collection_filter_feature_null; handler->feature_end = &wk_collection_filter_feature_end; handler->geometry_start = &wk_collection_filter_geometry_start; handler->geometry_end = &wk_collection_filter_geometry_end; handler->ring_start = &wk_collection_filter_ring_start; handler->ring_end = &wk_collection_filter_ring_end; handler->coord = &wk_collection_filter_coord; handler->error = &wk_collection_filter_error; handler->deinitialize = &wk_collection_filter_deinitialize; handler->finalizer = &wk_collection_filter_finalize; collection_filter_t* collection_filter = (collection_filter_t*)malloc(sizeof(collection_filter_t)); if (collection_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } collection_filter->next = (wk_handler_t*)R_ExternalPtrAddr(handler_xptr); if (collection_filter->next->api_version != 1) { wk_handler_destroy(handler); // # nocov free(collection_filter); // # nocov Rf_error("Invalid API version in collection_filter"); // # nocov } collection_filter->geometry_type_out = geometry_type_int; collection_filter->part_id = 0; collection_filter->feature_id = -1; collection_filter->feature_id_out = 0; collection_filter->feature_id_sexp = feature_id; #ifndef HAS_ALTREP collection_filter->feature_id_spec = feature_id_spec; #endif collection_filter->n_feature_id_spec = Rf_xlength(feature_id); collection_filter->is_new_feature = 0; collection_filter->last_feature_id_spec = NA_INTEGER; handler->handler_data = collection_filter; // We need both the external pointer SEXP and the feature_id SEXP // to be valid for the lifetime of this object return wk_handler_create_xptr(handler, handler_xptr, feature_id); } wk/src/handle-xy.c0000644000176200001440000000652114515070354013527 0ustar liggesusers #define R_NO_REMAP #include #include #include "altrep.h" #include "wk-v1.h" #define HANDLE_CONTINUE_OR_BREAK(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) \ continue; \ else if (result == WK_ABORT) \ break SEXP wk_read_xy(SEXP data, wk_handler_t* handler) { R_xlen_t n_features = Rf_xlength(VECTOR_ELT(data, 0)); int coord_size = Rf_length(data); double* data_ptr[4]; R_xlen_t data_ptr_i = 0; #ifdef HAS_ALTREP SEXP altrep_buffer = PROTECT(Rf_allocVector(REALSXP, ALTREP_CHUNK_SIZE * 4)); for (int j = 0; j < coord_size; j++) { data_ptr[j] = REAL(altrep_buffer) + (ALTREP_CHUNK_SIZE * j); } #else for (int j = 0; j < coord_size; j++) { data_ptr[j] = REAL(VECTOR_ELT(data, j)); } #endif wk_vector_meta_t vector_meta; WK_VECTOR_META_RESET(vector_meta, WK_POINT); vector_meta.size = n_features; if (Rf_inherits(data, "wk_xyz") || Rf_inherits(data, "wk_xyzm")) { vector_meta.flags |= WK_FLAG_HAS_Z; } if (Rf_inherits(data, "wk_xym") || Rf_inherits(data, "wk_xyzm")) { vector_meta.flags |= WK_FLAG_HAS_M; } if (handler->vector_start(&vector_meta, handler->handler_data) == WK_CONTINUE) { int result; double coord[4]; wk_meta_t meta; WK_META_RESET(meta, WK_POINT); meta.flags = vector_meta.flags | WK_FLAG_HAS_BOUNDS; for (R_xlen_t i = 0; i < n_features; i++) { if (((i + 1) % 1000) == 0) R_CheckUserInterrupt(); HANDLE_CONTINUE_OR_BREAK( handler->feature_start(&vector_meta, i, handler->handler_data)); #ifdef HAS_ALTREP data_ptr_i = i % ALTREP_CHUNK_SIZE; if (data_ptr_i == 0) { for (int j = 0; j < coord_size; j++) { REAL_GET_REGION(VECTOR_ELT(data, j), i, ALTREP_CHUNK_SIZE, data_ptr[j]); } } #else data_ptr_i = i; #endif int coord_empty = 1; int coord_null = 1; for (int j = 0; j < coord_size; j++) { coord[j] = data_ptr[j][data_ptr_i]; meta.bounds_min[j] = data_ptr[j][data_ptr_i]; meta.bounds_max[j] = data_ptr[j][data_ptr_i]; coord_null = coord_null && ISNA(coord[j]); coord_empty = coord_empty && ISNAN(coord[j]); } if (coord_null) { HANDLE_CONTINUE_OR_BREAK(handler->null_feature(handler->handler_data)); } else if (coord_empty) { meta.size = 0; HANDLE_CONTINUE_OR_BREAK( handler->geometry_start(&meta, WK_PART_ID_NONE, handler->handler_data)); HANDLE_CONTINUE_OR_BREAK( handler->geometry_end(&meta, WK_PART_ID_NONE, handler->handler_data)); } else { meta.size = 1; HANDLE_CONTINUE_OR_BREAK( handler->geometry_start(&meta, WK_PART_ID_NONE, handler->handler_data)); HANDLE_CONTINUE_OR_BREAK(handler->coord(&meta, coord, 0, handler->handler_data)); HANDLE_CONTINUE_OR_BREAK( handler->geometry_end(&meta, WK_PART_ID_NONE, handler->handler_data)); } if (handler->feature_end(&vector_meta, i, handler->handler_data) == WK_ABORT) { break; } } } #ifdef HAS_ALTREP UNPROTECT(1); #endif SEXP result = PROTECT(handler->vector_end(&vector_meta, handler->handler_data)); UNPROTECT(1); return result; } SEXP wk_c_read_xy(SEXP data, SEXP handlerXptr) { return wk_handler_run_xptr(&wk_read_xy, data, handlerXptr); } wk/src/flatten-filter.c0000644000176200001440000003157314510132141014547 0ustar liggesusers#define R_NO_REMAP #include #include #include "wk-v1.h" typedef struct { wk_handler_t* next; int recursion_depth; int recursion_depth_out; int recursion_depth_threshold; wk_vector_meta_t vector_meta; int feature_id; int feature_id_out; int add_details; SEXP details; int* details_ptr[1]; R_xlen_t details_size; } flatten_filter_t; #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) { \ Rf_error("wk_flatten_filter() does not support WK_ABORT_FEATURE"); \ } \ if (result != WK_CONTINUE) return result #define META_IS_COLLECTION(meta) \ ((meta->geometry_type == WK_GEOMETRY) || (meta->geometry_type == WK_MULTIPOINT) || \ (meta->geometry_type == WK_MULTILINESTRING) || \ (meta->geometry_type == WK_MULTIPOLYGON) || \ (meta->geometry_type == WK_GEOMETRYCOLLECTION)) static inline int wk_flatten_filter_keep(flatten_filter_t* flatten_filter, const wk_meta_t* meta) { int is_collection = META_IS_COLLECTION(meta); int recursion_level_above_threshold = flatten_filter->recursion_depth >= flatten_filter->recursion_depth_threshold; return !is_collection || recursion_level_above_threshold; } static inline void wk_flatten_filter_init_details(flatten_filter_t* flatten_filter, R_xlen_t initial_size) { if (!flatten_filter->add_details) { return; } if (initial_size == WK_VECTOR_SIZE_UNKNOWN) { initial_size = 1024; } flatten_filter->feature_id = -1; if (flatten_filter->details != R_NilValue) { R_ReleaseObject(flatten_filter->details); // # nocov } const char* names[] = {"feature_id", ""}; flatten_filter->details = PROTECT(Rf_mkNamed(VECSXP, names)); R_PreserveObject(flatten_filter->details); UNPROTECT(1); flatten_filter->details_size = initial_size; for (int i = 0; i < 1; i++) { SEXP item = PROTECT(Rf_allocVector(INTSXP, flatten_filter->details_size)); SET_VECTOR_ELT(flatten_filter->details, i, item); flatten_filter->details_ptr[i] = INTEGER(item); UNPROTECT(1); } } static inline void wk_flatten_filter_append_details(flatten_filter_t* flatten_filter) { if (flatten_filter->details == R_NilValue) { return; } if (flatten_filter->feature_id_out >= flatten_filter->details_size) { R_xlen_t new_size = flatten_filter->details_size * 2 + 1; for (int i = 0; i < 1; i++) { SEXP new_item = PROTECT(Rf_allocVector(INTSXP, new_size)); memcpy(INTEGER(new_item), INTEGER(VECTOR_ELT(flatten_filter->details, i)), flatten_filter->details_size * sizeof(int)); SET_VECTOR_ELT(flatten_filter->details, i, new_item); flatten_filter->details_ptr[i] = INTEGER(new_item); UNPROTECT(1); } flatten_filter->details_size = new_size; } flatten_filter->details_ptr[0][flatten_filter->feature_id_out] = flatten_filter->feature_id + 1; } static inline void wk_flatten_filter_finalize_details(flatten_filter_t* flatten_filter) { if (flatten_filter->details == R_NilValue) { return; } flatten_filter->feature_id_out++; if (flatten_filter->feature_id_out != flatten_filter->details_size) { for (int i = 0; i < 1; i++) { SEXP new_item = PROTECT(Rf_allocVector(INTSXP, flatten_filter->feature_id_out)); memcpy(INTEGER(new_item), INTEGER(VECTOR_ELT(flatten_filter->details, i)), flatten_filter->feature_id_out * sizeof(int)); SET_VECTOR_ELT(flatten_filter->details, i, new_item); UNPROTECT(1); } flatten_filter->details_size = flatten_filter->feature_id_out; } } void wk_flatten_filter_initialize(int* dirty, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; *dirty = 1; flatten_filter->next->initialize(&flatten_filter->next->dirty, flatten_filter->next->handler_data); } int wk_flatten_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; flatten_filter->feature_id_out = -1; flatten_filter->recursion_depth_out = 0; memcpy(&(flatten_filter->vector_meta), meta, sizeof(wk_vector_meta_t)); if (flatten_filter->recursion_depth_threshold > 0) { if (META_IS_COLLECTION(meta)) { flatten_filter->vector_meta.size = WK_VECTOR_SIZE_UNKNOWN; } if (meta->geometry_type == WK_MULTIPOINT) { flatten_filter->vector_meta.geometry_type = WK_POINT; } else if (meta->geometry_type == WK_MULTILINESTRING) { flatten_filter->vector_meta.geometry_type = WK_LINESTRING; } else if (meta->geometry_type == WK_MULTIPOLYGON) { flatten_filter->vector_meta.geometry_type = WK_POLYGON; } else if (meta->geometry_type == WK_GEOMETRYCOLLECTION) { flatten_filter->vector_meta.geometry_type = WK_GEOMETRY; } } wk_flatten_filter_init_details(flatten_filter, flatten_filter->vector_meta.size); return flatten_filter->next->vector_start(&(flatten_filter->vector_meta), flatten_filter->next->handler_data); } SEXP wk_flatten_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; SEXP result = PROTECT(flatten_filter->next->vector_end( &(flatten_filter->vector_meta), flatten_filter->next->handler_data)); if (result != R_NilValue) { wk_flatten_filter_finalize_details(flatten_filter); Rf_setAttrib(result, Rf_install("wk_details"), flatten_filter->details); } UNPROTECT(1); return result; } int wk_flatten_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; flatten_filter->feature_id++; flatten_filter->recursion_depth = 0; return WK_CONTINUE; } int wk_flatten_filter_feature_null(void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; int result; flatten_filter->feature_id_out++; wk_flatten_filter_append_details(flatten_filter); HANDLE_OR_RETURN(flatten_filter->next->feature_start( &(flatten_filter->vector_meta), flatten_filter->feature_id_out, flatten_filter->next->handler_data)); result = flatten_filter->next->null_feature(flatten_filter->next->handler_data); if (result != WK_CONTINUE) { return result; } return flatten_filter->next->feature_end(&(flatten_filter->vector_meta), flatten_filter->feature_id_out, flatten_filter->next->handler_data); } int wk_flatten_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { return WK_CONTINUE; } int wk_flatten_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; int result; int keep = wk_flatten_filter_keep(flatten_filter, meta); flatten_filter->recursion_depth++; flatten_filter->recursion_depth_out += keep; if (keep) { uint32_t part_id_out; if (flatten_filter->recursion_depth_out > 1) { part_id_out = part_id; } else { part_id_out = WK_PART_ID_NONE; flatten_filter->feature_id_out++; wk_flatten_filter_append_details(flatten_filter); HANDLE_OR_RETURN(flatten_filter->next->feature_start( &(flatten_filter->vector_meta), flatten_filter->feature_id_out, flatten_filter->next->handler_data)); } HANDLE_OR_RETURN(flatten_filter->next->geometry_start( meta, part_id_out, flatten_filter->next->handler_data)); } return WK_CONTINUE; } int wk_flatten_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; int result; flatten_filter->recursion_depth--; int keep = wk_flatten_filter_keep(flatten_filter, meta); flatten_filter->recursion_depth_out -= keep; if (keep) { uint32_t part_id_out = flatten_filter->recursion_depth_out > 0 ? part_id : WK_PART_ID_NONE; HANDLE_OR_RETURN(flatten_filter->next->geometry_end( meta, part_id_out, flatten_filter->next->handler_data)); if (flatten_filter->recursion_depth_out == 0) { HANDLE_OR_RETURN(flatten_filter->next->feature_end( &(flatten_filter->vector_meta), flatten_filter->feature_id_out, flatten_filter->next->handler_data)); } } return WK_CONTINUE; } int wk_flatten_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; return flatten_filter->next->ring_start(meta, size, ring_id, flatten_filter->next->handler_data); } int wk_flatten_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; return flatten_filter->next->ring_end(meta, size, ring_id, flatten_filter->next->handler_data); } int wk_flatten_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t feature_id_out, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; return flatten_filter->next->coord(meta, coord, feature_id_out, flatten_filter->next->handler_data); } int wk_flatten_filter_error(const char* message, void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; return flatten_filter->next->error(message, flatten_filter->next->handler_data); } void wk_flatten_filter_deinitialize(void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; if (flatten_filter->details != R_NilValue) { R_ReleaseObject(flatten_filter->details); flatten_filter->details = R_NilValue; } flatten_filter->next->deinitialize(flatten_filter->next->handler_data); } void wk_flatten_filter_finalize(void* handler_data) { flatten_filter_t* flatten_filter = (flatten_filter_t*)handler_data; if (flatten_filter != NULL) { // finalizer for flatten_filter->next is run by the externalptr finalizer // and should not be called here free(flatten_filter); } } SEXP wk_c_flatten_filter_new(SEXP handler_xptr, SEXP max_depth, SEXP add_details) { int max_depth_int = INTEGER(max_depth)[0]; int add_details_int = LOGICAL(add_details)[0]; wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_flatten_filter_initialize; handler->vector_start = &wk_flatten_filter_vector_start; handler->vector_end = &wk_flatten_filter_vector_end; handler->feature_start = &wk_flatten_filter_feature_start; handler->null_feature = &wk_flatten_filter_feature_null; handler->feature_end = &wk_flatten_filter_feature_end; handler->geometry_start = &wk_flatten_filter_geometry_start; handler->geometry_end = &wk_flatten_filter_geometry_end; handler->ring_start = &wk_flatten_filter_ring_start; handler->ring_end = &wk_flatten_filter_ring_end; handler->coord = &wk_flatten_filter_coord; handler->error = &wk_flatten_filter_error; handler->deinitialize = &wk_flatten_filter_deinitialize; handler->finalizer = &wk_flatten_filter_finalize; flatten_filter_t* flatten_filter = (flatten_filter_t*)malloc(sizeof(flatten_filter_t)); if (flatten_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } flatten_filter->next = R_ExternalPtrAddr(handler_xptr); if (flatten_filter->next->api_version != 1) { Rf_error("Can't run a wk_handler with api_version '%d'", flatten_filter->next->api_version); // # nocov } WK_VECTOR_META_RESET(flatten_filter->vector_meta, WK_GEOMETRY); flatten_filter->add_details = add_details_int; flatten_filter->recursion_depth_threshold = max_depth_int; flatten_filter->recursion_depth = 0; flatten_filter->recursion_depth_out = 0; flatten_filter->details = R_NilValue; flatten_filter->details_size = 0; flatten_filter->feature_id = 0; flatten_filter->feature_id_out = 0; handler->handler_data = flatten_filter; // include the external pointer as a tag for this external pointer // which guarnatees that it will not be garbage collected until // this object is garbage collected return wk_handler_create_xptr(handler, handler_xptr, R_NilValue); } wk/src/sfc-writer.c0000644000176200001440000010225714531511633013724 0ustar liggesusers #define R_NO_REMAP #include #include #include #include "wk-v1.h" #define SFC_FLAGS_NOT_YET_DEFINED UINT32_MAX #define SFC_GEOMETRY_TYPE_NOT_YET_DEFINED -1 #define SFC_MAX_RECURSION_DEPTH 32 #define SFC_WRITER_GEOM_LENGTH SFC_MAX_RECURSION_DEPTH + 2 #define SFC_INITIAL_SIZE_IF_UNKNOWN 32 #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) typedef struct { // Flag to promote all simple geometries to multi int promote_multi; // output vector list() SEXP sfc; // container list() geometries SEXP geom[SFC_WRITER_GEOM_LENGTH]; // keep track of recursion level and number of parts seen in a geometry int64_t recursion_level; R_xlen_t part_id[SFC_WRITER_GEOM_LENGTH]; // the current coordinate sequence and information about // where we are in the coordinate sequence SEXP coord_seq; int coord_size; uint32_t coord_id; int coord_seq_rows; // attr(sfc, "bbox"): xmin, ymin, xmax, ymax double bbox[4]; // attr(sfc, "z_range"): zmin, zmax double z_range[2]; // attr(sfc, "m_range"): mmin, mmax double m_range[2]; // attr(sfc, "precision") double precision; // used to tell if all items are the same type for output class int geometry_type; // when all elements are empty, sfc holds the classes of these objects // so in addition to knowing the common geometry type, we need to know // all types that were encountered in the off chance that they are all empty // using a bitwise OR with (1 << (wk geometry type)) int all_geometry_types; // used to enforce requirement that all sub geometries to have the same dimensions uint32_t flags; // attr(sfc, "n_empty") R_xlen_t n_empty; // sfc views NULL as equivalent to EMPTY, but we can skip this replacement if // there were not any NULLs (almost 100% of the time) int any_null; // needed to access feat_id in geometry handlers R_xlen_t feat_id; } sfc_writer_t; sfc_writer_t* sfc_writer_new(int promote_multi) { sfc_writer_t* writer = (sfc_writer_t*)malloc(sizeof(sfc_writer_t)); if (writer == NULL) { return NULL; // # nocov } writer->promote_multi = promote_multi; writer->sfc = R_NilValue; for (int i = 0; i < SFC_WRITER_GEOM_LENGTH; i++) { writer->geom[i] = R_NilValue; writer->part_id[i] = 0; } writer->recursion_level = 0; writer->coord_seq = R_NilValue; writer->coord_id = -1; writer->coord_size = 2; writer->coord_seq_rows = -1; writer->bbox[0] = R_PosInf; writer->bbox[1] = R_PosInf; writer->bbox[2] = R_NegInf; writer->bbox[3] = R_NegInf; writer->z_range[0] = R_PosInf; writer->z_range[1] = R_NegInf; writer->m_range[0] = R_PosInf; writer->m_range[1] = R_NegInf; writer->precision = R_PosInf; writer->geometry_type = SFC_GEOMETRY_TYPE_NOT_YET_DEFINED; writer->all_geometry_types = 0; writer->flags = SFC_FLAGS_NOT_YET_DEFINED; writer->n_empty = 0; writer->any_null = 0; writer->feat_id = 0; return writer; } int sfc_writer_is_nesting_geometrycollection(sfc_writer_t* writer) { return (writer->recursion_level > 0) && Rf_inherits(writer->geom[writer->recursion_level - 1], "GEOMETRYCOLLECTION"); } int sfc_writer_is_nesting_multipoint(sfc_writer_t* writer) { return Rf_inherits(writer->coord_seq, "MULTIPOINT"); } static inline int sfc_double_all_na_or_nan(int n_values, const double* values) { for (int i = 0; i < n_values; i++) { if (!ISNA(values[i]) && !ISNAN(values[i])) { return 0; } } return 1; } // this is intended to replicate NA_crs_ SEXP sfc_na_crs(void) { const char* crs_names[] = {"input", "wkt", ""}; SEXP crs = PROTECT(Rf_mkNamed(VECSXP, crs_names)); SEXP crs_input = PROTECT(Rf_allocVector(STRSXP, 1)); SET_STRING_ELT(crs_input, 0, NA_STRING); SET_VECTOR_ELT(crs, 0, crs_input); UNPROTECT(1); SEXP crs_wkt = PROTECT(Rf_allocVector(STRSXP, 1)); SET_STRING_ELT(crs_wkt, 0, NA_STRING); SET_VECTOR_ELT(crs, 1, crs_wkt); UNPROTECT(1); Rf_setAttrib(crs, R_ClassSymbol, Rf_mkString("crs")); UNPROTECT(1); return crs; } SEXP sfc_writer_empty_sfg(int geometry_type, uint32_t flags) { SEXP result = R_NilValue; int coord_size; if ((flags & WK_FLAG_HAS_Z) && (flags & WK_FLAG_HAS_M)) { coord_size = 4; } else if ((flags & WK_FLAG_HAS_Z) || (flags & WK_FLAG_HAS_M)) { coord_size = 3; } else { coord_size = 2; } switch (geometry_type) { case WK_POINT: result = PROTECT(Rf_allocVector(REALSXP, coord_size)); for (int i = 0; i < coord_size; i++) { REAL(result)[i] = NA_REAL; } break; case WK_LINESTRING: result = PROTECT(Rf_allocMatrix(REALSXP, 0, coord_size)); break; case WK_POLYGON: result = PROTECT(Rf_allocVector(VECSXP, 0)); break; case WK_MULTIPOINT: result = PROTECT(Rf_allocMatrix(REALSXP, 0, coord_size)); break; case WK_MULTILINESTRING: result = PROTECT(Rf_allocVector(VECSXP, 0)); break; case WK_MULTIPOLYGON: result = PROTECT(Rf_allocVector(VECSXP, 0)); break; case WK_GEOMETRYCOLLECTION: result = PROTECT(Rf_allocVector(VECSXP, 0)); break; default: Rf_error("Can't generate empty 'sfg' for geometry type '%d'", geometry_type); // # nocov } UNPROTECT(1); return result; } SEXP sfc_writer_promote_multi(SEXP item, int geometry_type, uint32_t flags, uint32_t size) { int coord_size; if ((flags & WK_FLAG_HAS_Z) && (flags & WK_FLAG_HAS_M)) { coord_size = 4; } else if ((flags & WK_FLAG_HAS_Z) || (flags & WK_FLAG_HAS_M)) { coord_size = 3; } else { coord_size = 2; } switch (geometry_type) { case WK_POINT: { if (size > 0) { SEXP result = PROTECT(Rf_allocMatrix(REALSXP, 1, coord_size)); memcpy(REAL(result), REAL(item), coord_size * sizeof(double)); UNPROTECT(1); return result; } else { return Rf_allocMatrix(REALSXP, 0, coord_size); } } case WK_LINESTRING: case WK_POLYGON: { if (size > 0) { SEXP result = PROTECT(Rf_allocVector(VECSXP, 1)); Rf_setAttrib(item, R_ClassSymbol, R_NilValue); SET_VECTOR_ELT(result, 0, item); UNPROTECT(1); return result; } else { return Rf_allocVector(VECSXP, 0); } } default: return item; } } void sfc_writer_maybe_add_class_to_sfg(sfc_writer_t* writer, SEXP item, int geometry_type, uint32_t flags) { if (writer->recursion_level == 0 || sfc_writer_is_nesting_geometrycollection(writer)) { // in the form XY(ZM), GEOM_TYPE, sfg SEXP class = PROTECT(Rf_allocVector(STRSXP, 3)); SET_STRING_ELT(class, 2, Rf_mkChar("sfg")); if ((flags & WK_FLAG_HAS_Z) && (flags & WK_FLAG_HAS_M)) { SET_STRING_ELT(class, 0, Rf_mkChar("XYZM")); } else if (flags & WK_FLAG_HAS_Z) { SET_STRING_ELT(class, 0, Rf_mkChar("XYZ")); } else if (flags & WK_FLAG_HAS_M) { SET_STRING_ELT(class, 0, Rf_mkChar("XYM")); } else { SET_STRING_ELT(class, 0, Rf_mkChar("XY")); } switch (geometry_type) { case WK_POINT: SET_STRING_ELT(class, 1, Rf_mkChar("POINT")); break; case WK_LINESTRING: SET_STRING_ELT(class, 1, Rf_mkChar("LINESTRING")); break; case WK_POLYGON: SET_STRING_ELT(class, 1, Rf_mkChar("POLYGON")); break; case WK_MULTIPOINT: SET_STRING_ELT(class, 1, Rf_mkChar("MULTIPOINT")); break; case WK_MULTILINESTRING: SET_STRING_ELT(class, 1, Rf_mkChar("MULTILINESTRING")); break; case WK_MULTIPOLYGON: SET_STRING_ELT(class, 1, Rf_mkChar("MULTIPOLYGON")); break; case WK_GEOMETRYCOLLECTION: SET_STRING_ELT(class, 1, Rf_mkChar("GEOMETRYCOLLECTION")); break; default: Rf_error("Can't generate class 'sfg' for geometry type '%d'", geometry_type); // # nocov } Rf_setAttrib(item, R_ClassSymbol, class); UNPROTECT(1); } } void sfc_writer_update_dimensions(sfc_writer_t* writer, const wk_meta_t* meta, uint32_t size) { if (size > 0) { if (writer->flags == SFC_FLAGS_NOT_YET_DEFINED) { writer->flags = meta->flags; } else if (writer->flags != meta->flags) { Rf_error("Can't convert geometries with incompatible dimensions to 'sfc'"); } } } void sfc_writer_update_vector_attributes(sfc_writer_t* writer, const wk_meta_t* meta, int geometry_type, uint32_t size) { // all geometry types specifically matters for when everything is EMPTY writer->all_geometry_types = writer->all_geometry_types | (1 << (geometry_type - 1)); // these matter even for EMPTY if (writer->geometry_type == SFC_GEOMETRY_TYPE_NOT_YET_DEFINED) { writer->geometry_type = geometry_type; } else if (writer->geometry_type != geometry_type) { writer->geometry_type = WK_GEOMETRY; } // update empty count writer->n_empty += size == 0; // update dimensions sfc_writer_update_dimensions(writer, meta, size); // update precision writer->precision = MIN(writer->precision, meta->precision); } void sfc_writer_update_ranges(sfc_writer_t* writer, const wk_meta_t* meta, const double* coord) { writer->bbox[0] = MIN(writer->bbox[0], coord[0]); writer->bbox[1] = MIN(writer->bbox[1], coord[1]); writer->bbox[2] = MAX(writer->bbox[2], coord[0]); writer->bbox[3] = MAX(writer->bbox[3], coord[1]); if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) { writer->z_range[0] = MIN(writer->z_range[0], coord[2]); writer->z_range[1] = MAX(writer->z_range[1], coord[2]); writer->m_range[0] = MIN(writer->m_range[0], coord[3]); writer->m_range[1] = MAX(writer->m_range[1], coord[3]); } else if (meta->flags & WK_FLAG_HAS_Z) { writer->z_range[0] = MIN(writer->z_range[0], coord[2]); writer->z_range[1] = MAX(writer->z_range[1], coord[2]); } else if (meta->flags & WK_FLAG_HAS_M) { writer->m_range[0] = MIN(writer->m_range[0], coord[2]); writer->m_range[1] = MAX(writer->m_range[1], coord[2]); } } SEXP sfc_writer_alloc_coord_seq(uint32_t size_hint, int coord_size) { if (size_hint == WK_SIZE_UNKNOWN) { size_hint = SFC_INITIAL_SIZE_IF_UNKNOWN; } return Rf_allocMatrix(REALSXP, size_hint, coord_size); } SEXP sfc_writer_realloc_coord_seq(SEXP coord_seq, uint32_t new_size) { uint32_t current_size = Rf_nrows(coord_seq); int coord_size = Rf_ncols(coord_seq); SEXP new_coord_seq = PROTECT(Rf_allocMatrix(REALSXP, new_size, coord_size)); double* old_values = REAL(coord_seq); double* new_values = REAL(new_coord_seq); for (int j = 0; j < coord_size; j++) { memcpy(new_values + (j * new_size), old_values + (j * current_size), sizeof(double) * current_size); } if (Rf_inherits(coord_seq, "sfg")) { SEXP class = PROTECT(Rf_getAttrib(coord_seq, R_ClassSymbol)); Rf_setAttrib(new_coord_seq, R_ClassSymbol, class); UNPROTECT(1); } UNPROTECT(1); return new_coord_seq; } SEXP sfc_writer_finalize_coord_seq(SEXP coord_seq, uint32_t final_size) { uint32_t current_size = Rf_nrows(coord_seq); int coord_size = Rf_ncols(coord_seq); SEXP new_coord_seq = PROTECT(Rf_allocMatrix(REALSXP, final_size, coord_size)); double* old_values = REAL(coord_seq); double* new_values = REAL(new_coord_seq); for (int j = 0; j < coord_size; j++) { memcpy(new_values + (j * final_size), old_values + (j * current_size), sizeof(double) * final_size); } if (Rf_inherits(coord_seq, "sfg")) { SEXP class = PROTECT(Rf_getAttrib(coord_seq, R_ClassSymbol)); Rf_setAttrib(new_coord_seq, R_ClassSymbol, class); UNPROTECT(1); } UNPROTECT(1); return new_coord_seq; } SEXP sfc_writer_alloc_geom(uint32_t size_hint) { if (size_hint == WK_SIZE_UNKNOWN) { size_hint = SFC_INITIAL_SIZE_IF_UNKNOWN; } return Rf_allocVector(VECSXP, size_hint); } SEXP sfc_writer_realloc_geom(SEXP geom, R_xlen_t new_size) { R_xlen_t current_size = Rf_xlength(geom); SEXP new_geom = PROTECT(Rf_allocVector(VECSXP, new_size)); for (R_xlen_t i = 0; i < current_size; i++) { SET_VECTOR_ELT(new_geom, i, VECTOR_ELT(geom, i)); } if (Rf_inherits(geom, "sfg")) { SEXP class = PROTECT(Rf_getAttrib(geom, R_ClassSymbol)); Rf_setAttrib(new_geom, R_ClassSymbol, class); UNPROTECT(1); } UNPROTECT(1); return new_geom; } SEXP sfc_writer_finalize_geom(SEXP geom, R_xlen_t final_size) { SEXP new_geom = PROTECT(Rf_allocVector(VECSXP, final_size)); for (R_xlen_t i = 0; i < final_size; i++) { SET_VECTOR_ELT(new_geom, i, VECTOR_ELT(geom, i)); } if (Rf_inherits(geom, "sfg")) { SEXP class = PROTECT(Rf_getAttrib(geom, R_ClassSymbol)); Rf_setAttrib(new_geom, R_ClassSymbol, class); UNPROTECT(1); } UNPROTECT(1); return new_geom; } static inline void sfc_writer_sfc_append(sfc_writer_t* writer, SEXP value) { R_xlen_t current_size = Rf_xlength(writer->sfc); if (writer->feat_id >= current_size) { SEXP new_result = PROTECT(Rf_allocVector(VECSXP, current_size * 2 + 1)); for (R_xlen_t i = 0; i < current_size; i++) { SET_VECTOR_ELT(new_result, i, VECTOR_ELT(writer->sfc, i)); } R_ReleaseObject(writer->sfc); writer->sfc = new_result; R_PreserveObject(writer->sfc); UNPROTECT(1); } SET_VECTOR_ELT(writer->sfc, writer->feat_id, value); writer->feat_id++; } static inline void sfc_writer_sfc_finalize(sfc_writer_t* writer) { R_xlen_t current_size = Rf_xlength(writer->sfc); if (writer->feat_id != current_size) { SEXP new_result = PROTECT(Rf_allocVector(VECSXP, writer->feat_id)); for (R_xlen_t i = 0; i < writer->feat_id; i++) { SET_VECTOR_ELT(new_result, i, VECTOR_ELT(writer->sfc, i)); } R_ReleaseObject(writer->sfc); writer->sfc = new_result; R_PreserveObject(writer->sfc); UNPROTECT(1); } } int sfc_writer_vector_start(const wk_vector_meta_t* vector_meta, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; if (writer->sfc != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } if (vector_meta->size == WK_VECTOR_SIZE_UNKNOWN) { writer->sfc = PROTECT(Rf_allocVector(VECSXP, 1024)); } else { writer->sfc = PROTECT(Rf_allocVector(VECSXP, vector_meta->size)); } R_PreserveObject(writer->sfc); UNPROTECT(1); writer->feat_id = 0; return WK_CONTINUE; } int sfc_writer_feature_start(const wk_vector_meta_t* vector_meta, R_xlen_t feat_id, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; writer->recursion_level = 0; return WK_CONTINUE; } int sfc_writer_null_feature(void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; // sfc doesn't do NULLs and replaces them with GEOMETRYCOLLECTION EMPTY // however, as the dimensions have to align among features we asign a NULL here and fix // in vector_end() writer->any_null = 1; sfc_writer_sfc_append(writer, R_NilValue); return WK_ABORT_FEATURE; } int sfc_writer_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; // ignore start of POINT nested in MULTIPOINT int nesting_multipoint = sfc_writer_is_nesting_multipoint(writer); if (meta->geometry_type == WK_POINT && nesting_multipoint) { return WK_CONTINUE; } else if (nesting_multipoint) { Rf_error("Expected geometry type nested within MULTIPOINT to be a POINT"); } if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) { writer->coord_size = 4; } else if ((meta->flags & WK_FLAG_HAS_Z) || (meta->flags & WK_FLAG_HAS_M)) { writer->coord_size = 3; } else { writer->coord_size = 2; } // there isn't quite enough information here yet for points, which can // be considered empty if coordinates are NA if ((writer->recursion_level == 0) && (meta->geometry_type != WK_POINT) && !writer->promote_multi) { sfc_writer_update_vector_attributes(writer, meta, meta->geometry_type, meta->size); } else if ((writer->recursion_level < 0) || (writer->recursion_level >= SFC_MAX_RECURSION_DEPTH)) { Rf_error("Invalid recursion depth whilst parsing 'sfg': %d", (int)writer->recursion_level); } // if POINT, LINESTRING, or MULTIPOINT // replace coordinate sequence with a fresh one // otherwise, create a list() container and push it to the writer->geom[] stack switch (meta->geometry_type) { case WK_POINT: if (writer->coord_seq != R_NilValue) R_ReleaseObject(writer->coord_seq); writer->coord_seq = PROTECT(Rf_allocVector(REALSXP, writer->coord_size)); // empty point is NA, NA ... if (meta->size == 0) { for (int i = 0; i < writer->coord_size; i++) { REAL(writer->coord_seq)[i] = NA_REAL; } } sfc_writer_maybe_add_class_to_sfg(writer, writer->coord_seq, meta->geometry_type, meta->flags); R_PreserveObject(writer->coord_seq); UNPROTECT(1); writer->coord_id = 0; writer->coord_seq_rows = 1; break; case WK_LINESTRING: case WK_MULTIPOINT: if (writer->coord_seq != R_NilValue) R_ReleaseObject(writer->coord_seq); writer->coord_seq = PROTECT(sfc_writer_alloc_coord_seq(meta->size, writer->coord_size)); sfc_writer_maybe_add_class_to_sfg(writer, writer->coord_seq, meta->geometry_type, meta->flags); R_PreserveObject(writer->coord_seq); UNPROTECT(1); writer->coord_id = 0; writer->coord_seq_rows = Rf_nrows(writer->coord_seq); break; case WK_POLYGON: case WK_MULTILINESTRING: case WK_MULTIPOLYGON: case WK_GEOMETRYCOLLECTION: if (writer->geom[writer->recursion_level] != R_NilValue) { R_ReleaseObject(writer->geom[writer->recursion_level]); } writer->geom[writer->recursion_level] = PROTECT(sfc_writer_alloc_geom(meta->size)); sfc_writer_maybe_add_class_to_sfg(writer, writer->geom[writer->recursion_level], meta->geometry_type, meta->flags); R_PreserveObject(writer->geom[writer->recursion_level]); UNPROTECT(1); writer->part_id[writer->recursion_level] = 0; break; default: Rf_error("Can't convert geometry type '%d' to sfg", meta->geometry_type); // # nocov break; } writer->recursion_level++; return WK_CONTINUE; } int sfc_writer_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; if (writer->coord_seq != NULL) { R_ReleaseObject(writer->coord_seq); } writer->coord_seq = PROTECT(sfc_writer_alloc_coord_seq(size, writer->coord_size)); R_PreserveObject(writer->coord_seq); UNPROTECT(1); writer->coord_id = 0; writer->coord_seq_rows = Rf_nrows(writer->coord_seq); writer->recursion_level++; return WK_CONTINUE; } int sfc_writer_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; // This point might be EMPTY, in which case it will cause the ranges to be all NaN if ((meta->geometry_type != WK_POINT) || (!sfc_double_all_na_or_nan(writer->coord_size, coord))) { sfc_writer_update_ranges(writer, meta, coord); } // realloc the coordinate sequence if necessary if (writer->coord_id >= writer->coord_seq_rows) { SEXP new_coord_seq = PROTECT( sfc_writer_realloc_coord_seq(writer->coord_seq, writer->coord_id * 1.5 + 1)); R_ReleaseObject(writer->coord_seq); writer->coord_seq = new_coord_seq; R_PreserveObject(writer->coord_seq); UNPROTECT(1); writer->coord_seq_rows = Rf_nrows(writer->coord_seq); } double* current_values = REAL(writer->coord_seq); for (int i = 0; i < writer->coord_size; i++) { current_values[i * writer->coord_seq_rows + writer->coord_id] = coord[i]; } writer->coord_id++; return WK_CONTINUE; } int sfc_writer_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; writer->recursion_level--; if (writer->recursion_level < 0) { Rf_error("Recursion level underflowed"); // # nocov } SEXP geom; if (writer->coord_id < Rf_nrows(writer->coord_seq)) { geom = PROTECT(sfc_writer_finalize_coord_seq(writer->coord_seq, writer->coord_id)); } else { geom = PROTECT(writer->coord_seq); } R_ReleaseObject(writer->coord_seq); writer->coord_seq = R_NilValue; // may need to reallocate the container R_xlen_t container_len = Rf_xlength(writer->geom[writer->recursion_level - 1]); if (ring_id >= container_len) { SEXP new_geom = PROTECT(sfc_writer_realloc_geom( writer->geom[writer->recursion_level - 1], container_len * 1.5 + 1)); R_ReleaseObject(writer->geom[writer->recursion_level - 1]); writer->geom[writer->recursion_level - 1] = new_geom; R_PreserveObject(writer->geom[writer->recursion_level - 1]); UNPROTECT(1); } SET_VECTOR_ELT(writer->geom[writer->recursion_level - 1], ring_id, geom); writer->part_id[writer->recursion_level - 1]++; UNPROTECT(1); return WK_CONTINUE; } int sfc_writer_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; // ignore end of POINT nested in MULTIPOINT int nesting_multipoint = sfc_writer_is_nesting_multipoint(writer); if ((meta->geometry_type == WK_POINT) && nesting_multipoint) { return WK_CONTINUE; } writer->recursion_level--; if (writer->recursion_level < 0) { Rf_error("Recursion level underflowed"); // # nocov } SEXP geom; switch (meta->geometry_type) { case WK_POINT: geom = PROTECT(writer->coord_seq); R_ReleaseObject(writer->coord_seq); writer->coord_seq = R_NilValue; break; case WK_LINESTRING: case WK_MULTIPOINT: if (writer->coord_id < Rf_nrows(writer->coord_seq)) { geom = PROTECT(sfc_writer_finalize_coord_seq(writer->coord_seq, writer->coord_id)); } else { geom = PROTECT(writer->coord_seq); } R_ReleaseObject(writer->coord_seq); writer->coord_seq = R_NilValue; break; case WK_POLYGON: case WK_MULTILINESTRING: case WK_MULTIPOLYGON: case WK_GEOMETRYCOLLECTION: if (writer->part_id[writer->recursion_level] < Rf_xlength(writer->geom[writer->recursion_level])) { geom = PROTECT(sfc_writer_finalize_geom(writer->geom[writer->recursion_level], writer->part_id[writer->recursion_level])); } else { geom = PROTECT(writer->geom[writer->recursion_level]); } // R_ReleaseObject() is called on `geom` in finalize() or // when it is replaced in geometry_start() break; default: Rf_error("Can't convert geometry type '%d' to sfg", meta->geometry_type); // # nocov break; // # nocov } // Top-level geometries have their dimensions checked but nested must be as well if ((writer->recursion_level) > 0 && (meta->geometry_type == WK_POINT)) { int all_na = sfc_double_all_na_or_nan(writer->coord_size, REAL(geom)); sfc_writer_update_dimensions(writer, meta, meta->size && !all_na); } else if (writer->recursion_level > 0) { sfc_writer_update_dimensions(writer, meta, meta->size); } // if we're above a top-level geometry, this geometry needs to be added to the parent // otherwise, it needs to be added to sfc if (writer->recursion_level > 0) { // may need to reallocate the container R_xlen_t container_len = Rf_xlength(writer->geom[writer->recursion_level - 1]); if (part_id >= container_len) { SEXP new_geom = PROTECT(sfc_writer_realloc_geom( writer->geom[writer->recursion_level - 1], container_len * 1.5 + 1)); R_ReleaseObject(writer->geom[writer->recursion_level - 1]); writer->geom[writer->recursion_level - 1] = new_geom; R_PreserveObject(writer->geom[writer->recursion_level - 1]); UNPROTECT(1); } SET_VECTOR_ELT(writer->geom[writer->recursion_level - 1], part_id, geom); writer->part_id[writer->recursion_level - 1]++; } else if (meta->geometry_type == WK_POINT) { // at the top level, we have to check again if all point coordinates are NA // because this is 'empty' for the purposes of sfc // We didn't update this earlier because we didn't know if the point was // empty yet or not! int all_na = sfc_double_all_na_or_nan(writer->coord_size, REAL(geom)); // Promote geometry to multi if necessary if (writer->promote_multi) { SEXP item_to_append = PROTECT( sfc_writer_promote_multi(geom, WK_POINT, meta->flags, meta->size && !all_na)); sfc_writer_maybe_add_class_to_sfg(writer, item_to_append, WK_MULTIPOINT, meta->flags); sfc_writer_update_vector_attributes(writer, meta, WK_MULTIPOINT, meta->size && !all_na); sfc_writer_sfc_append(writer, item_to_append); UNPROTECT(1); } else { sfc_writer_update_vector_attributes(writer, meta, WK_POINT, meta->size && !all_na); sfc_writer_sfc_append(writer, geom); } } else if (writer->promote_multi) { SEXP item_to_append = PROTECT( sfc_writer_promote_multi(geom, meta->geometry_type, meta->flags, meta->size)); int geometry_type_to_append = meta->geometry_type <= WK_POLYGON ? meta->geometry_type + 3L : meta->geometry_type; sfc_writer_maybe_add_class_to_sfg(writer, item_to_append, geometry_type_to_append, meta->flags); sfc_writer_update_vector_attributes(writer, meta, geometry_type_to_append, meta->size); sfc_writer_sfc_append(writer, item_to_append); UNPROTECT(1); } else { sfc_writer_sfc_append(writer, geom); } UNPROTECT(1); return WK_CONTINUE; } SEXP sfc_writer_vector_end(const wk_vector_meta_t* vector_meta, void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; sfc_writer_sfc_finalize(writer); // replace NULLs with EMPTY of an appropriate type if (writer->any_null) { wk_meta_t meta; if (writer->geometry_type == WK_GEOMETRY || writer->geometry_type == SFC_GEOMETRY_TYPE_NOT_YET_DEFINED) { WK_META_RESET(meta, WK_GEOMETRYCOLLECTION); // also update the type list for attr(sfc, "classes") writer->all_geometry_types = writer->all_geometry_types | (1 << (WK_GEOMETRYCOLLECTION - 1)); } else { WK_META_RESET(meta, writer->geometry_type); } if (writer->flags != SFC_FLAGS_NOT_YET_DEFINED) { meta.flags = writer->flags; } if (writer->geometry_type == SFC_GEOMETRY_TYPE_NOT_YET_DEFINED) { writer->geometry_type = WK_GEOMETRYCOLLECTION; } meta.size = 0; writer->recursion_level = 0; SEXP empty = PROTECT(sfc_writer_empty_sfg(meta.geometry_type, meta.flags)); sfc_writer_maybe_add_class_to_sfg(writer, empty, meta.geometry_type, meta.flags); for (R_xlen_t i = 0; i < Rf_xlength(writer->sfc); i++) { if (VECTOR_ELT(writer->sfc, i) == R_NilValue) { writer->n_empty++; SET_VECTOR_ELT(writer->sfc, i, empty); } } UNPROTECT(1); } // attr(sfc, "precision") SEXP precision; if (writer->precision == R_PosInf) { precision = PROTECT(Rf_ScalarReal(0.0)); } else { precision = PROTECT(Rf_ScalarReal(writer->precision)); } Rf_setAttrib(writer->sfc, Rf_install("precision"), precision); UNPROTECT(1); // attr(sfc, "bbox") const char* bbox_names[] = {"xmin", "ymin", "xmax", "ymax", ""}; SEXP bbox = PROTECT(Rf_mkNamed(REALSXP, bbox_names)); Rf_setAttrib(bbox, R_ClassSymbol, Rf_mkString("bbox")); // the bounding box may or may not have a crs attribute // when all features are empty if (Rf_xlength(writer->sfc) == writer->n_empty) { SEXP na_crs = PROTECT(sfc_na_crs()); Rf_setAttrib(bbox, Rf_install("crs"), na_crs); UNPROTECT(1); } // if the bounding box was never updated, set it to NAs if (writer->bbox[0] == R_PosInf) { writer->bbox[0] = NA_REAL; writer->bbox[1] = NA_REAL; writer->bbox[2] = NA_REAL; writer->bbox[3] = NA_REAL; } memcpy(REAL(bbox), writer->bbox, sizeof(double) * 4); Rf_setAttrib(writer->sfc, Rf_install("bbox"), bbox); UNPROTECT(1); // attr(sfc, "z_range"), attr(sfc, "m_range") if (writer->flags == SFC_FLAGS_NOT_YET_DEFINED) { writer->flags = 0; } if (writer->flags & WK_FLAG_HAS_Z) { // if the z_range was never updated, set it to NAs if (writer->z_range[0] == R_PosInf) { writer->z_range[0] = NA_REAL; writer->z_range[1] = NA_REAL; } const char* z_range_names[] = {"zmin", "zmax", ""}; SEXP z_range = PROTECT(Rf_mkNamed(REALSXP, z_range_names)); Rf_setAttrib(z_range, R_ClassSymbol, Rf_mkString("z_range")); memcpy(REAL(z_range), writer->z_range, sizeof(double) * 2); Rf_setAttrib(writer->sfc, Rf_install("z_range"), z_range); UNPROTECT(1); } if (writer->flags & WK_FLAG_HAS_M) { // if the m_range was never updated, set it to NAs if (writer->m_range[0] == R_PosInf) { writer->m_range[0] = NA_REAL; writer->m_range[1] = NA_REAL; } const char* m_range_names[] = {"mmin", "mmax", ""}; SEXP m_range = PROTECT(Rf_mkNamed(REALSXP, m_range_names)); Rf_setAttrib(m_range, R_ClassSymbol, Rf_mkString("m_range")); memcpy(REAL(m_range), writer->m_range, sizeof(double) * 2); Rf_setAttrib(writer->sfc, Rf_install("m_range"), m_range); UNPROTECT(1); } // attr(sfc, "crs") // this should be handled in R; however, inserting a placeholder here // because the print() method for sfc will error otherwise SEXP na_crs = PROTECT(sfc_na_crs()); Rf_setAttrib(writer->sfc, Rf_install("crs"), na_crs); UNPROTECT(1); // attr(sfc, "n_empty") SEXP n_empty = PROTECT(Rf_ScalarInteger(writer->n_empty)); Rf_setAttrib(writer->sfc, Rf_install("n_empty"), n_empty); UNPROTECT(1); // class(sfc) SEXP class = PROTECT(Rf_allocVector(STRSXP, 2)); switch (writer->geometry_type) { case WK_POINT: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_POINT")); break; case WK_LINESTRING: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_LINESTRING")); break; case WK_POLYGON: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_POLYGON")); break; case WK_MULTIPOINT: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_MULTIPOINT")); break; case WK_MULTILINESTRING: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_MULTILINESTRING")); break; case WK_MULTIPOLYGON: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_MULTIPOLYGON")); break; case WK_GEOMETRYCOLLECTION: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_GEOMETRYCOLLECTION")); break; default: SET_STRING_ELT(class, 0, Rf_mkChar("sfc_GEOMETRY")); break; } SET_STRING_ELT(class, 1, Rf_mkChar("sfc")); Rf_setAttrib(writer->sfc, R_ClassSymbol, class); UNPROTECT(1); // attr(sfc, "classes") (only for sfc_GEOMETRY) // This is class(x[[i]])[[2]] for each sfg in the output sfc R_xlen_t out_length = Rf_xlength(writer->sfc); if (writer->geometry_type == WK_GEOMETRY || out_length == 0) { SEXP classes = PROTECT(Rf_allocVector(STRSXP, out_length)); for (R_xlen_t i = 0; i < out_length; i++) { SEXP item = VECTOR_ELT(writer->sfc, i); SEXP cls = Rf_getAttrib(item, R_ClassSymbol); SET_STRING_ELT(classes, i, STRING_ELT(cls, 1)); } Rf_setAttrib(writer->sfc, Rf_install("classes"), classes); UNPROTECT(1); } return writer->sfc; } void sfc_writer_deinitialize(void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; if (writer->sfc != R_NilValue) { R_ReleaseObject(writer->sfc); writer->sfc = R_NilValue; } for (int i = 0; i < (SFC_WRITER_GEOM_LENGTH); i++) { if (writer->geom[i] != R_NilValue) { R_ReleaseObject(writer->geom[i]); writer->geom[i] = R_NilValue; } } if (writer->coord_seq != R_NilValue) { R_ReleaseObject(writer->coord_seq); writer->coord_seq = R_NilValue; } } void sfc_writer_finalize(void* handler_data) { sfc_writer_t* writer = (sfc_writer_t*)handler_data; if (writer != NULL) { free(writer); } } SEXP wk_c_sfc_writer_new(SEXP promote_multi_sexp) { int promote_multi = LOGICAL(promote_multi_sexp)[0]; wk_handler_t* handler = wk_handler_create(); handler->finalizer = &sfc_writer_finalize; handler->vector_start = &sfc_writer_vector_start; handler->feature_start = &sfc_writer_feature_start; handler->null_feature = &sfc_writer_null_feature; handler->geometry_start = &sfc_writer_geometry_start; handler->ring_start = &sfc_writer_ring_start; handler->coord = &sfc_writer_coord; handler->ring_end = &sfc_writer_ring_end; handler->geometry_end = &sfc_writer_geometry_end; handler->vector_end = &sfc_writer_vector_end; handler->deinitialize = &sfc_writer_deinitialize; handler->handler_data = sfc_writer_new(promote_multi); if (handler->handler_data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue); return xptr; } wk/src/trans-affine.c0000644000176200001440000000322614510132141014176 0ustar liggesusers#define R_NO_REMAP #include #include #include #include "wk-v1.h" int wk_trans_affine_trans(R_xlen_t feature_id, const double* xyzm_in, double* xyzm_out, void* trans_data) { double* t = (double*)trans_data; xyzm_out[0] = t[0] * xyzm_in[0] + t[2] * xyzm_in[1] + t[4]; xyzm_out[1] = t[1] * xyzm_in[0] + t[3] * xyzm_in[1] + t[5]; xyzm_out[2] = xyzm_in[2]; xyzm_out[3] = xyzm_in[3]; return WK_CONTINUE; } void wk_trans_affine_finalize(void* trans_data) { free(trans_data); } SEXP wk_c_trans_affine_new(SEXP trans_matrix) { if (!Rf_isMatrix(trans_matrix) || (Rf_nrows(trans_matrix) != 3) || (Rf_ncols(trans_matrix) != 3)) { Rf_error("`trans_matrix` must be a 3x3 matrix"); } // create the wk_trans object wk_trans_t* trans = wk_trans_create(); trans->trans = &wk_trans_affine_trans; trans->finalizer = &wk_trans_affine_finalize; // simplify the affine transform data to six numbers double* trans_matrix_ptr = REAL(trans_matrix); double* t = (double*)malloc(6 * sizeof(double)); if (t == NULL) { free(trans); // # nocov Rf_error("Failed to alloc double[6]"); // # nocov } t[0] = trans_matrix_ptr[0]; t[1] = trans_matrix_ptr[1]; t[2] = trans_matrix_ptr[3]; t[3] = trans_matrix_ptr[4]; t[4] = trans_matrix_ptr[6]; t[5] = trans_matrix_ptr[7]; // this *is* the only data we need trans->trans_data = t; // keep the trans matrix as a tag so that we can return it in as.matrix() return wk_trans_create_xptr(trans, trans_matrix, R_NilValue); } SEXP wk_c_trans_affine_as_matrix(SEXP trans_xptr) { return R_ExternalPtrTag(trans_xptr); } wk/src/handle-sfc.c0000644000176200001440000003052514510132141013627 0ustar liggesusers #define R_NO_REMAP #include #include #include #include "wk-v1.h" #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result != WK_CONTINUE) return result #define HANDLE_CONTINUE_OR_BREAK(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) \ continue; \ else if (result == WK_ABORT) \ break int wk_sfc_read_sfg(SEXP x, wk_handler_t* handler, uint32_t part_id, double precision); int wk_sfc_read_point(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id); int wk_sfc_read_linestring(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id); int wk_sfc_read_polygon(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id); int wk_sfc_read_multipoint(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id); int wk_sfc_read_multilinestring(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id); int wk_sfc_read_multipolygon(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id); int wk_sfc_read_geometrycollection(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id); void wk_update_meta_from_sfg(SEXP x, wk_meta_t* meta); void wk_update_vector_meta_from_sfc(SEXP x, wk_vector_meta_t* vector_meta); double wk_sfc_precision(SEXP x); SEXP wk_c_read_sfc_impl(SEXP data, wk_handler_t* handler) { R_xlen_t n_features = Rf_xlength(data); wk_vector_meta_t vector_meta; WK_VECTOR_META_RESET(vector_meta, WK_GEOMETRY); vector_meta.size = n_features; wk_update_vector_meta_from_sfc(data, &vector_meta); double precision = wk_sfc_precision(data); if (handler->vector_start(&vector_meta, handler->handler_data) != WK_ABORT) { int result; SEXP item; for (R_xlen_t i = 0; i < n_features; i++) { if (((i + 1) % 1000) == 0) R_CheckUserInterrupt(); HANDLE_CONTINUE_OR_BREAK( handler->feature_start(&vector_meta, i, handler->handler_data)); item = VECTOR_ELT(data, i); if (item == R_NilValue) { HANDLE_CONTINUE_OR_BREAK(handler->null_feature(handler->handler_data)); } else { HANDLE_CONTINUE_OR_BREAK( wk_sfc_read_sfg(item, handler, WK_PART_ID_NONE, precision)); } if (handler->feature_end(&vector_meta, i, handler->handler_data) == WK_ABORT) { break; } } } return handler->vector_end(&vector_meta, handler->handler_data); } SEXP wk_c_read_sfc(SEXP data, SEXP handler_xptr) { return wk_handler_run_xptr(&wk_c_read_sfc_impl, data, handler_xptr); } int wk_sfc_read_sfg(SEXP x, wk_handler_t* handler, uint32_t part_id, double precision) { wk_meta_t meta; WK_META_RESET(meta, WK_GEOMETRY); wk_update_meta_from_sfg(x, &meta); meta.precision = precision; if (Rf_inherits(x, "POINT")) { return wk_sfc_read_point(x, handler, &meta, part_id); } else if (Rf_inherits(x, "LINESTRING")) { return wk_sfc_read_linestring(x, handler, &meta, part_id); } else if (Rf_inherits(x, "POLYGON")) { return wk_sfc_read_polygon(x, handler, &meta, part_id); } else if (Rf_inherits(x, "MULTIPOINT")) { return wk_sfc_read_multipoint(x, handler, &meta, part_id); } else if (Rf_inherits(x, "MULTILINESTRING")) { return wk_sfc_read_multilinestring(x, handler, &meta, part_id); } else if (Rf_inherits(x, "MULTIPOLYGON")) { return wk_sfc_read_multipolygon(x, handler, &meta, part_id); } else if (Rf_inherits(x, "GEOMETRYCOLLECTION")) { return wk_sfc_read_geometrycollection(x, handler, &meta, part_id); } else if (Rf_inherits(x, "sfg")) { Rf_error("Unsupported sfg type"); } else { Rf_error("Element of sfc list must inherit from 'sfg'"); } // should never be reached return WK_ABORT; // # nocov } int wk_sfc_read_point(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id) { int result; meta->geometry_type = WK_POINT; meta->size = 0; double* values = REAL(x); int coord_size = Rf_length(x); for (int i = 0; i < coord_size; i++) { if (!ISNA(values[i]) && !ISNAN(values[i])) { meta->size = 1; break; } } HANDLE_OR_RETURN(handler->geometry_start(meta, part_id, handler->handler_data)); if (meta->size) { double coord[4]; memcpy(coord, REAL(x), sizeof(double) * coord_size); HANDLE_OR_RETURN(handler->coord(meta, coord, 0, handler->handler_data)); } HANDLE_OR_RETURN(handler->geometry_end(meta, part_id, handler->handler_data)); return WK_CONTINUE; } int wk_sfc_read_linestring(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id) { int result; meta->geometry_type = WK_LINESTRING; meta->size = Rf_nrows(x); int coord_size = Rf_ncols(x); HANDLE_OR_RETURN(handler->geometry_start(meta, part_id, handler->handler_data)); double coord[4]; double* coords = REAL(x); for (uint32_t i = 0; i < meta->size; i++) { for (int j = 0; j < coord_size; j++) { coord[j] = coords[j * meta->size + i]; } HANDLE_OR_RETURN(handler->coord(meta, coord, i, handler->handler_data)); } HANDLE_OR_RETURN(handler->geometry_end(meta, part_id, handler->handler_data)); return WK_CONTINUE; } int wk_sfc_read_polygon(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id) { int result; meta->geometry_type = WK_POLYGON; meta->size = Rf_xlength(x); HANDLE_OR_RETURN(handler->geometry_start(meta, part_id, handler->handler_data)); SEXP ring; for (uint32_t ring_id = 0; ring_id < meta->size; ring_id++) { ring = VECTOR_ELT(x, ring_id); uint32_t ring_size = Rf_nrows(ring); int coord_size = Rf_ncols(ring); HANDLE_OR_RETURN( handler->ring_start(meta, meta->size, ring_id, handler->handler_data)); double coord[4]; double* coords = REAL(ring); for (uint32_t i = 0; i < ring_size; i++) { for (int j = 0; j < coord_size; j++) { coord[j] = coords[j * ring_size + i]; } HANDLE_OR_RETURN(handler->coord(meta, coord, i, handler->handler_data)); } HANDLE_OR_RETURN(handler->ring_end(meta, meta->size, ring_id, handler->handler_data)); } HANDLE_OR_RETURN(handler->geometry_end(meta, part_id, handler->handler_data)); return WK_CONTINUE; } int wk_sfc_read_multipoint(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id) { int result; meta->geometry_type = WK_MULTIPOINT; meta->size = Rf_nrows(x); int coord_size = Rf_ncols(x); wk_meta_t child_meta; WK_META_RESET(child_meta, WK_POINT); child_meta.size = 1; child_meta.flags = meta->flags; HANDLE_OR_RETURN(handler->geometry_start(meta, part_id, handler->handler_data)); double coord[4]; double* coords = REAL(x); for (uint32_t i = 0; i < meta->size; i++) { for (int j = 0; j < coord_size; j++) { coord[j] = coords[j * meta->size + i]; } HANDLE_OR_RETURN(handler->geometry_start(&child_meta, i, handler->handler_data)); HANDLE_OR_RETURN(handler->coord(&child_meta, coord, 0, handler->handler_data)); HANDLE_OR_RETURN(handler->geometry_end(&child_meta, i, handler->handler_data)); } HANDLE_OR_RETURN(handler->geometry_end(meta, part_id, handler->handler_data)); return WK_CONTINUE; } int wk_sfc_read_multilinestring(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id) { int result; meta->geometry_type = WK_MULTILINESTRING; wk_meta_t child_meta; WK_META_RESET(child_meta, WK_LINESTRING); child_meta.flags = meta->flags; meta->size = Rf_xlength(x); HANDLE_OR_RETURN(handler->geometry_start(meta, part_id, handler->handler_data)); for (uint32_t child_part_id = 0; child_part_id < meta->size; child_part_id++) { HANDLE_OR_RETURN(wk_sfc_read_linestring(VECTOR_ELT(x, child_part_id), handler, &child_meta, child_part_id)); } HANDLE_OR_RETURN(handler->geometry_end(meta, part_id, handler->handler_data)); return WK_CONTINUE; } int wk_sfc_read_multipolygon(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id) { int result; meta->geometry_type = WK_MULTIPOLYGON; wk_meta_t child_meta; WK_META_RESET(child_meta, WK_POLYGON); child_meta.flags = meta->flags; meta->size = Rf_xlength(x); HANDLE_OR_RETURN(handler->geometry_start(meta, part_id, handler->handler_data)); for (uint32_t child_part_id = 0; child_part_id < meta->size; child_part_id++) { HANDLE_OR_RETURN(wk_sfc_read_polygon(VECTOR_ELT(x, child_part_id), handler, &child_meta, child_part_id)); } HANDLE_OR_RETURN(handler->geometry_end(meta, part_id, handler->handler_data)); return WK_CONTINUE; } int wk_sfc_read_geometrycollection(SEXP x, wk_handler_t* handler, wk_meta_t* meta, uint32_t part_id) { int result; meta->geometry_type = WK_GEOMETRYCOLLECTION; meta->size = Rf_xlength(x); HANDLE_OR_RETURN(handler->geometry_start(meta, part_id, handler->handler_data)); for (uint32_t child_part_id = 0; child_part_id < meta->size; child_part_id++) { HANDLE_OR_RETURN(wk_sfc_read_sfg(VECTOR_ELT(x, child_part_id), handler, child_part_id, meta->precision)); } HANDLE_OR_RETURN(handler->geometry_end(meta, part_id, handler->handler_data)); return WK_CONTINUE; } void wk_update_meta_from_sfg(SEXP x, wk_meta_t* meta) { if (Rf_inherits(x, "XY")) { // don't need to do anything here; default meta is xy } else if (Rf_inherits(x, "XYZ")) { meta->flags |= WK_FLAG_HAS_Z; } else if (Rf_inherits(x, "XYM")) { meta->flags |= WK_FLAG_HAS_M; } else if (Rf_inherits(x, "XYZM")) { meta->flags |= WK_FLAG_HAS_Z; meta->flags |= WK_FLAG_HAS_M; } else if (Rf_inherits(x, "sfg")) { Rf_error("Can't guess dimensions from class of 'sfg'"); } } void wk_update_vector_meta_from_sfc(SEXP x, wk_vector_meta_t* vector_meta) { // provide geometry type based on class if (Rf_inherits(x, "sfc_POINT")) { vector_meta->geometry_type = WK_POINT; } else if (Rf_inherits(x, "sfc_LINESTRING")) { vector_meta->geometry_type = WK_LINESTRING; } else if (Rf_inherits(x, "sfc_POLYGON")) { vector_meta->geometry_type = WK_POLYGON; } else if (Rf_inherits(x, "sfc_MULTIPOINT")) { vector_meta->geometry_type = WK_MULTIPOINT; } else if (Rf_inherits(x, "sfc_MULTILINESTRING")) { vector_meta->geometry_type = WK_MULTILINESTRING; } else if (Rf_inherits(x, "sfc_MULTIPOLYGON")) { vector_meta->geometry_type = WK_MULTIPOLYGON; } else if (Rf_inherits(x, "sfc_GEOMETRYCOLLECTION")) { vector_meta->geometry_type = WK_GEOMETRYCOLLECTION; } else { vector_meta->geometry_type = WK_GEOMETRY; } // if z or m coords are present, ranges are provided SEXP z_range = Rf_getAttrib(x, Rf_install("z_range")); if (z_range != R_NilValue) { vector_meta->flags |= WK_FLAG_HAS_Z; } SEXP m_range = Rf_getAttrib(x, Rf_install("m_range")); if (m_range != R_NilValue) { vector_meta->flags |= WK_FLAG_HAS_M; } // sfc objects come with a cached bbox // This appears to always be xmin, ymin, xmax, ymax // when attached to an sfc object SEXP bbox = Rf_getAttrib(x, Rf_install("bbox")); if ((Rf_xlength(x) > 0) && (bbox != R_NilValue)) { vector_meta->bounds_min[0] = REAL(bbox)[0]; vector_meta->bounds_min[1] = REAL(bbox)[1]; vector_meta->bounds_max[0] = REAL(bbox)[2]; vector_meta->bounds_max[1] = REAL(bbox)[3]; vector_meta->flags |= WK_FLAG_HAS_BOUNDS; } // Also include ZM values in the provided ranges if ((z_range != R_NilValue) && (m_range != R_NilValue)) { vector_meta->bounds_min[2] = REAL(z_range)[1]; vector_meta->bounds_max[2] = REAL(z_range)[2]; vector_meta->bounds_min[3] = REAL(m_range)[1]; vector_meta->bounds_max[3] = REAL(m_range)[2]; } else if (z_range != R_NilValue) { vector_meta->bounds_min[2] = REAL(z_range)[1]; vector_meta->bounds_max[2] = REAL(z_range)[2]; } else if (m_range != R_NilValue) { vector_meta->bounds_min[2] = REAL(m_range)[1]; vector_meta->bounds_max[2] = REAL(m_range)[2]; } } double wk_sfc_precision(SEXP x) { SEXP prec = Rf_getAttrib(x, Rf_install("precision")); if ((TYPEOF(prec) == INTSXP) && (Rf_length(prec) == 1)) { return INTEGER(prec)[0]; } else if ((TYPEOF(prec) == REALSXP) && (Rf_length(prec) == 1)) { return REAL(prec)[0]; } else { return WK_PRECISION_NONE; } } wk/src/Makevars0000644000176200001440000000004214404137501013151 0ustar liggesusersPKG_CPPFLAGS = -I../inst/include/ wk/src/debug-filter.c0000644000176200001440000002760714531511633014215 0ustar liggesusers #define R_NO_REMAP #include #include #include "wk-v1.h" typedef struct { int level; wk_handler_t* next; } debug_filter_t; // this is not a pretty solution to the vector_meta*/meta* issue void wk_debug_filter_print_vector_meta(const wk_vector_meta_t* meta) { switch (meta->geometry_type) { case WK_POINT: Rprintf("POINT"); break; case WK_LINESTRING: Rprintf("LINESTRING"); break; case WK_POLYGON: Rprintf("POLYGON"); break; case WK_MULTIPOINT: Rprintf("MULTIPOINT"); break; case WK_MULTILINESTRING: Rprintf("MULTILINESTRING"); break; case WK_MULTIPOLYGON: Rprintf("MULTIPOLYGON"); break; case WK_GEOMETRYCOLLECTION: Rprintf("GEOMETRYCOLLECTION"); break; default: Rprintf("", (int)meta->geometry_type); break; } if ((meta->flags & WK_FLAG_HAS_Z) || (meta->flags & WK_FLAG_HAS_M) || (meta->flags & WK_FLAG_HAS_BOUNDS)) { Rprintf(" "); } if (meta->flags & WK_FLAG_HAS_Z) Rprintf("Z"); if (meta->flags & WK_FLAG_HAS_M) Rprintf("M"); if (meta->flags & WK_FLAG_HAS_BOUNDS) Rprintf("B"); if (meta->size != WK_VECTOR_SIZE_UNKNOWN) { if (meta->size == 0) { Rprintf("[EMPTY]"); } else { Rprintf("[%d]", (int)meta->size); } } else { Rprintf("[UNKNOWN]"); } Rprintf(" <%p>", (void*)meta); } void wk_debug_filter_print_meta(const wk_meta_t* meta) { switch (meta->geometry_type) { case WK_POINT: Rprintf("POINT"); break; case WK_LINESTRING: Rprintf("LINESTRING"); break; case WK_POLYGON: Rprintf("POLYGON"); break; case WK_MULTIPOINT: Rprintf("MULTIPOINT"); break; case WK_MULTILINESTRING: Rprintf("MULTILINESTRING"); break; case WK_MULTIPOLYGON: Rprintf("MULTIPOLYGON"); break; case WK_GEOMETRYCOLLECTION: Rprintf("GEOMETRYCOLLECTION"); break; default: Rprintf("", (int)meta->geometry_type); break; } if ((meta->flags & WK_FLAG_HAS_Z) || (meta->flags & WK_FLAG_HAS_M) || (meta->srid != WK_SRID_NONE) || (meta->flags & WK_FLAG_HAS_BOUNDS) || (meta->precision != WK_PRECISION_NONE)) { Rprintf(" "); } if (meta->flags & WK_FLAG_HAS_Z) Rprintf("Z"); if (meta->flags & WK_FLAG_HAS_M) Rprintf("M"); if (meta->srid != WK_SRID_NONE) Rprintf("S"); if (meta->flags & WK_FLAG_HAS_BOUNDS) Rprintf("B"); if (meta->precision != WK_PRECISION_NONE) Rprintf("P"); if (meta->size != WK_SIZE_UNKNOWN) { if (meta->size == 0) { Rprintf("[EMPTY]"); } else { Rprintf("[%d]", (int)meta->size); } } else { Rprintf("[UNKNOWN]"); } Rprintf(" <%p>", (void*)meta); } void wk_debug_filter_print_indent(debug_filter_t* debug_filter) { for (int i = 0; i < debug_filter->level; i++) { Rprintf(" "); } } void wk_debug_filter_reset(debug_filter_t* debug_filter, int value) { debug_filter->level = value; } void wk_debug_filter_indent(debug_filter_t* debug_filter) { debug_filter->level++; } void wk_debug_filter_dedent(debug_filter_t* debug_filter) { debug_filter->level--; } void wk_debug_filter_print_result(int result) { switch (result) { case WK_CONTINUE: Rprintf(" => WK_CONTINUE\n"); break; case WK_ABORT_FEATURE: Rprintf(" => WK_ABORT_FEATURE\n"); break; case WK_ABORT: Rprintf(" => WK_ABORT\n"); break; default: Rprintf(" => [uknown %d]\n", (int)result); break; } } void wk_debug_filter_initialize(int* dirty, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; *dirty = 1; wk_debug_filter_reset(debug_filter, 0); Rprintf("initialize (dirty = %d ", (int)debug_filter->next->dirty); debug_filter->next->initialize(&debug_filter->next->dirty, debug_filter->next->handler_data); Rprintf(" -> %d)\n", *dirty); } int wk_debug_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_print_indent(debug_filter); Rprintf("vector_start: "); wk_debug_filter_print_vector_meta(meta); wk_debug_filter_indent(debug_filter); int result = debug_filter->next->vector_start(meta, debug_filter->next->handler_data); wk_debug_filter_print_result(result); return result; } SEXP wk_debug_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_dedent(debug_filter); // indenting here is more confusing than helpful Rprintf("vector_end: <%p>\n", (void*)meta); return debug_filter->next->vector_end(meta, debug_filter->next->handler_data); ; } int wk_debug_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_print_indent(debug_filter); Rprintf("feature_start (%d): <%p> ", (int)feat_id + 1, (void*)meta); int result = debug_filter->next->feature_start(meta, feat_id, debug_filter->next->handler_data); wk_debug_filter_print_result(result); wk_debug_filter_indent(debug_filter); return result; } int wk_debug_filter_feature_null(void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_print_indent(debug_filter); Rprintf("null_feature "); int result = debug_filter->next->null_feature(debug_filter->next->handler_data); wk_debug_filter_print_result(result); return result; } int wk_debug_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_dedent(debug_filter); wk_debug_filter_print_indent(debug_filter); Rprintf("feature_end (%d): <%p> ", (int)feat_id + 1, (void*)meta); int result = debug_filter->next->feature_end(meta, feat_id, debug_filter->next->handler_data); wk_debug_filter_print_result(result); return result; } int wk_debug_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_print_indent(debug_filter); if (part_id == WK_PART_ID_NONE) { Rprintf("geometry_start (): "); } else { Rprintf("geometry_start (%d): ", (int)part_id + 1); } wk_debug_filter_print_meta(meta); int result = debug_filter->next->geometry_start(meta, part_id, debug_filter->next->handler_data); wk_debug_filter_print_result(result); wk_debug_filter_indent(handler_data); return result; } int wk_debug_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_dedent(debug_filter); wk_debug_filter_print_indent(debug_filter); if (part_id == WK_PART_ID_NONE) { Rprintf("geometry_end () "); } else { Rprintf("geometry_end (%d) ", (int)part_id + 1); } int result = debug_filter->next->geometry_end(meta, part_id, debug_filter->next->handler_data); wk_debug_filter_print_result(result); return result; } int wk_debug_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_print_indent(debug_filter); if (size != WK_SIZE_UNKNOWN) { Rprintf("ring_start[%d] (%d): <%p> ", (int)size, (int)ring_id + 1, (void*)meta); } else { Rprintf("ring_start (%d): <%p> ", (int)ring_id + 1, (void*)meta); } wk_debug_filter_indent(debug_filter); int result = debug_filter->next->ring_start(meta, size, ring_id, debug_filter->next->handler_data); wk_debug_filter_print_result(result); return result; } int wk_debug_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_dedent(debug_filter); wk_debug_filter_print_indent(debug_filter); if (size != WK_SIZE_UNKNOWN) { Rprintf("ring_end[%d] (%d): <%p> ", (int)size, (int)ring_id + 1, (void*)meta); } else { Rprintf("ring_end (%d): <%p> ", (int)ring_id + 1, (void*)meta); } int result = debug_filter->next->ring_end(meta, size, ring_id, debug_filter->next->handler_data); wk_debug_filter_print_result(result); return result; } int wk_debug_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_print_indent(debug_filter); Rprintf("coord (%d): <%p> (%f %f", (int)coord_id + 1, (void*)meta, coord[0], coord[1]); if (meta->flags & WK_FLAG_HAS_Z || meta->flags & WK_FLAG_HAS_M) Rprintf(" %f", coord[2]); if (meta->flags & WK_FLAG_HAS_Z && meta->flags & WK_FLAG_HAS_M) Rprintf(" %f", coord[3]); Rprintf(") "); int result = debug_filter->next->coord(meta, coord, coord_id, debug_filter->next->handler_data); wk_debug_filter_print_result(result); return result; } int wk_debug_filter_error(const char* message, void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; wk_debug_filter_print_indent(debug_filter); Rprintf("error: %s", message); int result = debug_filter->next->error(message, debug_filter->next->handler_data); wk_debug_filter_print_result(result); if (result == WK_ABORT_FEATURE) { wk_debug_filter_reset(debug_filter, 1); } else if (result == WK_ABORT) { wk_debug_filter_reset(debug_filter, 0); } return result; } void wk_debug_filter_deinitialize(void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; Rprintf("deinitialize"); debug_filter->next->deinitialize(debug_filter->next->handler_data); Rprintf("\n"); } void wk_debug_filter_finalize(void* handler_data) { debug_filter_t* debug_filter = (debug_filter_t*)handler_data; if (debug_filter != NULL) { // finalizer for debug_filter->next is run by the externalptr finalizer // and should not be called here free(debug_filter); } } SEXP wk_c_debug_filter_new(SEXP handler_xptr) { wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_debug_filter_initialize; handler->vector_start = &wk_debug_filter_vector_start; handler->vector_end = &wk_debug_filter_vector_end; handler->feature_start = &wk_debug_filter_feature_start; handler->null_feature = &wk_debug_filter_feature_null; handler->feature_end = &wk_debug_filter_feature_end; handler->geometry_start = &wk_debug_filter_geometry_start; handler->geometry_end = &wk_debug_filter_geometry_end; handler->ring_start = &wk_debug_filter_ring_start; handler->ring_end = &wk_debug_filter_ring_end; handler->coord = &wk_debug_filter_coord; handler->error = &wk_debug_filter_error; handler->deinitialize = &wk_debug_filter_deinitialize; handler->finalizer = &wk_debug_filter_finalize; debug_filter_t* debug_filter = (debug_filter_t*)malloc(sizeof(debug_filter_t)); if (debug_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } debug_filter->level = 0; debug_filter->next = R_ExternalPtrAddr(handler_xptr); if (debug_filter->next->api_version != 1) { Rf_error("Can't run a wk_handler with api_version '%d'", debug_filter->next->api_version); // # nocov } handler->handler_data = debug_filter; // include the external pointer as a tag for this external pointer // which guarnatees that it will not be garbage collected until // this object is garbage collected return wk_handler_create_xptr(handler, handler_xptr, R_NilValue); } wk/src/wkt-writer.cpp0000644000176200001440000001720014510132141014275 0ustar liggesusers #include #include #include #include "internal/wk-v1-handler.hpp" class WKTWriterHandler : public WKVoidHandler { public: SEXP result; std::stringstream out; std::string current_item; std::vector stack; R_xlen_t feat_id; WKTWriterHandler(int precision, bool trim) { this->result = R_NilValue; this->out.imbue(std::locale::classic()); this->out.precision(precision); if (trim) { this->out.unsetf(out.fixed); } else { this->out.setf(out.fixed); } } void resultInit(R_xlen_t size) { SEXP new_result = PROTECT(Rf_allocVector(STRSXP, size)); if (this->result != R_NilValue) { R_ReleaseObject(this->result); } this->result = new_result; R_PreserveObject(this->result); UNPROTECT(1); } void resultEnsureSize() { R_xlen_t current_size = Rf_xlength(this->result); if (this->feat_id >= current_size) { SEXP new_result = PROTECT(Rf_allocVector(STRSXP, current_size * 2 + 1)); for (R_xlen_t i = 0; i < current_size; i++) { SET_STRING_ELT(new_result, i, STRING_ELT(this->result, i)); } if (this->result != R_NilValue) { R_ReleaseObject(this->result); } this->result = new_result; R_PreserveObject(this->result); UNPROTECT(1); } } void resultFinalize() { R_xlen_t current_size = Rf_xlength(this->result); if (this->feat_id != current_size) { SEXP new_result = PROTECT(Rf_allocVector(STRSXP, this->feat_id)); for (R_xlen_t i = 0; i < this->feat_id; i++) { SET_STRING_ELT(new_result, i, STRING_ELT(this->result, i)); } if (this->result != R_NilValue) { R_ReleaseObject(this->result); } this->result = new_result; R_PreserveObject(this->result); UNPROTECT(1); } } void resultAppend(const std::string& item) { this->resultEnsureSize(); SET_STRING_ELT(this->result, this->feat_id, Rf_mkCharLen(item.data(), item.size())); this->feat_id++; } void resultAppendNull() { this->resultEnsureSize(); SET_STRING_ELT(this->result, this->feat_id, NA_STRING); this->feat_id++; } bool isNestingCollection() { return !this->stack.empty() && (this->stack.back().geometry_type == WK_GEOMETRYCOLLECTION); } int vector_start(const wk_vector_meta_t* meta) { this->feat_id = 0; if (meta->size != WK_VECTOR_SIZE_UNKNOWN) { this->resultInit(meta->size); } else { this->resultInit(1024); } return WK_CONTINUE; } virtual int feature_start(const wk_vector_meta_t* meta, R_xlen_t feature_id) { out.str(""); this->stack.clear(); return WK_CONTINUE; } virtual int null_feature() { this->resultAppendNull(); return WK_ABORT_FEATURE; } int geometry_start(const wk_meta_t* meta, uint32_t part_id) { if ((part_id != 0) && !this->stack.empty()) { out << ", "; } if ((meta->srid != WK_SRID_NONE) && this->stack.empty()) { out << "SRID=" << meta->srid << ";"; } if (this->stack.empty() || this->isNestingCollection()) { switch (meta->geometry_type) { case WK_POINT: out << "POINT "; break; case WK_LINESTRING: out << "LINESTRING "; break; case WK_POLYGON: out << "POLYGON "; break; case WK_MULTIPOINT: out << "MULTIPOINT "; break; case WK_MULTILINESTRING: out << "MULTILINESTRING "; break; case WK_MULTIPOLYGON: out << "MULTIPOLYGON "; break; case WK_GEOMETRYCOLLECTION: out << "GEOMETRYCOLLECTION "; break; default: std::stringstream err; err << "Can't write geometry type '" << meta->geometry_type << "' as WKT"; return this->error(err.str().c_str()); } if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) { out << "ZM "; } else if ((meta->flags & WK_FLAG_HAS_Z)) { out << "Z "; } else if ((meta->flags & WK_FLAG_HAS_M)) { out << "M "; } } if (meta->size == 0) { out << "EMPTY"; } else { out << "("; } this->stack.push_back(*meta); return WK_CONTINUE; } int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { if (ring_id > 0) { out << ", "; } out << "("; return WK_CONTINUE; } virtual int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id) { if (coord_id > 0) { out << ", "; } out << coord[0] << " " << coord[1]; if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) { out << " " << coord[2] << " " << coord[3]; } else if ((meta->flags & WK_FLAG_HAS_Z) || (meta->flags & WK_FLAG_HAS_M)) { out << " " << coord[2]; } return WK_CONTINUE; } int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { out << ")"; return WK_CONTINUE; } int geometry_end(const wk_meta_t* meta, uint32_t part_id) { this->stack.pop_back(); if (meta->size != 0) { out << ")"; } return WK_CONTINUE; } int feature_end(const wk_vector_meta_t* meta, R_xlen_t feature_id) { current_item = this->out.str(); this->resultAppend(current_item); return WK_CONTINUE; } virtual SEXP vector_end(const wk_vector_meta_t* meta) { if (this->result != R_NilValue) { this->resultFinalize(); SEXP cls = PROTECT(Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(cls, 0, Rf_mkChar("wk_wkt")); SET_STRING_ELT(cls, 1, Rf_mkChar("wk_vctr")); Rf_setAttrib(this->result, R_ClassSymbol, cls); UNPROTECT(1); } return this->result; } void deinitialize() { if (this->result != R_NilValue) { R_ReleaseObject(this->result); this->result = R_NilValue; } } }; class WKTFormatHandler : public WKTWriterHandler { public: WKTFormatHandler(int precision, bool trim, int max_coords) : WKTWriterHandler(precision, trim), current_coords(0), max_coords(max_coords) {} int feature_start(const wk_vector_meta_t* meta, R_xlen_t feature_id) { this->current_coords = 0; return WKTWriterHandler::feature_start(meta, feature_id); } int null_feature() { this->out << ""; return WK_CONTINUE; } int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id) { WKTWriterHandler::coord(meta, coord, coord_id); if (++this->current_coords >= this->max_coords) { this->out << "..."; this->current_item = this->out.str(); this->resultAppend(this->current_item); return WK_ABORT_FEATURE; } else { return WK_CONTINUE; } } int error(const char* message) { this->out << "!!! " << message; this->current_item = this->out.str(); this->resultAppend(this->current_item); return WK_ABORT_FEATURE; } SEXP vector_end(const wk_vector_meta_t* meta) { if (this->result != R_NilValue) { this->resultFinalize(); } return this->result; } private: int current_coords; int max_coords; }; extern "C" SEXP wk_c_wkt_writer(SEXP precision_sexp, SEXP trim_sexp) { int precision = INTEGER(precision_sexp)[0]; int trim = LOGICAL(trim_sexp)[0]; return WKHandlerFactory::create_xptr( new WKTWriterHandler(precision, trim)); } extern "C" SEXP wk_c_wkt_formatter(SEXP precision_sexp, SEXP trim_sexp, SEXP max_coords_sexp) { int precision = INTEGER(precision_sexp)[0]; int trim = LOGICAL(trim_sexp)[0]; int max_coords = INTEGER(max_coords_sexp)[0]; return WKHandlerFactory::create_xptr( new WKTFormatHandler(precision, trim, max_coords)); } wk/src/vctr.c0000644000176200001440000000374414454533430012621 0ustar liggesusers#define R_NO_REMAP #include #include #define MAX(a, b) (((a) > (b)) ? (a) : (b)) SEXP wk_c_wkb_is_na(SEXP geom) { R_xlen_t size = Rf_xlength(geom); SEXP result = PROTECT(Rf_allocVector(LGLSXP, size)); int* pResult = LOGICAL(result); for (R_xlen_t i = 0; i < size; i++) { pResult[i] = VECTOR_ELT(geom, i) == R_NilValue; } UNPROTECT(1); return result; } SEXP wk_c_wkb_is_raw_or_null(SEXP geom) { R_xlen_t size = Rf_xlength(geom); SEXP result = PROTECT(Rf_allocVector(LGLSXP, size)); int* pResult = LOGICAL(result); int typeOf; for (R_xlen_t i = 0; i < size; i++) { typeOf = TYPEOF(VECTOR_ELT(geom, i)); pResult[i] = (typeOf == NILSXP) || (typeOf == RAWSXP); } UNPROTECT(1); return result; } static R_xlen_t wk_max_length(const SEXP geom) { const R_xlen_t size = Rf_xlength(geom); R_xlen_t max = 0; for (R_xlen_t i = 0; i < size; i++) { max = MAX(max, Rf_xlength(VECTOR_ELT(geom, i))); } return max; } static void wk_bin_to_hex(char* dst, const unsigned char* src, const R_xlen_t n) { static const char hex[16] = "0123456789abcdef"; for (R_xlen_t i = 0; i < n; i++) { const unsigned char byte = src[i]; dst[2 * i] = hex[(byte >> 4) & 0xf]; dst[2 * i + 1] = hex[byte & 0xf]; } dst[2 * n] = '\0'; } SEXP wk_c_wkb_to_hex(const SEXP geom) { const R_xlen_t size = Rf_xlength(geom); SEXP result = PROTECT(Rf_allocVector(STRSXP, size)); const R_xlen_t buf_size = wk_max_length(geom) * 2 + 1; SEXP buf_shelter = PROTECT(Rf_allocVector(RAWSXP, buf_size)); char* buf = (char*)RAW(buf_shelter); for (R_xlen_t i = 0; i < size; i++) { if (((i + 1) % 1000) == 0) R_CheckUserInterrupt(); const SEXP item = VECTOR_ELT(geom, i); const R_xlen_t item_len = Rf_xlength(item); if (item == R_NilValue) { SET_STRING_ELT(result, i, NA_STRING); continue; } wk_bin_to_hex(buf, RAW(item), item_len); SET_STRING_ELT(result, i, Rf_mkChar(buf)); } UNPROTECT(2); return result; } wk/src/vertex-filter.c0000644000176200001440000002327314515070354014441 0ustar liggesusers #define R_NO_REMAP #include #include #include "wk-v1.h" #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result != WK_CONTINUE) return result typedef struct { wk_handler_t* next; wk_vector_meta_t vector_meta; wk_meta_t meta; int add_details; SEXP details; int* details_ptr[3]; R_xlen_t details_size; int feature_id; int part_id; int ring_id; R_xlen_t coord_id; } vertex_filter_t; static inline void wk_vertex_filter_init_details(vertex_filter_t* vertex_filter, R_xlen_t initial_size) { if (!vertex_filter->add_details) { return; } if (initial_size == WK_VECTOR_SIZE_UNKNOWN) { initial_size = 1024; } vertex_filter->feature_id = -1; vertex_filter->part_id = -1; vertex_filter->ring_id = -1; if (vertex_filter->details != R_NilValue) { R_ReleaseObject(vertex_filter->details); // # nocov } const char* names[] = {"feature_id", "part_id", "ring_id", ""}; vertex_filter->details = PROTECT(Rf_mkNamed(VECSXP, names)); R_PreserveObject(vertex_filter->details); UNPROTECT(1); vertex_filter->details_size = initial_size; for (int i = 0; i < 3; i++) { SEXP item = PROTECT(Rf_allocVector(INTSXP, vertex_filter->details_size)); SET_VECTOR_ELT(vertex_filter->details, i, item); vertex_filter->details_ptr[i] = INTEGER(item); UNPROTECT(1); } } static inline void wk_vertex_filter_append_details(vertex_filter_t* vertex_filter) { if (vertex_filter->details == R_NilValue) { return; } if (vertex_filter->coord_id >= vertex_filter->details_size) { R_xlen_t new_size = vertex_filter->details_size * 2 + 1; for (int i = 0; i < 3; i++) { SEXP new_item = PROTECT(Rf_allocVector(INTSXP, new_size)); memcpy(INTEGER(new_item), INTEGER(VECTOR_ELT(vertex_filter->details, i)), vertex_filter->details_size * sizeof(int)); SET_VECTOR_ELT(vertex_filter->details, i, new_item); vertex_filter->details_ptr[i] = INTEGER(new_item); UNPROTECT(1); } vertex_filter->details_size = new_size; } vertex_filter->details_ptr[0][vertex_filter->coord_id] = vertex_filter->feature_id + 1; vertex_filter->details_ptr[1][vertex_filter->coord_id] = vertex_filter->part_id + 1; vertex_filter->details_ptr[2][vertex_filter->coord_id] = vertex_filter->ring_id + 1; vertex_filter->coord_id++; } static inline void wk_vertex_filter_finalize_details(vertex_filter_t* vertex_filter) { if (vertex_filter->details == R_NilValue) { return; } if (vertex_filter->coord_id != vertex_filter->details_size) { for (int i = 0; i < 3; i++) { SEXP new_item = PROTECT(Rf_allocVector(INTSXP, vertex_filter->coord_id)); memcpy(INTEGER(new_item), INTEGER(VECTOR_ELT(vertex_filter->details, i)), vertex_filter->coord_id * sizeof(int)); SET_VECTOR_ELT(vertex_filter->details, i, new_item); UNPROTECT(1); } vertex_filter->details_size = vertex_filter->coord_id; } } void wk_vertex_filter_initialize(int* dirty, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; *dirty = 1; vertex_filter->next->initialize(&vertex_filter->next->dirty, vertex_filter->next->handler_data); } int wk_vertex_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; vertex_filter->coord_id = 0; memcpy(&(vertex_filter->vector_meta), meta, sizeof(wk_vector_meta_t)); if (meta->geometry_type != WK_POINT) { vertex_filter->vector_meta.size = WK_VECTOR_SIZE_UNKNOWN; } vertex_filter->vector_meta.geometry_type = WK_POINT; wk_vertex_filter_init_details(vertex_filter, vertex_filter->vector_meta.size); return vertex_filter->next->vector_start(&(vertex_filter->vector_meta), vertex_filter->next->handler_data); } int wk_vertex_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; vertex_filter->feature_id++; return WK_CONTINUE; } int wk_vertex_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { return WK_CONTINUE; } int wk_vertex_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; vertex_filter->part_id++; memcpy(&(vertex_filter->meta), meta, sizeof(wk_meta_t)); vertex_filter->meta.geometry_type = WK_POINT; vertex_filter->meta.flags &= ~WK_FLAG_HAS_BOUNDS; vertex_filter->meta.size = WK_SIZE_UNKNOWN; return WK_CONTINUE; } int wk_vertex_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { return WK_CONTINUE; } int wk_vertex_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; vertex_filter->ring_id++; return WK_CONTINUE; } int wk_vertex_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { return WK_CONTINUE; } int wk_vertex_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; int result; wk_vertex_filter_append_details(vertex_filter); HANDLE_OR_RETURN(vertex_filter->next->feature_start(&(vertex_filter->vector_meta), vertex_filter->coord_id, vertex_filter->next->handler_data)); HANDLE_OR_RETURN(vertex_filter->next->geometry_start( &(vertex_filter->meta), WK_PART_ID_NONE, vertex_filter->next->handler_data)); HANDLE_OR_RETURN(vertex_filter->next->coord(&(vertex_filter->meta), coord, 0, vertex_filter->next->handler_data)); HANDLE_OR_RETURN(vertex_filter->next->geometry_end( &(vertex_filter->meta), WK_PART_ID_NONE, vertex_filter->next->handler_data)); HANDLE_OR_RETURN(vertex_filter->next->feature_end(&(vertex_filter->vector_meta), vertex_filter->coord_id, vertex_filter->next->handler_data)); return WK_CONTINUE; } SEXP wk_vertex_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; SEXP result = PROTECT(vertex_filter->next->vector_end( &(vertex_filter->vector_meta), vertex_filter->next->handler_data)); if (result != R_NilValue) { wk_vertex_filter_finalize_details(vertex_filter); Rf_setAttrib(result, Rf_install("wk_details"), vertex_filter->details); } UNPROTECT(1); return result; } int wk_vertex_filter_error(const char* message, void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; return vertex_filter->next->error(message, vertex_filter->next->handler_data); } void wk_vertex_filter_deinitialize(void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; if (vertex_filter->details != R_NilValue) { R_ReleaseObject(vertex_filter->details); vertex_filter->details = R_NilValue; } vertex_filter->next->deinitialize(vertex_filter->next->handler_data); } void wk_vertex_filter_finalize(void* handler_data) { vertex_filter_t* vertex_filter = (vertex_filter_t*)handler_data; if (vertex_filter != NULL) { // finalizer for vertex_filter->next is run by the externalptr finalizer // and should not be called here free(vertex_filter); } } SEXP wk_c_vertex_filter_new(SEXP handler_xptr, SEXP add_details) { wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_vertex_filter_initialize; handler->vector_start = &wk_vertex_filter_vector_start; handler->vector_end = &wk_vertex_filter_vector_end; handler->feature_start = &wk_vertex_filter_feature_start; handler->feature_end = &wk_vertex_filter_feature_end; handler->geometry_start = &wk_vertex_filter_geometry_start; handler->geometry_end = &wk_vertex_filter_geometry_end; handler->ring_start = &wk_vertex_filter_ring_start; handler->ring_end = &wk_vertex_filter_ring_end; handler->coord = &wk_vertex_filter_coord; handler->error = &wk_vertex_filter_error; handler->deinitialize = &wk_vertex_filter_deinitialize; handler->finalizer = &wk_vertex_filter_finalize; vertex_filter_t* vertex_filter = (vertex_filter_t*)malloc(sizeof(vertex_filter_t)); if (vertex_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } vertex_filter->next = R_ExternalPtrAddr(handler_xptr); if (vertex_filter->next->api_version != 1) { Rf_error("Can't run a wk_handler with api_version '%d'", vertex_filter->next->api_version); // # nocov } WK_VECTOR_META_RESET(vertex_filter->vector_meta, WK_GEOMETRY); vertex_filter->add_details = LOGICAL(add_details)[0]; vertex_filter->details = R_NilValue; vertex_filter->details_size = 0; vertex_filter->coord_id = 0; vertex_filter->feature_id = 0; vertex_filter->part_id = 0; vertex_filter->ring_id = 0; handler->handler_data = vertex_filter; // include the external pointer as a tag for this external pointer // which guarnatees that it will not be garbage collected until // this object is garbage collected return wk_handler_create_xptr(handler, handler_xptr, R_NilValue); } wk/src/make-linestring-filter.c0000644000176200001440000002343214510132141016176 0ustar liggesusers#define R_NO_REMAP #include #include #include "altrep.h" #include "wk-v1.h" #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) { \ Rf_error("wk_linestring_filter() does not support WK_ABORT_FEATURE"); \ } \ if (result != WK_CONTINUE) return result typedef struct { wk_handler_t* next; R_xlen_t feature_id; SEXP feature_id_sexp; #ifndef HAS_ALTREP int* feature_id_spec; #endif R_xlen_t n_feature_id_spec; int last_feature_id_spec; int is_new_feature; R_xlen_t feature_id_out; uint32_t coord_id; wk_meta_t meta; wk_vector_meta_t vector_meta; } linestring_filter_t; static inline int wk_linestring_start(linestring_filter_t* linestring_filter) { int result; linestring_filter->feature_id_out++; HANDLE_OR_RETURN(linestring_filter->next->feature_start( &(linestring_filter->vector_meta), linestring_filter->feature_id_out, linestring_filter->next->handler_data)); HANDLE_OR_RETURN( linestring_filter->next->geometry_start(&(linestring_filter->meta), WK_PART_ID_NONE, linestring_filter->next->handler_data)); linestring_filter->coord_id = 0; return WK_CONTINUE; } static inline int wk_linestring_end(linestring_filter_t* linestring_filter) { int result; HANDLE_OR_RETURN( linestring_filter->next->geometry_end(&(linestring_filter->meta), WK_PART_ID_NONE, linestring_filter->next->handler_data)); HANDLE_OR_RETURN(linestring_filter->next->feature_end( &(linestring_filter->vector_meta), linestring_filter->feature_id_out, linestring_filter->next->handler_data)); return WK_CONTINUE; } void wk_linestring_filter_initialize(int* dirty, void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; *dirty = 1; linestring_filter->next->initialize(&linestring_filter->next->dirty, linestring_filter->next->handler_data); } int wk_linestring_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; linestring_filter->feature_id = -1; linestring_filter->feature_id_out = -1; memcpy(&(linestring_filter->vector_meta), meta, sizeof(wk_vector_meta_t)); linestring_filter->vector_meta.geometry_type = WK_LINESTRING; linestring_filter->vector_meta.size = WK_VECTOR_SIZE_UNKNOWN; WK_META_RESET(linestring_filter->meta, WK_LINESTRING); return linestring_filter->next->vector_start(&(linestring_filter->vector_meta), linestring_filter->next->handler_data); } int wk_linestring_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; linestring_filter->feature_id++; R_xlen_t spec_i = linestring_filter->feature_id % linestring_filter->n_feature_id_spec; #ifdef HAS_ALTREP int feature_id_spec = INTEGER_ELT(linestring_filter->feature_id_sexp, spec_i); #else int feature_id_spec = linestring_filter->feature_id_spec[spec_i]; #endif int feature_id_spec_changed = feature_id_spec != linestring_filter->last_feature_id_spec; linestring_filter->last_feature_id_spec = feature_id_spec; linestring_filter->is_new_feature = feature_id_spec_changed || (linestring_filter->feature_id == 0); return WK_CONTINUE; } int wk_linestring_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; int result; if (linestring_filter->is_new_feature) { if (linestring_filter->feature_id_out >= 0) { HANDLE_OR_RETURN(wk_linestring_end(linestring_filter)); } linestring_filter->meta.flags = meta->flags; linestring_filter->meta.flags &= ~WK_FLAG_HAS_BOUNDS; linestring_filter->meta.precision = meta->precision; linestring_filter->meta.srid = meta->srid; HANDLE_OR_RETURN(wk_linestring_start(linestring_filter)); linestring_filter->is_new_feature = 0; } else { // check dimensions againist current meta because handlers make the assumption // that all coordinates passed have the same dimension for a single geometry int diff_z = (linestring_filter->meta.flags & WK_FLAG_HAS_Z) ^ (meta->flags & WK_FLAG_HAS_Z); int diff_m = (linestring_filter->meta.flags & WK_FLAG_HAS_M) ^ (meta->flags & WK_FLAG_HAS_M); int diff_srid = linestring_filter->meta.srid != meta->srid; if (diff_z || diff_m || diff_srid) { Rf_error( "Can't create linestring using geometries with differing dimensions or SRID"); } } HANDLE_OR_RETURN(linestring_filter->next->coord(&(linestring_filter->meta), coord, linestring_filter->coord_id, linestring_filter->next->handler_data)); linestring_filter->coord_id++; return WK_CONTINUE; } SEXP wk_linestring_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; // if there weren't any features we need to start one int result = WK_CONTINUE; if (linestring_filter->feature_id_out == -1) { linestring_filter->meta.size = 0; result = wk_linestring_start(linestring_filter); } if (result != WK_ABORT) { wk_linestring_end(linestring_filter); } return linestring_filter->next->vector_end(&(linestring_filter->vector_meta), linestring_filter->next->handler_data); } int wk_linestring_filter_feature_null(void* handler_data) { return WK_CONTINUE; } int wk_linestring_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { return WK_CONTINUE; } int wk_linestring_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { return WK_CONTINUE; } int wk_linestring_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { return WK_CONTINUE; } int wk_linestring_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { return WK_CONTINUE; } int wk_linestring_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { return WK_CONTINUE; } int wk_linestring_filter_error(const char* message, void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; int result; HANDLE_OR_RETURN( linestring_filter->next->error(message, linestring_filter->next->handler_data)); return WK_CONTINUE; } void wk_linestring_filter_deinitialize(void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; linestring_filter->next->deinitialize(linestring_filter->next->handler_data); } void wk_linestring_filter_finalize(void* handler_data) { linestring_filter_t* linestring_filter = (linestring_filter_t*)handler_data; if (linestring_filter != NULL) { // finalizer for linestring_filter->next is run by the externalptr finalizer // and should not be called here free(linestring_filter); } } SEXP wk_c_linestring_filter_new(SEXP handler_xptr, SEXP feature_id) { #ifndef HAS_ALTREP int* feature_id_spec = INTEGER(feature_id); #endif wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_linestring_filter_initialize; handler->vector_start = &wk_linestring_filter_vector_start; handler->vector_end = &wk_linestring_filter_vector_end; handler->feature_start = &wk_linestring_filter_feature_start; handler->null_feature = &wk_linestring_filter_feature_null; handler->feature_end = &wk_linestring_filter_feature_end; handler->geometry_start = &wk_linestring_filter_geometry_start; handler->geometry_end = &wk_linestring_filter_geometry_end; handler->ring_start = &wk_linestring_filter_ring_start; handler->ring_end = &wk_linestring_filter_ring_end; handler->coord = &wk_linestring_filter_coord; handler->error = &wk_linestring_filter_error; handler->deinitialize = &wk_linestring_filter_deinitialize; handler->finalizer = &wk_linestring_filter_finalize; linestring_filter_t* linestring_filter = (linestring_filter_t*)malloc(sizeof(linestring_filter_t)); if (linestring_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } linestring_filter->next = (wk_handler_t*)R_ExternalPtrAddr(handler_xptr); if (linestring_filter->next->api_version != 1) { wk_handler_destroy(handler); // # nocov free(linestring_filter); Rf_error("Invalid API version in linestring_filter"); // # nocov } linestring_filter->coord_id = 0; linestring_filter->feature_id = -1; linestring_filter->feature_id_out = 0; linestring_filter->feature_id_sexp = feature_id; #ifndef HAS_ALTREP linestring_filter->feature_id_spec = feature_id_spec; #endif linestring_filter->n_feature_id_spec = Rf_xlength(feature_id); linestring_filter->is_new_feature = 0; linestring_filter->last_feature_id_spec = NA_INTEGER; handler->handler_data = linestring_filter; // We need both the external pointer SEXP and the feature_id SEXP // to be valid for the lifetime of this object return wk_handler_create_xptr(handler, handler_xptr, feature_id); } wk/src/port.h0000644000176200001440000000346214510132141012614 0ustar liggesusers // Endian tools ----------------------------- #include // for WORDS_BIGENDIAN // IS_LITTLE_ENDIAN, IS_BIG_ENDIAN #if defined(WORDS_BIGENDIAN) #define IS_BIG_ENDIAN #undef IS_LITTLE_ENDIAN #else #define IS_LITTLE_ENDIAN #undef IS_BIG_ENDIAN #endif // The following guarantees declaration of the byte swap functions // (bswap_16, bswap_32, bswap_64). // from s2 library port.h #if defined(_WIN32) #ifdef __cplusplus #include #else #include #endif #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 #ifdef __cplusplus #include #else #include #endif static inline uint16_t bswap_16(uint16_t x) { #ifdef __cplusplus return static_cast(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); #else return (uint16_t)(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); // NOLINT #endif // __cplusplus } #define bswap_16(x) bswap_16(x) static inline uint32_t bswap_32(uint32_t x) { return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24)); } #define bswap_32(x) bswap_32(x) static inline uint64_t bswap_64(uint64_t 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 wk/src/meta-handler.c0000644000176200001440000002111314510132141014155 0ustar liggesusers #define R_NO_REMAP #include #include #include #include #include "wk-v1.h" typedef struct { SEXP result; R_xlen_t result_size; R_xlen_t feat_id; int is_root; int coord_size; } meta_handler_t; SEXP meta_handler_alloc_result(R_xlen_t size) { const char* names[] = {"geometry_type", "size", "has_z", "has_m", "srid", "precision", "is_empty", ""}; SEXP result = PROTECT(Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(result, 0, Rf_allocVector(INTSXP, size)); SET_VECTOR_ELT(result, 1, Rf_allocVector(INTSXP, size)); SET_VECTOR_ELT(result, 2, Rf_allocVector(LGLSXP, size)); SET_VECTOR_ELT(result, 3, Rf_allocVector(LGLSXP, size)); SET_VECTOR_ELT(result, 4, Rf_allocVector(INTSXP, size)); SET_VECTOR_ELT(result, 5, Rf_allocVector(REALSXP, size)); SET_VECTOR_ELT(result, 6, Rf_allocVector(LGLSXP, size)); UNPROTECT(1); return result; } SEXP meta_handler_realloc_result(SEXP result, R_xlen_t new_size) { SEXP new_result = PROTECT(meta_handler_alloc_result(new_size)); R_xlen_t size_cpy; if (Rf_xlength(VECTOR_ELT(result, 0)) < new_size) { size_cpy = Rf_xlength(VECTOR_ELT(result, 0)); } else { size_cpy = new_size; } memcpy(INTEGER(VECTOR_ELT(new_result, 0)), INTEGER(VECTOR_ELT(result, 0)), sizeof(int) * size_cpy); memcpy(INTEGER(VECTOR_ELT(new_result, 1)), INTEGER(VECTOR_ELT(result, 1)), sizeof(int) * size_cpy); memcpy(LOGICAL(VECTOR_ELT(new_result, 2)), LOGICAL(VECTOR_ELT(result, 2)), sizeof(int) * size_cpy); memcpy(LOGICAL(VECTOR_ELT(new_result, 3)), LOGICAL(VECTOR_ELT(result, 3)), sizeof(int) * size_cpy); memcpy(INTEGER(VECTOR_ELT(new_result, 4)), INTEGER(VECTOR_ELT(result, 4)), sizeof(int) * size_cpy); memcpy(REAL(VECTOR_ELT(new_result, 5)), REAL(VECTOR_ELT(result, 5)), sizeof(double) * size_cpy); memcpy(LOGICAL(VECTOR_ELT(new_result, 6)), LOGICAL(VECTOR_ELT(result, 6)), sizeof(int) * size_cpy); UNPROTECT(1); return new_result; } static inline void meta_handler_result_append(meta_handler_t* data, int geometry_type, int size, int has_z, int has_m, int srid, double precision, int is_empty) { if (data->feat_id >= data->result_size) { SEXP new_result = PROTECT(meta_handler_realloc_result(data->result, data->feat_id * 2 + 1)); R_ReleaseObject(data->result); data->result = new_result; R_PreserveObject(data->result); UNPROTECT(1); data->result_size = data->feat_id * 2 + 1; } INTEGER(VECTOR_ELT(data->result, 0))[data->feat_id] = geometry_type; INTEGER(VECTOR_ELT(data->result, 1))[data->feat_id] = size; LOGICAL(VECTOR_ELT(data->result, 2))[data->feat_id] = has_z; LOGICAL(VECTOR_ELT(data->result, 3))[data->feat_id] = has_m; INTEGER(VECTOR_ELT(data->result, 4))[data->feat_id] = srid; REAL(VECTOR_ELT(data->result, 5))[data->feat_id] = precision; LOGICAL(VECTOR_ELT(data->result, 6))[data->feat_id] = is_empty; data->feat_id++; } int meta_handler_vector_start(const wk_vector_meta_t* meta, void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; if (data->result != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { data->result = PROTECT(meta_handler_alloc_result(1024)); data->result_size = 1024; } else { data->result = PROTECT(meta_handler_alloc_result(meta->size)); data->result_size = meta->size; } R_PreserveObject(data->result); UNPROTECT(1); return WK_CONTINUE; } int meta_handler_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; data->feat_id = feat_id; data->is_root = 1; return WK_CONTINUE; } int meta_handler_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; if (!data->is_root) { return WK_CONTINUE; } data->is_root = 0; int result_size; if (meta->size == WK_SIZE_UNKNOWN) { result_size = NA_INTEGER; } else { result_size = meta->size; } int result_srid; if (meta->srid == WK_SRID_NONE) { result_srid = NA_INTEGER; } else { result_srid = meta->srid; } int has_z = (meta->flags & WK_FLAG_HAS_Z) != 0; int has_m = (meta->flags & WK_FLAG_HAS_M) != 0; data->coord_size = 2 + has_z + has_m; meta_handler_result_append(data, meta->geometry_type, result_size, has_z, has_m, result_srid, meta->precision, // Empty unless proven otherwise 1); return WK_CONTINUE; } int meta_handler_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; for (int i = 0; i < data->coord_size; i++) { if (!ISNA(coord[i]) && !ISNAN(coord[i])) { // Not empty! LOGICAL(VECTOR_ELT(data->result, 6))[data->feat_id - 1] = 0; return WK_ABORT_FEATURE; } } return WK_CONTINUE; } int meta_handler_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { return WK_ABORT_FEATURE; } int meta_handler_null_feature(void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; meta_handler_result_append(data, NA_INTEGER, NA_INTEGER, NA_LOGICAL, NA_LOGICAL, NA_INTEGER, NA_REAL, NA_LOGICAL); return WK_ABORT_FEATURE; } SEXP meta_handler_vector_end(const wk_vector_meta_t* meta, void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; if (data->result_size != data->feat_id) { SEXP new_result = PROTECT(meta_handler_realloc_result(data->result, data->feat_id)); R_ReleaseObject(data->result); data->result = R_NilValue; UNPROTECT(1); return new_result; } else { return data->result; } } void meta_handler_deinitialize(void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; if (data->result != R_NilValue) { R_ReleaseObject(data->result); data->result = R_NilValue; } } void meta_handler_finalize(void* handler_data) { meta_handler_t* data = (meta_handler_t*)handler_data; if (data != NULL) { free(data); } } SEXP wk_c_meta_handler_new(void) { wk_handler_t* handler = wk_handler_create(); handler->vector_start = &meta_handler_vector_start; handler->feature_start = &meta_handler_feature_start; handler->null_feature = &meta_handler_null_feature; handler->geometry_start = &meta_handler_geometry_start; handler->coord = &meta_handler_coord; handler->geometry_end = &meta_handler_geometry_end; handler->vector_end = &meta_handler_vector_end; handler->deinitialize = &meta_handler_deinitialize; handler->finalizer = &meta_handler_finalize; meta_handler_t* data = (meta_handler_t*)malloc(sizeof(meta_handler_t)); if (data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } data->feat_id = 0; data->is_root = 1; data->coord_size = 2; data->result = R_NilValue; handler->handler_data = data; SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue); return xptr; } int vector_meta_handler_vector_start(const wk_vector_meta_t* meta, void* handler_data) { return WK_ABORT; } SEXP vector_meta_handler_vector_end(const wk_vector_meta_t* meta, void* handler_data) { const char* names[] = {"geometry_type", "size", "has_z", "has_m", ""}; SEXP result = PROTECT(Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(result, 0, Rf_ScalarInteger(meta->geometry_type)); if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { SET_VECTOR_ELT(result, 1, Rf_ScalarReal(NA_REAL)); } else { SET_VECTOR_ELT(result, 1, Rf_ScalarReal(meta->size)); } if (meta->flags & WK_FLAG_DIMS_UNKNOWN) { SET_VECTOR_ELT(result, 2, Rf_ScalarLogical(NA_LOGICAL)); SET_VECTOR_ELT(result, 3, Rf_ScalarLogical(NA_LOGICAL)); } else { SET_VECTOR_ELT(result, 2, Rf_ScalarLogical((meta->flags & WK_FLAG_HAS_Z) != 0)); SET_VECTOR_ELT(result, 3, Rf_ScalarLogical((meta->flags & WK_FLAG_HAS_M) != 0)); } UNPROTECT(1); return result; } SEXP wk_c_vector_meta_handler_new(void) { wk_handler_t* handler = wk_handler_create(); handler->vector_start = &vector_meta_handler_vector_start; handler->vector_end = &vector_meta_handler_vector_end; SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue); return xptr; } wk/src/orient-filter.cpp0000644000176200001440000001007514515070353014757 0ustar liggesusers#include #include "internal/wk-v1-handler.hpp" #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result != WK_CONTINUE) return result enum class Direction { Clockwise = -1, CounterClockwise = 1 }; static int meta_coord_dim(uint32_t flags) { bool has_z = (flags & WK_FLAG_HAS_Z) != 0; bool has_m = (flags & WK_FLAG_HAS_M) != 0; return 2 + has_z + has_m; } class OrientFilter : public WKVoidHandler { public: OrientFilter(wk_handler_t* next, Direction direction) : next(next), direction(direction) {} void initialize(int* dirty) { WKVoidHandler::initialize(dirty); next->initialize(&next->dirty, next->handler_data); } int vector_start(const wk_vector_meta_t* meta) { coords.reserve(256); return next->vector_start(meta, next->handler_data); } int feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id) { is_polygon_ring = false; return next->feature_start(meta, feat_id, next->handler_data); } int null_feature() { return next->null_feature(next->handler_data); } int geometry_start(const wk_meta_t* meta, uint32_t part_id) { n_dim = meta_coord_dim(meta->flags); return next->geometry_start(meta, part_id, next->handler_data); } int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { is_polygon_ring = true; coords.clear(); return next->ring_start(meta, size, ring_id, next->handler_data); } int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id) { if (!is_polygon_ring) { return next->coord(meta, coord, coord_id, next->handler_data); } // defer handler coord until ring_end coords.insert(coords.end(), coord, coord + n_dim); return WK_CONTINUE; } int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { is_polygon_ring = false; int result; size_t n_coords = coords.size() / n_dim; if (need_reorder(ring_id == 0)) { // reverse coords for (uint32_t i = 0, j = n_coords - 1; i < n_coords; i++, j--) { HANDLE_OR_RETURN(next->coord(meta, &coords[j * n_dim], i, next->handler_data)); } } else { // original order for (uint32_t i = 0; i < n_coords; i++) { HANDLE_OR_RETURN(next->coord(meta, &coords[i * n_dim], i, next->handler_data)); } } return next->ring_end(meta, size, ring_id, next->handler_data); } int geometry_end(const wk_meta_t* meta, uint32_t part_id) { return next->geometry_end(meta, part_id, next->handler_data); } int feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return next->feature_end(meta, feat_id, next->handler_data); } SEXP vector_end(const wk_vector_meta_t* meta) { return next->vector_end(meta, next->handler_data); } void deinitialize() { return next->deinitialize(next->handler_data); } private: wk_handler_t* next; const Direction direction; bool is_polygon_ring; std::vector coords; uint32_t n_dim; double signed_area() const { if (coords.size() < n_dim * 3) { return 0.0; } double area = 0.0; double x0 = coords[0]; // shoelace formula // https://en.wikipedia.org/wiki/Shoelace_formula // x0 fixed at origin reduces overflow, removes wrap-around index logic for (uint32_t i = 1 * n_dim; i < coords.size() - n_dim; i += n_dim) { double x = coords[i] - x0; double ym = coords[i - n_dim + 1]; double yp = coords[i + n_dim + 1]; area += x * (yp - ym); } return area / 2.0; } bool need_reorder(bool is_outer_ring) const { double area = signed_area(); bool ring_is_ccw = ((area > 0) == is_outer_ring); bool orient_ccw = direction == Direction::CounterClockwise; return area != 0 && ring_is_ccw != orient_ccw; } }; extern "C" SEXP wk_c_orient_filter_new(SEXP handler_xptr, SEXP direction) { Direction direction_enum = static_cast(INTEGER(direction)[0]); wk_handler_t* next = static_cast(R_ExternalPtrAddr(handler_xptr)); return WKHandlerFactory::create_xptr( new OrientFilter(next, direction_enum), handler_xptr); } wk/src/wk-v1.c0000644000176200001440000000003114106220314012556 0ustar liggesusers #include "wk-v1-impl.c" wk/src/identity-filter.c0000644000176200001440000001373214510132141014740 0ustar liggesusers#define R_NO_REMAP #include #include #include "wk-v1.h" typedef struct { wk_handler_t* next; } identity_filter_t; void wk_identity_filter_initialize(int* dirty, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; *dirty = 1; identity_filter->next->initialize(&identity_filter->next->dirty, identity_filter->next->handler_data); } int wk_identity_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->vector_start(meta, identity_filter->next->handler_data); } SEXP wk_identity_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->vector_end(meta, identity_filter->next->handler_data); } int wk_identity_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->feature_start(meta, feat_id, identity_filter->next->handler_data); } int wk_identity_filter_feature_null(void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->null_feature(identity_filter->next->handler_data); } int wk_identity_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->feature_end(meta, feat_id, identity_filter->next->handler_data); } int wk_identity_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->geometry_start(meta, part_id, identity_filter->next->handler_data); } int wk_identity_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->geometry_end(meta, part_id, identity_filter->next->handler_data); } int wk_identity_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->ring_start(meta, size, ring_id, identity_filter->next->handler_data); } int wk_identity_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->ring_end(meta, size, ring_id, identity_filter->next->handler_data); } int wk_identity_filter_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->coord(meta, coord, coord_id, identity_filter->next->handler_data); } int wk_identity_filter_error(const char* message, void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; return identity_filter->next->error(message, identity_filter->next->handler_data); } void wk_identity_filter_deinitialize(void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; identity_filter->next->deinitialize(identity_filter->next->handler_data); } void wk_identity_filter_finalize(void* handler_data) { identity_filter_t* identity_filter = (identity_filter_t*)handler_data; if (identity_filter != NULL) { // finalizer for identity_filter->next is run by the externalptr finalizer // and should not be called here free(identity_filter); } } SEXP wk_c_identity_filter_new(SEXP handler_xptr) { wk_handler_t* handler = wk_handler_create(); handler->initialize = &wk_identity_filter_initialize; handler->vector_start = &wk_identity_filter_vector_start; handler->vector_end = &wk_identity_filter_vector_end; handler->feature_start = &wk_identity_filter_feature_start; handler->null_feature = &wk_identity_filter_feature_null; handler->feature_end = &wk_identity_filter_feature_end; handler->geometry_start = &wk_identity_filter_geometry_start; handler->geometry_end = &wk_identity_filter_geometry_end; handler->ring_start = &wk_identity_filter_ring_start; handler->ring_end = &wk_identity_filter_ring_end; handler->coord = &wk_identity_filter_coord; handler->error = &wk_identity_filter_error; handler->deinitialize = &wk_identity_filter_deinitialize; handler->finalizer = &wk_identity_filter_finalize; identity_filter_t* identity_filter = (identity_filter_t*)malloc(sizeof(identity_filter_t)); if (identity_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } identity_filter->next = R_ExternalPtrAddr(handler_xptr); if (identity_filter->next->api_version != 1) { Rf_error("Can't run a wk_handler with api_version '%d'", identity_filter->next->api_version); // # nocov } handler->handler_data = identity_filter; // include the external pointer as a tag for this external pointer // which guarnatees that it will not be garbage collected until // this object is garbage collected return wk_handler_create_xptr(handler, handler_xptr, R_NilValue); } wk/src/altrep.h0000644000176200001440000000151314151152004013113 0ustar liggesusers #include "Rversion.h" #if defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0) #define HAS_ALTREP #endif #if defined(HAS_ALTREP) #if R_VERSION < R_Version(3, 6, 0) // workaround because R's not so conveniently uses `class` // as a variable name, and C++ is not happy about that // // SEXP R_new_altrep(R_altrep_class_t class, SEXP data1, SEXP data2); // #define class klass // Because functions declared in have C linkage #ifdef __cplusplus extern "C" { #endif #include #ifdef __cplusplus } #endif // undo the workaround #undef class #else #include #endif #if (defined(R_VERSION) && R_VERSION >= R_Version(3, 6, 0)) #define HAS_ALTREP_RAW #endif #endif #define ALTREP_CHUNK_SIZE 1024 // uncomment to force a check without ALTREP defines // #undef HAS_ALTREP wk/R/0000755000176200001440000000000014515070354011100 5ustar liggesuserswk/R/set.R0000644000176200001440000000342614125354157012026 0ustar liggesusers #' Set coordinate values #' #' @inheritParams wk_handle #' @param z,m A vector of Z or M values applied feature-wise and recycled #' along `handleable`. Use `NA` to keep the existing value of a given #' feature. #' @param value An [xy()], [xyz()], [xym()], or [xyzm()] of coordinates #' used to replace values in the input. Use `NA` to keep the existing #' value. #' @param use_z,use_m Used to declare the output type. Use `TRUE` to #' ensure the output has that dimension, `FALSE` to ensure it does not, #' and `NA` to leave the dimension unchanged. #' #' @export #' #' @examples #' wk_set_z(wkt("POINT (0 1)"), 2) #' wk_set_m(wkt("POINT (0 1)"), 2) #' wk_drop_z(wkt("POINT ZM (0 1 2 3)")) #' wk_drop_m(wkt("POINT ZM (0 1 2 3)")) #' wk_set_z <- function(handleable, z, ...) { wk_set_base(handleable, wk_trans_set(xyz(NA, NA, z), use_z = TRUE), ...) } #' @rdname wk_set_z #' @export wk_set_m <- function(handleable, m, ...) { wk_set_base(handleable, wk_trans_set(xym(NA, NA, m), use_m = TRUE), ...) } #' @rdname wk_set_z #' @export wk_drop_z <- function(handleable, ...) { wk_set_base(handleable, wk_trans_set(xy(NA, NA), use_z = FALSE), ...) } #' @rdname wk_set_z #' @export wk_drop_m <- function(handleable, ...) { wk_set_base(handleable, wk_trans_set(xy(NA, NA), use_m = FALSE), ...) } #' @rdname wk_set_z #' @export wk_trans_set <- function(value, use_z = NA, use_m = NA) { value <- as_xy(value) value <- as_xy(value, dims = c("x", "y", "z", "m")) new_wk_trans( .Call(wk_c_trans_set_new, value, as.logical(use_z)[1], as.logical(use_m)[1]), "wk_trans_set" ) } wk_set_base <- function(handleable, trans, ...) { result <- wk_handle(handleable, wk_transform_filter(wk_writer(handleable), trans), ...) wk_set_crs(wk_restore(handleable, result), wk_crs(handleable)) } wk/R/count.R0000644000176200001440000000211614106220314012340 0ustar liggesusers #' Count geometry components #' #' Counts the number of geometries, rings, and coordinates found within #' each feature. As opposed to [wk_meta()], this handler will iterate #' over the entire geometry. #' #' @inheritParams wk_handle #' #' @return A data.frame with one row for every feature encountered and #' columns: #' - `n_geom`: The number of geometries encountered, including the #' root geometry. Will be zero for a null feature. #' - `n_ring`: The number of rings encountered. Will be zero for a #' null feature. #' - `n_coord`: The number of coordinates encountered. Will be zero #' for a null feature. #' @export #' #' @examples #' wk_count(as_wkt("LINESTRING (0 0, 1 1)")) #' wk_count(as_wkb("LINESTRING (0 0, 1 1)")) #' wk_count <- function(handleable, ...) { UseMethod("wk_count") } #' @rdname wk_count #' @export wk_count.default <- function(handleable, ...) { new_data_frame(wk_handle(handleable, wk_count_handler(), ...)) } #' @rdname wk_count #' @export wk_count_handler <- function() { new_wk_handler(.Call(wk_c_count_handler_new), "wk_count_handler") } wk/R/make.R0000644000176200001440000000551514472006062012143 0ustar liggesusers #' Create lines, polygons, and collections #' #' @inheritParams wk_handle #' @param feature_id An identifier where changes in sequential #' values indicate a new feature. This is recycled silently #' as needed. #' @param ring_id An identifier where changes in sequential #' values indicate a new ring. Rings are automatically #' closed. This is recycled silently as needed. #' @param geometry_type The collection type to create. #' @param geodesic Use `TRUE` or `FALSE` to explicitly force #' the geodesic-ness of the output. #' #' @return An object of the same class as `handleable` with #' whose coordinates have been assembled into the given #' type. #' @export #' #' @examples #' wk_linestring(xy(c(1, 1), c(2, 3))) #' wk_polygon(xy(c(0, 1, 0), c(0, 0, 1))) #' wk_collection(xy(c(1, 1), c(2, 3))) #' wk_linestring <- function(handleable, feature_id = 1L, ..., geodesic = NULL) { writer <- wk_writer(handleable, generic = TRUE) result <- wk_handle(handleable, wk_linestring_filter(writer, as.integer(feature_id)), ...) wk_crs(result) <- wk_crs(handleable) wk_is_geodesic(result) <- geodesic %||% wk_is_geodesic(handleable) result } #' @rdname wk_linestring #' @export wk_polygon <- function(handleable, feature_id = 1L, ring_id = 1L, ..., geodesic = NULL) { writer <- wk_writer(handleable, generic = TRUE) result <- wk_handle( handleable, wk_polygon_filter( writer, as.integer(feature_id), as.integer(ring_id) ), ... ) wk_crs(result) <- wk_crs(handleable) wk_is_geodesic(result) <- geodesic %||% wk_is_geodesic(handleable) result } #' @rdname wk_linestring #' @export wk_collection <- function(handleable, geometry_type = wk_geometry_type("geometrycollection"), feature_id = 1L, ...) { writer <- wk_writer(handleable, generic = TRUE) result <- wk_handle( handleable, wk_collection_filter( writer, as.integer(geometry_type)[1], as.integer(feature_id) ), ... ) result <- wk_set_crs(result, wk_crs(handleable)) wk_set_geodesic(result, wk_is_geodesic(handleable)) } #' @rdname wk_linestring #' @export wk_linestring_filter <- function(handler, feature_id = 1L) { new_wk_handler( .Call(wk_c_linestring_filter_new, as_wk_handler(handler), feature_id), "wk_linestring_filter" ) } #' @rdname wk_linestring #' @export wk_polygon_filter <- function(handler, feature_id = 1L, ring_id = 1L) { new_wk_handler( .Call(wk_c_polygon_filter_new, handler, feature_id, ring_id), "wk_polygon_filter" ) } #' @rdname wk_linestring #' @export wk_collection_filter <- function(handler, geometry_type = wk_geometry_type("geometrycollection"), feature_id = 1L) { new_wk_handler( .Call(wk_c_collection_filter_new, as_wk_handler(handler), geometry_type, feature_id), "wk_collection_filter" ) } wk/R/class-data-frame.R0000644000176200001440000000637014305771446014344 0ustar liggesusers #' Use data.frame with wk #' #' @inheritParams wk_handle #' @inheritParams wk_crs #' @inheritParams wk_translate #' @inheritParams wk_identity #' @inheritParams wk_is_geodesic #' #' @export #' #' @examples #' wk_handle(data.frame(a = wkt("POINT (0 1)")), wkb_writer()) #' wk_translate(wkt("POINT (0 1)"), data.frame(col_name = wkb())) #' wk_translate(data.frame(a = wkt("POINT (0 1)")), data.frame(wkb())) #' wk_handle.data.frame <- function(handleable, handler, ...) { col <- handleable_column_name(handleable) wk_handle(handleable[[col]], handler, ...) } #' @export wk_writer.data.frame <- function(handleable, ...) { col <- handleable_column_name(handleable) wk_writer(handleable[[col]], ...) } #' @export wk_crs.data.frame <- function(x) { col <- handleable_column_name(x) wk_crs(x[[col]]) } #' @export wk_set_crs.data.frame <- function(x, crs) { col <- handleable_column_name(x) x[[col]] <- wk_set_crs(x[[col]], crs) x } #' @export wk_is_geodesic.data.frame <- function(x) { col <- handleable_column_name(x) wk_is_geodesic(x[[col]]) } #' @export wk_set_geodesic.data.frame <- function(x, geodesic) { col <- handleable_column_name(x) x[[col]] <- wk_set_geodesic(x[[col]], geodesic) x } #' @rdname wk_handle.data.frame #' @export wk_restore.data.frame <- function(handleable, result, ...) { col <- handleable_column_name(handleable) if(nrow(handleable) == length(result)) { handleable[[col]] <- result handleable } else if (nrow(handleable) == 1) { handleable <- handleable[rep(1L, length(result)), , drop = FALSE] handleable[[col]] <- result handleable } else { stop( sprintf( "Can't assign result of length %d to data frame with %d rows", length(result), nrow(handleable) ), call. = FALSE ) } } #' @rdname wk_handle.data.frame #' @export wk_restore.tbl_df <- function(handleable, result, ...) { tibble::as_tibble(wk_restore.data.frame(handleable, result, ...)) } #' @rdname wk_handle.data.frame #' @export wk_translate.data.frame <- function(handleable, to, ...) { col <- handleable_column_name(to) col_value <- wk_translate(handleable, to[[col]], ...) if (inherits(handleable, "data.frame")) { handleable_col <- handleable_column_name(handleable) attributes(handleable) <- list(names = names(handleable)) handleable[handleable_col] <- list(col_value) new_data_frame(handleable) } else { df_raw <- list(col_value) names(df_raw) <- col new_data_frame(df_raw) } } #' @rdname wk_handle.data.frame #' @export wk_translate.tbl_df <- function(handleable, to, ...) { tibble::as_tibble(wk_translate.data.frame(handleable, to, ...)) } #' @rdname wk_handle_slice #' @export wk_handle_slice.data.frame <- function(handleable, handler, from = NULL, to = NULL, ...) { handleable_col <- handleable_column_name(handleable) wk_handle_slice( handleable[[handleable_col]], handler, from = from, to = to, ... ) } handleable_column_name <- function(df) { has_method <- vapply(df, is_handleable, FUN.VALUE = logical(1)) if (!any(has_method)) { stop( "To be used with wk_handle(), a data.frame must have at least one handleable column.", call. = FALSE ) } names(df)[which(has_method)[1L]] } wk/R/pkg-sf.R0000644000176200001440000002103314515070354012411 0ustar liggesusers #' @rdname wk_handle #' @export wk_handle.sfg <- function(handleable, handler, ...) { wk_handle(sf::st_sfc(handleable), handler, ...) } #' @rdname wk_handle #' @export wk_handle.sf <- function(handleable, handler, ...) { wk_handle(sf::st_geometry(handleable), handler, ...) } #' @rdname wk_handle #' @export wk_handle.bbox <- function(handleable, handler, ...) { wk_handle(as_rct(handleable), handler, ...) } #' @rdname wk_writer #' @export wk_writer.sfc <- function(handleable, ...) { sfc_writer() } #' @rdname wk_writer #' @export wk_writer.sf <- function(handleable, ...) { sfc_writer() } #' @rdname wk_translate #' @export wk_translate.sfc <- function(handleable, to, ...) { result <- wk_handle(handleable, sfc_writer(), ...) attr(result, "crs") <- sf::st_crs(wk_crs_output(handleable, to)) result } #' @rdname wk_handle.data.frame #' @export wk_translate.sf <- function(handleable, to, ...) { col_value <- wk_handle(handleable, sfc_writer(), ...) crs_out <- sf::st_crs(wk_crs_output(handleable, to)) if (inherits(handleable, "sf")) { sf::st_geometry(handleable) <- col_value } else if (inherits(handleable, "data.frame")) { col <- handleable_column_name(handleable) handleable[col] <- list(col_value) handleable <- sf::st_as_sf(handleable, sf_column_name = col) } else { handleable <- sf::st_as_sf(data.frame(geometry = col_value)) } sf::st_crs(handleable) <- crs_out handleable } #' @rdname wk_handle.data.frame #' @export wk_restore.sf <- function(handleable, result, ...) { col <- handleable_column_name(handleable) if(nrow(handleable) == length(result)) { sf::st_geometry(handleable) <- result handleable } else if (nrow(handleable) == 1) { handleable <- handleable[rep(1L, length(result)), , drop = FALSE] sf::st_geometry(handleable) <- result handleable } else { stop( sprintf( "Can't assign result of length %d to sf with %d rows", length(result), nrow(handleable) ), call. = FALSE ) } } #' @export wk_crs.sfc <- function(x) { sf::st_crs(x) } #' @export wk_set_crs.sfc <- function(x, crs) { sf::st_crs(x) <- sf::st_crs(crs) x } #' @export wk_crs.sf <- function(x) { sf::st_crs(x) } #' @export wk_set_crs.sf <- function(x, crs) { sf::st_crs(x) <- sf::st_crs(crs) x } #' @export wk_crs.sfg <- function(x) { sf::NA_crs_ } #' @export as_wkb.sfc <- function(x, ...) { wk_translate(x, new_wk_wkb(crs = wk_crs_inherit())) } #' @export as_wkb.sfg <- function(x, ...) { wk_translate(x, new_wk_wkb(crs = wk_crs_inherit())) } #' @export wk_crs_equal_generic.crs <- function(x, y, ...) { x == sf::st_crs(y) } #' @export wk_crs_proj_definition.crs <- function(crs, proj_version = NULL, verbose = FALSE) { if (is.na(crs)) { wk_crs_proj_definition(NULL) } else if (isTRUE(verbose)) { crs$Wkt %||% crs$wkt } else if (isTRUE(is.na(crs$epsg)) || isTRUE(grepl("^[0-9A-Za-z]+:[0-9A-Za-z]+$", crs$input))) { wk_crs_proj_definition(crs$input) } else { paste0("EPSG:", crs$epsg) } } #' @export wk_crs_projjson.crs <- function(crs) { json <- crs$ProjJson if (is.null(json)) { # i.e., GDAL is not >= 3.1.0 NextMethod() } else { json } } wk_crs_from_sf <- function(x) { crs <- sf::st_crs(x) if (is.na(crs)) NULL else crs } sf_crs_from_wk <- function(x) { sf::st_crs(wk_crs(x)) } #' @export as_xy.sfc <- function(x, ...) { if (length(x) == 0) { xy(crs = wk_crs_from_sf(x)) } else if (inherits(x, "sfc_POINT")) { coords <- sf::st_coordinates(x) dims <- colnames(coords) dimnames(coords) <- NULL if (anyNA(coords)) { coords[is.na(coords)] <- NaN } if (identical(dims, c("X", "Y"))) { new_wk_xy( list( x = coords[, 1, drop = TRUE], y = coords[, 2, drop = TRUE] ), crs = wk_crs_from_sf(x) ) } else if (identical(dims, c("X", "Y", "Z"))) { new_wk_xyz( list( x = coords[, 1, drop = TRUE], y = coords[, 2, drop = TRUE], z = coords[, 3, drop = TRUE] ), crs = wk_crs_from_sf(x) ) } else if (identical(dims, c("X", "Y", "M"))) { new_wk_xym( list( x = coords[, 1, drop = TRUE], y = coords[, 2, drop = TRUE], m = coords[, 3, drop = TRUE] ), crs = wk_crs_from_sf(x) ) } else if (identical(dims, c("X", "Y", "Z", "M"))) { new_wk_xyzm( list( x = coords[, 1, drop = TRUE], y = coords[, 2, drop = TRUE], z = coords[, 3, drop = TRUE], m = coords[, 4, drop = TRUE] ), crs = wk_crs_from_sf(x) ) } else { stop("Unknown dimensions.", call. = FALSE) # nocov } } else { NextMethod() } } #' @export as_rct.bbox <- function(x, ...) { x_bare <- unclass(x) new_wk_rct(as.list(x_bare[c("xmin", "ymin", "xmax", "ymax")]), crs = wk_crs_from_sf(x)) } #' @export as_wkb.sf <- function(x, ...) { as_wkb(sf::st_geometry(x), ...) } #' @export as_wkt.sf <- function(x, ...) { as_wkt(sf::st_geometry(x), ...) } #' @export as_xy.sf <- function(x, ..., dims = NULL) { as_xy(sf::st_geometry(x), ..., dims = dims) } # dynamically exported st_as_sfc.wk_wkb <- function(x, ...) { sf::st_set_crs(wk_handle(x, sfc_writer()), sf_crs_from_wk(x)) } st_as_sf.wk_wkb <- function(x, ...) { sf::st_as_sf( new_data_frame( list(geometry = st_as_sfc.wk_wkb(x, ...)) ) ) } st_as_sfc.wk_wkt <- function(x, ...) { sf::st_set_crs(wk_handle(x, sfc_writer()), sf_crs_from_wk(x)) } st_as_sf.wk_wkt <- function(x, ...) { sf::st_as_sf( new_data_frame( list(geometry = st_as_sfc.wk_wkt(x, ...)) ) ) } st_as_sfc.wk_xy <- function(x, ...) { if (all(!is.na(x))) { st_as_sf.wk_xy(x, ...)$geometry } else { sf::st_as_sfc(as_wkb(x), ...) } } st_as_sf.wk_xy <- function(x, ...) { is_na_or_nan <- Reduce("&", lapply(unclass(x), is.na)) if ((length(x) > 0) && all(!is_na_or_nan)) { sf::st_as_sf(as.data.frame(x), coords = xy_dims(x), crs = sf_crs_from_wk(x)) } else { sf::st_as_sf( new_data_frame( list(geometry = sf::st_as_sfc(as_wkb(x), ...)) ) ) } } st_as_sfc.wk_rct <- function(x, ...) { sf::st_set_crs(wk_handle(x, sfc_writer()), sf_crs_from_wk(x)) } st_as_sfc.wk_crc <- function(x, ...) { sf::st_set_crs(wk_handle(x, sfc_writer()), sf_crs_from_wk(x)) } st_as_sf.wk_rct <- function(x, ...) { sf::st_as_sf( new_data_frame( list(geometry = st_as_sfc.wk_rct(x, ...)) ) ) } st_as_sf.wk_crc <- function(x, ...) { sf::st_as_sf( new_data_frame( list(geometry = st_as_sfc.wk_crc(x, ...)) ) ) } st_as_sfc.wk_grd <- function(x, ...) { result <- wk_handle(x, sfc_writer()) sf::st_crs(result) <- sf::st_crs(wk_crs(x)) result } st_as_sf.wk_grd <- function(x, ...) { sf::st_as_sf(data.frame(geometry = st_as_sfc.wk_grd(x))) } # st_geometry methods() st_geometry.wk_wkb <- function(x, ...) { st_as_sfc.wk_wkb(x, ...) } st_geometry.wk_wkt <- function(x, ...) { st_as_sfc.wk_wkt(x, ...) } st_geometry.wk_xy <- function(x, ...) { st_as_sfc.wk_xy(x, ...) } st_geometry.wk_rct <- function(x, ...) { st_as_sfc.wk_rct(x, ...) } st_geometry.wk_crc <- function(x, ...) { st_as_sfc.wk_crc(x, ...) } st_geometry.wk_grd <- function(x, ...) { st_as_sfc.wk_grd(x) } # st_bbox() methods st_bbox.wk_wkb <- function(x, ...) { sf::st_bbox(wk_bbox(x)) } st_bbox.wk_wkt <- function(x, ...) { sf::st_bbox(wk_bbox(x)) } st_bbox.wk_xy <- function(x, ...) { sf::st_bbox(wk_bbox(x)) } st_bbox.wk_rct <- function(x, ...) { sf::st_bbox(unlist(x), crs = wk_crs(x)) } st_bbox.wk_crc <- function(x, ...) { sf::st_bbox(wk_bbox(x)) } st_bbox.wk_grd <- function(x, ...) { sf::st_bbox(wk_bbox(x)) } # st_crs() methods st_crs.wk_wkb <- function(x, ...) { sf::st_crs(wk_crs(x)) } st_crs.wk_wkt <- function(x, ...) { sf::st_crs(wk_crs(x)) } st_crs.wk_xy <- function(x, ...) { sf::st_crs(wk_crs(x)) } st_crs.wk_rct <- function(x, ...) { sf::st_crs(wk_crs(x)) } st_crs.wk_crc <- function(x, ...) { sf::st_crs(wk_crs(x)) } st_crs.wk_grd <- function(x, ...) { sf::st_crs(wk_crs(x$bbox)) } # st_crs<-() methods `st_crs<-.wk_wkb` <- function(x, value) { wk_set_crs(x, sf::st_crs(value)) } `st_crs<-.wk_wkt` <- function(x, value) { wk_set_crs(x, sf::st_crs(value)) } `st_crs<-.wk_xy` <- function(x, value) { wk_set_crs(x, sf::st_crs(value)) } `st_crs<-.wk_rct` <- function(x, value) { wk_set_crs(x, sf::st_crs(value)) } `st_crs<-.wk_crc` <- function(x, value) { wk_set_crs(x, sf::st_crs(value)) } `st_crs<-.wk_grd` <- function(x, value) { wk_set_crs(x, sf::st_crs(value)) } wk/R/utils.R0000644000176200001440000000200114515070354012354 0ustar liggesusers `%||%` <- function(x, y) { if (is.null(x)) y else x } new_data_frame <- function(x) { structure(x, row.names = c(NA, length(x[[1]])), class = "data.frame") } # rep_len became an S3 generic in R 3.6, so we need to use # something else to make sure recycle_common() works on old # R versions rep_len_compat <- function(x, length_out) { rep(x, length.out = length_out) } recycle_common <- function(...) { dots <- list(...) lengths <- vapply(dots, length, integer(1)) non_constant_lengths <- unique(lengths[lengths != 1]) if (length(non_constant_lengths) == 0) { final_length <- 1 } else if(length(non_constant_lengths) == 1) { final_length <- non_constant_lengths } else { lengths_label <- paste0(non_constant_lengths, collapse = ", ") stop(sprintf("Incompatible lengths: %s", lengths_label)) } dots[lengths != final_length] <- lapply(dots[lengths != final_length], rep_len_compat, final_length) dots } is_vector_class <- function(x) { identical(class(x[integer(0)]), class(x)) } wk/R/zzz.R0000644000176200001440000000505314454533430012064 0ustar liggesusers # nocov start .onLoad <- function(...) { # Register S3 methods for Suggests for (cls in c("wk_wkb", "wk_wkt", "wk_xy", "wk_xyz", "wk_xym", "wk_xyzm", "wk_rct", "wk_crc")) { s3_register("vctrs::vec_proxy", cls) s3_register("vctrs::vec_restore", cls) s3_register("vctrs::vec_cast", cls) s3_register("vctrs::vec_ptype2", cls) } for (cls in c("wk_wkb", "wk_wkt", "wk_xy", "wk_rct", "wk_crc")) { s3_register("sf::st_as_sfc", cls) s3_register("sf::st_as_sf", cls) s3_register("sf::st_geometry", cls) s3_register("sf::st_bbox", cls) s3_register("sf::st_crs", cls) s3_register("sf::st_crs<-", cls) s3_register("readr::output_column", cls) } # grd is not a vector class, but does have sf methods s3_register("sf::st_as_sfc", "wk_grd") s3_register("sf::st_as_sf", "wk_grd") s3_register("sf::st_geometry", "wk_grd") s3_register("sf::st_bbox", "wk_grd") s3_register("sf::st_crs", "wk_grd") s3_register("sf::st_crs<-", "wk_grd") # wkb vec_proxy_equal s3_register("vctrs::vec_proxy_equal", "wk_wkb") } .onUnload <- function (libpath) { library.dynam.unload("wk", libpath) } s3_register <- function(generic, class, method = NULL) { stopifnot(is.character(generic), length(generic) == 1) stopifnot(is.character(class), length(class) == 1) pieces <- strsplit(generic, "::")[[1]] stopifnot(length(pieces) == 2) package <- pieces[[1]] generic <- pieces[[2]] caller <- parent.frame() get_method_env <- function() { top <- topenv(caller) if (isNamespace(top)) { asNamespace(environmentName(top)) } else { caller } } get_method <- function(method, env) { if (is.null(method)) { get(paste0(generic, ".", class), envir = get_method_env()) } else { method } } method_fn <- get_method(method) stopifnot(is.function(method_fn)) # Always register hook in case package is later unloaded & reloaded setHook( packageEvent(package, "onLoad"), function(...) { ns <- asNamespace(package) # Refresh the method, it might have been updated by `devtools::load_all()` method_fn <- get_method(method) registerS3method(generic, class, method_fn, envir = ns) } ) # Avoid registration failures during loading (pkgload or regular) if (!isNamespaceLoaded(package)) { return(invisible()) } envir <- asNamespace(package) # Only register if generic can be accessed if (exists(generic, envir)) { registerS3method(generic, class, method_fn, envir = envir) } invisible() } # nocov end wk/R/wkt.R0000644000176200001440000000511514305771446012041 0ustar liggesusers #' Mark character vectors as well-known text #' #' @param x A [character()] vector containing well-known text. #' @inheritParams new_wk_wkb #' @param ... Unused #' #' @return A [new_wk_wkt()] #' @export #' #' @examples #' wkt("POINT (20 10)") #' wkt <- function(x = character(), crs = wk_crs_auto(), geodesic = FALSE) { x <- as.character(x) crs <- wk_crs_auto_value(x, crs) wkt <- new_wk_wkt(x, crs = crs, geodesic = geodesic_attr(geodesic)) validate_wk_wkt(wkt) wkt } #' @rdname wkt #' @export parse_wkt <- function(x, crs = wk_crs_auto(), geodesic = FALSE) { x <- as.character(x) crs <- wk_crs_auto_value(x, crs) wkt <- new_wk_wkt(x, crs = crs, geodesic = geodesic_attr(geodesic)) parse_base(wkt, wk_problems(wkt)) } #' @rdname wkt #' @export as_wkt <- function(x, ...) { UseMethod("as_wkt") } #' @rdname wkt #' @export as_wkt.default <- function(x, ...) { wk_translate( x, new_wk_wkt(crs = wk_crs_inherit(), geodesic = wk_geodesic_inherit()) ) } #' @rdname wkt #' @export as_wkt.character <- function(x, ..., crs = NULL, geodesic = FALSE) { wkt(x, crs = crs, geodesic = geodesic) } #' @rdname wkt #' @export as_wkt.wk_wkt <- function(x, ...) { x } #' S3 Details for wk_wkt #' #' @param x A (possibly) [wkt()] vector #' @inheritParams new_wk_wkb #' #' @export #' new_wk_wkt <- function(x = character(), crs = NULL, geodesic = NULL) { if (typeof(x) != "character" || !is.null(attributes(x))) { stop("wkt input must be a character() without attributes", call. = FALSE) } structure(x, class = c("wk_wkt", "wk_vctr"), crs = crs, geodesic = geodesic) } #' @rdname new_wk_wkt #' @export is_wk_wkt <- function(x) { inherits(x, "wk_wkt") } #' @rdname new_wk_wkt #' @export validate_wk_wkt <- function(x) { if (typeof(x) != "character") { stop("wkt() must be of type character()", call. = FALSE) } if (!inherits(x, "wk_wkt") || !inherits(x, "wk_vctr")) { stop('wkt() must inherit from c("wk_wkt", "wk_vctr")', call. = FALSE) } else { problems <- wk_problems(x) } stop_for_problems(problems) invisible(x) } #' @export `[<-.wk_wkt` <- function(x, i, value) { replacement <- as_wkt(value) crs_out <- wk_crs_output(x, replacement) geodesic_out <- wk_is_geodesic_output(x, replacement) x <- unclass(x) x[i] <- replacement attr(x, "crs") <- NULL attr(x, "geodesic") <- NULL new_wk_wkt(x, crs = crs_out, geodesic = geodesic_attr(geodesic_out)) } #' @export format.wk_wkt <- function(x, ..., max_coords = 6) { wk_format(x, max_coords = max_coords) } #' @export as.character.wk_wkt <- function(x, ...) { attr(x, "crs") <- NULL unclass(x) } wk/R/handle-slice.R0000644000176200001440000000222714145575672013572 0ustar liggesusers #' Handle specific regions of objects #' #' @inheritParams wk_handle #' @param from 1-based index of the feature to start from #' @param to 1-based index of the feature to end at #' #' @return A subset of `handleable` #' @export #' #' @examples #' wk_handle_slice(xy(1:5, 1:5), wkt_writer(), from = 3, to = 5) #' wk_handle_slice( #' data.frame(let = letters[1:5], geom = xy(1:5, 1:5)), #' wkt_writer(), #' from = 3, to = 5 #' ) #' wk_handle_slice <- function(handleable, handler = wk_writer(handleable), from = NULL, to = NULL, ...) { UseMethod("wk_handle_slice") } #' @rdname wk_handle_slice #' @export wk_handle_slice.default <- function(handleable, handler = wk_writer(handleable), from = NULL, to = NULL, ...) { # make sure we're dealing with a handleable and a vector stopifnot(is_handleable(handleable), is_vector_class(handleable)) from <- from %||% 1L to <- to %||% length(handleable) from <- max(from, 1L) to <- min(to, length(handleable)) if (to >= from) { wk_handle(handleable[from:to], handler, ...) } else { wk_handle(handleable[integer(0)], handler, ...) } } wk/R/chunk.R0000644000176200001440000001025314320424456012334 0ustar liggesusers #' Chunking strategies #' #' It is often impractical, inefficient, or impossible to perform #' an operation on a vector of geometries with all the geometries loaded #' into memory at the same time. These functions help generalize the #' pattern of split-apply-combine to one or more handlers recycled along a #' common length. These functions are designed for developers rather than users #' and should be considered experimental. #' #' @param reduce For [wk_chunk_strategy_coordinates()] this refers to #' the function used with [Reduce()] to combine coordinate counts #' from more than one handleable. #' @param n_chunks,chunk_size Exactly one of the number of #' chunks or the chunk size. For [wk_chunk_strategy_feature()] #' the chunk size refers to the number of features; for #' [wk_chunk_strategy_coordinates()] this refers to the number #' of coordinates as calculated from multiple handleables #' using `reduce`. #' #' @return A function that returns a `data.frame` with columns `from` and `to` #' when called with a `handleable` and the feature count. #' @export #' #' @examples #' feat <- c(as_wkt(xy(1:4, 1:4)), wkt("LINESTRING (1 1, 2 2)")) #' wk_chunk_strategy_single()(list(feat), 5) #' wk_chunk_strategy_feature(chunk_size = 2)(list(feat), 5) #' wk_chunk_strategy_coordinates(chunk_size = 2)(list(feat), 5) #' wk_chunk_strategy_single <- function() { function(handleables, n_features) { new_data_frame(list(from = 1, to = n_features)) } } #' @rdname wk_chunk_strategy_single #' @export wk_chunk_strategy_feature <- function(n_chunks = NULL, chunk_size = NULL) { force(n_chunks) force(chunk_size) function(handleables, n_features) { chunk_info <- chunk_info(n_features, n_chunks = n_chunks, chunk_size = chunk_size) from <- (chunk_info$chunk_size * (seq_len(chunk_info$n_chunks) - 1L)) + 1L to <- chunk_info$chunk_size * seq_len(chunk_info$n_chunks) to[chunk_info$n_chunks] <- n_features new_data_frame(list(from = from, to = to)) } } #' @rdname wk_chunk_strategy_single #' @export wk_chunk_strategy_coordinates <- function(n_chunks = NULL, chunk_size = NULL, reduce = "*") { force(n_chunks) force(reduce) function(handleables, n_features) { coord_count <- lapply(handleables, function(handleable) { vm <- wk_vector_meta(handleable) if (identical(vm$geometry_type, 1L)) { 1L } else { wk_count(handleable)$n_coord } }) coord_count <- Reduce(reduce, coord_count) if (identical(coord_count, 1L)) { return(wk_chunk_strategy_feature(n_chunks, chunk_size)(handleables, n_features)) } coord_count <- rep_len(coord_count, n_features) coord_count_total <- sum(coord_count) chunk_info <- chunk_info(coord_count_total, n_chunks, chunk_size) from <- rep(NA_integer_, chunk_info$n_chunks) to <- rep(NA_integer_, chunk_info$n_chunks) from[1] <- 1L coord_count_chunk <- (coord_count_total / chunk_info$n_chunks) coord_count_feat <- cumsum(coord_count) for (chunk_id in seq_len(chunk_info$n_chunks - 1L)) { next_coord_gt <- coord_count_feat >= coord_count_chunk if (!any(next_coord_gt)) { to[chunk_id] <- n_features break } i <- max(min(which(next_coord_gt)), from[chunk_id] + 1L) to[chunk_id] <- i from[chunk_id + 1L] <- i + 1L coord_count[1:i] <- 0L coord_count_feat <- cumsum(coord_count) } valid <- !is.na(from) from <- from[valid] to <- to[valid] if (is.na(to[length(to)])) { to[length(to)] <- n_features } new_data_frame(list(from = from, to = to)) } } chunk_info <- function(n_features, n_chunks = NULL, chunk_size = NULL) { if (is.null(n_chunks) && is.null(chunk_size)) { stop("Must specify exactly one of `n_chunks` or `chunk_size`", call. = FALSE) } else if (is.null(n_chunks)) { n_chunks <- ((n_features - 1L) %/% chunk_size) + 1L } else if (is.null(chunk_size)) { if (n_features == 0) { n_chunks <- 0L chunk_size <- 1L } else { chunk_size <- ((n_features - 1L) %/% n_chunks) + 1L } } else { stop("Must specify exactly one of `n_chunks` or `chunk_size`", call. = FALSE) } list(n_chunks = n_chunks, chunk_size = chunk_size) } wk/R/sfc-writer.R0000644000176200001440000000025714507375520013320 0ustar liggesusers #' @rdname wk_writer #' @export sfc_writer <- function(promote_multi = FALSE) { new_wk_handler(.Call(wk_c_sfc_writer_new, as.logical(promote_multi)[1]), "wk_sfc_writer") } wk/R/deprecated.R0000644000176200001440000000232314164565642013334 0ustar liggesusers #' Deprecated functions #' #' These functions are deprecated and will be removed in a future version. #' #' @param wkb A `list()` of [raw()] vectors, such as that #' returned by `sf::st_as_binary()`. #' @param wkt A character vector containing well-known text. #' @param trim Trim unnecessary zeroes in the output? #' @param precision The rounding precision to use when writing #' (number of decimal places). #' @param endian Force the endian of the resulting WKB. #' @param ... Used to keep backward compatibility with previous #' versions of these functions. #' #' @export #' @rdname deprecated #' wkb_translate_wkt <- function(wkb, ..., precision = 16, trim = TRUE) { unclass(wk_handle.wk_wkb(wkb, wkt_writer(precision, trim))) } #' @rdname deprecated #' @export wkb_translate_wkb <- function(wkb, ..., endian = NA_integer_) { unclass(wk_handle.wk_wkb(wkb, wkb_writer(endian = endian))) } #' @rdname deprecated #' @export wkt_translate_wkt <- function(wkt, ..., precision = 16, trim = TRUE) { unclass(wk_handle.wk_wkt(wkt, wkt_writer(precision, trim))) } #' @rdname deprecated #' @export wkt_translate_wkb <- function(wkt, ..., endian = NA_integer_) { unclass(wk_handle.wk_wkt(wkt, wkb_writer(endian = endian))) } wk/R/crc.R0000644000176200001440000000461714321145376012004 0ustar liggesusers #' 2D Circle Vectors #' #' @param x,y Coordinates of the center #' @param r Circle radius #' @param ... Extra arguments passed to `as_crc()`. #' @inheritParams new_wk_wkb #' #' @return A vector along the recycled length of bounds. #' @export #' #' @examples #' crc(1, 2, 3) #' crc <- function(x = double(), y = double(), r = double(), crs = wk_crs_auto()) { vec <- new_wk_crc( recycle_common( x = as.double(x), y = as.double(y), r = as.double(r) ), crs = wk_crs_auto_value(x, crs) ) validate_wk_crc(vec) vec } #' @rdname crc #' @export as_crc <- function(x, ...) { UseMethod("as_crc") } #' @rdname crc #' @export as_crc.wk_crc <- function(x, ...) { x } #' @rdname crc #' @export as_crc.matrix <- function(x, ..., crs = NULL) { if (ncol(x) == 3) { colnames(x) <- c("x", "y", "r") } as_crc(as.data.frame(x), ..., crs = crs) } #' @rdname crc #' @export as_crc.data.frame <- function(x, ..., crs = NULL) { stopifnot(all(c("x", "y", "r") %in% names(x))) new_wk_crc(lapply(x[c("x", "y", "r")], as.double), crs = crs) } validate_wk_crc <- function(x) { validate_wk_rcrd(x) stopifnot( identical(names(unclass(x)), c("x", "y", "r")) ) invisible(x) } #' S3 details for crc objects #' #' @param x A [crc()] #' @inheritParams new_wk_wkb #' #' @export #' new_wk_crc <- function(x = list(x = double(), y = double(), r = double()), crs = NULL) { structure(x, class = c("wk_crc", "wk_rcrd"), crs = crs) } #' @export format.wk_crc <- function(x, ...) { x <- unclass(x) sprintf( "[%s %s, r = %s]", format(x$x, ...), format(x$y, ...), format(x$r, ...) ) } #' @export `[<-.wk_crc` <- function(x, i, value) { replacement <- as_crc(value) result <- Map("[<-", unclass(x), i, unclass(replacement)) names(result) <- c("x", "y", "r") new_wk_crc(result, crs = wk_crs_output(x, replacement)) } #' Circle accessors #' #' @param x A [crc()] vector #' #' @return Components of the [crc()] vector #' @export #' #' @examples #' x <- crc(1, 2, r = 3) #' crc_x(x) #' crc_y(x) #' crc_r(x) #' crc_center(x) #' crc_x <- function(x) { unclass(as_crc(x))$x } #' @rdname crc_x #' @export crc_y <- function(x) { unclass(as_crc(x))$y } #' @rdname crc_x #' @export crc_center <- function(x) { x <- as_crc(x) crs <- wk_crs(x) x <- unclass(x) xy(x$x, x$y, crs = crs) } #' @rdname crc_x #' @export crc_r <- function(x) { unclass(as_crc(x))$r } wk/R/vertex-filter.R0000644000176200001440000000663614515070354014036 0ustar liggesusers #' Extract vertices #' #' These functions provide ways to extract individual coordinate values. #' Whereas `wk_vertices()` returns a vector of coordinates as in the same #' format as the input, `wk_coords()` returns a data frame with coordinates #' as columns. #' #' `wk_coords<-` is the replacement-function version of 'wk_coords'. #' Using the engine of [wk_trans_explicit()] the coordinates of an object #' can be transformed in a generic way using R functions as needed. #' #' @inheritParams wk_handle #' @param add_details Use `TRUE` to add a "wk_details" attribute, which #' contains columns `feature_id`, `part_id`, and `ring_id`. #' @inheritParams wk_trans_set #' #' @return #' - `wk_vertices()` extracts vertices and returns the in the same format as #' the handler #' - `wk_coords()` returns a data frame with columns columns `feature_id` #' (the index of the feature from whence it came), `part_id` (an arbitrary #' integer identifying the point, line, or polygon from whence it came), #' `ring_id` (an arbitrary integer identifying individual rings within #' polygons), and one column per coordinate (`x`, `y`, and/or `z` and/or `m`). #' @export #' #' @examples #' wk_vertices(wkt("LINESTRING (0 0, 1 1)")) #' wk_coords(wkt("LINESTRING (0 0, 1 1)")) #' #' # wk_coords() replacement function #' x <- xy(1:5, 1:5) #' y <- as_wkt(x) #' wk_coords(y) <- cbind(5:1, 0:4) #' wk_coords(x) <- y[5:1] #' y #' x #' wk_vertices <- function(handleable, ...) { # the results of this handler are not necessarily the same length as the input, # so we need to special-case data frames if (is.data.frame(handleable)) { result <- wk_handle( handleable, wk_vertex_filter(wk_writer(handleable), add_details = TRUE), ... ) feature_id <- attr(result, "wk_details", exact = TRUE)$feature_id attr(result, "wk_details") <- NULL result <- wk_restore(handleable[feature_id, , drop = FALSE], result, ...) } else { result <- wk_handle(handleable, wk_vertex_filter(wk_writer(handleable, generic = TRUE)), ...) result <- wk_restore(handleable, result, ...) } wk_set_crs(result, wk_crs(handleable)) } #' @rdname wk_vertices #' @export wk_coords <- function(handleable, ...) { UseMethod("wk_coords") } #' @export wk_coords.default <- function(handleable, ...) { result <- wk_handle( handleable, wk_vertex_filter(xy_writer(), add_details = TRUE), ... ) details <- attr(result, "wk_details", exact = TRUE) attr(result, "wk_details") <- NULL new_data_frame(c(details, unclass(result))) } #' @rdname wk_vertices #' @export `wk_coords<-` <- function(handleable, use_z = NA, use_m = NA, value) { wk_transform(handleable, wk_trans_explicit(value, use_z = use_z, use_m = use_m)) } #' @rdname wk_vertices #' @export wk_vertex_filter <- function(handler, add_details = FALSE) { new_wk_handler( .Call("wk_c_vertex_filter_new", as_wk_handler(handler), as.logical(add_details)[1]), "wk_vertex_filter" ) } #' @export wk_coords.wk_xy <- function(handleable, ...) { feature_id <- seq_along(handleable) is_na <- Reduce("&", lapply(unclass(handleable), is.na)) has_coord <- !is_na if (!all(has_coord)) { handleable <- handleable[has_coord] feature_id <- feature_id[has_coord] } new_data_frame( c( list( feature_id = feature_id, part_id = feature_id, ring_id = rep_len(0L, length(feature_id)) ), unclass(handleable) ) ) } wk/R/wk-rcrd.R0000644000176200001440000000661714161345517012611 0ustar liggesusers new_wk_rcrd <- function(x, template) { stopifnot( is.list(x), is.null(attr(x, "class")), !is.null(names(x)), all(names(x) != "") ) structure( x, class = unique(class(template)), crs = attr(template, "crs", exact = TRUE), geodesic = attr(template, "geodesic", exact = TRUE) ) } validate_wk_rcrd <- function(x) { x_bare <- unclass(x) stopifnot( typeof(x) == "list", !is.null(names(x_bare)), all(names(x_bare) != ""), all(vapply(x_bare, is.double, logical(1))) ) invisible(x) } #' @export format.wk_rcrd <- function(x, ...) { vapply(x, function(item) paste0(format(unclass(item), ...), collapse = "\n"), character(1)) } #' @export print.wk_rcrd <- function(x, ...) { crs <- wk_crs(x) is_geodesic <- wk_is_geodesic(x) header <- sprintf("%s[%s]", class(x)[1], length(x)) if (!is.null(crs)) { header <- paste0(header, " with CRS=", wk_crs_format(crs)) } if (isTRUE(is_geodesic)) { header <- paste0("geodesic ", header) } cat(sprintf("<%s>\n", header)) if (length(x) == 0) { return(invisible(x)) } max_print <- getOption("max.print", 1000) x_head <- format(utils::head(x, max_print)) out <- format(x_head) print(out, quote = FALSE) if (length(x) > max_print) { cat(sprintf("Reached max.print (%s)\n", max_print)) } invisible(x) } #' @export str.wk_rcrd <- function(object, ...) { str.wk_vctr(object, ...) } #' @export as.character.wk_rcrd <- function(x, ...) { format(x, ...) } #' @export is.na.wk_rcrd <- function(x, ...) { is_na <- lapply(unclass(x), is.na) Reduce("&", is_na) } #' @export `[.wk_rcrd` <- function(x, i) { new_wk_rcrd(lapply(unclass(x), "[", i), x) } #' @export `[[.wk_rcrd` <- function(x, i) { x[i] } #' @export `$.wk_rcrd` <- function(x, i) { stop("`$` is not meaningful for 'wk_rcrd' objects", call. = FALSE) } #' @export `[[<-.wk_rcrd` <- function(x, i, value) { x[i] <- value x } #' @export names.wk_rcrd <- function(x) { NULL } #' @export `names<-.wk_rcrd` <- function(x, value) { if (is.null(value)) { x } else { stop("Names of a 'wk_rcrd' must be NULL.") } } #' @export length.wk_rcrd <- function(x) { length(unclass(x)[[1]]) } #' @export rep.wk_rcrd <- function(x, ...) { new_wk_rcrd(lapply(unclass(x), rep, ...), x) } #' @method rep_len wk_rcrd #' @export rep_len.wk_rcrd <- function(x, ...) { new_wk_rcrd(lapply(unclass(x), rep_len, ...), x) } #' @export c.wk_rcrd <- function(...) { dots <- list(...) classes <- lapply(dots, class) first_class <- classes[[1]] if (!all(vapply(classes, identical, first_class, FUN.VALUE = logical(1)))) { stop("Can't combine 'wk_rcrd' objects that do not have identical classes.", call. = FALSE) } # compute output crs attr(dots[[1]], "crs") <- wk_crs_output(...) geodesic <- wk_is_geodesic_output(...) attr(dots[[1]], "geodesic") <- if (geodesic) TRUE else NULL new_wk_vctr(do.call(Map, c(list(c), lapply(dots, unclass))), dots[[1]]) } # data.frame() will call as.data.frame() with optional = TRUE #' @export as.data.frame.wk_rcrd <- function(x, ..., optional = FALSE) { if (!optional) { new_data_frame(unclass(x)) } else { new_data_frame(list(x)) } } #' @export as.matrix.wk_rcrd <- function(x, ...) { x_bare <- unclass(x) matrix( unlist(x_bare, use.names = FALSE), nrow = length(x), ncol = length(x_bare), byrow = FALSE, dimnames = list(NULL, names(x_bare)) ) } wk/R/handle-wkt.R0000644000176200001440000000106014160220603013244 0ustar liggesusers #' @rdname wk_handle #' @export wk_handle.wk_wkt <- function(handleable, handler, ...) { handler <- as_wk_handler(handler) .Call( wk_c_read_wkt, list(handleable, TRUE), handler ) } #' Test handlers for handling of unknown size vectors #' #' @inheritParams wk_handle #' @export #' #' @examples #' handle_wkt_without_vector_size(wkt(), wk_vector_meta_handler()) #' handle_wkt_without_vector_size <- function(handleable, handler) { handler <- as_wk_handler(handler) .Call( wk_c_read_wkt, list(handleable, FALSE), handler ) } wk/R/handle-sfc.R0000644000176200001440000000025114106220314013212 0ustar liggesusers #' @rdname wk_handle #' @export wk_handle.sfc <- function(handleable, handler, ...) { handler <- as_wk_handler(handler) .Call(wk_c_read_sfc, handleable, handler) } wk/R/wk-vctr.R0000644000176200001440000001014214344517050012615 0ustar liggesusers #' @export print.wk_vctr <- function(x, ...) { crs <- wk_crs(x) is_geodesic <- wk_is_geodesic(x) header <- sprintf("%s[%s]", class(x)[1], length(x)) if (!is.null(crs)) { header <- paste0(header, " with CRS=", wk_crs_format(crs)) } if (isTRUE(is_geodesic)) { header <- paste0("geodesic ", header) } cat(sprintf("<%s>\n", header)) if (length(x) == 0) { return(invisible(x)) } max_print <- getOption("max.print", 1000) x_head <- format(utils::head(x, max_print)) out <- stats::setNames(format(x_head), names(x_head)) print(out, quote = FALSE) if (length(x) > max_print) { cat(sprintf("Reached max.print (%s)\n", max_print)) } invisible(x) } # lifted from vctrs::obj_leaf() #' @export str.wk_vctr <- function(object, ..., indent.str = "", width = getOption("width")) { if (length(object) == 0) { cat(paste0(" ", class(object)[1], "[0]\n")) return(invisible(object)) } # estimate possible number of elements that could be displayed # to avoid formatting too many width <- width - nchar(indent.str) - 2 length <- min(length(object), ceiling(width / 5)) formatted <- format(object[seq_len(length)], trim = TRUE) title <- paste0(" ", class(object)[1], "[1:", length(object), "]") cat( paste0( title, " ", strtrim(paste0(formatted, collapse = ", "), width - nchar(title)), "\n" ) ) invisible(object) } #' @export `[.wk_vctr` <- function(x, i) { new_wk_vctr(NextMethod(), x) } #' @export `[[.wk_vctr` <- function(x, i) { x[i] } #' @export `[[<-.wk_vctr` <- function(x, i, value) { x[i] <- value x } #' @export c.wk_vctr <- function(...) { dots <- list(...) classes <- lapply(dots, class) first_class <- classes[[1]] if (!all(vapply(classes, identical, first_class, FUN.VALUE = logical(1)))) { stop("Can't combine 'wk_vctr' objects that do not have identical classes.", call. = FALSE) } # compute output crs, geodesic attr(dots[[1]], "crs") <- wk_crs_output(...) geodesic <- wk_is_geodesic_output(...) attr(dots[[1]], "geodesic") <- if (geodesic) TRUE else NULL new_wk_vctr(NextMethod(), dots[[1]]) } #' @export rep.wk_vctr <- function(x, ...) { new_wk_vctr(NextMethod(), x) } #' @method rep_len wk_vctr #' @export rep_len.wk_vctr <- function(x, ...) { new_wk_vctr(NextMethod(), x) } # data.frame() will call as.data.frame() with optional = TRUE #' @export as.data.frame.wk_vctr <- function(x, ..., optional = FALSE) { if (!optional) { stop(sprintf("cannot coerce object of tyoe '%s' to data.frame", class(x)[1])) } else { new_data_frame(list(x)) } } new_wk_vctr <- function(x, template) { structure( x, class = unique(class(template)), crs = attr(template, "crs", exact = TRUE), geodesic = attr(template, "geodesic", exact = TRUE) ) } parse_base <- function(x, problems) { x[!is.na(problems)] <- x[NA_integer_] problems_df <- action_for_problems( problems, function(msg) warning(paste0(msg, '\nSee attr(, "problems") for details.'), call. = FALSE) ) if (nrow(problems_df) > 0) { problems_df$actual <- unclass(x)[problems_df$row] attr(x, "problems") <- problems_df } x } stop_for_problems <- function(problems) { action_for_problems(problems, stop, call. = FALSE) } action_for_problems <- function(problems, action, ...) { if (any(!is.na(problems))) { n_problems <- sum(!is.na(problems)) summary_problems <- utils::head(which(!is.na(problems))) problem_summary <- paste0( sprintf("[%s] %s", summary_problems, problems[summary_problems]), collapse = "\n" ) if (n_problems > length(summary_problems)) { problem_summary <- paste0( problem_summary, sprintf("\n...and %s more problems", n_problems - length(summary_problems)) ) } action( sprintf( "Encountered %s parse problem%s:\n%s", n_problems, if (n_problems == 1) "" else "s", problem_summary ), ... ) } data.frame( row = which(!is.na(problems)), col = rep_len(NA_integer_, sum(!is.na(problems))), expected = problems[!is.na(problems)], stringsAsFactors = FALSE ) } wk/R/handler.R0000644000176200001440000000435714145575672012665 0ustar liggesusers #' Read geometry vectors #' #' The handler is the basic building block of the wk package. In #' particular, the [wk_handle()] generic allows operations written #' as handlers to "just work" with many different input types. The #' wk package provides the [wk_void()] handler, the [wk_format()] #' handler, the [wk_debug()] handler, the [wk_problems()] handler, #' and [wk_writer()]s for [wkb()], [wkt()], [xy()], and [sf::st_sfc()]) #' vectors. #' #' @param handler_ptr An external pointer to a newly created WK handler #' @param handler A [wk_handler][wk_handle] object. #' @param subclass The handler subclass #' @param handleable A geometry vector (e.g., [wkb()], [wkt()], [xy()], #' [rct()], or [sf::st_sfc()]) for which [wk_handle()] is defined. #' @param n_segments,resolution The number of segments to use when approximating #' a circle. The default uses `getOption("wk.crc_n_segments")` so that #' this value can be set for implicit conversions (e.g., `as_wkb()`). #' Alternatively, set the minimum distance between points on the circle #' (used to estimate `n_segments`). The default is obtained #' using `getOption("wk.crc_resolution")`. #' @param ... Passed to the [wk_handle()] method. #' #' @return A WK handler. #' @export #' wk_handle <- function(handleable, handler, ...) { UseMethod("wk_handle") } #' @rdname wk_handle #' @export is_handleable <- function(handleable) { force(handleable) # use vector_meta because it doesn't ever iterate over an entire vector tryCatch({wk_vector_meta(handleable); TRUE}, error = function(e) FALSE) } #' @rdname wk_handle #' @export new_wk_handler <- function(handler_ptr, subclass = character()) { stopifnot(typeof(handler_ptr) == "externalptr") structure(handler_ptr, class = union(subclass, "wk_handler")) } #' @rdname wk_handle #' @export is_wk_handler <- function(handler) { inherits(handler, "wk_handler") } #' @rdname wk_handle #' @export as_wk_handler <- function(handler, ...) { if (is.function(handler)) { handler() } else if (is_wk_handler(handler)) { handler } else { stop("`handler` must be a wk handler object", call. = FALSE) } } #' @export print.wk_handler <- function(x, ...) { cat(sprintf("<%s at %s>\n", class(x)[1], .Call(wk_c_handler_addr, x))) invisible(x) } wk/R/wk-package.R0000644000176200001440000000036114106220314013222 0ustar liggesusers#' @keywords internal "_PACKAGE" # The following block is used by usethis to automatically manage # roxygen namespace tags. Modify with care! ## usethis namespace: start #' @useDynLib wk, .registration = TRUE ## usethis namespace: end NULL wk/R/xy-writer.R0000644000176200001440000000017114106220314013161 0ustar liggesusers #' @rdname wk_writer #' @export xy_writer <- function() { new_wk_handler(.Call(wk_c_xy_writer_new), "wk_xy_writer") } wk/R/writer.R0000644000176200001440000000342714507375520012551 0ustar liggesusers #' Write geometry vectors #' #' When writing transformation functions, it is often useful to know which #' handler should be used to create a (potentially modified) version #' of an object. Some transformers (e.g., [wk_vertices()]) modify #' the geometry type of an object, in which case a generic writer is needed. #' This defaults to [wkb_writer()] because it is fast and can handle #' all geometry types. #' #' @inheritParams wk_handle #' @param precision If `trim` is `TRUE`, the total number of significant digits to keep #' for each result or the number of digits after the decimal place otherwise. #' @param trim Use `FALSE` to keep trailing zeroes after the decimal place. #' @param endian Use 1 for little endian, 0 for big endian, or NA for #' system endian. #' @param generic Use `TRUE` to obtain a writer that can write all geometry #' types. #' @param buffer_size Control the initial buffer size used when writing WKB. #' @param promote_multi Use TRUE to promote all simple geometries to a multi #' type when reading to sfc. This is useful to increase the likelihood that #' the sfc will contain a single geometry type. #' @param ... Passed to the writer constructor. #' #' @return A [wk_handler][wk_handle]. #' @export #' wk_writer <- function(handleable, ..., generic = FALSE) { UseMethod("wk_writer") } #' @rdname wk_writer #' @export wk_writer.default <- function(handleable, ...) { wkb_writer() } #' @rdname wk_writer #' @export wk_writer.wk_wkt <- function(handleable, ..., precision = 16, trim = TRUE) { wkt_writer(precision, trim) } #' @rdname wk_writer #' @export wk_writer.wk_wkb <- function(handleable, ...) { wkb_writer() } #' @rdname wk_writer #' @export wk_writer.wk_xy <- function(handleable, ..., generic = FALSE) { if (generic) wkb_writer() else xy_writer() } wk/R/data.R0000644000176200001440000000233514321676615012146 0ustar liggesusers #' Common CRS Representations #' #' These fixtures are calculated from PROJ version 9.1.0 and the database #' built from its source. They are used internally to transform and inspect #' coordinate reference systems. #' #' @examples #' head(wk_proj_crs_view) #' colnames(wk_proj_crs_json) #' "wk_proj_crs_view" #' @rdname wk_proj_crs_view "wk_proj_crs_json" #' Create example geometry objects #' #' @param which An example name. Valid example names are #' - "nc" (data derived from the sf package) #' - "point", "linestring", "polygon", "multipoint", #' "multilinestring", "multipolygon", "geometrycollection" #' - One of the above with the "_z", "_m", or "_zm" suffix. #' @inheritParams wk_crs #' @inheritParams wk_is_geodesic #' #' @return A [wkt()] with the specified example. #' @export #' #' @examples #' wk_example("polygon") #' wk_example <- function(which = "nc", crs = NA, geodesic = FALSE) { all_examples <- wk::wk_example_wkt match.arg(which, names(all_examples)) handleable <- all_examples[[which]] if (!identical(crs, NA)) { wk::wk_crs(handleable) <- crs } wk::wk_is_geodesic(handleable) <- geodesic handleable } #' @rdname wk_example "wk_example_wkt" wk/R/bbox.R0000644000176200001440000000404114163110540012143 0ustar liggesusers #' 2D bounding rectangles #' #' @inheritParams wk_handle #' #' @return A [rct()] of length 1. #' @export #' #' @examples #' wk_bbox(wkt("LINESTRING (1 2, 3 5)")) #' wk_bbox <- function(handleable, ...) { UseMethod("wk_bbox") } #' @rdname wk_bbox #' @export wk_envelope <- function(handleable, ...) { UseMethod("wk_envelope") } #' @rdname wk_bbox #' @export wk_bbox.default <- function(handleable, ...) { if (wk_is_geodesic(handleable)) { stop("Can't compute bbox for geodesic object", call. = FALSE) } result <- wk_handle(handleable, wk_bbox_handler(), ...) wk_crs(result) <- wk_crs(handleable) result } #' @rdname wk_bbox #' @export wk_envelope.default <- function(handleable, ...) { if (wk_is_geodesic(handleable)) { stop("Can't compute envelope for geodesic object", call. = FALSE) } result <- wk_handle(handleable, wk_envelope_handler(), ...) wk_crs(result) <- wk_crs(handleable) result } #' @rdname wk_bbox #' @export wk_envelope.wk_rct <- function(handleable, ...) { handleable } #' @rdname wk_bbox #' @export wk_envelope.wk_crc <- function(handleable, ...) { unclassed <- unclass(handleable) rct_data <- list( xmin = unclassed$x - unclassed$r, ymin = unclassed$y - unclassed$r, xmax = unclassed$x + unclassed$r, ymax = unclassed$y + unclassed$r ) new_wk_rct(rct_data, crs = attr(handleable, "crs", exact = TRUE)) } #' @rdname wk_bbox #' @export wk_envelope.wk_xy <- function(handleable, ...) { unclassed <- unclass(handleable) rct_data <- c(unclassed[1:2], unclassed[1:2]) names(rct_data) <- c("xmin", "ymin", "xmax", "ymax") new_wk_rct(rct_data, crs = attr(handleable, "crs", exact = TRUE)) } # Note to future self: re-implementing wk_bbox() using range() # for record-style vectors is not faster than the default method #' @rdname wk_bbox #' @export wk_bbox_handler <- function() { new_wk_handler(.Call(wk_c_bbox_handler_new), "wk_bbox_handler") } #' @rdname wk_bbox #' @export wk_envelope_handler <- function() { new_wk_handler(.Call(wk_c_envelope_handler_new), "wk_envelope_handler") } wk/R/trans-explicit.R0000644000176200001440000000150614471771474014207 0ustar liggesusers #' Transform using explicit coordinate values #' #' A [wk_trans][wk::wk_transform] implementation that replaces coordinate values #' using a vector of pre-calculated coordinates. This is used to perform generic #' transforms using R functions and system calls that are impossible or impractical #' to implement at the C level. #' #' @inheritParams wk_trans_set #' #' @export #' #' @seealso [wk_coords()] which has a replacement version "`wk_coords<-`" #' @examples #' trans <- wk_trans_explicit(xy(1:5, 1:5)) #' wk_transform(rep(xy(0, 0), 5), trans) wk_trans_explicit <- function(value, use_z = NA, use_m = NA) { value <- wk::as_xy(value) value <- wk::as_xy(value, dims = c("x", "y", "z", "m")) wk::new_wk_trans( .Call(wk_c_trans_explicit_new, value, as.logical(use_z)[1], as.logical(use_m)[1]), "wk_trans_explicit" ) } wk/R/grd-plot.R0000644000176200001440000001431514315177334012763 0ustar liggesusers #' Plot grid objects #' #' @inheritParams wk_plot #' @param image A raster or nativeRaster to pass to [graphics::rasterImage()]. #' use `NULL` to do a quick-and-dirty rescale of the data such that the low #' value is black and the high value is white. #' @param oversample A scale on the number of pixels on the device to use for #' sampling estimation of large raster values. Use `Inf` to disable. #' @param border Color to use for polygon borders. Use `NULL` for the default #' and `NA` to skip plotting borders. #' @param interpolate Use `TRUE` to perform interpolation between color values. #' #' @return `x`, invisibly. #' @export #' @importFrom graphics plot #' #' @examples #' plot(grd_rct(volcano)) #' plot(grd_xy(volcano)) #' plot.wk_grd_xy <- function(x, ...) { plot(as_xy(x), ...) invisible(x) } #' @rdname plot.wk_grd_xy #' @export plot.wk_grd_rct <- function(x, ..., image = NULL, interpolate = FALSE, oversample = 4, border = NA, asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE) { if (!add) { bbox <- unclass(bbox) bbox <- bbox %||% unclass(wk_bbox(x)) xlim <- c(bbox$xmin, bbox$xmax) ylim <- c(bbox$ymin, bbox$ymax) graphics::plot( numeric(0), numeric(0), xlim = xlim, ylim = ylim, xlab = xlab, ylab = ylab, asp = asp ) } # empty raster can skip plotting rct <- unclass(x$bbox) if (identical(rct$xmax - rct$xmin, -Inf) || identical(rct$ymax - rct$ymin, -Inf)) { return(invisible(x)) } # as.raster() takes care of the default details here # call with native = TRUE, but realize this isn't implemented # everywhere (so we may get a regular raster back) if (is.null(image)) { # calculate an image based on a potential downsampling of the original usr <- graphics::par("usr") x_downsample_step <- grd_calculate_plot_step(x, oversample = oversample, usr = usr) # if we're so zoomed out that nothing should be plotted, we are done if (any(is.na(x_downsample_step))) { return(invisible(x)) } x_downsample <- grd_crop( x, rct(usr[1], usr[3], usr[2], usr[4], crs = wk_crs(x)), step = x_downsample_step ) # update bbox with cropped version and check for empty result rct <- unclass(x_downsample$bbox) if (identical(rct$xmax - rct$xmin, -Inf) || identical(rct$ymax - rct$ymin, -Inf)) { return(invisible(x)) } image <- as.raster(x_downsample, native = TRUE) } if (!inherits(image, "nativeRaster")) { image <- as.raster(image, native = TRUE) } graphics::rasterImage( image, rct$xmin, rct$ymin, rct$xmax, rct$ymax, interpolate = interpolate ) if (!identical(border, NA)) { if (is.null(border)) { border <- graphics::par("fg") } # simplify borders by drawing segments nx <- dim(x$data)[2] ny <- dim(x$data)[1] width <- rct$xmax - rct$xmin height <- rct$ymax - rct$ymin xs <- seq(rct$xmin, rct$xmax, by = width / nx) ys <- seq(rct$ymin, rct$ymax, by = height / ny) graphics::segments(xs, rct$ymin, xs, rct$ymax, col = border) graphics::segments(rct$xmin, ys, rct$xmax, ys, col = border) } invisible(x) } grd_calculate_plot_step <- function(grid, oversample = c(2, 2), usr = graphics::par("usr"), device = NULL) { # estimate resolution usr <- graphics::par("usr") usr_x <- usr[1:2] usr_y <- usr[3:4] device_x <- graphics::grconvertX(usr_x, to = "device") device_y <- graphics::grconvertY(usr_y, to = "device") # Use resolution of 1 at the device level, scale to usr coords. # this is sort of like pixels but not always; an oversample of # about 4 is needed for the operation to be invisible to the user scale_x <- abs(diff(device_x) / diff(usr_x)) scale_y <- abs(diff(device_y) / diff(usr_y)) resolution_x <- 1 / scale_x resolution_y <- 1 / scale_y # we need to know how many cells are going to be resolved by a crop # to see how many fewer of them we need to sample ranges <- grd_cell_range(grid, rct(usr_x[1], usr_y[1], usr_x[2], usr_y[2])) nx <- ranges$j["stop"] - ranges$j["start"] ny <- ranges$i["stop"] - ranges$i["start"] dx <- abs(diff(usr_x) / nx) dy <- abs(diff(usr_y) / ny) # calculate which step value is closest (rounding down, clamping to valid limits) step <- (c(resolution_y, resolution_x) / oversample) %/% c(dy, dx) step <- pmax(1L, step) # if the step is more than the number of pixels, it really shouldn't be # displayed at all if (any(step > c(ny, nx))) { return(c(NA_integer_, NA_integer_)) } step } #' @export #' @importFrom grDevices as.raster as.raster.wk_grd_rct <- function(x, ..., i = NULL, j = NULL, native = NA) { # as.raster() works when values are [0..1]. We can emulate # this default by rescaling the image data if it's not already # a raster or nativeRaster. if (inherits(x$data, "nativeRaster") || grDevices::is.raster(x$data)) { grd_data_subset(x$data, i = i, j = j) } else if (prod(dim(x)) == 0) { as.raster(matrix(nrow = dim(x)[1], ncol = dim(x)[2])) } else if (length(dim(x)) == 2L || all(dim(x)[c(-1L, -2L)] == 1L)) { # try to interpret character() as hex colours, else # try to resolve x$data as a double() array if (is.character(x$data)) { return(as.raster(grd_data_subset(x$data, i = i, j = j))) } data <- grd_data_subset(x$data, i = i, j = j) storage.mode(data) <- "double" # we've checked that it's safe to drop the non-xy dimensions and # collected the data so that we know the axis order is R-native dim(data) <- dim(data)[1:2] range <- suppressWarnings(range(data, finite = TRUE)) if (all(is.finite(range)) && (diff(range) > .Machine$double.eps)) { image <- (data - range[1]) / diff(range) } else if (all(is.finite(range))) { # constant value image <- data image[] <- 0.5 } else { # all NA values image <- matrix(nrow = dim(data)[1], ncol = dim(data)[2]) } as.raster(image) } else { stop( "Can't convert non-numeric or non-matrix grid to raster image", call. = FALSE ) } } wk/R/flatten.R0000644000176200001440000000312314472006062012654 0ustar liggesusers #' Extract simple geometries #' #' @inheritParams wk_handle #' @param max_depth The maximum (outer) depth to remove. #' @param add_details Use `TRUE` to add a "wk_details" attribute, which #' contains columns `feature_id`, `part_id`, and `ring_id`. #' #' @return `handleable` transformed such that collections have been #' expanded and only simple geometries (point, linestring, polygon) #' remain. #' @export #' #' @examples #' wk_flatten(wkt("MULTIPOINT (1 1, 2 2, 3 3)")) #' wk_flatten( #' wkt("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 1))))"), #' max_depth = 2 #' ) #' wk_flatten <- function(handleable, ..., max_depth = 1) { if (is.data.frame(handleable)) { result <- wk_handle( handleable, wk_flatten_filter(wk_writer(handleable), max_depth, add_details = TRUE), ... ) feature_id <- attr(result, "wk_details", exact = TRUE)$feature_id attr(result, "wk_details") <- NULL result <- wk_restore(handleable[feature_id, , drop = FALSE], result, ...) } else { result <- wk_handle(handleable, wk_flatten_filter(wk_writer(handleable, generic = TRUE), max_depth), ...) result <- wk_restore(handleable, result, ...) } result <- wk_set_crs(result, wk_crs(handleable)) wk_set_geodesic(result, wk_is_geodesic(handleable)) } #' @rdname wk_flatten #' @export wk_flatten_filter <- function(handler, max_depth = 1L, add_details = FALSE) { new_wk_handler( .Call( "wk_c_flatten_filter_new", as_wk_handler(handler), as.integer(max_depth)[1], as.logical(add_details)[1] ), "wk_flatten_filter" ) } wk/R/wkb.R0000644000176200001440000000706314454533430012015 0ustar liggesusers #' Mark lists of raw vectors as well-known binary #' #' @param x A [list()] of [raw()] vectors or `NULL`. #' @inheritParams new_wk_wkb #' @param ... Unused #' #' @return A [new_wk_wkb()] #' @export #' #' @examples #' as_wkb("POINT (20 10)") #' wkb <- function(x = list(), crs = wk_crs_auto(), geodesic = FALSE) { crs <- wk_crs_auto_value(x, crs) attributes(x) <- NULL wkb <- new_wk_wkb(x, crs = crs, geodesic = geodesic_attr(geodesic)) validate_wk_wkb(wkb) wkb } #' @rdname wkb #' @export parse_wkb <- function(x, crs = wk_crs_auto(), geodesic = FALSE) { crs <- wk_crs_auto_value(x, crs) attributes(x) <- NULL wkb <- new_wk_wkb(x, crs = crs, geodesic = geodesic_attr(geodesic)) parse_base(wkb, wk_problems(wkb)) } #' @rdname wkb #' @export wk_platform_endian <- function() { match(.Platform$endian, c("big", "little")) - 1L } #' @rdname wkb #' @export as_wkb <- function(x, ...) { UseMethod("as_wkb") } #' @rdname wkb #' @export as_wkb.default <- function(x, ...) { wk_translate( x, new_wk_wkb(crs = wk_crs_inherit(), geodesic = wk_geodesic_inherit()), ... ) } #' @rdname wkb #' @export as_wkb.character <- function(x, ..., crs = NULL, geodesic = FALSE) { as_wkb(wkt(x, crs = crs, geodesic = geodesic), ...) } #' @rdname wkb #' @export as_wkb.wk_wkb <- function(x, ...) { x } #' @rdname wkb #' @export as_wkb.blob <- function(x, ..., crs = NULL, geodesic = FALSE) { as_wkb(wkb(x, crs = crs, geodesic = geodesic), ...) } #' @rdname wkb #' @export as_wkb.WKB <- function(x, ..., crs = NULL, geodesic = FALSE) { as_wkb(wkb(x, crs = crs, geodesic = geodesic), ...) } #' S3 Details for wk_wkb #' #' @param x A (possibly) [wkb()] vector #' @param crs A value to be propagated as the CRS for this vector. #' @inheritParams wk_is_geodesic #' #' @export #' new_wk_wkb <- function(x = list(), crs = NULL, geodesic = NULL) { if (typeof(x) != "list" || !is.null(attributes(x))) { stop("wkb input must be a list without attributes", call. = FALSE) } structure(x, class = c("wk_wkb", "wk_vctr"), crs = crs, geodesic = geodesic) } #' @rdname new_wk_wkb #' @export validate_wk_wkb <- function(x) { if (typeof(x) != "list") { stop("wkb() must be of type list()", call. = FALSE) } good_types <- .Call(wk_c_wkb_is_raw_or_null, x) if (!all(good_types)) { stop("items in wkb input must be raw() or NULL", call. = FALSE) } if (!inherits(x, "wk_wkb") || !inherits(x, "wk_vctr")) { attributes(x) <- NULL problems <- wk_problems(new_wk_wkb(x)) } else { problems <- wk_problems(x) } stop_for_problems(problems) invisible(x) } #' @rdname new_wk_wkb #' @export is_wk_wkb <- function(x) { inherits(x, "wk_wkb") } #' @export `[<-.wk_wkb` <- function(x, i, value) { replacement <- as_wkb(value) crs_out <- wk_crs_output(x, replacement) geodesic_out <- wk_is_geodesic_output(x, replacement) x <- unclass(x) x[i] <- replacement attr(x, "crs") <- NULL attr(x, "geodesic") <- NULL new_wk_wkb(x, crs = crs_out, geodesic = geodesic_attr(geodesic_out)) } #' @export is.na.wk_wkb <- function(x) { .Call(wk_c_wkb_is_na, x) } #' @export format.wk_wkb <- function(x, ...) { paste0("<", wk_format(x), ">") } # as far as I can tell, this is the only way to change # how the object appears in the viewer #' @export as.character.wk_wkb <- function(x, ...) { format(x, ...) } #' Convert well-known binary to hex #' #' @param x A [wkb()] vector #' #' @return A hex encoded [wkb()] vector #' @export #' #' @examples #' x <- as_wkb(xyz(1:5, 6:10, 11:15)) #' wkb_to_hex(x) #' wkb_to_hex <- function(x) { .Call(wk_c_wkb_to_hex, as_wkb(x)) } wk/R/grd-extract.R0000644000176200001440000000336514315177334013462 0ustar liggesusers #' Extract values from a grid #' #' Unlike [grd_subset()], which subsets like a matrix, [grd_extract()] returns #' values. #' #' @inheritParams grd_summary #' @inheritParams grd_subset #' @inheritParams grd_cell #' @param i,j Index values as in [grd_subset()] except recycled to a common #' size. #' #' @return A matrix or vector with two fewer dimensions than the input. #' @export #' grd_extract <- function(grid, i = NULL, j = NULL) { grd_data_extract(grid$data, i, j) } #' @rdname grd_extract #' @export grd_extract_nearest <- function(grid, point, out_of_bounds = c("censor", "squish")) { out_of_bounds <- match.arg(out_of_bounds) s <- grd_summary(grid) ij <- grd_cell(grid, point) ij <- ij_handle_out_of_bounds2(ij, list(s$ny, s$nx), out_of_bounds) grd_data_extract(grid$data, ij) } #' @rdname grd_extract #' @export grd_data_extract <- function(grid_data, i = NULL, j = NULL) { ij <- ij_from_args(i, j) # This doesn't make sense in this context where ij is more like a data.frame stopifnot(!is.null(ij$i), !is.null(ij$j)) ij <- recycle_common(i = ij$i, j = ij$j) # Again, the nativeRaster is silently row-major if (inherits(grid_data, "nativeRaster")) { flat_index <- (ij$i - 1L) * dim(grid_data)[2] + (ij$j - 1L) + 1L return(array(grid_data[flat_index])) } # Only implemented for the first two dimensions right now if (length(dim(grid_data)) == 2L || prod(dim(grid_data)) == prod(dim(grid_data)[1:2])) { flat_index <- (ij$j - 1L) * dim(grid_data)[1] + (ij$i - 1L) + 1L result <- array(grid_data[flat_index]) # Restore missing dimensions dim(result) <- c(length(flat_index), dim(grid_data)[-c(1L, 2L)]) result } else { stop("grd_data_extract() not implemented for non-matrix-like data") } } wk/R/handle-wkb.R0000644000176200001440000000025414106220314013225 0ustar liggesusers #' @rdname wk_handle #' @export wk_handle.wk_wkb <- function(handleable, handler, ...) { handler <- as_wk_handler(handler) .Call(wk_c_read_wkb, handleable, handler) } wk/R/transform.R0000644000176200001440000000260614106220314013227 0ustar liggesusers #' Apply coordinate transformations #' #' @inheritParams wk_handle #' @param trans An external pointer to a wk_trans object #' #' @export #' #' @examples #' wk_transform(xy(0, 0), wk_affine_translate(2, 3)) #' wk_transform <- function(handleable, trans, ...) { result <- wk_handle( handleable, wk_transform_filter(wk_writer(handleable), trans), ... ) wk_restore(handleable, result, ...) } #' @rdname wk_transform #' @export wk_transform_filter <- function(handler, trans) { new_wk_handler( .Call(wk_c_trans_filter_new, as_wk_handler(handler), as_wk_trans(trans)), "wk_transform_filter" ) } #' Generic transform class #' #' @param ... Passed to S3 methods #' @param trans_ptr An external pointer to a wk_trans_t transform #' struct. #' @param subclass An optional subclass to apply to the pointer #' @param x An object to be converted to a transform. #' @inheritParams wk_transform #' #' @export #' wk_trans_inverse <- function(trans, ...) { UseMethod("wk_trans_inverse") } #' @rdname wk_trans_inverse #' @export as_wk_trans <- function(x, ...) { UseMethod("as_wk_trans") } #' @rdname wk_trans_inverse #' @export as_wk_trans.wk_trans <- function(x, ...) { x } #' @rdname wk_trans_inverse #' @export new_wk_trans <- function(trans_ptr, subclass = character()) { stopifnot(typeof(trans_ptr) == "externalptr") structure(trans_ptr, class = union(subclass, "wk_trans")) } wk/R/xyzm.R0000644000176200001440000002042214515070354012232 0ustar liggesusers #' Efficient point vectors #' #' @param x,y,z,m Coordinate values. #' @param dims A set containing one or more of `c("x", "y", "z", "m")`. #' @param ... Passed to methods. #' @inheritParams new_wk_wkb #' #' @return A vector of coordinate values. #' @export #' #' @examples #' xy(1:5, 1:5) #' xyz(1:5, 1:5, 10) #' xym(1:5, 1:5, 10) #' xyzm(1:5, 1:5, 10, 12) #' #' # NA, NA maps to a null/na feature; NaN, NaN maps to EMPTY #' as_wkt(xy(NaN, NaN)) #' as_wkt(xy(NA, NA)) #' xy <- function(x = double(), y = double(), crs = wk_crs_auto()) { vec <- new_wk_xy(recycle_common(x = as.double(x), y = as.double(y)), crs = wk_crs_auto_value(x, crs)) validate_wk_xy(vec) vec } #' @rdname xy #' @export xyz <- function(x = double(), y = double(), z = double(), crs = wk_crs_auto()) { vec <- new_wk_xyz(recycle_common(x = as.double(x), y = as.double(y), z = as.double(z)), crs = wk_crs_auto_value(x, crs)) validate_wk_xyz(vec) vec } #' @rdname xy #' @export xym <- function(x = double(), y = double(), m = double(), crs = wk_crs_auto()) { vec <- new_wk_xym(recycle_common(x = as.double(x), y = as.double(y), m = as.double(m)), crs = wk_crs_auto_value(x, crs)) validate_wk_xym(vec) vec } #' @rdname xy #' @export xyzm <- function(x = double(), y = double(), z = double(), m = double(), crs = wk_crs_auto()) { vec <- new_wk_xyzm( recycle_common( x = as.double(x), y = as.double(y), z = as.double(z), m = as.double(m) ), crs = wk_crs_auto_value(x, crs) ) validate_wk_xyzm(vec) vec } #' @rdname xy #' @export xy_dims <- function(x) { names(unclass(x)) } #' @rdname xy #' @export as_xy <- function(x, ...) { UseMethod("as_xy") } #' @rdname xy #' @export as_xy.default <- function(x, ..., dims = NULL) { result <- wk_handle(x, xy_writer()) wk_crs(result) <- wk_crs(x) if (is.null(dims)) { result } else { as_xy(result, dims = dims) } } #' @rdname xy #' @export as_xy.wk_xy <- function(x, ..., dims = NULL) { if (is.null(dims)) { x } else if (setequal(dims, c("x", "y"))) { new_wk_xy(fill_missing_dims(unclass(x), c("x", "y"), length(x)), crs = wk_crs(x)) } else if (setequal(dims, c("x", "y", "z"))) { new_wk_xyz(fill_missing_dims(unclass(x), c("x", "y", "z"), length(x)), crs = wk_crs(x)) } else if (setequal(dims, c("x", "y", "m"))) { new_wk_xym(fill_missing_dims(unclass(x), c("x", "y", "m"), length(x)), crs = wk_crs(x)) } else if (setequal(dims, c("x", "y", "z", "m"))) { new_wk_xyzm(fill_missing_dims(unclass(x), c("x", "y", "z", "m"), length(x)), crs = wk_crs(x)) } else { stop("Unknown dims in as_xy().", call. = FALSE) } } #' @rdname xy #' @export as_xy.matrix <- function(x, ..., crs = NULL) { x[] <- as.numeric(x) colnames(x) <- tolower(colnames(x)) cols <- colnames(x) if (!is.null(cols)) { dim_cols <- intersect(c("x", "y", "z", "m"), cols) if (length(dim_cols) == 0) { stop( paste0( "Can't guess dimensions of matrix with column names\n", paste0("'", cols, "'", collapse = ", ") ), call. = FALSE ) } if (!identical(dim_cols, colnames(x))) { x <- x[, dim_cols, drop = FALSE] } } # prevent named subsets dimnames(x) <- NULL if (ncol(x) == 2) { new_wk_xy( list( x = x[, 1, drop = TRUE], y = x[, 2, drop = TRUE] ), crs = crs ) } else if (ncol(x) == 4) { new_wk_xyzm( list( x = x[, 1, drop = TRUE], y = x[, 2, drop = TRUE], z = x[, 3, drop = TRUE], m = x[, 4, drop = TRUE] ), crs = crs ) } else if (identical(cols, c("x", "y", "m"))) { new_wk_xym( list( x = x[, 1, drop = TRUE], y = x[, 2, drop = TRUE], m = x[, 3, drop = TRUE] ), crs = crs ) } else if (ncol(x) == 3) { new_wk_xyz( list( x = x[, 1, drop = TRUE], y = x[, 2, drop = TRUE], z = x[, 3, drop = TRUE] ), crs = crs ) } else { stop( sprintf("Can't guess dimensions of matrix with %s columns", ncol(x)), call. = FALSE ) } } #' @rdname xy #' @export as_xy.data.frame <- function(x, ..., dims = NULL, crs = NULL) { col_handleable <- vapply(x, is_handleable, logical(1)) if (any(col_handleable)) { stopifnot(missing(crs)) return(as_xy.default(x[[which(col_handleable)[1]]], dims = dims)) } if (is.null(dims)) { dims <- intersect(c("x", "y", "z", "m"), names(x)) } if (setequal(dims, c("x", "y"))) { new_wk_xy(fill_missing_dims(unclass(x), c("x", "y"), nrow(x)), crs = crs) } else if (setequal(dims, c("x", "y", "z"))) { new_wk_xyz(fill_missing_dims(unclass(x), c("x", "y", "z"), nrow(x)), crs = crs) } else if (setequal(dims, c("x", "y", "m"))) { new_wk_xym(fill_missing_dims(unclass(x), c("x", "y", "m"), nrow(x)), crs = crs) } else if (setequal(dims, c("x", "y", "z", "m"))) { new_wk_xyzm(fill_missing_dims(unclass(x), c("x", "y", "z", "m"), nrow(x)), crs = crs) } else { stop("Unknown dims in as_xy.data.frame().", call. = FALSE) } } fill_missing_dims <- function(x, dims, len) { missing_dims <- setdiff(dims, names(x)) x[missing_dims] <- lapply( stats::setNames(missing_dims, missing_dims), function(x) rep_len(NA_real_, len) ) lapply(x[dims], as.double) } #' S3 details for xy objects #' #' @param x A [xy()] object. #' @inheritParams new_wk_wkb #' #' @export #' new_wk_xy <- function(x = list(x = double(), y = double()), crs = NULL) { structure(x, class = c("wk_xy", "wk_rcrd"), crs = crs) } #' @rdname new_wk_xy #' @export new_wk_xyz <- function(x = list(x = double(), y = double(), z = double()), crs = NULL) { structure(x, class = c("wk_xyz", "wk_xy", "wk_rcrd"), crs = crs) } #' @rdname new_wk_xy #' @export new_wk_xym <- function(x = list(x = double(), y = double(), m = double()), crs = NULL) { structure(x, class = c("wk_xym", "wk_xy", "wk_rcrd"), crs = crs) } #' @rdname new_wk_xy #' @export new_wk_xyzm <- function(x = list(x = double(), y = double(), z = double(), m = double()), crs = NULL) { structure(x, class = c("wk_xyzm", "wk_xyz", "wk_xym", "wk_xy", "wk_rcrd"), crs = crs) } #' @rdname new_wk_xy #' @export validate_wk_xy <- function(x) { validate_wk_rcrd(x) stopifnot(identical(names(unclass(x)), c("x", "y"))) invisible(x) } #' @rdname new_wk_xy #' @export validate_wk_xyz <- function(x) { validate_wk_rcrd(x) stopifnot(identical(names(unclass(x)), c("x", "y", "z"))) invisible(x) } #' @rdname new_wk_xy #' @export validate_wk_xym <- function(x) { validate_wk_rcrd(x) stopifnot(identical(names(unclass(x)), c("x", "y", "m"))) invisible(x) } #' @rdname new_wk_xy #' @export validate_wk_xyzm <- function(x) { validate_wk_rcrd(x) stopifnot(identical(names(unclass(x)), c("x", "y", "z", "m"))) invisible(x) } #' @export format.wk_xy <- function(x, ...) { x <- unclass(x) sprintf("(%s %s)", format(x$x, ...), format(x$y, ...)) } #' @export format.wk_xyz <- function(x, ...) { x <- unclass(x) sprintf("Z (%s %s %s)", format(x$x, ...), format(x$y, ...), format(x$z, ...)) } #' @export format.wk_xym <- function(x, ...) { x <- unclass(x) sprintf("M (%s %s %s)", format(x$x, ...), format(x$y, ...), format(x$m, ...)) } #' @export format.wk_xyzm <- function(x, ...) { x <- unclass(x) sprintf("ZM (%s %s %s %s)", format(x$x, ...), format(x$y, ...), format(x$z, ...), format(x$m, ...)) } #' @export `[<-.wk_xy` <- function(x, i, value) { replacement <- as_xy(value) result <- Map( "[<-", unclass(x), list(i), fill_missing_dims(unclass(replacement), xy_dims(x), length(replacement)) ) names(result) <- names(unclass(x)) structure(result, class = class(x), crs = wk_crs_output(x, replacement)) } #' @export is.na.wk_xy <- function(x, ...) { is_na <- Reduce("&", lapply(unclass(x), is.na)) is_nan <- Reduce("&", lapply(unclass(x), is.nan)) is_na & !is_nan } #' XY vector extractors #' #' @param x An [xy()] vector #' #' @return Components of the [xy()] vector or NULL if the dimension is missing #' @export #' #' @examples #' x <- xyz(1:5, 6:10, 11:15) #' xy_x(x) #' xy_y(x) #' xy_z(x) #' xy_m(x) #' xy_x <- function(x) { unclass(as_xy(x))$x } #' @rdname xy_x #' @export xy_y <- function(x) { unclass(as_xy(x))$y } #' @rdname xy_x #' @export xy_z <- function(x) { unclass(as_xy(x))$z } #' @rdname xy_x #' @export xy_m <- function(x) { unclass(as_xy(x))$m } wk/R/grd-subset.R0000644000176200001440000003570414315177334013317 0ustar liggesusers #' Subset grid objects #' #' The [grd_subset()] method handles the subsetting of a [grd()] #' in x-y space. Ordering of indices is not considered and logical #' indies are recycled silently along dimensions. The result of #' a [grd_subset()] is always a [grd()] of the same type whose #' relationship to x-y space has not changed. #' #' @inheritParams grd_cell #' @inheritParams grd_summary #' @param grid_data The `data` member of a [grd()]. This is typically an #' array but can also be an S3 object with an array-like subset method. #' The [native raster][grDevices::as.raster] is special-cased as its #' subset method requires non-standard handling. #' @param i,j 1-based index values. `i` indices correspond to decreasing #' `y` values; `j` indices correspond to increasing `x` values. #' Values outside the range `1:nrow|ncol(data)` will be censored to #' `NA` including 0 and negative values. #' @param ... Passed to subset methods #' #' @return A modified `grid` whose cell centres have not changed location #' as a result of the subset. #' @export #' #' @examples #' grid <- grd_rct(volcano) #' grd_subset(grid, 1:20, 1:30) #' grd_crop(grid, rct(-10, -10, 10, 10)) #' grd_extend(grid, rct(-10, -10, 10, 10)) #' grd_subset <- function(grid, i = NULL, j = NULL, ...) { UseMethod("grd_subset") } #' @rdname grd_subset #' @export grd_crop <- function(grid, bbox, ..., step = 1L, snap = NULL) { UseMethod("grd_crop") } #' @rdname grd_subset #' @export grd_extend <- function(grid, bbox, ..., step = 1L, snap = NULL) { UseMethod("grd_extend") } #' @export grd_subset.wk_grd_rct <- function(grid, i = NULL, j = NULL, ...) { grd_subset_grd_internal(grid, i, j) } #' @export grd_subset.wk_grd_xy <- function(grid, i = NULL, j = NULL, ...) { grd_subset_grd_internal(grid, i, j) } grd_subset_grd_internal <- function(grid, i = NULL, j = NULL) { ij <- ij_from_args(i, j) # convert i and j into start, stop, step i <- ij_to_slice_one(ij$i, dim(grid)[1]) j <- ij_to_slice_one(ij$j, dim(grid)[2]) # calculate bbox s <- grd_summary(grid) dx <- unname(j["step"] * s$dx) dy <- unname(i["step"] * s$dy) center_min <- unclass(grd_cell_xy(grid, i["stop"], j["start"] + 1L)) center_max <- unclass(grd_cell_xy(grid, i["start"] + 1L, j["stop"])) rct_new <- list( xmin = center_min$x, ymin = center_min$y, xmax = center_max$x, ymax = center_max$y ) # check for empty subsets in both directions if (!is.finite(rct_new$xmax - rct_new$xmin)) { rct_new$xmin <- Inf rct_new$xmax <- -Inf } else if (inherits(grid, "wk_grd_rct")) { rct_new$xmin <- rct_new$xmin - dx / 2 rct_new$xmax <- rct_new$xmax + dx / 2 } if (!is.finite(rct_new$ymax - rct_new$ymin)) { rct_new$ymin <- Inf rct_new$ymax <- -Inf } else if (inherits(grid, "wk_grd_rct")) { rct_new$ymin <- rct_new$ymin - dy / 2 rct_new$ymax <- rct_new$ymax + dy / 2 } grid$data <- grd_data_subset(grid$data, i, j) grid$bbox <- new_wk_rct(rct_new, crs = wk_crs(grid)) grid } #' @rdname grd_subset #' @export grd_crop.wk_grd_rct <- function(grid, bbox, ..., step = 1L, snap = NULL) { snap <- snap %||% list(grd_snap_next, grd_snap_previous) ij <- grd_cell_range(grid, bbox, step = step, snap = snap) ij$i["start"] <- max(ij$i["start"], 0L) ij$i["stop"] <- min(ij$i["stop"], dim(grid)[1]) ij$j["start"] <- max(ij$j["start"], 0L) ij$j["stop"] <- min(ij$j["stop"], dim(grid)[2]) grd_subset(grid, ij) } #' @rdname grd_subset #' @export grd_crop.wk_grd_xy <- function(grid, bbox, ..., step = 1L, snap = NULL) { snap <- snap %||% list(ceiling, floor) ij <- grd_cell_range(grid, bbox, step = step, snap = snap) ij$i["start"] <- max(ij$i["start"], 0L) ij$i["stop"] <- min(ij$i["stop"], dim(grid)[1]) ij$j["start"] <- max(ij$j["start"], 0L) ij$j["stop"] <- min(ij$j["stop"], dim(grid)[2]) grd_subset(grid, ij) } #' @rdname grd_subset #' @export grd_extend.wk_grd_rct <- function(grid, bbox, ..., step = 1L, snap = NULL) { snap <- snap %||% list(grd_snap_next, grd_snap_previous) grd_subset(grid, grd_cell_range(grid, bbox, step = step, snap = snap)) } #' @rdname grd_subset #' @export grd_extend.wk_grd_xy <- function(grid, bbox, ..., step = 1L, snap = NULL) { snap <- snap %||% list(ceiling, floor) grd_subset(grid, grd_cell_range(grid, bbox, step = step, snap = snap)) } #' @rdname grd_subset #' @export grd_data_subset <- function(grid_data, i = NULL, j = NULL) { ij <- ij_from_args(i, j) ij$i <- ij_expand_one(ij$i, dim(grid_data)[1], out_of_bounds = "censor") ij$j <- ij_expand_one(ij$j, dim(grid_data)[2], out_of_bounds = "censor") if (inherits(grid_data, "nativeRaster")) { # special case the nativeRaster, whose dims are lying about # the ordering needed to index it attrs <- attributes(grid_data) dim(grid_data) <- rev(dim(grid_data)) grid_data <- grid_data[ij$j, ij$i, drop = FALSE] attrs$dim <- rev(dim(grid_data)) attributes(grid_data) <- attrs grid_data } else { # we want to keep everything for existing dimensions # this means generating a list of missings to fill # the correct number of additional dimensions n_more_dims <- length(dim(grid_data)) - 2L more_dims <- alist(1, )[rep(2, n_more_dims)] do.call("[", c(list(grid_data, ij$i, ij$j), more_dims, list(drop = FALSE))) } } #' Grid cell operators #' #' @inheritParams grd_summary #' @inheritParams grd_subset #' @param bbox An [rct()] object. #' @param out_of_bounds One of 'keep', 'censor', 'discard', or 'squish' #' @param step The difference between adjascent indices in the output #' @param point A [handleable][wk_handle] of points. #' @param snap A function that transforms real-valued indices to integer #' indices (e.g., [floor()], [ceiling()], or [round()]). #' For [grd_cell_range()], a `list()` with exactly two elements to be called #' for the minimum and maximum index values, respectively. #' @param ... Unused #' #' @return #' - `grd_cell()`: returns a `list(i, j)` of index values corresponding #' to the input points and adjusted according to `snap`. Index values #' will be outside `dim(grid)` for points outside `wk_bbox(grid)` including #' negative values. #' - `grd_cell_range()` returns a slice describing the range of indices #' in the `i` and `j` directions. #' - `grd_cell_rct()` returns a [rct()] of the cell extent at `i, j`. #' - `grd_cell_xy()` returns a [xy()] of the cell center at `i, j`. #' @export #' #' @examples #' grid <- grd(nx = 3, ny = 2) #' grd_cell(grid, xy(0.5, 0.5)) #' grd_cell_range(grid, grid$bbox) #' grd_cell_rct(grid, 1, 1) #' grd_cell_xy(grid, 1, 1) #' grd_cell <- function(grid, point, ..., snap = grd_snap_next) { UseMethod("grd_cell") } #' @export grd_cell.wk_grd_rct <- function(grid, point, ..., snap = grd_snap_next) { s <- grd_summary(grid) point <- unclass(as_xy(point)) i <- if (s$width == -Inf) rep(NA_real_, length(point$x)) else (s$ymax - point$y) / s$dy j <- if (s$height == -Inf) rep(NA_real_, length(point$x)) else (point$x - s$xmin) / s$dx new_data_frame(list(i = unname(snap(i - 0.5) + 1L), j = unname(snap(j - 0.5) + 1L))) } #' @export grd_cell.wk_grd_xy <- function(grid, point, ..., snap = grd_snap_next) { grd_cell(as_grd_rct(grid), point, snap = snap) } #' @rdname grd_cell #' @export grd_cell_range <- function(grid, bbox = wk_bbox(grid), ..., step = 1L, snap = grd_snap_next) { UseMethod("grd_cell_range") } #' @export grd_cell_range.default <- function(grid, bbox = wk_bbox(grid), ..., step = 1L, snap = grd_snap_next) { # normalized so that xmin < xmax, ymin < ymax if (inherits(bbox, "wk_rct")) { bbox <- wk_bbox(as_wkb(bbox)) } else { bbox <- wk_bbox(bbox) } # step can be length to for i, j steps if (length(step) == 1L) { step <- step[c(1L, 1L)] } if (is.function(snap)) { snap <- list(snap, snap) } indices <- grd_cell(grid, as_xy(wk_vertices(bbox))) # return a consistent value for an empty grid subset s <- grd_summary(grid) rct_target <- unclass(bbox) rct_target_width <- rct_target$xmax - rct_target$xmin rct_target_height <- rct_target$ymax - rct_target$ymin indices_min <- grd_cell(grid, xy(rct_target$xmin, rct_target$ymax), snap = snap[[1]]) indices_max <- grd_cell(grid, xy(rct_target$xmax, rct_target$ymin), snap = snap[[2]]) if (rct_target_height == -Inf || s$height == -Inf) { i <- integer() } else { i <- c(start = indices_min$i - 1L, stop = indices_max$i, step = 1L) } if (rct_target_width == -Inf || s$width == -Inf) { j <- integer() } else { j <- c(start = indices_min$j - 1L, stop = indices_max$j, step = 1L) } # process downsample if requested if (!identical(step, c(1L, 1L))) { n <- c(0L, 0L) if (!identical(i, integer())) { n[1] <- i["stop"] - i["start"] } if (!identical(j, integer())) { n[2] <- j["stop"] - j["start"] } step <- pmin(n, pmax(1L, step)) if (!identical(i, integer())) { i["step"] <- step[1] i["start"] <- i["start"] + (step[1] %/% 2L) i["stop"] <- i["stop"] - ((step[1] + 1L) %/% 2L) } if (!identical(j, integer())) { j["step"] <- step[2] j["start"] <- j["start"] + (step[2] %/% 2L) j["stop"] <- j["stop"] - ((step[2] + 1L) %/% 2L) } } list(i = i, j = j) } #' @rdname grd_cell #' @export grd_cell_rct <- function(grid, i, j = NULL, ...) { UseMethod("grd_cell_rct") } #' @rdname grd_cell #' @export grd_cell_rct.wk_grd_rct <- function(grid, i, j = NULL, ..., out_of_bounds = "keep") { s <- grd_summary(grid) # non-numeric values don't make sense here because i and j are vectorized # instead of crossed to form the final values ij <- ij_from_args(i, j) if (!is.numeric(ij$i) || !is.numeric(ij$j)) { stop("`i` and `j` must be numeric index vectors in grd_cell_rct()") } # recycle to a common length ij[] <- recycle_common(ij$i, ij$j) # handle out_of_bounds ij <- ij_handle_out_of_bounds2(ij, list(s$ny, s$nx), out_of_bounds) xmin <- s$xmin + (ij$j - 1) * s$dx xmax <- s$xmin + ij$j * s$dx ymin <- s$ymax - ij$i * s$dy ymax <- s$ymax - (ij$i - 1) * s$dy rct(xmin, ymin, xmax, ymax, crs = wk_crs(grid)) } #' @rdname grd_cell #' @export grd_cell_rct.wk_grd_xy <- function(grid, i, j = NULL, ..., out_of_bounds = "keep") { grd_cell_rct(as_grd_rct(grid), i, j, out_of_bounds = out_of_bounds) } #' @rdname grd_cell #' @export grd_cell_xy <- function(grid, i, j = NULL, ...) { UseMethod("grd_cell_xy") } #' @rdname grd_cell #' @export grd_cell_xy.wk_grd_rct <- function(grid, i, j = NULL, ..., out_of_bounds = "keep") { s <- grd_summary(grid) # non-numeric values don't make sense here because i and j are vectorized # instead of crossed to form the final values ij <- ij_from_args(i, j) if (!is.numeric(ij$i) || !is.numeric(ij$j)) { stop("`i` and `j` must be numeric index vectors in grd_cell_rct()") } # recycle to a common length ij[] <- recycle_common(ij$i, ij$j) # handle out_of_bounds ij <- ij_handle_out_of_bounds2(ij, list(s$ny, s$nx), out_of_bounds) x <- s$xmin + (ij$j - 1) * s$dx + s$dx / 2 y <- s$ymax - (ij$i - 1) * s$dy - s$dy / 2 xy(x, y, crs = wk_crs(grid)) } #' @rdname grd_cell #' @export grd_cell_xy.wk_grd_xy <- function(grid, i, j = NULL, ..., out_of_bounds = "keep") { grd_cell_xy(as_grd_rct(grid), i, j, out_of_bounds = out_of_bounds) } ij_from_args <- function(i, j = NULL) { if (is.null(i) && is.null(j)) { list(i = NULL, j = NULL) } else if (is.null(j) && is.list(i)) { i } else { list(i = i, j = j) } } ij_expand_one <- function(i, n, out_of_bounds = "keep") { if (is.null(i)) { i <- if (n > 0) seq(1L, n) else integer() } else if (identical(names(i), c("start", "stop", "step"))) { value_na <- is.na(i) i[value_na] <- c(0L, n, 1L)[value_na] if (i["stop"] > i["start"]) { i <- unname(seq(i["start"] + 1L, i["stop"], by = i["step"])) } else { i <- integer() } } else if (is.numeric(i)) { i <- i } else { stop( "index vectors must be NULL, numeric, or c(start = , stop =, step =)", call. = FALSE ) } if (out_of_bounds == "censor") { i[(i > n) | (i < 1)] <- NA_integer_ } else if (out_of_bounds == "keep") { # do nothing } else if (out_of_bounds == "discard") { i <- i[(i <= n) & (i >= 1)] } else if (out_of_bounds == "squish") { i[i < 1L] <- 1L i[i > n] <- n } else { stop( "`out_of_bounds` must be one of 'censor', 'keep', 'discard', or 'squish'", call. = FALSE ) } i } ij_to_slice_one <- function(i, n) { if (is.null(i)) { i <- if (n == 0L) integer() else c(start = 0L, stop = n, step = 1L) } else if (identical(names(i), c("start", "stop", "step"))) { value_na <- is.na(i) i[value_na] <- c(0L, n, 1L)[value_na] i <- if (i["start"] >= i["stop"]) integer() else i } else if (is.numeric(i)) { if (length(i) == 0L) { i <- integer() } else if ((length(i) == 1L) && is.finite(i)) { i <- c(start = i - 1L, stop = i, step = 1L) } else { if (any(!is.finite(i))) { stop("numeric index vectors must be finite in `grd_subset()`", call. = FALSE) } step <- unique(diff(i)) if ((length(step) != 1) || (step <= 0)) { stop("numeric index vectors must be equally spaced and ascending", call. = FALSE) } i <- c(start = min(i) - 1L, stop = max(i), step = step) } } else { stop( "index vectors must be NULL, numeric, or c(start = , stop =, step =)", call. = FALSE ) } i } # used by extractors to handle out-of-bounds points and/or cells ij_handle_out_of_bounds2 <- function(ij, n, out_of_bounds) { if (out_of_bounds == "keep") { return(ij) } oob_i <- !is.na(ij$i) & ((ij$i > n[[1]]) | (ij$i < 1L)) oob_j <- !is.na(ij$j) & ((ij$j > n[[2]]) | (ij$j < 1L)) oob_either <- oob_i | oob_j if (!any(oob_either)) { return(ij) } if (out_of_bounds == "censor") { ij$i[oob_either] <- NA_integer_ ij$j[oob_either] <- NA_integer_ } else if (out_of_bounds == "discard") { ij$i <- ij$i[!oob_either] ij$j <- ij$j[!oob_either] } else if (out_of_bounds == "squish") { ij$i[!is.na(ij$i) & (ij$i < 1L)] <- 1L ij$j[!is.na(ij$j) & (ij$j < 1L)] <- 1L ij$i[!is.na(ij$i) & (ij$i > n[[1]])] <- n[[1]] ij$j[!is.na(ij$j) & (ij$j > n[[2]])] <- n[[2]] } else { stop( "`out_of_bounds` must be one of 'censor', 'keep', 'discard', or 'squish'", call. = FALSE ) } ij } #' Index snap functions #' #' These functions can be used in [grd_cell()] and #' [grd_cell_range()]. These functions differ in the way #' they round 0.5: [grd_snap_next()] always rounds up #' and [grd_snap_previous()] always rounds down. You can #' also use [floor()] and [ceiling()] as index #' snap functions. #' #' @param x A vector of rescaled but non-integer indices #' #' @return A vector of integer indices #' @export #' #' @examples #' grd_snap_next(seq(0, 2, 0.25)) #' grd_snap_previous(seq(0, 2, 0.25)) #' grd_snap_next <- function(x) { ifelse(((x + 0.5) %% 1L) == 0, ceiling(x), round(x)) } #' @rdname grd_snap_next #' @export grd_snap_previous <- function(x) { ifelse(((x + 0.5) %% 1L) == 0, floor(x), round(x)) } wk/R/wk-crs.R0000644000176200001440000002274514311405267012442 0ustar liggesusers #' Set and get vector CRS #' #' The wk package doesn't operate on CRS objects, but does propagate them #' through subsetting and concatenation. A CRS object can be any R object, #' and x can be any object whose 'crs' attribute carries a CRS. These functions #' are S3 generics to keep them from being used #' on objects that do not use this system of CRS propagation. #' #' @param x,... Objects whose "crs" attribute is used to carry a CRS. #' @param crs An object that can be interpreted as a CRS #' @param value See `crs`. #' #' @export #' wk_crs <- function(x) { UseMethod("wk_crs") } #' @rdname wk_crs #' @export wk_crs.wk_vctr <- function(x) { attr(x, "crs", exact = TRUE) } #' @rdname wk_crs #' @export wk_crs.wk_rcrd <- function(x) { attr(x, "crs", exact = TRUE) } #' @rdname wk_crs #' @export `wk_crs<-` <- function(x, value) { wk_set_crs(x, value) } #' @rdname wk_crs #' @export wk_set_crs <- function(x, crs) { UseMethod("wk_set_crs") } #' @export wk_set_crs.wk_vctr <- function(x, crs) { attr(x, "crs") <- crs x } #' @export wk_set_crs.wk_rcrd <- function(x, crs) { attr(x, "crs") <- crs x } #' @rdname wk_crs #' @export wk_crs_output <- function(...) { dots <- list(...) crs <- lapply(dots, wk_crs) Reduce(wk_crs2, crs) } #' @rdname wk_crs #' @export wk_is_geodesic_output <- function(...) { dots <- list(...) geodesic <- lapply(dots, wk_is_geodesic) Reduce(wk_is_geodesic2, geodesic) } wk_crs2 <- function(x, y) { if (inherits(y, "wk_crs_inherit")) { x } else if (inherits(x, "wk_crs_inherit")) { y } else if (wk_crs_equal(x, y)) { x } else { stop(sprintf("CRS objects '%s' and '%s' are not equal.", format(x), format(y)), call. = FALSE) } } wk_is_geodesic2 <- function(x, y) { if (identical(x, y)) { x } else if (identical(x, NA)) { y } else if (identical(y, NA)) { x } else { stop("objects have differing values for geodesic", call. = FALSE) } } #' Compare CRS objects #' #' The [wk_crs_equal()] function uses special S3 dispatch on [wk_crs_equal_generic()] #' to evaluate whether or not two CRS values can be considered equal. When implementing #' [wk_crs_equal_generic()], every attempt should be made to make `wk_crs_equal(x, y)` #' and `wk_crs_equal(y, x)` return identically. #' #' @param x,y Objects stored in the `crs` attribute of a vector. #' @param ... Unused #' #' @return `TRUE` if `x` and `y` can be considered equal, `FALSE` otherwise. #' @export #' wk_crs_equal <- function(x, y) { if (is.object(y)) { wk_crs_equal_generic(y, x) } else { wk_crs_equal_generic(x, y) } } #' @rdname wk_crs_equal #' @export wk_crs_equal_generic <- function(x, y, ...) { UseMethod("wk_crs_equal_generic") } #' @export wk_crs_equal_generic.default <- function(x, y, ...) { identical(x, y) } #' @export wk_crs_equal_generic.integer <- function(x, y, ...) { isTRUE(x == y) } #' @export wk_crs_equal_generic.double <- function(x, y, ...) { isTRUE(x == y) } #' Set and get vector geodesic edge interpolation #' #' @param x An R object that contains edges #' @param geodesic `TRUE` if edges must be interpolated as geodesics when #' coordinates are spherical, `FALSE` otherwise. #' @param value See `geodesic`. #' #' @return `TRUE` if edges must be interpolated as geodesics when #' coordinates are spherical, `FALSE` otherwise. #' @export #' wk_is_geodesic <- function(x) { UseMethod("wk_is_geodesic") } #' @rdname wk_is_geodesic #' @export wk_set_geodesic <- function(x, geodesic) { UseMethod("wk_set_geodesic") } #' @rdname wk_is_geodesic #' @export `wk_is_geodesic<-` <- function(x, value) { wk_set_geodesic(x, value) } #' @rdname wk_is_geodesic #' @export wk_geodesic_inherit <- function() { NA } #' @export wk_is_geodesic.default <- function(x) { FALSE } #' @export wk_is_geodesic.wk_wkb <- function(x) { attr(x, "geodesic", exact = TRUE) %||% FALSE } #' @export wk_is_geodesic.wk_wkt <- function(x) { attr(x, "geodesic", exact = TRUE) %||% FALSE } #' @export wk_set_geodesic.default <- function(x, geodesic) { if (geodesic) { warning( sprintf( "Ignoring wk_set_geodesic(x, TRUE) for object of class '%s'", class(x)[1] ) ) } x } #' @export wk_set_geodesic.wk_wkb <- function(x, geodesic) { attr(x, "geodesic") <- geodesic_attr(geodesic) x } #' @export wk_set_geodesic.wk_wkt <- function(x, geodesic) { attr(x, "geodesic") <- geodesic_attr(geodesic) x } geodesic_attr <- function(geodesic) { if (!is.logical(geodesic) || (length(geodesic) != 1L)) { stop("`geodesic` must be TRUE, FALSE, or NA", call. = FALSE) } if (identical(geodesic, FALSE)) { NULL } else { geodesic } } #' CRS object generic methods #' #' @param crs An arbitrary R object #' @param verbose Use `TRUE` to request a more verbose version of the #' PROJ definition (e.g., PROJJSON). The default of `FALSE` should return #' the most compact version that completely describes the CRS. An #' authority:code string (e.g., "OGC:CRS84") is the recommended way #' to represent a CRS when `verbose` is `FALSE`, if possible, falling #' back to the most recent version of WKT2 or PROJJSON. #' @param proj_version A [package_version()] of the PROJ version, or #' `NULL` if the PROJ version is unknown. #' #' @return #' - `wk_crs_proj_definition()` Returns a string used to represent the #' CRS in PROJ. For recent PROJ version you'll want to return PROJJSON; #' however you should check `proj_version` if you want this to work with #' older versions of PROJ. #' - `wk_crs_projjson()` Returns a PROJJSON string or NA_character_ if this #' representation is unknown or can't be calculated. #' @export #' #' @examples #' wk_crs_proj_definition("EPSG:4326") #' wk_crs_proj_definition <- function(crs, proj_version = NULL, verbose = FALSE) { UseMethod("wk_crs_proj_definition") } #' @rdname wk_crs_proj_definition #' @export wk_crs_projjson <- function(crs) { UseMethod("wk_crs_projjson") } #' @export wk_crs_projjson.default <- function(crs) { maybe_auth_code_or_json <- wk_crs_proj_definition(crs, verbose = FALSE) # check for most probably JSON if (isTRUE(grepl("^\\{.*?\\}$", maybe_auth_code_or_json))) { return(maybe_auth_code_or_json) } # look up by auth_name / code split <- strsplit(maybe_auth_code_or_json, ":", fixed = TRUE)[[1]] query <- new_data_frame(list(auth_name = split[1], code = split[2])) merge(query, wk::wk_proj_crs_json, all.x = TRUE)$projjson } #' @rdname wk_crs_proj_definition #' @export wk_crs_proj_definition.NULL <- function(crs, proj_version = NULL, verbose = FALSE) { NA_character_ } #' @rdname wk_crs_proj_definition #' @export wk_crs_proj_definition.wk_crs_inherit <- function(crs, proj_version = NULL, verbose = FALSE) { NA_character_ } #' @rdname wk_crs_proj_definition #' @export wk_crs_proj_definition.character <- function(crs, proj_version = NULL, verbose = FALSE) { stopifnot(length(crs) == 1) crs } #' @rdname wk_crs_proj_definition #' @export wk_crs_proj_definition.double <- function(crs, proj_version = NULL, verbose = FALSE) { stopifnot(length(crs) == 1) if (is.na(crs)) wk_crs_proj_definition(NULL) else paste0("EPSG:", crs) } #' @rdname wk_crs_proj_definition #' @export wk_crs_proj_definition.integer <- function(crs, proj_version = NULL, verbose = FALSE) { stopifnot(length(crs) == 1) if (is.na(crs)) wk_crs_proj_definition(NULL) else paste0("EPSG:", crs) } #' Special CRS values #' #' The CRS handling in the wk package requires two sentinel CRS values. #' The first, [wk_crs_inherit()], signals that the vector should inherit #' a CRS of another vector if combined. This is useful for empty, `NULL`, #' and/or zero-length geometries. The second, [wk_crs_auto()], is used #' as the default argument of `crs` for constructors so that zero-length #' geometries are assigned a CRS of `wk_crs_inherit()` by default. #' #' @param x A raw input to a construuctor whose length and crs attributte #' is used to determine the default CRS returned by [wk_crs_auto()]. #' @param crs A value for the coordinate reference system supplied by #' the user. #' #' @export #' #' @examples #' wk_crs_auto_value(list(), wk_crs_auto()) #' wk_crs_auto_value(list(), 1234) #' wk_crs_auto_value(list(NULL), wk_crs_auto()) #' wk_crs_inherit <- function() { structure(list(), class = "wk_crs_inherit") } #' @rdname wk_crs_inherit #' @export wk_crs_longlat <- function(crs = NULL) { if (inherits(crs, "wk_crs_inherit") || is.null(crs) || identical(crs, "WGS84")) { return("OGC:CRS84") } crs_proj <- wk_crs_proj_definition(crs) switch( crs_proj, "OGC:CRS84" = , "EPSG:4326" = , "WGS84" = "OGC:CRS84", "OGC:CRS27" = , "EPSG:4267" = , "NAD27" = "OGC:CRS27", "OGC:CRS83" = , "EPSG:4269" = , "NAD83" = "OGC:CRS83", stop( sprintf( "Can't guess authority-compliant long/lat definition from CRS '%s'", format(crs_proj) ) ) ) } #' @rdname wk_crs_inherit #' @export wk_crs_auto <- function() { structure(list(), class = "wk_crs_auto") } #' @rdname wk_crs_inherit #' @export wk_crs_auto_value <- function(x, crs) { if (inherits(crs, "wk_crs_auto")) { if (length(x) == 0) wk_crs_inherit() else attr(x, "crs", exact = TRUE) } else { crs } } #' @export format.wk_crs_inherit <- function(x, ...) { format("wk_crs_inherit()", ...) } #' @export print.wk_crs_inherit <- function(x, ...) { cat("\n") } wk_crs_format <- function(x, ...) { tryCatch( wk_crs_proj_definition(x, verbose = FALSE), error = function(e) format(x, ...) ) } wk/R/grd.R0000644000176200001440000002741614315177334012015 0ustar liggesusers #' Raster-like objects #' #' [grd()] objects are just an array (any object with more than #' two [dim()]s) and a bounding box (a [rct()], which may or #' may not have a [wk_crs()] attached). The ordering of the dimensions #' is y (indices increasing downwards), x (indices increasing to the right). #' This follows the ordering of [as.raster()]/[rasterImage()] and aligns #' with the printing of matrices. #' #' @param data An object with two or more dimensions. Most usefully, a matrix. #' @param bbox A [rct()] containing the bounds and CRS of the object. You can #' specify a [rct()] with `xmin > xmax` or `ymin > ymax` which will flip #' the underlying data and return an object with a normalized bounding #' box and data. #' @param nx,ny,dx,dy Either a number of cells in the x- and y- directions #' or delta in the x- and y-directions (in which case `bbox` must #' be specified). #' @param type Use "polygons" to return a grid whose objects can be #' represented using an [rct()]; use "centers" to return a grid whose #' objects are the center of the [rct()] grid; use "corners" to return #' a grid along the corners of `bbox`. #' @param x An object to convert to a grid #' @param ... Passed to S3 methods #' #' @return #' - `grd()` returns a `grd_rct()` for `type == "polygons` or #' a `grd_xy()` otherwise. #' - `grd_rct()` returns an object of class "wk_grd_rct". #' - `grd_xy()` returns an object of class "wk_grd_xy". #' @export #' #' @examples #' # create a grid with no data (just for coordinates) #' (grid <- grd(nx = 2, ny = 2)) #' as_rct(grid) #' as_xy(grid) #' plot(grid, border = "black") #' #' # more usefully, wraps a matrix or nd array + bbox #' # approx volcano in New Zealand Transverse Mercator #' bbox <- rct( #' 5917000, 1757000 + 870, #' 5917000 + 610, 1757000, #' crs = "EPSG:2193" #' ) #' (grid <- grd_rct(volcano, bbox)) #' #' # these come with a reasonable default plot method for matrix data #' plot(grid) #' #' # you can set the data or the bounding box after creation #' grid$bbox <- rct(0, 0, 1, 1) #' #' # subset by indices or rct #' plot(grid[1:2, 1:2]) #' plot(grid[c(start = NA, stop = NA, step = 2), c(start = NA, stop = NA, step = 2)]) #' plot(grid[rct(0, 0, 0.5, 0.5)]) #' grd <- function(bbox = NULL, nx = NULL, ny = NULL, dx = NULL, dy = NULL, type = c("polygons", "corners", "centers")) { if (is.null(bbox)) { bbox <- NULL } else if (inherits(bbox, "wk_rct")) { bbox } else { wk_bbox(bbox) } type <- match.arg(type) if (is.null(nx) && is.null(ny) && !is.null(dx) && !is.null(dy) && !is.null(bbox)) { rct <- unclass(bbox) width <- rct$xmax - rct$xmin height <- rct$ymax - rct$ymin if (type == "polygons") { nx <- width / dx ny <- height / dy } else if (type == "corners") { nx <- width / dx + 1 ny <- height / dy + 1 } else if (type == "centers") { nx <- width / dx ny <- height / dy bbox <- rct( rct$xmin + dx / 2, rct$ymin + dy / 2, rct$xmax - dx / 2, rct$ymax - dy / 2, crs = wk_crs(bbox) ) } } else if (is.null(dx) && is.null(dy) && !is.null(nx) && !is.null(ny)) { if (is.null(bbox)) { bbox <- rct(0, 0, nx, ny) } nx <- nx ny <- ny if (type == "centers") { rct <- unclass(bbox) width <- rct$xmax - rct$xmin height <- rct$ymax - rct$ymin dx <- width / nx dy <- height / ny bbox <- rct( rct$xmin + dx / 2, rct$ymin + dy / 2, rct$xmax - dx / 2, rct$ymax - dy / 2, crs = wk_crs(bbox) ) } else if (type == "corners") { nx <- nx + 1 ny <- ny + 1 } } else { stop( "Must specify dx, dy, and bbox OR nx and ny.", call. = FALSE ) } # use a length-zero logical() with correct x and y dims data <- array(dim = c(ny, nx, 0)) if (type == "polygons") { grd_rct(data, bbox) } else { grd_xy(data, bbox) } } #' @rdname grd #' @export grd_rct <- function(data, bbox = rct(0, 0, dim(data)[2], dim(data)[1])) { bbox <- if (inherits(bbox, "wk_rct")) bbox else wk_bbox(bbox) # normalize data and bbox so that max > min normalized <- grd_internal_normalize(data, bbox) data <- normalized[[1]] bbox <- normalized[[2]] # with zero values, bbox in that direction is empty rct <- unclass(bbox) if (dim(data)[2] == 0) { rct$xmin <- Inf rct$xmax <- -Inf } if (dim(data)[1] == 0) { rct$ymin <- Inf rct$ymax <- -Inf } bbox <- new_wk_rct(rct, crs = wk_crs(bbox)) new_wk_grd(list(data = data, bbox = bbox), "wk_grd_rct") } #' @rdname grd #' @export grd_xy <- function(data, bbox = rct(0, 0, dim(data)[2] - 1, dim(data)[1] - 1)) { bbox <- if (inherits(bbox, "wk_rct")) bbox else wk_bbox(bbox) # normalize data and bbox so that max > min normalized <- grd_internal_normalize(data, bbox) data <- normalized[[1]] bbox <- normalized[[2]] # with zero values, bbox in that direction is empty rct <- unclass(bbox) if (dim(data)[2] == 0) { rct$xmin <- Inf rct$xmax <- -Inf } if (dim(data)[1] == 0) { rct$ymin <- Inf rct$ymax <- -Inf } bbox <- new_wk_rct(rct, crs = wk_crs(bbox)) # with one value in the x dimension, we need a zero width bbox if (dim(data)[2] == 1) { stopifnot( unclass(bbox)$xmax == unclass(bbox)$xmin ) } # with one value in the y dimension, we need a zero height bbox if (dim(data)[1] == 1) { stopifnot( unclass(bbox)$ymax == unclass(bbox)$ymin ) } new_wk_grd(list(data = data, bbox = bbox), "wk_grd_xy") } #' @rdname grd #' @export as_grd_rct <- function(x, ...) { UseMethod("as_grd_rct") } #' @rdname grd #' @export as_grd_rct.wk_grd_rct <- function(x, ...) { x } #' @rdname grd #' @export as_grd_rct.wk_grd_xy <- function(x, ...) { # from a grd_xy, we assume these were the centres s <- grd_summary(x) bbox <- rct( s$xmin - s$dx / 2, s$ymin - s$dy / 2, s$xmax + s$dx / 2, s$ymax + s$dy / 2, crs = wk_crs(x$bbox) ) grd_rct(x$data, bbox = bbox) } #' @rdname grd #' @export as_grd_xy <- function(x, ...) { UseMethod("as_grd_xy") } #' @rdname grd #' @export as_grd_xy.wk_grd_xy <- function(x, ...) { x } #' @rdname grd #' @export as_grd_xy.wk_grd_rct <- function(x, ...) { # from a grid_rct() we take the centers s <- grd_summary(x) bbox <- rct( s$xmin + s$dx / 2, s$ymin + s$dy / 2, s$xmax - s$dx / 2, s$ymax - s$dy / 2, crs = wk_crs(x$bbox) ) grd_xy(x$data, bbox = bbox) } #' S3 details for grid objects #' #' @param x A [grd()] #' @param subclass An optional subclass. #' #' @return An object inheriting from 'grd' #' #' @export #' new_wk_grd <- function(x, subclass = character()) { structure(x, class = union(subclass, "wk_grd")) } #' Grid information #' #' @param grid A [grd_xy()], [grd_rct()], or other object #' implementing `grd_*()` methods. #' @return #' - `grd_summary()` returns a `list()` with components #' `xmin`, `ymin`, `xmax`, `ymax`, #' `nx`, `ny`, `dx`, `dy`, `width`, and `height`. #' @export #' #' @examples #' grd_summary(grd(nx = 3, ny = 2)) #' grd_summary <- function(grid) { UseMethod("grd_summary") } #' @export grd_summary.wk_grd_rct <- function(grid) { nx <- dim(grid$data)[2] ny <- dim(grid$data)[1] rct <- unclass(grid$bbox) width <- rct$xmax - rct$xmin height <- rct$ymax - rct$ymin dx <- width / nx dy <- height / ny list( xmin = rct$xmin, ymin = rct$ymin, xmax = rct$xmax, ymax = rct$ymax, nx = nx, ny = ny, dx = dx, dy = dy, width = width, height = height ) } #' @export grd_summary.wk_grd_xy <- function(grid) { nx <- dim(grid$data)[2] ny <- dim(grid$data)[1] rct <- unclass(grid$bbox) width <- rct$xmax - rct$xmin height <- rct$ymax - rct$ymin dx <- if (nx > 1) width / (nx - 1) else 0 dy <- if (ny > 1) height / (ny - 1) else 0 list( xmin = rct$xmin, ymin = rct$ymin, xmax = rct$xmax, ymax = rct$ymax, nx = nx, ny = ny, dx = dx, dy = dy, width = width, height = height ) } # interface for wk methods #' @export wk_bbox.wk_grd <- function(handleable, ...) { handleable$bbox } #' @export wk_crs.wk_grd <- function(x) { attr(x$bbox, "crs", exact = TRUE) } #' @export wk_set_crs.wk_grd <- function(x, crs) { x$bbox <- wk_set_crs(x$bbox, crs) x } # interface for setting data and bbox #' @export `[[<-.wk_grd` <- function(x, i, value) { x_bare <- unclass(x) if (identical(i, "data")) { stopifnot(length(dim(value)) >= 2) x_bare$data <- value } else if (identical(i, "bbox")) { if (inherits(value, "wk_rct")) { # normalize so that max > min, but empty (Inf -Inf) is OK rct <- unclass(value) # missings make no sense in this context if (any(vapply(rct, is.na, logical(1)))) { stop("Can't set missing bounding box for grd objects", call. = FALSE) } if ((rct$xmin > rct$xmax) && (rct$xmin != Inf) && (rct$xmax != -Inf)) { rct[c("xmin", "xmax")] <- rct[c("xmax", "xmin")] } if ((rct$ymin > rct$ymax) && (rct$ymin != Inf) && (rct$ymax != -Inf)) { rct[c("ymin", "ymax")] <- rct[c("ymax", "ymin")] } value <- new_wk_rct(rct, crs = wk_crs(value)) } else { value <- wk_bbox(value) } x_bare$bbox <- value } else { stop("Can't set element of a grd that is not 'data' or 'bbox'", call. = FALSE) } class(x_bare) <- class(x) x_bare } #' @export `$<-.wk_grd` <- function(x, i, value) { x[[i]] <- value x } # interface for matrix-like extraction and subsetting #' @export dim.wk_grd <- function(x) { dim(x$data) } #' @export `[.wk_grd` <- function(x, i, j, ..., drop = FALSE) { # for this method we never drop dimensions (can use $data[] to do this) stopifnot(identical(drop, FALSE)) bbox <- NULL if (missing(i)) { i <- NULL } if (missing(j)) { j <- NULL } # allow combination of i, j to be a rct() instead if (inherits(i, "wk_rct") && is.null(j)) { result_xy <- grd_crop(x, bbox = i) if (length(dim(x$data)) > 2) { result_xy$data <- result_xy$data[, , , ..., drop = FALSE] } else { result_xy$data <- result_xy$data[, , ..., drop = FALSE] } } else if (inherits(i, "wk_rct")) { result_xy <- grd_crop(x, bbox = i) result_xy$data <- result_xy$data[, , j, ..., drop = FALSE] } else { result_xy <- grd_subset(x, i = i, j = j) result_xy$data <- result_xy$data[, , ..., drop = FALSE] } result_xy } #' @export format.wk_grd <- function(x, ...) { crs <- wk_crs(x) sprintf( "<%s [%s] => %s%s>", class(x)[1], paste0(dim(x$data), collapse = " x "), wk_bbox(x), if (is.null(crs)) "" else paste0(" with crs=", format(crs)) ) } #' @export print.wk_grd <- function(x, ...) { cat(paste0(format(x), "\n")) utils::str(x) invisible(x) } # normalize the data and the bbox such that xmax > xmin and ymax > ymin grd_internal_normalize <- function(x, bbox) { rct <- unclass(bbox) new_rct <- rct if ((rct$ymin > rct$ymax) && (dim(x)[1] > 0)) { new_rct$ymin <- rct$ymax new_rct$ymax <- rct$ymin if (inherits(x, "nativeRaster")) { # the dimensions of a nativeRaster are lying in the sense that # they are row-major but are being stored column-major in the way # that R's indexing functions work attrs <- attributes(x) dim(x) <- rev(dim(x)) x <- x[, ncol(x):1, drop = FALSE] attributes(x) <- attrs } else { x <- x[nrow(x):1, , drop = FALSE] } } if ((rct$xmin > rct$xmax) && (dim(x)[2] > 0)) { new_rct$xmin <- rct$xmax new_rct$xmax <- rct$xmin if (inherits(x, "nativeRaster")) { attrs <- attributes(x) dim(x) <- rev(dim(x)) x <- x[nrow(x):1, , drop = FALSE] attributes(x) <- attrs } else { x <- x[, ncol(x):1, drop = FALSE] } } list(x = x, bbox = new_wk_rct(new_rct, crs = wk_crs(bbox))) } wk/R/plot.R0000644000176200001440000002056014313206670012202 0ustar liggesusers #' Plot well-known geometry vectors #' #' @param x A [wkb()] or [wkt()] #' @param add Should a new plot be created, or should `handleable` be added to the #' existing plot? #' @param ... Passed to plotting functions for features: [graphics::points()] #' for point and multipoint geometries, [graphics::lines()] for linestring #' and multilinestring geometries, and [graphics::polypath()] for polygon #' and multipolygon geometries. #' @param bbox The limits of the plot as a [rct()] or compatible object #' @param asp,xlab,ylab Passed to [graphics::plot()] #' @param rule The rule to use for filling polygons (see [graphics::polypath()]) #' @inheritParams wk_handle #' #' @return The input, invisibly. #' @importFrom graphics plot #' @export #' #' @examples #' plot(as_wkt("LINESTRING (0 0, 1 1)")) #' plot(as_wkb("LINESTRING (0 0, 1 1)")) #' wk_plot <- function(handleable, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE) { UseMethod("wk_plot") } #' @rdname wk_plot #' @export wk_plot.default <- function(handleable, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE) { # this is too hard without vctrs (already in Suggests) if (!requireNamespace("vctrs", quietly = TRUE)) { stop("Package 'vctrs' is required for wk_plot()", call. = FALSE) # nocov } if (isTRUE(wk_is_geodesic(handleable))) { stop( paste0( "wk_plot.default() can't plot geodesic objects.\n", "Use `wk_set_geodesic(x, FALSE)` to ignore geodesic edge specification" ), call. = FALSE ) } # should be refactored x <- handleable if (!add) { bbox <- unclass(bbox) bbox <- bbox %||% unclass(wk_bbox(x)) xlim <- c(bbox$xmin, bbox$xmax) ylim <- c(bbox$ymin, bbox$ymax) graphics::plot( numeric(0), numeric(0), xlim = xlim, ylim = ylim, xlab = xlab, ylab = ylab, asp = asp ) } # for everything below we'll need to be able to subset if (!vctrs::vec_is(x)) { wk_plot(as_wkb(x), ..., rule = rule, add = TRUE) # nocov return(invisible(x)) # nocov } # get some background info size <- vctrs::vec_size(x) meta <- wk_meta(x) # points can be handled by as_xy() if (all(meta$geometry_type == 1L, na.rm = TRUE)) { coords <- unclass(as_xy(x)) graphics::points(coords, ...) return(invisible(x)) } # evaluate the dots dots <- list(..., rule = rule) is_scalar <- !vapply(dots, vctrs::vec_is, logical(1)) dots[is_scalar] <- lapply(dots[is_scalar], list) dots_length <- vapply(dots, vctrs::vec_size, integer(1)) dots_constant <- all(dots_length == 1L) is_rule <- length(dots) # point + multipoint is probably faster with a single coord vector if (all(meta$geometry_type %in% c(1, 4), na.rm = TRUE)) { coords <- wk_coords(x) if (dots_constant) { graphics::points(coords[c("x", "y")], ...) } else { dots$rule <- NULL dots <- vctrs::vec_recycle_common(!!!dots, .size = size) dots_tbl <- vctrs::new_data_frame(dots, n = size) do.call(graphics::points, c(coords[c("x", "y")], dots_tbl[coords$feature_id, , drop = FALSE])) } return(invisible(x)) } # it's not faster to flatten big vectors into a single go for anything else dots <- vctrs::vec_recycle_common(!!!dots, .size = size) for (i in seq_len(size)) { xi <- vctrs::vec_slice(x, i) dotsi <- lapply(dots, "[[", i) if (meta$geometry_type[i] %in% c(1, 4)) { wk_plot_point_or_multipoint(xi, dotsi[-is_rule]) } else if (meta$geometry_type[i] %in% c(2, 5)) { wk_plot_line_or_multiline(xi, dotsi[-is_rule]) } else if (meta$geometry_type[i] %in% c(3, 6)) { wk_plot_poly_or_multi_poly(xi, dotsi) } else { do.call(wk_plot, c(list(wk_flatten(xi, max_depth = .Machine$integer.max), add = TRUE), dotsi)) } } invisible(x) } wk_plot_point_or_multipoint <- function(x, dots) { dots_without_border <- dots[setdiff(names(dots), "border")] coords <- wk_coords(x) do.call(graphics::points, c(coords[c("x", "y")], dots_without_border)) } wk_plot_line_or_multiline <- function(x, dots) { coords <- wk_coords(x) if (nrow(coords) == 0) { return() } geom_id <- coords$part_id geom_id_lag <- c(-1L, geom_id[-length(geom_id)]) new_geom <- geom_id != geom_id_lag na_shift <- cumsum(new_geom) - 1L coords_seq <- seq_along(geom_id) coord_x <- rep(NA_real_, length(geom_id) + sum(new_geom) - 1L) coord_y <- rep(NA_real_, length(geom_id) + sum(new_geom) - 1L) coord_x[coords_seq + na_shift] <- coords$x coord_y[coords_seq + na_shift] <- coords$y dots$rule <- NULL do.call(graphics::lines, c(list(coord_x, coord_y), dots)) } wk_plot_poly_or_multi_poly <- function(x, dots) { coords <- wk_coords(x) if (nrow(coords) == 0) { return() } # for polygons we can use the coord vectors directly # because the graphics device expects open loops geom_id <- coords$ring_id n <- length(geom_id) # leave the last loop closed the avoid a trailing NA (which results in error) geom_id_lead <- c(geom_id[-1L], geom_id[n]) new_geom_next <- geom_id != geom_id_lead coords$x[new_geom_next] <- NA_real_ coords$y[new_geom_next] <- NA_real_ do.call(graphics::polypath, c(coords[c("x", "y")], dots)) } #' @rdname wk_plot #' @export plot.wk_wkt <- function(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE) { wk_plot( x, ..., asp = asp, bbox = bbox, xlab = xlab, ylab = ylab, rule = rule, add = add ) invisible(x) } #' @rdname wk_plot #' @export plot.wk_wkb <- function(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", rule = "evenodd", add = FALSE) { wk_plot( x, ..., asp = asp, bbox = bbox, xlab = xlab, ylab = ylab, rule = rule, add = add ) invisible(x) } #' @rdname wk_plot #' @export plot.wk_xy <- function(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE) { x_bare <- unclass(x) if (!add) { graphics::plot( double(), double(), xlim = range(x_bare$x, finite = TRUE), ylim = range(x_bare$y, finite = TRUE), xlab = xlab, ylab = ylab, asp = asp ) } graphics::points(x_bare$x, x_bare$y, ...) invisible(x) } #' @rdname wk_plot #' @export plot.wk_rct <- function(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE) { x_bare <- unclass(x) if (!add) { xlim_min <- range(x_bare$xmin, finite = TRUE) xlim_max <- range(x_bare$xmax, finite = TRUE) ylim_min <- range(x_bare$ymin, finite = TRUE) ylim_max <- range(x_bare$ymax, finite = TRUE) graphics::plot( double(), double(), xlim = range(c(xlim_min, xlim_max), finite = TRUE), ylim = range(c(ylim_min, ylim_max), finite = TRUE), xlab = xlab, ylab = ylab, asp = asp ) } graphics::rect(x_bare$xmin, x_bare$ymin, x_bare$xmax, x_bare$ymax, ...) invisible(x) } #' @rdname wk_plot #' @export plot.wk_crc <- function(x, ..., asp = 1, bbox = NULL, xlab = "", ylab = "", add = FALSE) { x_bare <- unclass(x) if (!add) { xlim_min <- range(x_bare$x + x_bare$r, finite = TRUE) xlim_max <- range(x_bare$x - x_bare$r, finite = TRUE) ylim_min <- range(x_bare$y + x_bare$r, finite = TRUE) ylim_max <- range(x_bare$y - x_bare$r, finite = TRUE) graphics::plot( double(), double(), xlim = range(c(xlim_min, xlim_max), finite = TRUE), ylim = range(c(ylim_min, ylim_max), finite = TRUE), xlab = xlab, ylab = ylab, asp = asp ) } # estimate resolution for turning circles into segments usr <- graphics::par("usr") usr_x <- usr[1:2] usr_y <- usr[3:4] device_x <- graphics::grconvertX(usr_x, to = "device") device_y <- graphics::grconvertY(usr_y, to = "device") # Use resolution of 1 at the device level, scale to usr coords. # Changing this number to 2 or 4 doesn't really affect the speed # at which these plot; a value of 1 tends to give very good # resolution and is acceptable even when a plot in the interactive # device is zoomed. scale_x <- diff(device_x) / diff(usr_x) scale_y <- diff(device_y) / diff(usr_y) scale <- min(abs(scale_x), abs(scale_y)) resolution_usr <- 1 / scale plot( wk_handle(x, wkb_writer(), resolution = resolution_usr), ..., add = TRUE ) invisible(x) } wk/R/void.R0000644000176200001440000000131014106220314012144 0ustar liggesusers #' Do nothing #' #' This handler does nothing and returns `NULL`. It is useful for #' benchmarking readers and handlers and when using filters #' that have side-effects (e.g., [wk_debug()]). Note that this #' handler stops on the first parse error; to see a list of parse #' errors see the [wk_problems()] handler. #' #' @inheritParams wk_handle #' #' @return `NULL` #' @export #' #' @examples #' wk_void(wkt("POINT (1 4)")) #' wk_handle(wkt("POINT (1 4)"), wk_void_handler()) #' wk_void <- function(handleable, ...) { invisible(wk_handle(handleable, wk_void_handler(), ...)) } #' @rdname wk_void #' @export wk_void_handler <- function() { new_wk_handler(.Call(wk_c_handler_void_new), "wk_void_handler") } wk/R/debug.R0000644000176200001440000000077414106220314012306 0ustar liggesusers #' Debug filters and handlers #' #' @inheritParams wk_handle #' #' @return The result of the `handler`. #' @export #' #' @examples #' wk_debug(wkt("POINT (1 1)")) #' wk_handle(wkt("POINT (1 1)"), wk_debug_filter()) #' wk_debug <- function(handleable, handler = wk_void_handler(), ...) { wk_handle(handleable, wk_debug_filter(handler)) } #' @rdname wk_debug #' @export wk_debug_filter <- function(handler = wk_void_handler()) { new_wk_handler(.Call(wk_c_debug_filter_new, handler), "wk_debug_filter") } wk/R/handle-xy.R0000644000176200001440000000025214106220314013100 0ustar liggesusers #' @rdname wk_handle #' @export wk_handle.wk_xy <- function(handleable, handler, ...) { handler <- as_wk_handler(handler) .Call(wk_c_read_xy, handleable, handler) } wk/R/handle-crc.R0000644000176200001440000000154714106220314013217 0ustar liggesusers #' @rdname wk_handle #' @export wk_handle.wk_crc <- function(handleable, handler, ..., n_segments = getOption("wk.crc_n_segments", NULL), resolution = getOption("wk.crc_resolution", NULL)) { if (is.null(n_segments) && is.null(resolution)) { n_segments <- 100L } else if (is.null(n_segments)) { n_segments <- ceiling(2 * pi / (resolution / unclass(handleable)$r)) } n_segments <- as.integer(pmax(4L, n_segments)) n_segments[is.na(n_segments)] <- 4L if ((length(n_segments) != 1) && (length(n_segments) != length(handleable))) { stop( sprintf( "`n_segments`/`resolution` must be length 1 or length of data (%s)", length(handleable) ), call. = FALSE ) } handler <- as_wk_handler(handler) .Call(wk_c_read_crc, handleable, handler, n_segments) } wk/R/wkt-writer.R0000644000176200001440000000034714160220603013334 0ustar liggesusers #' @rdname wk_writer #' @export wkt_writer <- function(precision = 16L, trim = TRUE) { new_wk_handler( .Call( wk_c_wkt_writer, as.integer(precision)[1], as.logical(trim)[1] ), "wk_wkt_writer" ) } wk/R/grd-handle.R0000644000176200001440000000770614315177334013246 0ustar liggesusers #' Handler interface for grid objects #' #' @inheritParams wk_handle #' @param data_order A vector of length 2 describing the order in which #' values should appear. The default, `c("y", "x")`, will output values #' in the same order as the default matrix storage in R (column-major). #' You can prefix a dimension with `-` to reverse the order of a #' dimension (e.g., `c("-y", "x")`). #' #' @return The result of the `handler`. #' @export #' #' @examples #' wk_handle(grd(nx = 3, ny = 3), wkt_writer()) #' wk_handle(grd(nx = 3, ny = 3, type = "centers"), wkt_writer()) #' wk_handle.wk_grd_xy <- function(handleable, handler, ..., data_order = c("y", "x")) { # eventually these will be more efficient and not resolve every cell wk_handle(as_xy(handleable, data_order = data_order), handler, ...) } #' @rdname wk_handle.wk_grd_xy #' @export wk_handle.wk_grd_rct <- function(handleable, handler, ..., data_order = c("y", "x")) { # eventually these will be more efficient and not resolve every cell wk_handle(as_rct(handleable, data_order = data_order), handler, ...) } #' @export as_xy.wk_grd_xy <- function(x, ..., data_order = c("y", "x")) { rct <- unclass(x$bbox) nx <- dim(x$data)[2] ny <- dim(x$data)[1] width <- rct$xmax - rct$xmin height <- rct$ymax - rct$ymin if (identical(width, -Inf) || identical(height, -Inf)) { return(xy(crs = wk_crs(x))) } if (nx == 1L) { xs <- rct$xmin } else { xs <- seq(rct$xmin, rct$xmax, by = width / (nx - 1)) } if (ny == 1L) { ys <- rct$ymin } else { ys <- seq(rct$ymax, rct$ymin, by = -height / (ny - 1)) } # Custom ordering such that coordinates can match up to data dim_order <- gsub("^[+-]", "", data_order) if (identical(dim_order, c("y", "x"))) { if (startsWith(data_order[1], "-")) { ys <- rev(ys) } if (startsWith(data_order[2], "-")) { xs <- rev(xs) } xy( rep(xs, each = length(ys)), rep(ys, length(xs)), crs = wk_crs(x$bbox) ) } else { if (startsWith(data_order[2], "-")) { ys <- rev(ys) } if (startsWith(data_order[1], "-")) { xs <- rev(xs) } xy( rep(xs, length(ys)), rep(ys, each = length(xs)), crs = wk_crs(x$bbox) ) } } #' @export as_rct.wk_grd_rct <- function(x, ..., data_order = c("y", "x")) { rct <- unclass(x$bbox) nx <- dim(x$data)[2] ny <- dim(x$data)[1] width <- rct$xmax - rct$xmin height <- rct$ymax - rct$ymin if (identical(width, -Inf) || identical(height, -Inf)) { return(rct(crs = wk_crs(x))) } # Custom ordering such that coordinates can match up to data xs <- seq(rct$xmin, rct$xmax, by = width / nx) ys <- seq(rct$ymax, rct$ymin, by = -height / ny) dim_order <- gsub("^[+-]", "", data_order) if (identical(dim_order, c("y", "x"))) { if (startsWith(data_order[1], "-")) { ys <- rev(ys) ymax <- rep(ys[-1], nx) ymin <- rep(ys[-length(ys)], nx) } else { ymin <- rep(ys[-1], nx) ymax <- rep(ys[-length(ys)], nx) } if (startsWith(data_order[2], "-")) { xs <- rev(xs) xmax <- rep(xs[-length(xs)], each = ny) xmin <- rep(xs[-1], each = ny) } else { xmin <- rep(xs[-length(xs)], each = ny) xmax <- rep(xs[-1], each = ny) } rct(xmin, ymin, xmax, ymax, crs = wk_crs(x$bbox)) } else { if (startsWith(data_order[2], "-")) { ys <- rev(ys) ymax <- rep(ys[-1], each = nx) ymin <- rep(ys[-length(ys)], each = nx) } else { ymin <- rep(ys[-1], each = nx) ymax <- rep(ys[-length(ys)], each = nx) } if (startsWith(data_order[1], "-")) { xs <- rev(xs) xmax <- rep(xs[-length(xs)], ny) xmin <- rep(xs[-1], ny) } else { xmin <- rep(xs[-length(xs)], ny) xmax <- rep(xs[-1], ny) } } rct(xmin, ymin, xmax, ymax, crs = wk_crs(x$bbox)) } #' @export as_xy.wk_grd_rct <- function(x, ...) { as_xy(as_grd_xy(x)) } #' @export as_rct.wk_grd_xy <- function(x, ...) { as_rct(as_grd_rct(x)) } wk/R/pkg-vctrs.R0000644000176200001440000005756614454533430013167 0ustar liggesusers #' Vctrs methods #' #' @param x,y,to,... See [vctrs::vec_cast()] and [vctrs::vec_ptype2()]. #' @rdname vctrs-methods #' @name vctrs-methods #' NULL # wkb() -------- vec_proxy.wk_wkb <- function(x, ...) { unclass(x) } vec_proxy_equal.wk_wkb <- function(x, ...) { wkb_to_hex(x) } vec_restore.wk_wkb <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) geodesic_out <- attr(to, "geodesic", exact = TRUE) %||% attr(x, "geodesic", exact = TRUE) attr(x, "crs") <- NULL attr(x, "geodesic") <- NULL new_wk_wkb(x, crs = crs_out, geodesic = geodesic_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_wkb vec_cast.wk_wkb <- function(x, to, ...) { UseMethod("vec_cast.wk_wkb") # nocov } #' @method vec_cast.wk_wkb default #' @export vec_cast.wk_wkb.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @method vec_cast.wk_wkb wk_wkb #' @export vec_cast.wk_wkb.wk_wkb <- function(x, to, ...) { wk_crs_output(x, to) wk_is_geodesic_output(x, to) x } #' @method vec_cast.wk_wkb wk_wkt #' @export vec_cast.wk_wkb.wk_wkt <- function(x, to, ...) { wk_crs_output(x, to) wk_is_geodesic_output(x, to) as_wkb(x) } #' @method vec_cast.wk_wkb wk_xy #' @export vec_cast.wk_wkb.wk_xy <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkb wk_xyz #' @export vec_cast.wk_wkb.wk_xyz <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkb wk_xym #' @export vec_cast.wk_wkb.wk_xym <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkb wk_xyzm #' @export vec_cast.wk_wkb.wk_xyzm <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkb wk_rct #' @export vec_cast.wk_wkb.wk_rct <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkb wk_crc #' @export vec_cast.wk_wkb.wk_crc <- function(x, to, ...) { wk_crs_output(x, to) as_wkb(x) } #' @rdname vctrs-methods #' @export vec_ptype2.wk_wkb vec_ptype2.wk_wkb <- function(x, y, ...) { UseMethod("vec_ptype2.wk_wkb", y) # nocov } #' @method vec_ptype2.wk_wkb default #' @export vec_ptype2.wk_wkb.default <- function(x, y, ..., x_arg = "x", y_arg = "y") { vctrs::vec_default_ptype2(x, y, x_arg = x_arg, y_arg = y_arg) # nocov } #' @method vec_ptype2.wk_wkb wk_wkb #' @export vec_ptype2.wk_wkb.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = geodesic_attr(wk_is_geodesic_output(x, y))) } #' @method vec_ptype2.wk_wkb wk_wkt #' @export vec_ptype2.wk_wkb.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = geodesic_attr(wk_is_geodesic_output(x, y))) } #' @method vec_ptype2.wk_wkb wk_xy #' @export vec_ptype2.wk_wkb.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkb wk_xyz #' @export vec_ptype2.wk_wkb.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkb wk_xym #' @export vec_ptype2.wk_wkb.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkb wk_xyzm #' @export vec_ptype2.wk_wkb.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkb wk_rct #' @export vec_ptype2.wk_wkb.wk_rct <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = geodesic_attr(wk_is_geodesic_output(x, y))) } #' @method vec_ptype2.wk_wkb wk_crc #' @export vec_ptype2.wk_wkb.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } # wkt() -------- vec_proxy.wk_wkt <- function(x, ...) { unclass(x) } vec_restore.wk_wkt <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) geodesic_out <- attr(to, "geodesic", exact = TRUE) %||% attr(x, "geodesic", exact = TRUE) attr(x, "crs") <- NULL attr(x, "geodesic") <- NULL new_wk_wkt(x, crs = crs_out, geodesic = geodesic_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_wkt vec_cast.wk_wkt <- function(x, to, ...) { UseMethod("vec_cast.wk_wkt") # nocov } #' @method vec_cast.wk_wkt default #' @export vec_cast.wk_wkt.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @method vec_cast.wk_wkt wk_wkt #' @export vec_cast.wk_wkt.wk_wkt <- function(x, to, ...) { wk_crs_output(x, to) wk_is_geodesic_output(x, to) x } #' @method vec_cast.wk_wkt wk_wkb #' @export vec_cast.wk_wkt.wk_wkb <- function(x, to, ...) { wk_crs_output(x, to) wk_is_geodesic_output(x, to) as_wkt(x) } #' @method vec_cast.wk_wkt wk_xy #' @export vec_cast.wk_wkt.wk_xy <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkt wk_xyz #' @export vec_cast.wk_wkt.wk_xyz <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkt wk_xym #' @export vec_cast.wk_wkt.wk_xym <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkt wk_xyzm #' @export vec_cast.wk_wkt.wk_xyzm <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkt wk_rct #' @export vec_cast.wk_wkt.wk_rct <- function(x, to, ...) { wk_translate(x, to) } #' @method vec_cast.wk_wkt wk_crc #' @export vec_cast.wk_wkt.wk_crc <- function(x, to, ...) { wk_translate(x, to) } #' @rdname vctrs-methods #' @export vec_ptype2.wk_wkt vec_ptype2.wk_wkt <- function(x, y, ...) { UseMethod("vec_ptype2.wk_wkt", y) # nocov } #' @method vec_ptype2.wk_wkt default #' @export vec_ptype2.wk_wkt.default <- function(x, y, ..., x_arg = "x", y_arg = "y") { vctrs::vec_default_ptype2(x, y, x_arg = x_arg, y_arg = y_arg) # nocov } #' @method vec_ptype2.wk_wkt wk_wkt #' @export vec_ptype2.wk_wkt.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = geodesic_attr(wk_is_geodesic_output(x, y))) } #' @method vec_ptype2.wk_wkt wk_wkb #' @export vec_ptype2.wk_wkt.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = geodesic_attr(wk_is_geodesic_output(x, y))) } #' @method vec_ptype2.wk_wkt wk_xy #' @export vec_ptype2.wk_wkt.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkt wk_xyz #' @export vec_ptype2.wk_wkt.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkt wk_xym #' @export vec_ptype2.wk_wkt.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkt wk_xyzm #' @export vec_ptype2.wk_wkt.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_wkt wk_rct #' @export vec_ptype2.wk_wkt.wk_rct <- function(x, y, ..., x_arg = "x", y_arg = "y") { wk_is_geodesic_output(x, y) new_wk_wkt(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_wkt wk_crc #' @export vec_ptype2.wk_wkt.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(x, "geodesic", exact = TRUE)) } # xy() -------- vec_proxy.wk_xy <- function(x, ...) { new_data_frame(unclass(x)) } vec_restore.wk_xy <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) attr(x, "crs") <- NULL attr(x, "row.names") <- NULL new_wk_xy(x, crs = crs_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_xy vec_cast.wk_xy <- function(x, to, ...) { UseMethod("vec_cast.wk_xy") # nocov } #' @method vec_cast.wk_xy default #' @export vec_cast.wk_xy.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @method vec_cast.wk_xy wk_xy #' @export vec_cast.wk_xy.wk_xy <- function(x, to, ...) { wk_crs_output(x, to) x } #' @method vec_cast.wk_xy wk_wkb #' @export vec_cast.wk_xy.wk_wkb <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x) } #' @method vec_cast.wk_xy wk_wkt #' @export vec_cast.wk_xy.wk_wkt <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x) } #' @method vec_cast.wk_xy wk_xyz #' @export vec_cast.wk_xy.wk_xyz <- function(x, to, ...) { wk_crs_output(x, to) vctrs::maybe_lossy_cast( as_xy(x, dims = c("x", "y")), x, to, !is.na(unclass(x)$z) ) } #' @method vec_cast.wk_xy wk_xym #' @export vec_cast.wk_xy.wk_xym <- function(x, to, ...) { wk_crs_output(x, to) vctrs::maybe_lossy_cast( as_xy(x, dims = c("x", "y")), x, to, !is.na(unclass(x)$m) ) } #' @method vec_cast.wk_xy wk_xyzm #' @export vec_cast.wk_xy.wk_xyzm <- function(x, to, ...) { wk_crs_output(x, to) vctrs::maybe_lossy_cast( as_xy(x, dims = c("x", "y")), x, to, !is.na(unclass(x)$z) & !is.na(unclass(x)$m) ) } #' @rdname vctrs-methods #' @export vec_ptype2.wk_xy vec_ptype2.wk_xy <- function(x, y, ...) { UseMethod("vec_ptype2.wk_xy", y) # nocov } #' @method vec_ptype2.wk_xy wk_xy #' @export vec_ptype2.wk_xy.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xy(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xy wk_wkb #' @export vec_ptype2.wk_xy.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xy wk_wkt #' @export vec_ptype2.wk_xy.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xy wk_xyz #' @export vec_ptype2.wk_xy.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyz(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xy wk_xym #' @export vec_ptype2.wk_xy.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xym(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xy wk_xyzm #' @export vec_ptype2.wk_xy.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xy wk_rct #' @export vec_ptype2.wk_xy.wk_rct <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xy wk_crc #' @export vec_ptype2.wk_xy.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } # xyz() -------- vec_proxy.wk_xyz <- function(x, ...) { new_data_frame(unclass(x)) } vec_restore.wk_xyz <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) attr(x, "crs") <- NULL attr(x, "row.names") <- NULL new_wk_xyz(x, crs = crs_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_xyz vec_cast.wk_xyz <- function(x, to, ...) { UseMethod("vec_cast.wk_xyz") # nocov } #' @method vec_cast.wk_xyz default #' @export vec_cast.wk_xyz.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @method vec_cast.wk_xyz wk_xyz #' @export vec_cast.wk_xyz.wk_xyz <- function(x, to, ...) { wk_crs_output(x, to) x } #' @method vec_cast.wk_xyz wk_wkb #' @export vec_cast.wk_xyz.wk_wkb <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z")) } #' @method vec_cast.wk_xyz wk_wkt #' @export vec_cast.wk_xyz.wk_wkt <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z")) } #' @method vec_cast.wk_xyz wk_xy #' @export vec_cast.wk_xyz.wk_xy <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z")) } #' @method vec_cast.wk_xyz wk_xym #' @export vec_cast.wk_xyz.wk_xym <- function(x, to, ...) { wk_crs_output(x, to) vctrs::maybe_lossy_cast( as_xy(x, dims = c("x", "y", "z")), x, to, !is.na(unclass(x)$m) ) } #' @method vec_cast.wk_xyz wk_xyzm #' @export vec_cast.wk_xyz.wk_xyzm <- function(x, to, ...) { wk_crs_output(x, to) vctrs::maybe_lossy_cast( as_xy(x, dims = c("x", "y", "z")), x, to, !is.na(unclass(x)$m) ) } #' @rdname vctrs-methods #' @export vec_ptype2.wk_xyz vec_ptype2.wk_xyz <- function(x, y, ...) { UseMethod("vec_ptype2.wk_xyz", y) # nocov } #' @method vec_ptype2.wk_xyz wk_xyz #' @export vec_ptype2.wk_xyz.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyz(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyz wk_wkb #' @export vec_ptype2.wk_xyz.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xyz wk_wkt #' @export vec_ptype2.wk_xyz.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xyz wk_xy #' @export vec_ptype2.wk_xyz.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyz(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyz wk_xym #' @export vec_ptype2.wk_xyz.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyz wk_xyzm #' @export vec_ptype2.wk_xyz.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyz wk_rct #' @export vec_ptype2.wk_xyz.wk_rct <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyz wk_crc #' @export vec_ptype2.wk_xyz.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } # xym() -------- vec_proxy.wk_xym <- function(x, ...) { new_data_frame(unclass(x)) } vec_restore.wk_xym <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) attr(x, "crs") <- NULL attr(x, "row.names") <- NULL new_wk_xym(x, crs = crs_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_xym vec_cast.wk_xym <- function(x, to, ...) { UseMethod("vec_cast.wk_xym") # nocov } #' @method vec_cast.wk_xym default #' @export vec_cast.wk_xym.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @method vec_cast.wk_xym wk_xym #' @export vec_cast.wk_xym.wk_xym <- function(x, to, ...) { wk_crs_output(x, to) x } #' @method vec_cast.wk_xym wk_wkb #' @export vec_cast.wk_xym.wk_wkb <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "m")) } #' @method vec_cast.wk_xym wk_wkt #' @export vec_cast.wk_xym.wk_wkt <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "m")) } #' @method vec_cast.wk_xym wk_xy #' @export vec_cast.wk_xym.wk_xy <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "m")) } #' @method vec_cast.wk_xym wk_xyz #' @export vec_cast.wk_xym.wk_xyz <- function(x, to, ...) { wk_crs_output(x, to) vctrs::maybe_lossy_cast( as_xy(x, dims = c("x", "y", "m")), x, to, !is.na(unclass(x)$z) ) } #' @method vec_cast.wk_xym wk_xyzm #' @export vec_cast.wk_xym.wk_xyzm <- function(x, to, ...) { wk_crs_output(x, to) vctrs::maybe_lossy_cast( as_xy(x, dims = c("x", "y", "m")), x, to, !is.na(unclass(x)$z) ) } #' @rdname vctrs-methods #' @export vec_ptype2.wk_xym vec_ptype2.wk_xym <- function(x, y, ...) { UseMethod("vec_ptype2.wk_xym", y) # nocov } #' @method vec_ptype2.wk_xym wk_xym #' @export vec_ptype2.wk_xym.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xym(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xym wk_wkb #' @export vec_ptype2.wk_xym.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xym wk_wkt #' @export vec_ptype2.wk_xym.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xym wk_xy #' @export vec_ptype2.wk_xym.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xym(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xym wk_xyz #' @export vec_ptype2.wk_xym.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xym wk_xyzm #' @export vec_ptype2.wk_xym.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xym wk_rct #' @export vec_ptype2.wk_xym.wk_rct <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xym wk_crc #' @export vec_ptype2.wk_xym.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } # xyzm() -------- vec_proxy.wk_xyzm <- function(x, ...) { new_data_frame(unclass(x)) } vec_restore.wk_xyzm <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) attr(x, "crs") <- NULL attr(x, "row.names") <- NULL new_wk_xyzm(x, crs = crs_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_xyzm vec_cast.wk_xyzm <- function(x, to, ...) { UseMethod("vec_cast.wk_xyzm") # nocov } #' @method vec_cast.wk_xyzm default #' @export vec_cast.wk_xyzm.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @method vec_cast.wk_xyzm wk_xyzm #' @export vec_cast.wk_xyzm.wk_xyzm <- function(x, to, ...) { wk_crs_output(x, to) x } #' @method vec_cast.wk_xyzm wk_wkb #' @export vec_cast.wk_xyzm.wk_wkb <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z", "m")) } #' @method vec_cast.wk_xyzm wk_wkt #' @export vec_cast.wk_xyzm.wk_wkt <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z", "m")) } #' @method vec_cast.wk_xyzm wk_xy #' @export vec_cast.wk_xyzm.wk_xy <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z", "m")) } #' @method vec_cast.wk_xyzm wk_xyz #' @export vec_cast.wk_xyzm.wk_xyz <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z", "m")) } #' @method vec_cast.wk_xyzm wk_xym #' @export vec_cast.wk_xyzm.wk_xym <- function(x, to, ...) { wk_crs_output(x, to) as_xy(x, dims = c("x", "y", "z", "m")) } #' @rdname vctrs-methods #' @export vec_ptype2.wk_xyzm vec_ptype2.wk_xyzm <- function(x, y, ...) { UseMethod("vec_ptype2.wk_xyzm", y) # nocov } #' @method vec_ptype2.wk_xyzm wk_xyzm #' @export vec_ptype2.wk_xyzm.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyzm wk_wkb #' @export vec_ptype2.wk_xyzm.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xyzm wk_wkt #' @export vec_ptype2.wk_xyzm.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_xyzm wk_xy #' @export vec_ptype2.wk_xyzm.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyzm wk_xyz #' @export vec_ptype2.wk_xyzm.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyzm wk_xym #' @export vec_ptype2.wk_xyzm.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_xyzm(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyzm wk_rct #' @export vec_ptype2.wk_xyzm.wk_rct <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_xyzm wk_crc #' @export vec_ptype2.wk_xyzm.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } # rct() -------- vec_proxy.wk_rct <- function(x, ...) { new_data_frame(unclass(x)) } vec_restore.wk_rct <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) attr(x, "crs") <- NULL attr(x, "row.names") <- NULL new_wk_rct(x, crs = crs_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_rct vec_cast.wk_rct <- function(x, to, ...) { UseMethod("vec_cast.wk_rct") # nocov } #' @method vec_cast.wk_rct default #' @export vec_cast.wk_rct.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @rdname vctrs-methods #' @export vec_ptype2.wk_rct vec_ptype2.wk_rct <- function(x, y, ...) { UseMethod("vec_ptype2.wk_rct", y) # nocov } #' @method vec_ptype2.wk_rct wk_rct #' @export vec_ptype2.wk_rct.wk_rct <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_rct(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_rct wk_wkb #' @export vec_ptype2.wk_rct.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_rct wk_wkt #' @export vec_ptype2.wk_rct.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_rct wk_xy #' @export vec_ptype2.wk_rct.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_rct wk_xyz #' @export vec_ptype2.wk_rct.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_rct wk_xym #' @export vec_ptype2.wk_rct.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_rct wk_xyzm #' @export vec_ptype2.wk_rct.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_rct wk_crc #' @export vec_ptype2.wk_rct.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } # crc() -------- vec_proxy.wk_crc <- function(x, ...) { new_data_frame(unclass(x)) } vec_restore.wk_crc <- function(x, to, ...) { crs_out <- attr(to, "crs", exact = TRUE) %||% attr(x, "crs", exact = TRUE) attr(x, "crs") <- NULL attr(x, "row.names") <- NULL new_wk_crc(x, crs = crs_out) } #' @rdname vctrs-methods #' @export vec_cast.wk_crc vec_cast.wk_crc <- function(x, to, ...) { UseMethod("vec_cast.wk_crc") # nocov } #' @method vec_cast.wk_crc default #' @export vec_cast.wk_crc.default <- function(x, to, ...) { vctrs::vec_default_cast(x, to) # nocov } #' @rdname vctrs-methods #' @export vec_ptype2.wk_crc vec_ptype2.wk_crc <- function(x, y, ...) { UseMethod("vec_ptype2.wk_crc", y) # nocov } #' @method vec_ptype2.wk_crc wk_crc #' @export vec_ptype2.wk_crc.wk_crc <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_crc(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_crc wk_wkb #' @export vec_ptype2.wk_crc.wk_wkb <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_crc wk_wkt #' @export vec_ptype2.wk_crc.wk_wkt <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkt(crs = wk_crs_output(x, y), geodesic = attr(y, "geodesic", exact = TRUE)) } #' @method vec_ptype2.wk_crc wk_xy #' @export vec_ptype2.wk_crc.wk_xy <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_crc wk_xyz #' @export vec_ptype2.wk_crc.wk_xyz <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_crc wk_xym #' @export vec_ptype2.wk_crc.wk_xym <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } #' @method vec_ptype2.wk_crc wk_xyzm #' @export vec_ptype2.wk_crc.wk_xyzm <- function(x, y, ..., x_arg = "x", y_arg = "y") { new_wk_wkb(crs = wk_crs_output(x, y)) } wk/R/filter.R0000644000176200001440000000163314472006062012510 0ustar liggesusers #' Copy a geometry vector #' #' @inheritParams wk_handle #' @param result The result of a filter operation intended to be a #' transformation. #' #' @return A copy of `handleable`. #' @export #' #' @examples #' wk_identity(wkt("POINT (1 2)")) #' wk_identity <- function(handleable, ...) { result <- wk_handle(handleable, wk_identity_filter(wk_writer(handleable)), ...) result <- wk_restore(handleable, result, ...) result <- wk_set_crs(result, wk_crs(handleable)) wk_set_geodesic(result, wk_is_geodesic(handleable)) } #' @rdname wk_identity #' @export wk_identity_filter <- function(handler) { new_wk_handler(.Call("wk_c_identity_filter_new", as_wk_handler(handler)), "wk_identity_filter") } #' @rdname wk_identity #' @export wk_restore <- function(handleable, result, ...) { UseMethod("wk_restore") } #' @rdname wk_identity #' @export wk_restore.default <- function(handleable, result, ...) { result } wk/R/rct.R0000644000176200001440000001066614321145376012026 0ustar liggesusers #' 2D rectangle vectors #' #' @param xmin,ymin,xmax,ymax Rectangle bounds. #' @param x An object to be converted to a [rct()]. #' @param ... Extra arguments passed to `as_rct()`. #' @inheritParams new_wk_wkb #' #' @return A vector along the recycled length of bounds. #' @export #' #' @examples #' rct(1, 2, 3, 4) #' rct <- function(xmin = double(), ymin = double(), xmax = double(), ymax = double(), crs = wk_crs_auto()) { vec <- new_wk_rct( recycle_common( xmin = as.double(xmin), ymin = as.double(ymin), xmax = as.double(xmax), ymax = as.double(ymax) ), crs = wk_crs_auto_value(xmin, crs) ) validate_wk_rct(vec) vec } #' @rdname rct #' @export as_rct <- function(x, ...) { UseMethod("as_rct") } #' @rdname rct #' @export as_rct.wk_rct <- function(x, ...) { x } #' @rdname rct #' @export as_rct.matrix <- function(x, ..., crs = NULL) { if (ncol(x) == 4) { colnames(x) <- c("xmin", "ymin", "xmax", "ymax") } as_rct(as.data.frame(x), ..., crs = crs) } #' @rdname rct #' @export as_rct.data.frame <- function(x, ..., crs = NULL) { stopifnot(all(c("xmin", "ymin", "xmax", "ymax") %in% names(x))) new_wk_rct( lapply(x[c("xmin", "ymin", "xmax", "ymax")], as.double), crs = crs ) } validate_wk_rct <- function(x) { validate_wk_rcrd(x) stopifnot( identical(names(unclass(x)), c("xmin", "ymin", "xmax", "ymax")) ) invisible(x) } #' S3 details for rct objects #' #' @param x A [rct()] #' @inheritParams new_wk_wkb #' #' @export #' new_wk_rct <- function(x = list(xmin = double(), ymin = double(), xmax = double(), ymax = double()), crs = NULL) { structure(x, class = c("wk_rct", "wk_rcrd"), crs = crs) } #' @export format.wk_rct <- function(x, ...) { x <- unclass(x) sprintf( "[%s %s %s %s]", format(x$xmin, ...), format(x$ymin, ...), format(x$xmax, ...), format(x$ymax, ...) ) } #' @export `[<-.wk_rct` <- function(x, i, value) { replacement <- as_rct(value) result <- Map("[<-", unclass(x), i, unclass(replacement)) names(result) <- c("xmin", "ymin", "xmax", "ymax") new_wk_rct( result, crs = wk_crs_output(x, replacement) ) } #' Rectangle accessors and operators #' #' @param x,y [rct()] vectors #' #' @return #' - `rct_xmin()`, `rct_xmax()`, `rct_ymin()`, and `rct_ymax()` return #' the components of the [rct()]. #' @export #' #' @examples #' x <- rct(0, 0, 10, 10) #' y <- rct(5, 5, 15, 15) #' #' rct_xmin(x) #' rct_ymin(x) #' rct_xmax(x) #' rct_ymax(x) #' rct_height(x) #' rct_width(x) #' rct_intersects(x, y) #' rct_intersection(x, y) #' rct_contains(x, y) #' rct_contains(x, rct(4, 4, 6, 6)) #' rct_xmin <- function(x) { x <- as_rct(x) unclass(x)$xmin } #' @rdname rct_xmin #' @export rct_ymin <- function(x) { x <- as_rct(x) unclass(x)$ymin } #' @rdname rct_xmin #' @export rct_xmax <- function(x) { x <- as_rct(x) unclass(x)$xmax } #' @rdname rct_xmin #' @export rct_ymax <- function(x) { x <- as_rct(x) unclass(x)$ymax } #' @rdname rct_xmin #' @export rct_width <- function(x) { x <- as_rct(x) x <- unclass(x) x$xmax - x$xmin } #' @rdname rct_xmin #' @export rct_height <- function(x) { x <- as_rct(x) x <- unclass(x) x$ymax - x$ymin } #' @rdname rct_xmin #' @export rct_intersects <- function(x, y) { x <- as_rct(x) y <- as_rct(y) wk_crs_output(x, y) x <- unclass(x) y <- unclass(y) limits <- list( xmin = pmax(x$xmin, y$xmin), xmax = pmin(x$xmax, y$xmax), ymin = pmax(x$ymin, y$ymin), ymax = pmin(x$ymax, y$ymax) ) (limits$xmax >= limits$xmin) & (limits$ymax >= limits$ymin) } #' @rdname rct_xmin #' @export rct_contains <- function(x, y) { x <- as_rct(x) y <- wk_envelope(y) wk_crs_output(x, y) x <- unclass(x) y <- unclass(y) (y$xmin >= x$xmin) & (y$xmax <= x$xmax) & (y$ymin >= x$ymin) & (y$ymax <= x$ymax) } #' @rdname rct_xmin #' @export rct_intersection <- function(x, y) { x <- as_rct(x) y <- as_rct(y) crs <- wk_crs_output(x, y) x <- unclass(x) y <- unclass(y) limits <- list( xmin = pmax(x$xmin, y$xmin), ymin = pmax(x$ymin, y$ymin), xmax = pmin(x$xmax, y$xmax), ymax = pmin(x$ymax, y$ymax) ) any_na <- Reduce("|", lapply(limits, is.na)) not_valid <- any_na | !((limits$xmax >= limits$xmin) & (limits$ymax >= limits$ymin)) limits$xmin[not_valid] <- NA_real_ limits$xmax[not_valid] <- NA_real_ limits$ymin[not_valid] <- NA_real_ limits$ymax[not_valid] <- NA_real_ new_wk_rct(limits, crs = crs) } wk/R/meta.R0000644000176200001440000000657514510124347012163 0ustar liggesusers #' Extract feature-level meta #' #' These functions return the non-coordinate information of a geometry #' and/or vector. They do not parse an entire geometry/vector and are #' intended to be very fast even for large vectors. #' #' @inheritParams wk_handle #' @param geometry_type An integer code for the geometry type. These #' integers follow the WKB specification (e.g., 1 for point, #' 7 for geometrycollection). #' @param geometry_type_label A character vector of (lowercase) #' geometry type labels as would be found in WKT (e.g., point, #' geometrycollection). #' #' @return A data.frame with columns: #' - `geometry_type`: An integer identifying the geometry type. #' A value of 0 indicates that the types of geometry in the vector #' are not known without parsing the entire vector. #' - `size`: For points and linestrings, the number of coordinates; for #' polygons, the number of rings; for collections, the number of #' child geometries. A value of zero indicates an EMPTY geometry. #' A value of `NA` means this value is unknown without parsing the #' entire geometry. #' - `has_z`: `TRUE` if coordinates contain a Z value. A value of `NA` #' means this value is unknown without parsing the entire vector. #' - `has_m`: `TRUE` if coordinates contain an M value. A value of `NA` #' means this value is unknown without parsing the entire vector. #' - `srid`: An integer identifying a CRS or NA if this value was not #' provided. #' - `precision`: A grid size or 0.0 if a grid size was not provided. #' Note that coordinate values may not have been rounded; the grid #' size only refers to the level of detail with which they should #' be interpreted. #' - `is_empty`: `TRUE` if there is at least one non-empty coordinate. #' For the purposes of this value, a non-empty coordinate is one that #' contains at least one value that is not `NA` or `NaN`. #' #' @export #' #' @examples #' wk_vector_meta(as_wkt("LINESTRING (0 0, 1 1)")) #' wk_meta(as_wkt("LINESTRING (0 0, 1 1)")) #' wk_meta(as_wkb("LINESTRING (0 0, 1 1)")) #' #' wk_geometry_type_label(1:7) #' wk_geometry_type(c("point", "geometrycollection")) #' wk_meta <- function(handleable, ...) { UseMethod("wk_meta") } #' @rdname wk_meta #' @export wk_meta.default <- function(handleable, ...) { new_data_frame(wk_handle(handleable, wk_meta_handler(), ...)) } #' @rdname wk_meta #' @export wk_vector_meta <- function(handleable, ...) { UseMethod("wk_vector_meta") } #' @rdname wk_meta #' @export wk_vector_meta.default <- function(handleable, ...) { new_data_frame(wk_handle(handleable, wk_vector_meta_handler(), ...)) } #' @rdname wk_meta #' @export wk_meta_handler <- function() { new_wk_handler(.Call(wk_c_meta_handler_new), "wk_meta_handler") } #' @rdname wk_meta #' @export wk_vector_meta_handler <- function() { new_wk_handler(.Call(wk_c_vector_meta_handler_new), "wk_vector_meta_handler") } #' @rdname wk_meta #' @export wk_geometry_type_label <- function(geometry_type) { c( "point", "linestring", "polygon", "multipoint", "multilinestring", "multipolygon", "geometrycollection" )[as.integer(geometry_type)] } #' @rdname wk_meta #' @export wk_geometry_type <- function(geometry_type_label) { match( geometry_type_label, c( "point", "linestring", "polygon", "multipoint", "multilinestring", "multipolygon", "geometrycollection" ) ) } wk/R/orient-filter.R0000644000176200001440000000231014471771472014014 0ustar liggesusers #' Orient polygon coordinates #' #' @inheritParams wk_handle #' @param direction The winding polygon winding direction #' #' @return `handleable` with consistently oriented polygons, in `direction` #' winding order. #' @export #' #' @examples #' wk_orient(wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")) #' wk_orient( #' wkt("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))"), #' direction = wk_clockwise() #' ) #' wk_orient <- function(handleable, ..., direction = wk_counterclockwise()) { result <- wk_handle( handleable, wk_orient_filter(wk_writer(handleable), direction), ... ) result <- wk_restore(handleable, result, ...) result <- wk_set_geodesic(result, wk_is_geodesic(handleable)) wk_set_crs(result, wk_crs(handleable)) } #' @rdname wk_orient #' @export wk_orient_filter <- function(handler, direction = wk_counterclockwise()) { stopifnot(direction %in% c(wk_clockwise(), wk_counterclockwise())) new_wk_handler( .Call( wk_c_orient_filter_new, as_wk_handler(handler), as.integer(direction)[1] ), "wk_orient_filter" ) } #' @rdname wk_orient #' @export wk_clockwise <- function() { -1L } #' @rdname wk_orient #' @export wk_counterclockwise <- function() { 1L } wk/R/translate.R0000644000176200001440000000071614163110540013213 0ustar liggesusers #' Translate geometry vectors #' #' @inheritParams wk_handle #' @param to A prototype object. #' #' @export #' wk_translate <- function(handleable, to, ...) { UseMethod("wk_translate", to) } #' @rdname wk_translate #' @export wk_translate.default <- function(handleable, to, ...) { result <- wk_handle(handleable, wk_writer(to), ...) attr(result, "crs") <- wk_crs_output(handleable, to) wk_set_geodesic(result, wk_is_geodesic_output(handleable, to)) } wk/R/problems.R0000644000176200001440000000141314106220314013032 0ustar liggesusers #' Validate well-known binary and well-known text #' #' The problems handler returns a character vector of parse #' errors and can be used to validate input of any type #' for which [wk_handle()] is defined. #' #' @inheritParams wk_handle #' #' @return A character vector of parsing errors. `NA` signifies #' that there was no parsing error. #' @export #' #' @examples #' wk_problems(new_wk_wkt(c("POINT EMTPY", "POINT (20 30)"))) #' wk_handle( #' new_wk_wkt(c("POINT EMTPY", "POINT (20 30)")), #' wk_problems_handler() #' ) #' wk_problems <- function(handleable, ...) { wk_handle(handleable, wk_problems_handler(), ...) } #' @rdname wk_problems #' @export wk_problems_handler <- function() { new_wk_handler(.Call(wk_c_problems_handler_new), "wk_problems_handler") } wk/R/pkg-readr.R0000644000176200001440000000073314311471710013075 0ustar liggesusers # registered in zzz.R output_column_wk <- function(x) { out <- as.character(as_wkt(x)) out[is.na(x)] <- NA_character_ out } output_column.wk_wkt <- function(x, name) { output_column_wk(x) } output_column.wk_wkb <- function(x, name) { output_column_wk(x) } output_column.wk_xy <- function(x, name) { output_column_wk(x) } output_column.wk_crc <- function(x, name) { output_column_wk(x) } output_column.wk_rct <- function(x, name) { output_column_wk(x) } wk/R/handle-rct.R0000644000176200001440000000025414106220314013232 0ustar liggesusers #' @rdname wk_handle #' @export wk_handle.wk_rct <- function(handleable, handler, ...) { handler <- as_wk_handler(handler) .Call(wk_c_read_rct, handleable, handler) } wk/R/wkb-writer.R0000644000176200001440000000033614106220314013307 0ustar liggesusers #' @rdname wk_writer #' @export wkb_writer <- function(buffer_size = 2048L, endian = NA_integer_) { new_wk_handler( .Call(wk_c_wkb_writer_new, as.integer(buffer_size), as.integer(endian)), "wk_wkb_writer" ) } wk/R/affine.R0000644000176200001440000000615714125354157012467 0ustar liggesusers #' Affine transformer #' #' @param trans_matrix A 3x3 transformation matrix #' @param x A [wk_trans_affine()] #' @param dx,dy Coordinate offsets in the x and y direction #' @param scale_x,scale_y Scale factor to apply in the x and y directions, respectively #' @param rct_in,rct_out The input and output bounds #' @param rotation_deg A rotation to apply in degrees counterclockwise. #' @param src,dst Point vectors of control points used to estimate the affine mapping #' (using [base::qr.solve()]). #' @param ... Zero or more transforms in the order they should be applied. #' #' @export #' wk_trans_affine <- function(trans_matrix) { new_wk_trans(.Call(wk_c_trans_affine_new, trans_matrix), "wk_trans_affine") } #' @export wk_trans_inverse.wk_trans_affine <- function(trans, ...) { wk_affine_invert(trans) } #' @rdname wk_trans_affine #' @export wk_affine_identity <- function() { wk_affine_translate(0, 0) } #' @rdname wk_trans_affine #' @export wk_affine_rotate <- function(rotation_deg) { theta <- -rotation_deg * pi / 180 trans_matrix <- matrix( c( cos(theta), +sin(theta), 0, -sin(theta), cos(theta), 0, 0, 0, 1 ), nrow = 3, byrow = TRUE ) wk_trans_affine(trans_matrix) } #' @rdname wk_trans_affine #' @export wk_affine_scale <- function(scale_x = 1, scale_y = 1) { wk_trans_affine(matrix(c(scale_x, 0, 0, 0, scale_y, 0, 0, 0, 1), ncol = 3)) } #' @rdname wk_trans_affine #' @export wk_affine_translate <- function(dx = 0, dy = 0) { wk_trans_affine(matrix(c(1, 0, 0, 0, 1, 0, dx, dy, 1), ncol = 3)) } #' @rdname wk_trans_affine #' @export wk_affine_fit <- function(src, dst) { src <- as_xy(src) dst <- as_xy(dst) n <- length(src) stopifnot(length(src) == length(dst)) src <- unclass(src) dst <- unclass(dst) src_mat <- cbind(src$x, src$y, rep_len(1, n)) dst_mat <- cbind(dst$x, dst$y, rep_len(1, n)) wk_trans_affine(t(qr.solve(src_mat, dst_mat))) } #' @rdname wk_trans_affine #' @export wk_affine_rescale <- function(rct_in, rct_out) { # use bbox to sanitize input as rct of length 1 rct_in <- unclass(wk_bbox(rct_in)) rct_out <- unclass(wk_bbox(rct_out)) width_in <- rct_in$xmax - rct_in$xmin height_in <- rct_in$ymax - rct_in$ymin width_out <- rct_out$xmax - rct_out$xmin height_out <- rct_out$ymax - rct_out$ymin dx <- rct_out$xmin - rct_in$xmin dy <- rct_out$ymin - rct_in$ymin wk_affine_compose( wk_affine_scale(width_out / width_in, height_out / height_in), wk_affine_translate(dx, dy) ) } #' @rdname wk_trans_affine #' @export wk_affine_compose <- function(...) { trans_matrix <- Reduce( `%*%`, lapply(rev(list(...)), as.matrix), init = as.matrix(wk_affine_identity()) ) wk_trans_affine(trans_matrix) } #' @rdname wk_trans_affine #' @export wk_affine_invert <- function(x) { wk_trans_affine(solve(as.matrix(x))) } #' @export as.matrix.wk_trans_affine <- function(x, ...) { .Call(wk_c_trans_affine_as_matrix, x) } #' @export format.wk_trans_affine <- function(x, ...) { format(as.matrix(x), ...) } #' @export print.wk_trans_affine <- function(x, ...) { cat("\n") print(as.matrix(x), ...) invisible(x) } wk/R/grd-tile.R0000644000176200001440000000761014315177334012742 0ustar liggesusers #' Compute overview grid tile #' #' A useful workflow for raster data in a memory bounded environment is to #' chunk a grid into sections or tiles. These functions compute tiles #' suitable for such processing. Use [grd_tile_summary()] to generate #' statistics for `level` values to choose for your application. #' #' @inheritParams grd_summary #' @param level An integer describing the overview level. This is related to #' the `step` value by a power of 2 (i.e., a level of `1` indicates a step of #' `2`, a level of `2` indicates a step of `4`, etc.). #' @param levels A vector of `level` values or `NULL` to use a sequence from #' 0 to the level that would result in a 1 x 1 grid. #' #' @return A [grd()] #' @export #' #' @examples #' grid <- grd_rct(volcano) #' grd_tile_summary(grid) #' grd_tile_template(grid, 3) #' grd_tile_template <- function(grid, level) { level <- normalize_level(grid, level) grid <- as_grd_rct(grid) # clamp the step to a reasonable bound s <- grd_summary(grid) step <- 2 ^ level step_clamp <- pmax(1L, pmin(c(s$ny, s$nx), step)) level_clamp <- ceiling(log2(step_clamp)) step_final <- 2 ^ level_clamp # calculate the new grid parameters final_dy <- s$dy * step_final[1] final_dx <- s$dx * step_final[2] final_ny <- ceiling(s$ny / step_final[1]) final_nx <- ceiling(s$nx / step_final[2]) final_bbox <- rct( s$xmin, s$ymax - final_ny * final_dy, s$xmin + final_nx * final_dx, s$ymax, crs = wk_crs(grid) ) grd_rct( array(dim = c(final_ny, final_nx, 0)), final_bbox ) } #' @rdname grd_tile_template #' @export grd_tile_summary <- function(grid, levels = NULL) { if (is.null(levels)) { s <- grd_summary(grid) level0 <- max(floor(log2(c(s$nx, s$ny)))) + 1L levels <- 0:level0 } overviews <- lapply(levels, function(level) grd_tile_template(grid, level)) summaries <- lapply(overviews, grd_summary) summary_df <- lapply(summaries, new_data_frame) cbind(level = levels, do.call(rbind, summary_df)) } #' Extract normalized grid tiles #' #' Unlike [grd_tile_template()], which returns a [grd()] whose elements are #' the boundaries of the specified tiles with no data attached, [grd_tile()] #' returns the actual tile with the data. #' #' @inheritParams grd_tile_summary #' @inheritParams grd_subset #' #' @return A [grd_subset()]ed version #' @export #' #' @examples #' grid <- grd_rct(volcano) #' plot(grd_tile(grid, 4, 1, 1)) #' #' plot(grd_tile(grid, 3, 1, 1), add = TRUE) #' plot(grd_tile(grid, 3, 1, 2), add = TRUE) #' plot(grd_tile(grid, 3, 2, 1), add = TRUE) #' plot(grd_tile(grid, 3, 2, 2), add = TRUE) #' #' grid <- as_grd_xy(grd_tile(grid, 4, 1, 1)) #' plot(grid, add = TRUE, pch = ".") #' plot(grd_tile(grid, 3, 1, 1), add = TRUE, col = "green", pch = ".") #' plot(grd_tile(grid, 3, 1, 2), add = TRUE, col = "red", pch = ".") #' plot(grd_tile(grid, 3, 2, 1), add = TRUE, col = "blue", pch = ".") #' plot(grd_tile(grid, 3, 2, 2), add = TRUE, col = "magenta", pch = ".") #' grd_tile <- function(grid, level, i, j = NULL) { UseMethod("grd_tile") } #' @rdname grd_tile #' @export grd_tile.wk_grd_rct <- function(grid, level, i, j = NULL) { overview <- grd_tile_template(grid, level) bbox <- grd_cell_rct(overview, i, j) ranges <- grd_cell_range(grid, bbox, snap = list(grd_snap_next, grd_snap_previous)) grd_subset(grid, ranges) } #' @rdname grd_tile #' @export grd_tile.wk_grd_xy <- function(grid, level, i, j = NULL) { grid_rct <- as_grd_rct(grid) overview <- grd_tile_template(grid_rct, level) bbox <- grd_cell_rct(overview, i, j) ranges <- grd_cell_range(grid, bbox, snap = list(grd_snap_next, grd_snap_previous)) grd_subset(grid, ranges) } # This could maybe in the future deal with negative levels based on the # grd_tile_summary() so that one could work down from coarse->fine normalize_level <- function(grid, level, s = grd_summary(grid)) { if (length(level) == 1L) { level <- c(level, level) } level } wk/R/format.R0000644000176200001440000000245614160220603012510 0ustar liggesusers #' Format well-known geometry for printing #' #' Provides an abbreviated version of the well-known text #' representation of a geometry. This returns a constant #' number of coordinates for each geometry, so is safe to #' use for geometry vectors with many (potentially large) #' features. Parse errors are passed on to the format string #' and do not cause this handler to error. #' #' @inheritParams wk_handle #' @inheritParams wk_writer #' @param max_coords The maximum number of coordinates to include #' in the output. #' #' @return A character vector of abbreviated well-known text. #' @export #' #' @examples #' wk_format(wkt("MULTIPOLYGON (((0 0, 10 0, 0 10, 0 0)))")) #' wk_format(new_wk_wkt("POINT ENTPY")) #' wk_handle( #' wkt("MULTIPOLYGON (((0 0, 10 0, 0 10, 0 0)))"), #' wkt_format_handler() #' ) #' wk_format <- function(handleable, precision = 7, trim = TRUE, max_coords = 6, ...) { wk_handle( handleable, wkt_format_handler(precision = precision, trim = trim, max_coords = max_coords), ... ) } #' @rdname wk_format #' @export wkt_format_handler <- function(precision = 7, trim = TRUE, max_coords = 6) { new_wk_handler( .Call( wk_c_wkt_formatter, as.integer(precision)[1], as.logical(trim)[1], as.integer(max_coords)[1] ), "wk_wkt_formatter" ) } wk/NEWS.md0000644000176200001440000002257714531517001012003 0ustar liggesusers# wk 0.9.1 - Fix format strings/arguments for R-devel (#209). # wk 0.9.0 ## Breaking changes - The common well-known binary representation of POINT EMPTY (i.e., POINT (nan nan)) is now handled as POINT EMPTY allowing empty points to roundtrip through `wkb()` vectors (#196, #204). - `xy(NA, NA)` is now read as a null feature instead of POINT EMPTY. This preserves the invariant that null features can also be identified using `is.na()` (#205). - `xy(NaN, NaN)` is now read as POINT EMPTY and `is.na(xy(NaN, NaN))` now returns `FALSE`. This means that both EMPTY and null points can roundtrip through `xy()` (#205). ## Bugfixes and improvements - `wk_meta()` now contains a new column `is_empty`, which is `TRUE` for any feature that contains at least one non-empty coordinate. This allows more efficient detection of features with zero coordinates (#197, #199). - Updated PROJ data to use the latest pull of the database packaged with PROJ 9.3.0 (#201). - The wk package now compiles once again on gcc 4.8 (#203, #206). - Fixed `sfc_writer()` to correctly attach the `classes` attribute to sfc output with mixed geometry types (#195). - Function `sfc_writer()` now has an argument `promote_multi` to write any input as the MULTI variant. This makes it more likely that an input vector will be read as a single geometry type (#198). - The `wk_collection_filter()` now correctly increments the `part_id` when calling the child handler (@brownag, #194). # wk 0.8.0 * Added `wkb_to_hex()` (@anthonynorth, #183). * Implemented `vctrs::vec_proxy_equal()` for `wkb()` vctrs (@anthonynorth, #183). * Fixed `sfc_writer()`, which had returned NULL for some inputs (e.g., via `wk_collection()`) (@anthonynorth, #182, #186). * Added `wk_clockwise()` and `wk_counterclockwise()` to re-wind polygon rings (@anthonynorth, #188). * New replacement-function mode for `wk_coords<-()` for in-place modification of coordinates (@mdsumner, #187). * New function `wk_trans_explicit()` migrated from crs2crs (@mdsumner, #187). # wk 0.7.3 * Fix tests for updated waldo package (#178). # wk 0.7.2 * Fix use-after-free warnings. # wk 0.7.1 * Fix implicit reliance on error `as.data.frame.default()`, which no longer occurs in r-devel (#166). # wk 0.7.0 * Remove legacy headers that are no longer used by any downstream package (#146). * `validate_wk_wkt()` now errors for an object that does not inherit from 'wk_wkt' (#123, #146). * Added `wk_crs_projjson()` to get a JSON representation of a CRS object. To make lookup possible based on shortcut-style CRS objects (e.g., `"EPSG:4326"` or `4326`), added data objects `wk_proj_crs_view` and `wk_proj_crs_json` that contain cached versions of rendered PROJJSON based on the latest PROJ version (#147). * Added a `wk_crs_proj_definition()` method for `wk_crs_inherit()` (#136, #147). * Conversion to sf now uses the `sfc_writer()` for all wk classes, making conversions faster and fixing at least one issue with conversion of NA geometries to sf (#135). * `wk_plot()` now plots `NULL`/`NA` geometries and mixed geometry types more reliably (#142, #143, #149). * Exported EMPTY geometries to well-known text now include dimension (e.g., `POINT Z EMPTY`) (#141, #150). * Fixed bug where `wk_polygon()` doubled some points when the input contained closed rings (#134, #151). * Fixed bug where `wk_count()` exposed uninitialized values for empty input (#139, #153). * The `xy_writer()` now opportunistically avoids allocating vectors for Z or M values unless they are actually needed (#131, #154). * Added example WKT for all geometry types and dimensions plus helper `wk_example()` to access them and set various properties (#155). * Fixes warnings when compiling with `-Wstrict-prototypes` (#157, #158). * Removed `wk_chunk_map_feature()` in favour of using chunking strategies directly (#132, #159). * Optimized `wk_coords()` for `xy()` objects (#138, #160). * Added accessor methods for record-style vectors: `rct_xmin()`, `rct_xmax()`, `rct_ymin()`, `rct_ymax()`, `rct_width()`, `rct_height()`, `crc_center()`, `crc_x()`, `crc_y()`, `crc_r()`, `xy_x()`, `xy_y()`, `xy_z()`, and `xy_m()` (#144, #161). * Added rectangle operators `rct_intersects()`, `rct_contains()`, and `rct_intersection()` (#161). # wk 0.6.0 * Fixed `wk_affine_rescale()` to apply the translate and scale operations in the correct order (#94). * Add `wk_handle_slice()` and `wk_chunk_map_feature()` to support a chunk + apply workflow when working with large vectors (#101, #107). * C and R code was rewritten to avoid materializing ALTREP vectors (#103, #109). * Added a `wk_crs_proj_definition()` generic for foreign CRS objects (#110, #112). * Added `wk_crs_longlat()` helper to help promote authority-compliant CRS choices (#112). * Added `wk_is_geodesic()`, `wk_set_geodesic()`, and argument `geodesic` in `wkt()` and `wkb()` as a flag for objects whose edges must be interpolated along a spherical/ellipsoidal trajectory (#112). * Added `sf::st_geometry()` and `sf::st_sfc()` methods for wk geometry vectors for better integration with sf (#113, #114). * Refactored well-known text parser to be more reusable and faster (#115, #104). * Minor performance enhancement for `is.na()` and `validate_wk_wkb()` when called on a very long `wkb()` vector (#117). * Fixed issue with `validate_wk_wkb()` and `validate_wk_wkt()`, which failed for most valid objects (#119). * Added `wk_envelope()` and `wk_envelope_handler()` to compute feature-wise bounding boxes (#120, #122). * Fixed headers and tests to pass on big endian systems (#105, #122). * Incorporated the geodesic attribute into vctrs methods, data frame columns, and bbox/envelope calculation (#124, #125). * Fix `as_xy()` for nested data frames and geodesic objects (#126, #128). * Remove deprecated `wkb_problems()`, `wkt_problems()`, `wkb_format()`, and `wkt_format()` (#129). * `wk_plot()` is now an S3 generic (#130). # wk 0.5.0 * Fixed bugs relating to the behaviour of wk classes as vectors (#64, #65, #67, #70). * `crc()` objects are now correctly exported as polygons with a closed loop (#66, #70). * Added `wk_vertices()` and `wk_coords()` to extract individual coordinate values from geometries with optional identifying information. For advanced users, the `wk_vertex_filter()` can be used as part of a pipeline to export coordinates as point geometries to another handler (#69, #71). * Added `wk_flatten()` to extract geometries from collections. For advanced users, the `wk_flatten_filter()` can be used as part of a pipeline (#75, #78). * `options("max.print")` is now respected by all vector classes (#72, #74). * Moved implementation of plot methods from wkutils to wk to simplify the dependency structure of both packages (#80, #76). * Added `wk_polygon()`, `wk_linestring()`, and `wk_collection()` to construct polygons, lines, and collections. For advanced users, `wk_polygon_filter()`, `wk_linestring_filter()`, and `wk_collection_filter()` can be used as part of a pipeline (#77, #84). * Added a C-level transform struct that can be used to simplify the the common pattern of transforming coordinates. These structs can be created by other packages; however, the `wk_trans_affine()` and `wk_trans_set()` transforms are also built using this feature. These are run using the new `wk_transform()` function and power the new `wk_set_z()`, `wk_set_m()`, `wk_drop_z()`, `wk_drop_m()`, functions (#87, #88, #89). # wk 0.4.1 * Fix LTO and MacOS 3.6.2 check errors (#61). # wk 0.4.0 * Removed `wksxp()` in favour of improved `sf::st_sfc()` support (#21). * Rewrite existing readers, writers, and handlers, using a new C API (#13). * Use new C API in favour of header-only approach for all wk functions (#19, #22). * Use cpp11 to manage safe use of callables that may longjmp from C++. * Vector classes now propagate `attr(, "crs")`, and check that operations that involve more than one vector have compatable CRS objects as determined by `wk_crs_equal()`. * Added an R-level framework for other packages to implement wk readers and handlers: `wk_handle()`, `wk_translate()`, and `wk_writer()` (#37). * Added a native reader and writer for `sf::st_sfc()` objects and implemented R-level generics for sfc, sfg, sf, and bbox objects (#28, #29, #38, #45). * Implement `crc()` vector class to represent circles (#40). * Added a 2D cartesian bounding box handler (`wk_bbox()`) (#42). * Refactored unit tests reflecting use of the new API and for improved test coverage (#44, #45, #46). * Added `wk_meta()`, `wk_vector_meta()`, and `wk_count()` to inspect properties of vectors (#53). * Modified all internal handlers such that they work with vectors of unknown length (#54). # wk 0.3.4 * Fixed reference to `wkutils::plot.wk_wksxp()`, which no longer exists. # wk 0.3.3 * Fixed WKB import of ZM geometries that do not use EWKB. * Added `xy()`, `xyz()`, `xym()` and `xyzm()` classes to efficiently store point geometries. * Added the `rct()` vector class to efficiently store two-dimensional rectangles. * Fixed the CRAN check failure caused by a circular dependency with the wkutils package. * Added S3 methods to coerce sf objects to and from `wkt()`, `wkb()` and `wksxp()`. # wk 0.3.2 * Fixed EWKB output for collections and multi-geometries that included SRID (#3). * Fixed CRAN check errors related to exception handling on MacOS/R 3.6.2. # wk 0.3.1 * Added a `NEWS.md` file to track changes to the package. wk/MD50000644000176200001440000002724314531546132011217 0ustar liggesusers16650e9b28e34171756b65d7a27fc98c *DESCRIPTION 3af0d5f6726a14ed40722905f3202a79 *LICENSE 01d50f827b7f47edcdf6681d52fa570d *NAMESPACE 857e49d189989b516b383690374f6ac6 *NEWS.md 07229ca016aac31aab432a2ebc28157f *R/affine.R d0e366211810d795520cd57940cef4b8 *R/bbox.R f8335a2d3ff1abdf1d249b2640197ca0 *R/chunk.R a6b5756079ddf54f724d3cd14d45c586 *R/class-data-frame.R c85aca4610e5df0fe504f21fe0a5c602 *R/count.R 0819282d8807cc35c64c5acba9f7d275 *R/crc.R 13ea3b10483a508235ad6b4a564ed427 *R/data.R 1ea41496e02be2c811b265327ab0c5e6 *R/debug.R 6f8fb587c2f43d702dd7fbffa865f8a1 *R/deprecated.R 2c43893c563acdf4fac6143cb173ad3a *R/filter.R b7bd54291fdb6c47cdf534fc7caf7a1e *R/flatten.R 5ae8ef67abc9c3cd864ea8794bffb202 *R/format.R 71efc2090bc28bc330a6bd56a8f1715e *R/grd-extract.R d63e8467bd951a16657186706c917556 *R/grd-handle.R 64ecd546adcf36b533905357ed2b6ef2 *R/grd-plot.R 0b77821b8ccebe42ac55a704490088a9 *R/grd-subset.R 1dc35b75a4052da14c0fa8107d60f352 *R/grd-tile.R 96f2c4fd29c8350fe8eb4532268bacdb *R/grd.R a216561279e1672fbc1dcc19e18e8c2a *R/handle-crc.R cbfcb9195a860c9e7b1ee5eb568b283f *R/handle-rct.R e5474f9832d64d4dabbb7c978b9d11e4 *R/handle-sfc.R e674743526fd3dce75dbb0b217965c99 *R/handle-slice.R e599ddc46785bef59b1b1765677eee11 *R/handle-wkb.R 5d2a802ae7e14b123a7018ddfe7bceb4 *R/handle-wkt.R 8ac464eacf5a841a6e7f9a3f08d140ae *R/handle-xy.R e16f95228fcd18484647f402131e4eec *R/handler.R 07abb26a1254f3d9484a44f6bd42e01e *R/make.R f46d3863112f207ea7b4066287dd60ff *R/meta.R 3ba323043c80ddd8bff259009bdec23f *R/orient-filter.R 331ae708a3ec5204c17663878e9376ea *R/pkg-readr.R 9709decbf833425d87b2e1e660d14110 *R/pkg-sf.R 95eb33eb301d9d601ab94c5ee522356d *R/pkg-vctrs.R da6027305ca3365ee1fd40da8a847651 *R/plot.R ff2b3478976082700958ad1e7785d630 *R/problems.R d20b5e4a4209cd80b69cc3a0a9dbebb3 *R/rct.R f4137c112b283959718fcf8ef7a731cc *R/set.R 3a28cf4f4609aedf3e4587c7f66cb940 *R/sfc-writer.R b75cf003af206713c2b3ea8ee1d8b784 *R/trans-explicit.R db01722e2087826a79be5c4aa9254401 *R/transform.R d436efe06000782bedf9d3ec9130184e *R/translate.R e8bbc21f285a4ec31386054e381d3c46 *R/utils.R d37ad8c96beacf72fda41b9d26604b19 *R/vertex-filter.R 8a5ff3cbb45bf9cb1df9be3e7c0a1398 *R/void.R 7e1ae87cffc204b5da2fcb68d720d287 *R/wk-crs.R ff67a2aa214f800b44d1f4c70888484d *R/wk-package.R 289479d91204317f19b4ab8c9ab4b04d *R/wk-rcrd.R 319bb59dffd856b03a47e7c005ce7105 *R/wk-vctr.R a39beb279063ebcc5c1a503079c435d6 *R/wkb-writer.R e99d9649a728588a58d2ab5299bdb3e7 *R/wkb.R 64f8f59370841ab7cb51012a14baca05 *R/wkt-writer.R 997b7a10c72132b979eaa53f4475c44d *R/wkt.R 549b8584126f8a6166254313143d346b *R/writer.R 711be411600a37b5bb1ae679b878be67 *R/xy-writer.R eefb39b116c8d40c4a071fe3fa22af0c *R/xyzm.R 3920dedf3f38f62b9e7532c88c2af8f9 *R/zzz.R 592c9f43f8e930870d09e9c56e16f3de *README.md 4cc23908be8fd6439f94ae0b2c38a5f0 *data/wk_example_wkt.rda 380f02f66e7c5780c2d7f84e9a936546 *data/wk_proj_crs_json.rda 18c51b793f388103ee756ce9c8829e91 *data/wk_proj_crs_view.rda a36987c06d31e52d7a219063419adc9f *inst/include/wk-v1-impl.c 34132b6487371726fa530ac8aac01d75 *inst/include/wk-v1.h bd893015cfbb75a15ad1c1cd59119312 *inst/include/wk/experimental/wk-v1-filter-cpp11.hpp ecaaf2dad9e6906ee6976a028751ab45 *inst/include/wk/experimental/wk-v1-handler-cpp11.hpp 805466a25bcbf23ec6918c1f485e05eb *inst/include/wk/experimental/wk-v1-reader-cpp11.hpp 1fabed39ac6ba232cfbdffe4aac65ed5 *man/crc.Rd cadaa0dd88e4c5c355add32456230a11 *man/crc_x.Rd d4755f9a404c2fc678f18c7591d84411 *man/deprecated.Rd 45d737607da26cc54bd6fabb9009b428 *man/grd.Rd 3964a67609452fdc9ff4964d064e00f2 *man/grd_cell.Rd beeebed72a1d9e10ed8e84dac70fdc47 *man/grd_extract.Rd 146e864ce1e861d76b5cf58b592af13f *man/grd_snap_next.Rd dc114ba4972d3255e912c22db6f2d48a *man/grd_subset.Rd 55e48a94124ec0966b39e20d34384a7a *man/grd_summary.Rd 8eed0b32c36845bab3057a570eb25019 *man/grd_tile.Rd 0f457477dfaf36ae0b91fc5309ac6e99 *man/grd_tile_template.Rd 021aebc167b6a8727b8e2e8ebf64eb25 *man/handle_wkt_without_vector_size.Rd 019a99103e4c9e32fe4d8e005affdf61 *man/new_wk_crc.Rd 0a5c62ad612050eba7cb57bf2896037b *man/new_wk_grd.Rd 179f42bc8837684f59a03d9ef67291fa *man/new_wk_rct.Rd 693efd158835b045a69c9691ac971d4a *man/new_wk_wkb.Rd c5d543123928b1bff81c08620416fe9e *man/new_wk_wkt.Rd 2d5d3d149609b33de00290ef147d848c *man/new_wk_xy.Rd d0081437b76adebd91be0f8e2aa3f617 *man/plot.wk_grd_xy.Rd dde2f79536dc45098edafbcc9098581b *man/rct.Rd db8f8bb3f42b420c2ae77a3c13721ad8 *man/rct_xmin.Rd 6ddc9c905a30ec55cb87b0b03212d76b *man/vctrs-methods.Rd 1740d8d091ce65a4066c04814702fcf8 *man/wk-package.Rd afc0a306c05612dd2b1f536372a86014 *man/wk_bbox.Rd 21babeb1ff9c52f534c5ea492feb8c9e *man/wk_chunk_strategy_single.Rd a12ae9ed6ca4faec7de2e04f768528a3 *man/wk_count.Rd 09f0b3e152bebac85e6db56e1312b7f7 *man/wk_crs.Rd 4ba75a566d23b1ac35722ef6f5d33b11 *man/wk_crs_equal.Rd 904e567ee4e886117e361c7397aa9504 *man/wk_crs_inherit.Rd 494a9fc4e36a04ec4e780766049d2222 *man/wk_crs_proj_definition.Rd d9a76039d8311f02b6ef3466b00c475d *man/wk_debug.Rd 15ca824dd46aa0637ac829a42a69837b *man/wk_example.Rd 780ac068de9265caccb8ff9089c39410 *man/wk_flatten.Rd 508710faba3c9f06732d68eddfb12190 *man/wk_format.Rd 1d5beccdeff76e4c01bf18d8458cbdef *man/wk_handle.Rd 63312b1fe818d17b380537cb92ceb619 *man/wk_handle.data.frame.Rd 87883843efe363e48185f1532f8aecdd *man/wk_handle.wk_grd_xy.Rd 1587996adffd121992c337360dea0fb7 *man/wk_handle_slice.Rd 00d75dc261250d9a564752f7e7c22e1e *man/wk_identity.Rd d0c9a62457a0b0b0fe0d1fdbb3b3e629 *man/wk_is_geodesic.Rd 840f8de1d0335f205c79b759cd10bbff *man/wk_linestring.Rd 74d49ccf92de24c368664fc361db65c1 *man/wk_meta.Rd 9b39e3ee8a90812c96b395293706f912 *man/wk_orient.Rd 3edf36d740305e4055d18cbe1cbba1b0 *man/wk_plot.Rd 079690e5589ba1f7336cbb69d8ab7443 *man/wk_problems.Rd c51484967ce56433e954b7582f152058 *man/wk_proj_crs_view.Rd e79553346c3dc64f032dac5a4c56951f *man/wk_set_z.Rd 33d7146629ae06fcb2feb686327d19d3 *man/wk_trans_affine.Rd f75e8963093743d44a746ef358e76e61 *man/wk_trans_explicit.Rd 6d13cd991603149c6864b36d6c90de6a *man/wk_trans_inverse.Rd 8f63984e0002b4bd8f31d20f1d80997b *man/wk_transform.Rd df06bec73d8dd55a872184243cd6ff95 *man/wk_translate.Rd 72fc3e0821f20504aae92e0048d54a92 *man/wk_vertices.Rd 07fa0f0ce3c5ab7f234162614e6b8dc0 *man/wk_void.Rd ba03ecd03def25a88ca0442e5a36e4c0 *man/wk_writer.Rd c1d551816b45141d463b7a9b76695180 *man/wkb.Rd 501e10bd33ec3a58fb6042fdbeb93f55 *man/wkb_to_hex.Rd 8114fdf243cf23dddeff95afb6e4f0a9 *man/wkt.Rd 606994707707bc99fd446e63f39d5c7d *man/xy.Rd ed8b9ca3f124ee18e78e731590e1b3eb *man/xy_x.Rd 1a315aa73f3882ca3d110365f272fbc0 *src/Makevars 17ba404d89dca491494045f3e5f2ae55 *src/altrep.h 39f45127bd61c3c2b60730de30f7ebaf *src/bbox-handler.c 421ae4c193b7c1d95bf42652936fdce9 *src/count-handler.c 13c1f8f9eea5ea86707b5f0c09d4120b *src/debug-filter.c 6372360eda139ecb9b0cd4007aa93bc0 *src/flatten-filter.c 33576bc5c3cd4917e13078f86e997516 *src/handle-crc.c c3dd461233d0989e2e6d076c4bf8f81e *src/handle-rct.c 5abc2234841d8b5ba8f81d2d10471b5b *src/handle-sfc.c 0e615488aaadb000136f9c20262af1b9 *src/handle-wkb.c adaf35297483808632d1bf827e905ddd *src/handle-wkt.cpp b50f2431d4b67f8206ccba6972d114e7 *src/handle-xy.c ae74d0dfe5f2ad66eb4414b23f42d333 *src/identity-filter.c d1159a0d3924943f6566450e20aaa117 *src/init.c 0e062e53e40472dfb0bc34e8161f4fd1 *src/internal/buffered-reader.hpp a4b55ab3a13ef91ae98585a3a1b1c5be *src/internal/fast_float/fast_float.h 91b01810f7ce7a39f6fe67d479a973e9 *src/internal/wk-v1-handler.hpp d594b5e13ecefc43c7c4eae8a43697b5 *src/make-collection-filter.c 232a53e2020039cd596a3f5ac13ac909 *src/make-linestring-filter.c 78d9541f67151838128556889bc6f1b0 *src/make-polygon-filter.c 79a8dac0ce89511f86ce1547d241af2b *src/meta-handler.c ebebbf0849d2aabacddafbde197f3799 *src/orient-filter.cpp 9b69fed76236a05b208faad3a3781dcc *src/port.h a5652a40313f680d33271c06736ae679 *src/problems-handler.c ee913106aa3085f3ecd654561f80cdf0 *src/sfc-writer.c 41f28bc033264f1af5ab47807e2b26ba *src/trans-affine.c 7b308e6ae2ee76dec2759d4b114d335e *src/trans-explicit.c 70c44dd5a3b52914c79d9ac7416b6ad3 *src/trans-set.c 0ddf4130430a991d07cdc7a3359d6a15 *src/transform.c 836e248b39cad289850d20a62b309967 *src/vctr.c d3ee0ead0276252e5bd18a8afa82f9ae *src/vertex-filter.c 412c90f1922b6c988750c3e2ec4acf9e *src/void-handler.c 3ff04996080488941707e8fd75d50d02 *src/wk-v1.c 3006c5e56bd95ebeb85915d6364092bc *src/wkb-writer.c 523aa95d625d37999ee94073a7aecc3e *src/wkt-writer.cpp e365829da93629896f37fdf324b53887 *src/xy-writer.c b17060e2f3f82fabb10c17b68e310237 *tests/testthat.R b55130f8468db0a678d9963b3a89a290 *tests/testthat/Rplots.pdf b57e570ebc04b62c0a97647c7cc64f83 *tests/testthat/test-affine.R 7390227c225c1c188630852ff0d3ade2 *tests/testthat/test-bbox.R 3db796ba859e5e04b8a95f633aba286f *tests/testthat/test-chunk.R 1f4b3a793218aeb67d6d806308a58446 *tests/testthat/test-class-data-frame.R ed065e6d1837ac6455d68c92dc829254 *tests/testthat/test-count.R e7042adb0f582d30d8972cf2fc5c893e *tests/testthat/test-crc.R 2c6c2557cb9bafed02db6b8e3fdd3152 *tests/testthat/test-data.R cb2fec2820c9b86cc18a9b90cb425aee *tests/testthat/test-debug.R d2b949681f33b116a19d6ab21a06a8bb *tests/testthat/test-filter.R f99ed656c08529e0f422b38c351a32c7 *tests/testthat/test-flatten.R 4b390cc1e7400ca42c9808149faff952 *tests/testthat/test-format.R f1e610399aaac5f50f18b98d899b13f8 *tests/testthat/test-grd-extract.R 34e6678ab7cb63cbe9b8094541622f03 *tests/testthat/test-grd-handle.R f7bcfa980daf00c1c31a6cf2215aadb3 *tests/testthat/test-grd-plot.R d41d8cd98f00b204e9800998ecf8427e *tests/testthat/test-grd-snap.R 46c1f8522df6df283d02e078b8566d97 *tests/testthat/test-grd-subset.R c80e213aecc375514145b96c0667a1b3 *tests/testthat/test-grd-tile.R 805648987bc7573ec5a3342140244bd0 *tests/testthat/test-grd.R 5dbfd3fe3296a47ec4d8f7db5c80776f *tests/testthat/test-handle-crc.R 3c3caecb814d2dbecbaf6d1f7535d33c *tests/testthat/test-handle-rct.R c55c88d5e01522aed112d1fb40e25056 *tests/testthat/test-handle-sfc.R ec0d8c681f262a04d4f384518a1e91f6 *tests/testthat/test-handle-slice.R 13f8b17710881d5e5e6730d9b37483f0 *tests/testthat/test-handle-wkb.R a962592f7c73f2bf04df8405c777188d *tests/testthat/test-handle-wkt.R 8e2a266d62265e700a393eb1eebc1daf *tests/testthat/test-handle-xy.R b6e101d8d5e0479019b0c4f73764a057 *tests/testthat/test-handler.R ff01b99195b3df6dd61186bd62820de6 *tests/testthat/test-make.R 2f4e5f768714b34e7f71dd0700a824c6 *tests/testthat/test-meta.R 0bf6ba1629deeef81160c8ccac11dba1 *tests/testthat/test-orient.R edfe9ddd44a784ab5cd77ce5e66274b9 *tests/testthat/test-pkg-readr.R aee563c2b125c9d74cd65a82a33956ac *tests/testthat/test-pkg-sf.R ea577734b5710a0db5141cb2c5582035 *tests/testthat/test-pkg-vctrs.R 149a73e07c14b2b758ad9d95dd22ee94 *tests/testthat/test-plot.R 8dbaeb61c3170d66455cc7dede88755d *tests/testthat/test-problems.R 73cd1968e78e28fb602937c8acc89934 *tests/testthat/test-rct.R b02cdbee1658aa72c93c8484c5b03a4e *tests/testthat/test-set.R 791287cbe80b3cee884495eabd88cd84 *tests/testthat/test-sfc-writer.R b79e482eefcd24672db5d638205e3882 *tests/testthat/test-trans_explicit.R 7a3662335a50f2c3f4802627c757708c *tests/testthat/test-transform.R 10c6beb5a16b70d562e4c0f1108642c4 *tests/testthat/test-translate.R a9872cde8c76036fa50ee0036f6ae9b4 *tests/testthat/test-utils.R 8b1b9236768762e597b7e79c8a5d5c22 *tests/testthat/test-vertex-filter.R 15dc5276e77ef3cb5b149ea02f8771fe *tests/testthat/test-void.R fc779c79243f25cbc38826755bdb73d6 *tests/testthat/test-wk-crs.R 1b79a4e5f7b7e94ed858f54306026757 *tests/testthat/test-wk-rcrd.R 924336e47b963700582232bb63121e83 *tests/testthat/test-wk-vctr.R bdbb098c5a4f29aeaf59905a4579678e *tests/testthat/test-wkb-writer.R 98653072ddcf44f03d68afc09bc8646e *tests/testthat/test-wkb.R 4112e162004f2a3f521d7f596fc83fa8 *tests/testthat/test-wkt-writer.R 69fedee814a3ba5e6ca532440e913d8a *tests/testthat/test-wkt.R e95a5af5c4d6ee02340fa9c6dac8f9d2 *tests/testthat/test-writer.R 919aa191d9df477ddf8b9a69d59e816a *tests/testthat/test-xy-writer.R cbcec9781c32a4f79694f231fccd3bf4 *tests/testthat/test-xyzm.R wk/inst/0000755000176200001440000000000014106220314011641 5ustar liggesuserswk/inst/include/0000755000176200001440000000000014531511633013275 5ustar liggesuserswk/inst/include/wk/0000755000176200001440000000000014531546132013720 5ustar liggesuserswk/inst/include/wk/experimental/0000755000176200001440000000000014160220603016403 5ustar liggesuserswk/inst/include/wk/experimental/wk-v1-handler-cpp11.hpp0000644000176200001440000001632614160220603022426 0ustar liggesusers #ifndef WK_V1_HANDLER_HPP_INCLUDED #define WK_V1_HANDLER_HPP_INCLUDED #include "cpp11/protect.hpp" #include "cpp11/declarations.hpp" #include "wk-v1.h" // ---- the class one should extend when writing handlers in C++ --- class WKVoidHandler { public: WKVoidHandler() {} virtual ~WKVoidHandler() {} virtual void initialize(int* dirty) { if (*dirty) { cpp11::stop("Can't re-use this wk_handler"); } *dirty = 1; } virtual int vector_start(const wk_vector_meta_t* meta) { return WK_CONTINUE; } virtual int feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return WK_CONTINUE; } virtual int null_feature() { return WK_CONTINUE; } virtual int geometry_start(const wk_meta_t* meta, uint32_t part_id) { return WK_CONTINUE; } virtual int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { return WK_CONTINUE; } virtual int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id) { return WK_CONTINUE; } virtual int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id) { return WK_CONTINUE; } virtual int geometry_end(const wk_meta_t* meta, uint32_t part_id) { return WK_CONTINUE; } virtual int feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return WK_CONTINUE; } virtual SEXP vector_end(const wk_vector_meta_t* meta) { return R_NilValue; } virtual void deinitialize() { } virtual int error(const char* message) { cpp11::stop(message); } }; // Need our own BEGIN_CPP11 and END_CPP11 because we don't always return an SEXP // and the macro contains 'return R_NilValue' which causes a compiler error // https://github.com/r-lib/cpp11/blob/master/inst/include/cpp11/declarations.hpp #define WK_BEGIN_CPP11 \ SEXP err = R_NilValue; \ const size_t ERROR_SIZE = 8192; \ char buf[ERROR_SIZE] = ""; \ try { #define WK_END_CPP11(_ret) \ } \ catch (cpp11::unwind_exception & e) { \ err = e.token; \ } \ catch (std::exception & e) { \ strncpy(buf, e.what(), ERROR_SIZE - 1); \ } \ catch (...) { \ strncpy(buf, "C++ error (unknown cause)", ERROR_SIZE - 1); \ } \ if (buf[0] != '\0') { \ Rf_errorcall(R_NilValue, "%s", buf); \ } else if (err != R_NilValue) { \ CPP11_UNWIND \ } \ return _ret; template class WKHandlerFactory { public: static wk_handler_t* create(HandlerType* handler_data) { wk_handler_t* handler = wk_handler_create(); handler->handler_data = handler_data; handler->initialize = &initialize; handler->vector_start = &vector_start; handler->vector_end = &vector_end; handler->feature_start = &feature_start; handler->null_feature = &null_feature; handler->feature_end = &feature_end; handler->geometry_start = &geometry_start; handler->geometry_end = &geometry_end; handler->ring_start = &ring_start; handler->ring_end = &ring_end; handler->coord = &coord; handler->error = &error; handler->deinitialize = &deinitialize; handler->finalizer = &finalizer; return handler; } static SEXP create_xptr(HandlerType* handler_data) { wk_handler_t* handler = create(handler_data); return wk_handler_create_xptr(handler, R_NilValue, R_NilValue); } private: static void finalizer(void* handler_data) noexcept { HandlerType* cpp_handler = (HandlerType*) handler_data; if (cpp_handler != NULL) { delete cpp_handler; } } static void initialize(int* dirty, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->initialize(dirty); WK_END_CPP11() } static int vector_start(const wk_vector_meta_t* meta, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->vector_start(meta); WK_END_CPP11(WK_ABORT) } static int feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->feature_start(meta, feat_id); WK_END_CPP11(WK_ABORT) } static int null_feature(void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->null_feature(); WK_END_CPP11(WK_ABORT) } static int geometry_start(const wk_meta_t* meta, uint32_t partId, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->geometry_start(meta, partId); WK_END_CPP11(WK_ABORT) } static int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ringId, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->ring_start(meta, size, ringId); WK_END_CPP11(WK_ABORT) } static int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->coord(meta, coord, coord_id); WK_END_CPP11(WK_ABORT) } static int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ringId, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->ring_end(meta, size, ringId); WK_END_CPP11(WK_ABORT) } static int geometry_end(const wk_meta_t* meta, uint32_t partId, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->geometry_end(meta, partId); WK_END_CPP11(WK_ABORT) } static int feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->feature_end(meta, feat_id); WK_END_CPP11(WK_ABORT) } static SEXP vector_end(const wk_vector_meta_t* meta, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->vector_end(meta); WK_END_CPP11(R_NilValue) } static void deinitialize(void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; cpp_handler->deinitialize(); WK_END_CPP11() } static int error(const char* message, void* handler_data) noexcept { WK_BEGIN_CPP11 HandlerType* cpp_handler = (HandlerType*) handler_data; return cpp_handler->error(message); WK_END_CPP11(WK_ABORT) } }; #endif wk/inst/include/wk/experimental/wk-v1-reader-cpp11.hpp0000644000176200001440000000500414160220603022242 0ustar liggesusers #ifndef WK_V1_READER_HPP_INCLUDED #define WK_V1_READER_HPP_INCLUDED #include "cpp11/external_pointer.hpp" #include "cpp11/protect.hpp" #include #include #include "wk-v1.h" class WKParseException: public std::runtime_error { public: WKParseException(std::string message): std::runtime_error(message) {} }; class WKHandlerXPtr { public: // The constructor and deleter are replacements for the run_handler_xptr() function. // Instead, the scope of the WKHandler is used to guarantee that (1) the handler // is not being re-used and (2) vectorFinalize() is called and is called // as soon as possible. WKHandlerXPtr(cpp11::sexp handler_xptr): handler((wk_handler_t*) cpp11::safe[R_ExternalPtrAddr](handler_xptr)) { cpp11::safe[this->handler->initialize](&(this->handler->dirty), this->handler->handler_data); } ~WKHandlerXPtr() { handler->deinitialize(handler->handler_data); } int vector_start(const wk_vector_meta_t* meta) { return cpp11::safe[handler->vector_start](meta, handler->handler_data); } int feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return cpp11::safe[handler->feature_start](meta, feat_id, handler->handler_data); } int null_feature() { return cpp11::safe[handler->null_feature](handler->handler_data); } int geometry_start(const wk_meta_t* meta, uint32_t partId) { return cpp11::safe[handler->geometry_start](meta, partId, handler->handler_data); } int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ringId) { return cpp11::safe[handler->ring_start](meta, size, ringId, handler->handler_data); } int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id) { return cpp11::safe[handler->coord](meta, coord, coord_id, handler->handler_data); } int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ringId) { return cpp11::safe[handler->ring_end](meta, size, ringId, handler->handler_data); } int geometry_end(const wk_meta_t* meta, uint32_t partId) { return cpp11::safe[handler->geometry_end](meta, partId, handler->handler_data); } int feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return cpp11::safe[handler->feature_end](meta, feat_id, handler->handler_data); } SEXP vector_end(const wk_vector_meta_t* meta) { return cpp11::safe[handler->vector_end](meta, handler->handler_data); } int error(const char* message) { return cpp11::safe[handler->error](message, handler->handler_data); } private: wk_handler_t* handler; }; #endif wk/inst/include/wk/experimental/wk-v1-filter-cpp11.hpp0000644000176200001440000000422014160220603022264 0ustar liggesusers #ifndef WK_V1_FILTER_HPP_INCLUDED #define WK_V1_FILTER_HPP_INCLUDED #include "cpp11/external_pointer.hpp" #include "wk-v1.h" #include "wk-v1-handler-cpp11.hpp" class WKIdentityFilter: public WKVoidHandler { public: WKIdentityFilter(cpp11::sexp next): next((wk_handler_t*) cpp11::safe[R_ExternalPtrAddr](next)) {} virtual void initialize(int* dirty) { cpp11::safe[next->initialize](&(next->dirty), next->handler_data); } virtual int vector_start(const wk_vector_meta_t* meta) { return cpp11::safe[next->vector_start](meta, next->handler_data); } virtual int feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return cpp11::safe[next->feature_start](meta, feat_id, next->handler_data); } virtual int null_feature() { return cpp11::safe[next->null_feature](next->handler_data); } virtual int geometry_start(const wk_meta_t* meta, uint32_t partId) { return cpp11::safe[next->geometry_start](meta, partId, next->handler_data); } virtual int ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ringId) { return cpp11::safe[next->ring_start](meta, size, ringId, next->handler_data); } virtual int coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id) { return cpp11::safe[next->coord](meta, coord, coord_id, next->handler_data); } virtual int ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ringId) { return cpp11::safe[next->ring_end](meta, size, ringId, next->handler_data); } virtual int geometry_end(const wk_meta_t* meta, uint32_t partId) { return cpp11::safe[next->geometry_end](meta, partId, next->handler_data); } virtual int feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id) { return cpp11::safe[next->feature_end](meta, feat_id, next->handler_data); } virtual SEXP vector_end(const wk_vector_meta_t* meta) { return cpp11::safe[next->vector_end](meta, next->handler_data); } virtual int error(const char* message) { return cpp11::safe[next->error](message, next->handler_data); } virtual void deinitialize() { return cpp11::safe[next->deinitialize](next->handler_data); } private: wk_handler_t* next; }; #endif wk/inst/include/wk-v1-impl.c0000644000176200001440000001256614531511633015357 0ustar liggesusers #include "wk-v1.h" #include void wk_default_handler_initialize(int* dirty, void* handler_data) { if (*dirty) { Rf_error("Can't re-use this wk_handler"); } *dirty = 1; } int wk_default_handler_vector_start(const wk_vector_meta_t* meta, void* handler_data) { return WK_CONTINUE; } SEXP wk_default_handler_vector_end(const wk_vector_meta_t* meta, void* handler_data) { return R_NilValue; } int wk_default_handler_feature(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { return WK_CONTINUE; } int wk_default_handler_null_feature(void* handler_data) { return WK_CONTINUE; } int wk_default_handler_geometry(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { return WK_CONTINUE; } int wk_default_handler_ring(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { return WK_CONTINUE; } int wk_default_handler_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { return WK_CONTINUE; } int wk_default_handler_error(const char* message, void* handler_data) { Rf_error("%s", message); return WK_ABORT; } void wk_default_handler_finalizer(void* handler_data) { } wk_handler_t* wk_handler_create(void) { wk_handler_t* handler = (wk_handler_t*) malloc(sizeof(wk_handler_t)); if (handler == NULL) { Rf_error("Failed to alloc handler"); // # nocov } handler->api_version = 1; handler->dirty = 0; handler->handler_data = NULL; handler->initialize = &wk_default_handler_initialize; handler->vector_start = &wk_default_handler_vector_start; handler->vector_end = &wk_default_handler_vector_end; handler->feature_start = &wk_default_handler_feature; handler->null_feature = &wk_default_handler_null_feature; handler->feature_end = &wk_default_handler_feature; handler->geometry_start = &wk_default_handler_geometry; handler->geometry_end = &wk_default_handler_geometry; handler->ring_start = &wk_default_handler_ring; handler->ring_end = &wk_default_handler_ring; handler->coord = &wk_default_handler_coord; handler->error = &wk_default_handler_error; handler->deinitialize = &wk_default_handler_finalizer; handler->finalizer = &wk_default_handler_finalizer; return handler; } void wk_handler_destroy(wk_handler_t* handler) { if (handler != NULL) { handler->finalizer(handler->handler_data); free(handler); } } void wk_handler_destroy_xptr(SEXP xptr) { wk_handler_destroy((wk_handler_t*) R_ExternalPtrAddr(xptr)); } SEXP wk_handler_create_xptr(wk_handler_t* handler, SEXP tag, SEXP prot) { SEXP xptr = R_MakeExternalPtr(handler, tag, prot); R_RegisterCFinalizerEx(xptr, &wk_handler_destroy_xptr, FALSE); return xptr; } struct wk_handler_run_data { SEXP (*read_fun)(SEXP read_data, wk_handler_t* handler); SEXP read_data; wk_handler_t* handler; }; void wk_handler_run_cleanup(void* data) { struct wk_handler_run_data* run_data = (struct wk_handler_run_data*) data; run_data->handler->deinitialize(run_data->handler->handler_data); } SEXP wk_handler_run_internal(void* data) { struct wk_handler_run_data* run_data = (struct wk_handler_run_data*) data; if (run_data->handler->api_version != 1) { // # nocov start Rf_error("Can't run a wk_handler with api_version '%d'", run_data->handler->api_version); // # nocov end } run_data->handler->initialize(&(run_data->handler->dirty), run_data->handler->handler_data); return run_data->read_fun(run_data->read_data, run_data->handler); } SEXP wk_handler_run_xptr(SEXP (*read_fun)(SEXP read_data, wk_handler_t* handler), SEXP read_data, SEXP xptr) { wk_handler_t* handler = (wk_handler_t*) R_ExternalPtrAddr(xptr); struct wk_handler_run_data run_data = { read_fun, read_data, handler }; return R_ExecWithCleanup(&wk_handler_run_internal, &run_data, &wk_handler_run_cleanup, &run_data); } int wk_default_trans_trans(R_xlen_t feature_id, const double* xyzm_in, double* xyzm_out, void* trans_data) { xyzm_out[0] = xyzm_in[0]; xyzm_out[1] = xyzm_in[1]; xyzm_out[2] = xyzm_in[2]; xyzm_out[3] = xyzm_in[3]; return WK_CONTINUE; } void wk_default_trans_finalizer(void* trans_data) { } void wk_default_trans_vector(void* trans_data) { } wk_trans_t* wk_trans_create(void) { wk_trans_t* trans = (wk_trans_t*) malloc(sizeof(wk_trans_t)); if (trans == NULL) { Rf_error("Failed to alloc wk_trans_t*"); // # nocov } trans->api_version = 1001; trans->use_z = NA_INTEGER; trans->use_m = NA_INTEGER; trans->xyzm_out_min[0] = R_NegInf; trans->xyzm_out_min[1] = R_NegInf; trans->xyzm_out_min[2] = R_NegInf; trans->xyzm_out_min[3] = R_NegInf; trans->xyzm_out_max[0] = R_PosInf; trans->xyzm_out_max[1] = R_PosInf; trans->xyzm_out_max[2] = R_PosInf; trans->xyzm_out_max[3] = R_PosInf; trans->trans = &wk_default_trans_trans; trans->vector_end = &wk_default_trans_vector; trans->finalizer = &wk_default_trans_finalizer; trans->trans_data = NULL; return trans; } void wk_trans_destroy(wk_trans_t* trans) { if (trans != NULL) { trans->finalizer(trans->trans_data); free(trans); } } void wk_trans_destroy_xptr(SEXP trans_xptr) { wk_trans_destroy((wk_trans_t*) R_ExternalPtrAddr(trans_xptr)); } SEXP wk_trans_create_xptr(wk_trans_t* trans, SEXP tag, SEXP prot) { SEXP trans_xptr = PROTECT(R_MakeExternalPtr(trans, tag, prot)); R_RegisterCFinalizer(trans_xptr, &wk_trans_destroy_xptr); UNPROTECT(1); return trans_xptr; } wk/inst/include/wk-v1.h0000644000176200001440000000677214317706616014440 0ustar liggesusers #ifndef WK_V1_H_INCLUDED #define WK_V1_H_INCLUDED #include // for uint_32_t #include #ifdef __cplusplus extern "C" { #endif #define WK_CONTINUE 0 #define WK_ABORT 1 #define WK_ABORT_FEATURE 2 #define WK_FLAG_HAS_BOUNDS 1 #define WK_FLAG_HAS_Z 2 #define WK_FLAG_HAS_M 4 #define WK_FLAG_DIMS_UNKNOWN 8 #define WK_PRECISION_NONE 0.0 #define WK_PART_ID_NONE UINT32_MAX #define WK_SIZE_UNKNOWN UINT32_MAX #define WK_VECTOR_SIZE_UNKNOWN -1 #define WK_SRID_NONE UINT32_MAX enum wk_geometery_type_enum { WK_GEOMETRY = 0, WK_POINT = 1, WK_LINESTRING = 2, WK_POLYGON = 3, WK_MULTIPOINT = 4, WK_MULTILINESTRING = 5, WK_MULTIPOLYGON = 6, WK_GEOMETRYCOLLECTION = 7 }; typedef struct { uint32_t geometry_type; uint32_t flags; uint32_t srid; uint32_t size; double precision; double bounds_min[4]; double bounds_max[4]; } wk_meta_t; typedef struct { uint32_t geometry_type; uint32_t flags; R_xlen_t size; double bounds_min[4]; double bounds_max[4]; } wk_vector_meta_t; #define WK_META_RESET(meta, geometry_type_) \ meta.geometry_type = geometry_type_; \ meta.flags = 0; \ meta.precision = WK_PRECISION_NONE; \ meta.srid = WK_SRID_NONE; \ meta.size = WK_SIZE_UNKNOWN #define WK_VECTOR_META_RESET(meta, geometry_type_) \ meta.geometry_type = geometry_type_; \ meta.flags = 0; \ meta.size = WK_VECTOR_SIZE_UNKNOWN typedef struct { int api_version; int dirty; void* handler_data; void (*initialize)(int* dirty, void* handler_data); int (*vector_start)(const wk_vector_meta_t* meta, void* handler_data); int (*feature_start)(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data); int (*null_feature)(void* handler_data); int (*geometry_start)(const wk_meta_t* meta, uint32_t part_id, void* handler_data); int (*ring_start)(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data); int (*coord)(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data); int (*ring_end)(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data); int (*geometry_end)(const wk_meta_t* meta, uint32_t part_id, void* handler_data); int (*feature_end)(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data); SEXP (*vector_end)(const wk_vector_meta_t* meta, void* handler_data); int (*error)(const char* message, void* handler_data); void (*deinitialize)(void* handler_data); void (*finalizer)(void* handler_data); } wk_handler_t; typedef struct { int api_version; void* trans_data; int use_z; int use_m; double xyzm_out_min[4]; double xyzm_out_max[4]; int (*trans)(R_xlen_t feature_id, const double* xyzm_in, double* xyzm_out, void* trans_data); void (*vector_end)(void* trans_data); void (*finalizer)(void* trans_data); } wk_trans_t; // implementations in wk-v1-impl.c, which must be included exactly once in an R package wk_handler_t* wk_handler_create(void); SEXP wk_handler_create_xptr(wk_handler_t* handler, SEXP tag, SEXP prot); void wk_handler_destroy(wk_handler_t* handler); SEXP wk_handler_run_xptr(SEXP (*read_fun)(SEXP read_data, wk_handler_t* handler), SEXP read_data, SEXP xptr); wk_trans_t* wk_trans_create(void); SEXP wk_trans_create_xptr(wk_trans_t* trans, SEXP tag, SEXP prot); void wk_trans_destroy(wk_trans_t* trans); #ifdef __cplusplus } // extern "C" { #endif #endif