From 55230f40fed8de8ac0b17bd3cbd2e5e9de9059a1 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 4 Feb 2024 12:29:44 -0500 Subject: [PATCH] update R package --- r/NAMESPACE | 3 + r/R/dev.R | 22 +- r/R/extendr-wrappers.R | 9 + r/src/Makevars | 5 +- r/src/rust/Cargo.lock | 328 +++++++++++++++++++------ r/src/rust/Cargo.toml | 12 +- r/src/rust/src/lib.rs | 77 ++++-- r/src/rust/src/sf_compat/coord.rs | 39 +++ r/src/rust/src/sf_compat/geometry.rs | 40 +++ r/src/rust/src/sf_compat/mod.rs | 24 ++ r/src/rust/src/sf_compat/multipoint.rs | 53 ++++ r/src/rust/src/sf_compat/point.rs | 43 ++++ r/src/rust/src/sf_compat/polygon.rs | 50 ++++ r/src/rust/src/sf_compat/polyline.rs | 95 +++++++ r/src/rust/src/sf_compat/sfc.rs | 225 +++++++++++++++++ 15 files changed, 919 insertions(+), 106 deletions(-) create mode 100644 r/src/rust/src/sf_compat/coord.rs create mode 100644 r/src/rust/src/sf_compat/geometry.rs create mode 100644 r/src/rust/src/sf_compat/mod.rs create mode 100644 r/src/rust/src/sf_compat/multipoint.rs create mode 100644 r/src/rust/src/sf_compat/point.rs create mode 100644 r/src/rust/src/sf_compat/polygon.rs create mode 100644 r/src/rust/src/sf_compat/polyline.rs create mode 100644 r/src/rust/src/sf_compat/sfc.rs diff --git a/r/NAMESPACE b/r/NAMESPACE index ca258f0..55bf7a6 100644 --- a/r/NAMESPACE +++ b/r/NAMESPACE @@ -1,5 +1,8 @@ # Generated by roxygen2: do not edit by hand export(parse_esri_json_raw) +export(parse_esri_json_raw_geoarrow) +export(parse_esri_json_raw_simd) export(parse_esri_json_str) +export(parse_esri_json_str_simd) useDynLib(serdesri, .registration = TRUE) diff --git a/r/R/dev.R b/r/R/dev.R index d594649..242a9e0 100644 --- a/r/R/dev.R +++ b/r/R/dev.R @@ -1,14 +1,26 @@ # library(serdesri) # -# url <- "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/ACS_Population_by_Race_and_Hispanic_Origin_Boundaries/FeatureServer/2/query?where=1=1&outFields=objectid&resultRecordCount=10&f=json" +# no_geo_url <- "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/ACS_Population_by_Race_and_Hispanic_Origin_Boundaries/FeatureServer/2/query?where=1=1&outFields=*&resultRecordCount=2000&f=json&returnGeometry=true" # -# req <- httr2::request(url) +# req <- httr2::request(no_geo_url) # resp <- httr2::req_perform(req) # json <- httr2::resp_body_string(resp) # # stream <- parse_esri_json_str(json, 2) -# stream -# # df <- as.data.frame(stream) +# head(df$geometry) +# +# +# bm <- bench::mark( +# current = arcgisutils::parse_esri_json(json), +# # arrow = arrow::as_record_batch_reader(parse_esri_json_str(json, 2))$read_table() , +# serde_json = as.data.frame(parse_esri_json_str(json, 2)), +# # simd_json = as.data.frame(parse_esri_json_str_simd(json, 2)), +# iterations = 10, +# check = FALSE, +# # relative = TRUE +# ) |> +# dplyr::select(1:7) |> +# print() +# # -# str(df, 1) diff --git a/r/R/extendr-wrappers.R b/r/R/extendr-wrappers.R index 3d84bcb..74eee09 100644 --- a/r/R/extendr-wrappers.R +++ b/r/R/extendr-wrappers.R @@ -14,8 +14,17 @@ NULL #' @export parse_esri_json_str <- function(str, n_dim) .Call(wrap__parse_esri_json_str, str, n_dim) +#' @export +parse_esri_json_str_simd <- function(str, n_dim) .Call(wrap__parse_esri_json_str_simd, str, n_dim) + +#' @export +parse_esri_json_raw_simd <- function(raw, n_dim) .Call(wrap__parse_esri_json_raw_simd, raw, n_dim) + #' @export parse_esri_json_raw <- function(raw, n_dim) .Call(wrap__parse_esri_json_raw, raw, n_dim) +#' @export +parse_esri_json_raw_geoarrow <- function(raw, n_dim) .Call(wrap__parse_esri_json_raw_geoarrow, raw, n_dim) + # nolint end diff --git a/r/src/Makevars b/r/src/Makevars index c0c5245..638999d 100644 --- a/r/src/Makevars +++ b/r/src/Makevars @@ -1,7 +1,7 @@ TARGET_DIR = ./rust/target LIBDIR = $(TARGET_DIR)/release -STATLIB = $(LIBDIR)/libserdesri.a -PKG_LIBS = -L$(LIBDIR) -lserdesri +STATLIB = $(LIBDIR)/libserdesri_r.a +PKG_LIBS = -L$(LIBDIR) -lserdesri_r all: C_clean @@ -13,6 +13,7 @@ $(STATLIB): # In some environments, ~/.cargo/bin might not be included in PATH, so we need # to set it here to ensure cargo can be invoked. It is appended to PATH and # therefore is only used if cargo is absent from the user's PATH. + export RUSTFLAGS="-C target-cpu=native" && \ if [ "$(NOT_CRAN)" != "true" ]; then \ export CARGO_HOME=$(CARGOTMP); \ fi && \ diff --git a/r/src/rust/Cargo.lock b/r/src/rust/Cargo.lock index d97aefb..27d5bd3 100644 --- a/r/src/rust/Cargo.lock +++ b/r/src/rust/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "const-random", @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "approx" @@ -63,10 +63,10 @@ dependencies = [ [[package]] name = "arrow" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa285343fba4d829d49985bdc541e3789cf6000ed0e84be7c039438df4a4e78c" dependencies = [ - "ahash", "arrow-arith", "arrow-array", "arrow-buffer", @@ -84,8 +84,9 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "753abd0a5290c1bcade7c6623a556f7d1659c5f4148b140b5b63ce7bd1a45705" dependencies = [ "arrow-array", "arrow-buffer", @@ -98,8 +99,9 @@ dependencies = [ [[package]] name = "arrow-array" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d390feeb7f21b78ec997a4081a025baef1e2e0d6069e181939b61864c9779609" dependencies = [ "ahash", "arrow-buffer", @@ -113,8 +115,9 @@ dependencies = [ [[package]] name = "arrow-buffer" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69615b061701bcdffbc62756bc7e85c827d5290b472b580c972ebbbf690f5aa4" dependencies = [ "bytes", "half", @@ -123,8 +126,9 @@ dependencies = [ [[package]] name = "arrow-cast" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e448e5dd2f4113bf5b74a1f26531708f5edcacc77335b7066f9398f4bcf4cdef" dependencies = [ "arrow-array", "arrow-buffer", @@ -140,8 +144,9 @@ dependencies = [ [[package]] name = "arrow-csv" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46af72211f0712612f5b18325530b9ad1bfbdc87290d5fbfd32a7da128983781" dependencies = [ "arrow-array", "arrow-buffer", @@ -158,8 +163,9 @@ dependencies = [ [[package]] name = "arrow-data" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d644b91a162f3ad3135ce1184d0a31c28b816a581e08f29e8e9277a574c64e" dependencies = [ "arrow-buffer", "arrow-schema", @@ -169,8 +175,9 @@ dependencies = [ [[package]] name = "arrow-ipc" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03dea5e79b48de6c2e04f03f62b0afea7105be7b77d134f6c5414868feefb80d" dependencies = [ "arrow-array", "arrow-buffer", @@ -182,8 +189,9 @@ dependencies = [ [[package]] name = "arrow-json" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8950719280397a47d37ac01492e3506a8a724b3fb81001900b866637a829ee0f" dependencies = [ "arrow-array", "arrow-buffer", @@ -201,8 +209,9 @@ dependencies = [ [[package]] name = "arrow-ord" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed9630979034077982d8e74a942b7ac228f33dd93a93b615b4d02ad60c260be" dependencies = [ "arrow-array", "arrow-buffer", @@ -215,8 +224,9 @@ dependencies = [ [[package]] name = "arrow-row" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007035e17ae09c4e8993e4cb8b5b96edf0afb927cd38e2dff27189b274d83dcf" dependencies = [ "ahash", "arrow-array", @@ -229,16 +239,18 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ff3e9c01f7cd169379d269f926892d0e622a704960350d09d331be3ec9e0029" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", ] [[package]] name = "arrow-select" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce20973c1912de6514348e064829e50947e35977bb9d7fb637dc99ea9ffd78c" dependencies = [ "ahash", "arrow-array", @@ -250,8 +262,9 @@ dependencies = [ [[package]] name = "arrow-string" -version = "49.0.0" -source = "git+https://github.com/apache/arrow-rs?rev=fbbb61d94282165f9bb9f73fb4d00a3af16d4aee#fbbb61d94282165f9bb9f73fb4d00a3af16d4aee" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f3b37f2aeece31a2636d1b037dabb69ef590e03bdc7eb68519b51ec86932a7" dependencies = [ "arrow-array", "arrow-buffer", @@ -265,7 +278,9 @@ dependencies = [ [[package]] name = "arrow_extendr" -version = "49.0.0-geoarrow" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b12712a26b95c28bc2c1952e119a07db8dbc4c0041077d8ebe1c5e5eec7cb95" dependencies = [ "arrow", "extendr-api", @@ -273,9 +288,9 @@ dependencies = [ [[package]] name = "atomic-polyfill" -version = "0.1.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ "critical-section", ] @@ -300,9 +315,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bumpalo" @@ -345,8 +360,10 @@ checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets", ] @@ -509,6 +526,15 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "float_next_after" version = "1.0.0" @@ -552,8 +578,9 @@ dependencies = [ [[package]] name = "geoarrow" -version = "0.0.1" -source = "git+https://github.com/geoarrow/geoarrow-rs#8767908bc2cd4c0e0a2ec6386e7adc5413d73071" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b8a50c3e5b58a16ca53c4119da271846224cfcd1aa72a6825c740659aa08d2" dependencies = [ "anyhow", "arrow", @@ -565,10 +592,15 @@ dependencies = [ "arrow-schema", "bumpalo", "byteorder", + "chrono", "geo", - "itertools 0.12.0", + "indexmap 2.1.0", + "itertools 0.12.1", "num_enum", + "phf", "rstar", + "serde", + "serde_json", "thiserror", ] @@ -583,13 +615,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -603,6 +637,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "halfbrown" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5681137554ddff44396e5f149892c769d45301dd9aa19c51602a89ee214cb0ec" +dependencies = [ + "hashbrown 0.13.2", + "serde", +] + [[package]] name = "hash32" version = "0.2.1" @@ -618,6 +662,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.2" @@ -630,9 +683,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", "hash32", @@ -709,9 +762,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -837,9 +890,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "num" @@ -920,18 +973,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -951,6 +1004,48 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -959,9 +1054,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ "toml_edit", ] @@ -984,11 +1079,46 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "ref-cast" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53313ec9f12686aeeffb43462c3ac77aa25f590a5f630eb2cde0de59417b29c7" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2566c4bf6845f2c2e83b27043c3f5dfcd5ba8f2937d6c00dc009bfb51a079dc4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -998,9 +1128,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -1053,9 +1183,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" @@ -1130,7 +1260,7 @@ dependencies = [ ] [[package]] -name = "serdesri" +name = "serdesri_r" version = "0.1.0" dependencies = [ "arrow", @@ -1138,19 +1268,47 @@ dependencies = [ "extendr-api", "serde_esri", "serde_json", + "simd-json", +] + +[[package]] +name = "simd-json" +version = "0.13.8" +source = "git+https://github.com/simd-lite/simd-json#b249c70d3255e028ee0b2a6d2ee70b886f910558" +dependencies = [ + "getrandom", + "halfbrown", + "lexical-core", + "ref-cast", + "serde", + "serde_json", + "simdutf8", + "value-trait", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "spade" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d3bf265ec2d5dd1ddf87863252123447c550491adba2c70c574173a95cd8ba" +checksum = "61addf9117b11d1f5b4bf6fe94242ba25f59d2d4b2080544b771bd647024fd00" dependencies = [ "hashbrown 0.14.2", "num-traits", @@ -1198,18 +1356,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" dependencies = [ "proc-macro2", "quote", @@ -1262,9 +1420,9 @@ checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap 2.1.0", "toml_datetime", @@ -1277,6 +1435,18 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "value-trait" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad8db98c1e677797df21ba03fca7d3bf9bec3ca38db930954e4fe6e1ea27eb4" +dependencies = [ + "float-cmp", + "halfbrown", + "itoa", + "ryu", +] + [[package]] name = "version_check" version = "0.9.4" @@ -1411,27 +1581,27 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" dependencies = [ "memchr", ] [[package]] name = "zerocopy" -version = "0.7.26" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.26" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", diff --git a/r/src/rust/Cargo.toml b/r/src/rust/Cargo.toml index 17a301d..24c2fb2 100644 --- a/r/src/rust/Cargo.toml +++ b/r/src/rust/Cargo.toml @@ -1,16 +1,18 @@ [package] -name = 'serdesri' +name = 'serdesri_r' publish = false version = '0.1.0' edition = '2021' [lib] crate-type = [ 'staticlib' ] -name = 'serdesri' +name = 'serdesri_r' [dependencies] -arrow = { git = "https://github.com/apache/arrow-rs", rev="fbbb61d94282165f9bb9f73fb4d00a3af16d4aee"} -arrow_extendr = { path = "../../../../arrow-extendr" } +# arrow = { git = "https://github.com/apache/arrow-rs", rev="fbbb61d94282165f9bb9f73fb4d00a3af16d4aee"} +arrow = { version = "50.0.0" } +arrow_extendr = { version = "50.0.0" } extendr-api = '*' -serde_esri = { path = "../../../", version = "0.1.0", features = ["geoarrow"] } +serde_esri = { path = "../../../", version = "0.1.0", features = ["geoarrow"]} serde_json = "1.0.108" +simd-json = { git = "https://github.com/simd-lite/simd-json", features = ["runtime-detection"]} diff --git a/r/src/rust/src/lib.rs b/r/src/rust/src/lib.rs index ef0d6ac..03ddd20 100644 --- a/r/src/rust/src/lib.rs +++ b/r/src/rust/src/lib.rs @@ -1,25 +1,58 @@ use extendr_api::prelude::*; +mod sf_compat; use serde_esri::{ arrow_compat::featureset_to_arrow, features::FeatureSet }; use arrow_extendr::to::IntoArrowRobj; -// use arrow::record_batch::RecordBatch; #[extendr] /// @export -fn parse_esri_json_str(str: &str, n_dim: i32) -> Robj { +fn parse_esri_json_str_simd(str: String, n_dim: i32) -> Robj { let n_dim = n_dim as usize; + let mut str = str; + let str = str.as_mut_str(); match n_dim { - 0 => fset_to_robj(serde_json::from_str::>(str).unwrap()), - 2 => fset_to_robj(serde_json::from_str::>(str).unwrap()), - 3 => fset_to_robj(serde_json::from_str::>(str).unwrap()), - 4 => fset_to_robj(serde_json::from_str::>(str).unwrap()), + // 0 => unsafe { fset_to_robj(simd_json::serde::from_str::>(str).unwrap()) }, + 2 => unsafe { crate::sf_compat::handle_features(simd_json::serde::from_str::>(str).unwrap()) }, + // 3 => unsafe { fset_to_robj(simd_json::serde::from_str::>(str).unwrap()) }, + // 4 => unsafe { fset_to_robj(simd_json::serde::from_str::>(str).unwrap()) }, _ => unimplemented!() } } +#[extendr] +/// @export +fn parse_esri_json_raw_simd(raw: Raw, n_dim: i32) -> Robj { + let n_dim = n_dim as usize; + let mut raw = raw; + let bytes = unsafe { raw.as_typed_slice_raw_mut() }; + match n_dim { + // 0 => unsafe { fset_to_robj(simd_json::serde::from_str::>(str).unwrap()) }, + 2 => crate::sf_compat::handle_features(simd_json::serde::from_slice::>(bytes).unwrap()), + // 3 => unsafe { fset_to_robj(simd_json::serde::from_str::>(str).unwrap()) }, + // 4 => unsafe { fset_to_robj(simd_json::serde::from_str::>(str).unwrap()) }, + _ => unimplemented!() + } +} + + +#[extendr] +/// @export +fn parse_esri_json_str(str: String, n_dim: i32) -> Robj { + let n_dim = n_dim as usize; + let str = str.as_str(); + + let res = match n_dim { + 2 => serde_json::from_str::>(str).unwrap(), + _ => unimplemented!() + }; + + let robj = crate::sf_compat::handle_features(res); + + robj +} #[extendr] /// @export @@ -28,28 +61,42 @@ fn parse_esri_json_raw(raw: Raw, n_dim: i32) -> Robj { let bytes = raw.as_slice(); match n_dim { - 0 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), - 2 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), - 3 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), - 4 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), + // 0 => (serde_json::from_slice::>(bytes).unwrap()), + 2 => crate::sf_compat::handle_features(serde_json::from_slice::>(bytes).unwrap()), + // 3 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), + // 4 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), _ => unimplemented!() } } -fn fset_to_robj(fset: FeatureSet) -> Robj { - featureset_to_arrow(fset) - .unwrap() - .into_arrow_robj() - .unwrap() +#[extendr] +/// @export +fn parse_esri_json_raw_geoarrow(raw: Raw, n_dim: i32) -> Robj { + let n_dim = n_dim as usize; + let bytes = raw.as_slice(); + + match n_dim { + // 0 => (serde_json::from_slice::>(bytes).unwrap()), + 2 => featureset_to_arrow(serde_json::from_slice::>(bytes).unwrap()).unwrap().into_arrow_robj().unwrap(), + // 3 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), + // 4 => fset_to_robj(serde_json::from_slice::>(bytes).unwrap()), + _ => unimplemented!() + } } + + + // Macro to generate exports. // This ensures exported functions are registered with R. // See corresponding C code in `entrypoint.c`. extendr_module! { mod serdesri; fn parse_esri_json_str; + fn parse_esri_json_str_simd; + fn parse_esri_json_raw_simd; fn parse_esri_json_raw; + fn parse_esri_json_raw_geoarrow; } diff --git a/r/src/rust/src/sf_compat/coord.rs b/r/src/rust/src/sf_compat/coord.rs new file mode 100644 index 0000000..d83e8d1 --- /dev/null +++ b/r/src/rust/src/sf_compat/coord.rs @@ -0,0 +1,39 @@ +use extendr_api::extendr_module; +use extendr_api::prelude::*; +use crate::sf_compat::AsSfg; +use serde_esri::geometry::EsriCoord; + +impl AsSfg for EsriCoord<2> { + fn as_sfg(&self) -> Robj { + let crds = Doubles::from_values(self.0); + crds + .into_robj() + .set_class(&["XY", "POINT", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriCoord<3> { + fn as_sfg(&self) -> Robj { + let crds = Doubles::from_values(self.0); + crds + .into_robj() + .set_class(&["XYZ", "POINT", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriCoord<4> { + fn as_sfg(&self) -> Robj { + let crds = Doubles::from_values(self.0); + crds + .into_robj() + .set_class(&["XYZM", "POINT", "sfg"]) + .unwrap() + } +} + + +extendr_module! { + mod coord; +} \ No newline at end of file diff --git a/r/src/rust/src/sf_compat/geometry.rs b/r/src/rust/src/sf_compat/geometry.rs new file mode 100644 index 0000000..f29be86 --- /dev/null +++ b/r/src/rust/src/sf_compat/geometry.rs @@ -0,0 +1,40 @@ +use extendr_api::prelude::*; +use crate::sf_compat::AsSfg; +use serde_esri::geometry::EsriGeometry; + +impl AsSfg for EsriGeometry<2> { + fn as_sfg(&self) -> Robj { + match self { + EsriGeometry::Point(p) => p.as_sfg(), + EsriGeometry::MultiPoint(mp) => mp.as_sfg(), + EsriGeometry::Polyline(pl) => pl.as_sfg(), + EsriGeometry::Polygon(pg) => pg.as_sfg(), + } + } +} + +impl AsSfg for EsriGeometry<3> { + fn as_sfg(&self) -> Robj { + match self { + EsriGeometry::Point(p) => p.as_sfg(), + EsriGeometry::MultiPoint(mp) => mp.as_sfg(), + EsriGeometry::Polyline(pl) => pl.as_sfg(), + EsriGeometry::Polygon(pg) => pg.as_sfg(), + } + } +} + +impl AsSfg for EsriGeometry<4> { + fn as_sfg(&self) -> Robj { + match self { + EsriGeometry::Point(p) => p.as_sfg(), + EsriGeometry::MultiPoint(mp) => mp.as_sfg(), + EsriGeometry::Polyline(pl) => pl.as_sfg(), + EsriGeometry::Polygon(pg) => pg.as_sfg(), + } + } +} + +extendr_module! { + mod geometry; +} \ No newline at end of file diff --git a/r/src/rust/src/sf_compat/mod.rs b/r/src/rust/src/sf_compat/mod.rs new file mode 100644 index 0000000..c40ed9b --- /dev/null +++ b/r/src/rust/src/sf_compat/mod.rs @@ -0,0 +1,24 @@ +use extendr_api::prelude::*; +mod coord; +mod geometry; +mod point; +mod polygon; +mod polyline; +mod multipoint; +mod sfc; +pub use sfc::handle_features; + +pub trait AsSfg { + fn as_sfg(&self) -> Robj; +} + + +extendr_module!{ + mod sf_compat; + use coord; + use point; + use polygon; + use multipoint; + use polyline; + use sfc; +} \ No newline at end of file diff --git a/r/src/rust/src/sf_compat/multipoint.rs b/r/src/rust/src/sf_compat/multipoint.rs new file mode 100644 index 0000000..8ba59eb --- /dev/null +++ b/r/src/rust/src/sf_compat/multipoint.rs @@ -0,0 +1,53 @@ +use extendr_api::prelude::*; +use crate::sf_compat::AsSfg; +use serde_esri::geometry::EsriMultiPoint; + +impl AsSfg for EsriMultiPoint<2> { + fn as_sfg(&self) -> Robj { + + let m = RArray::new_matrix( + self.points.len(), + 2, + |r, c| self.points[r].0[c] + ); + + m.into_robj() + .set_class(&["XY", "MULTIPOINT", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriMultiPoint<3> { + fn as_sfg(&self) -> Robj { + + let m = RArray::new_matrix( + self.points.len(), + 2, + |r, c| self.points[r].0[c] + ); + + m.into_robj() + .set_class(&["XYZ", "MULTIPOINT", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriMultiPoint<4> { + fn as_sfg(&self) -> Robj { + + let m = RArray::new_matrix( + self.points.len(), + 2, + |r, c| self.points[r].0[c] + ); + + m.into_robj() + .set_class(&["XYZ", "MULTIPOINT", "sfg"]) + .unwrap() + } +} + + +extendr_module! { + mod multipoint; +} \ No newline at end of file diff --git a/r/src/rust/src/sf_compat/point.rs b/r/src/rust/src/sf_compat/point.rs new file mode 100644 index 0000000..38869d5 --- /dev/null +++ b/r/src/rust/src/sf_compat/point.rs @@ -0,0 +1,43 @@ +use extendr_api::prelude::*; + +use crate::sf_compat::AsSfg; + +use serde_esri::geometry::EsriPoint; + + +impl AsSfg for EsriPoint { + fn as_sfg(&self) -> Robj { + let has_z = self.z.is_some(); + let has_m = self.m.is_some(); + + if has_z && has_m { + let crds = Doubles::from_values([self.x, self.y, self.z.unwrap(), self.m.unwrap()]); + crds. + into_robj() + .set_class(&["XYZM", "POINT", "sfg"]) + .unwrap() + } else if has_z { + let crds = Doubles::from_values([self.x, self.y, self.z.unwrap()]); + crds + .into_robj() + .set_class(&["XYZ", "POINT", "sfg"]) + .unwrap() + } else if has_m { + let crds = Doubles::from_values([self.x, self.y, self.m.unwrap()]); + crds + .into_robj() + .set_class(&["XYM", "POINT", "sfg"]) + .unwrap() + } else { + let crds = Doubles::from_values([self.x, self.y]); + crds + .into_robj() + .set_class(&["XY", "POINT", "sfg"]) + .unwrap() + } + } +} + +extendr_module! { + mod point; +} \ No newline at end of file diff --git a/r/src/rust/src/sf_compat/polygon.rs b/r/src/rust/src/sf_compat/polygon.rs new file mode 100644 index 0000000..4586458 --- /dev/null +++ b/r/src/rust/src/sf_compat/polygon.rs @@ -0,0 +1,50 @@ +use extendr_api::prelude::*; +use crate::sf_compat::AsSfg; +use serde_esri::geometry::EsriPolygon; + +impl AsSfg for EsriPolygon<2> { + fn as_sfg(&self) -> Robj { + let inner = self.rings + .iter() + .map(|ring| ring.as_sfg()) + .collect::(); + + inner + .into_robj() + .set_class(&["XY", "POLYGON", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriPolygon<3> { + fn as_sfg(&self) -> Robj { + let inner = self.rings + .iter() + .map(|ring| ring.as_sfg()) + .collect::(); + + inner + .into_robj() + .set_class(&["XYZ", "POLYGON", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriPolygon<4> { + fn as_sfg(&self) -> Robj { + let inner = self.rings + .iter() + .map(|ring| ring.as_sfg()) + .collect::(); + + inner + .into_robj() + .set_class(&["XYZ", "POLYGON", "sfg"]) + .unwrap() + } +} + + +extendr_module! { + mod polygon; +} \ No newline at end of file diff --git a/r/src/rust/src/sf_compat/polyline.rs b/r/src/rust/src/sf_compat/polyline.rs new file mode 100644 index 0000000..7aeada6 --- /dev/null +++ b/r/src/rust/src/sf_compat/polyline.rs @@ -0,0 +1,95 @@ +use extendr_api::prelude::*; +use crate::sf_compat::AsSfg; + +use serde_esri::geometry::{EsriLineString, EsriPolyline}; + + +// Impls for EsriLineString only return a matrix +impl AsSfg for EsriLineString<2> { + fn as_sfg(&self) -> Robj { + let inner_crds = &self.0; + + let m = RArray::new_matrix( + inner_crds.len(), + 2, + |r, c| inner_crds[r].0[c] + ); + + m.into() + } +} + +impl AsSfg for EsriLineString<3> { + fn as_sfg(&self) -> Robj { + let inner_crds = &self.0; + + let m = RArray::new_matrix( + inner_crds.len(), + 2, + |r, c| inner_crds[r].0[c] + ); + + m.into() + } +} + +impl AsSfg for EsriLineString<4> { + fn as_sfg(&self) -> Robj { + let inner_crds = &self.0; + + let m = RArray::new_matrix( + inner_crds.len(), + 2, + |r, c| inner_crds[r].0[c] + ); + + m.into() + } +} + + +impl AsSfg for EsriPolyline<2> { + fn as_sfg(&self) -> Robj { + let inner_lines = &self.paths; + + let m = List::from_values( + inner_lines.iter().map(|line| line.as_sfg()) + ); + + m.into_robj() + .set_class(&["XY", "MULTILINESTRING", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriPolyline<3> { + fn as_sfg(&self) -> Robj { + let inner_lines = &self.paths; + + let m = List::from_values( + inner_lines.iter().map(|line| line.as_sfg()) + ); + + m.into_robj() + .set_class(&["XYZ", "MULTILINESTRING", "sfg"]) + .unwrap() + } +} + +impl AsSfg for EsriPolyline<4> { + fn as_sfg(&self) -> Robj { + let inner_lines = &self.paths; + + let m = List::from_values( + inner_lines.iter().map(|line| line.as_sfg()) + ); + + m.into_robj() + .set_class(&["XYZM", "MULTILINESTRING", "sfg"]) + .unwrap() + } +} + +extendr_module! { + mod polyline; +} \ No newline at end of file diff --git a/r/src/rust/src/sf_compat/sfc.rs b/r/src/rust/src/sf_compat/sfc.rs new file mode 100644 index 0000000..2f4695b --- /dev/null +++ b/r/src/rust/src/sf_compat/sfc.rs @@ -0,0 +1,225 @@ +use std::{collections::HashMap}; +use crate::sf_compat::AsSfg; +use extendr_api::prelude::*; +use extendr_api::AsTypedSlice; +use serde_esri::{features::{FeatureSet, Field}, field_type::FieldType}; + + +pub fn schema(x: &FeatureSet<2>) -> HashMap { + let mut schema: HashMap = HashMap::new(); + let n = x.features.len(); + + // early return if the featureset is empty + if x.fields.is_none() { + return schema; + } + + x.fields.as_ref() + .unwrap() + .into_iter() + .for_each(| Field { name, field_type, .. } | { + let res = match &field_type { + FieldType::EsriFieldTypeSmallInteger => Integers::new(n).into_robj(), + FieldType::EsriFieldTypeInteger => Integers::new(n).into_robj(), + FieldType::EsriFieldTypeSingle => Doubles::new(n).into_robj(), + FieldType::EsriFieldTypeDouble => Doubles::new(n).into_robj(), + FieldType::EsriFieldTypeString => Strings::new(n).into_robj(), + FieldType::EsriFieldTypeDate => Doubles::new(n).into_robj(), + FieldType::EsriFieldTypeOid => Doubles::new(n).into_robj(), + FieldType::EsriFieldTypeBlob => List::new(n).into_robj(), + FieldType::EsriFieldTypeGuid => Strings::new(n).into_robj(), + FieldType::EsriFieldTypeGlobalId => Strings::new(n).into_robj(), + FieldType::EsriFieldTypeXml => Strings::new(n).into_robj(), + FieldType::EsriFieldTypeGeometry => unimplemented!(), + FieldType::EsriFieldTypeRaster => unimplemented!(), + }; + // TODO can we avoid cloning here? + schema.insert(name.clone(), (field_type.clone(), res)); + }); + + schema +} + + +pub fn handle_features(x: FeatureSet<2>) -> Robj { + let n = x.features.len(); + let mut hm = schema(&x); + // let mut geoms: Vec>> = Vec::with_capacity(n); + let mut res_geoms = List::new(n); + + // process all features + let _ = x.features + .into_iter() + .enumerate() + // for each feature + .for_each(|(i, f)| { + // push the geometry into a new vec + // geoms.push(f.geometry); + if f.geometry.is_some() { + res_geoms.set_elt(i, f.geometry.unwrap().as_sfg()).unwrap(); + }; + // for each attribute + f.attributes + .into_iter() + .for_each(|m| { + m.into_iter() + .for_each(|(k, v)| { + let (ft, col) = hm.get_mut(&k).unwrap(); + match ft { + FieldType::EsriFieldTypeSmallInteger => { + let val = v.as_i64(); + let rint = match val { + Some(i) => Rint::from(i as i32), + None => Rint::na(), + }; + let col: &mut [Rint] = col.as_typed_slice_mut().unwrap(); + col[i] = rint; + }, + FieldType::EsriFieldTypeInteger => { + let val = v.as_i64(); + let rint = match val { + Some(i) => Rint::from(i as i32), + None => Rint::na(), + }; + let col: &mut [Rint] = col.as_typed_slice_mut().unwrap(); + col[i] = rint; + }, + FieldType::EsriFieldTypeSingle => { + let val = v.as_f64(); + let rdbl = match val { + Some(i) => Rfloat::from(i as f64), + None => Rfloat::na(), + }; + let col: &mut [Rfloat] = col.as_typed_slice_mut().unwrap(); + col[i] = rdbl; + }, + FieldType::EsriFieldTypeDouble => { + let val = v.as_f64(); + let rdbl = match val { + Some(i) => Rfloat::from(i as f64), + None => Rfloat::na(), + }; + let col: &mut [Rfloat] = col.as_typed_slice_mut().unwrap(); + col[i] = rdbl; + }, + FieldType::EsriFieldTypeString => { + let val = v.as_str(); + let rstr = match val { + Some(i) => Rstr::from(i), + None => Rstr::na(), + }; + let mut col = Strings::from_robj(&col).unwrap(); + col.set_elt(i, rstr); + }, + // TODO handle dates + FieldType::EsriFieldTypeDate => { + let val = v.as_f64(); + let rdbl = match val { + Some(i) => Rfloat::from(i as f64), + None => Rfloat::na(), + }; + let col: &mut [Rfloat] = col.as_typed_slice_mut().unwrap(); + col[i] = rdbl; + }, + FieldType::EsriFieldTypeOid => { + let val = v.as_f64(); + let rdbl = match val { + Some(i) => Rfloat::from(i as f64), + None => Rfloat::na(), + }; + let col: &mut [Rfloat] = col.as_typed_slice_mut().unwrap(); + col[i] = rdbl; + }, + FieldType::EsriFieldTypeGuid => { + let val = v.as_str(); + let rstr = match val { + Some(i) => Rstr::from(i), + None => Rstr::na(), + }; + let mut col = Strings::from_robj(&col).unwrap(); + col.set_elt(i, rstr); + }, + FieldType::EsriFieldTypeGlobalId => { + let val = v.as_str(); + let rstr = match val { + Some(i) => Rstr::from(i), + None => Rstr::na(), + }; + let mut col = Strings::from_robj(&col).unwrap(); + col.set_elt(i, rstr); + }, + FieldType::EsriFieldTypeXml => { + let val = v.as_str(); + let rstr = match val { + Some(i) => Rstr::from(i), + None => Rstr::na(), + }; + let mut col = Strings::from_robj(&col).unwrap(); + col.set_elt(i, rstr); + }, + FieldType::EsriFieldTypeBlob => unimplemented!("Blob not supported. Email me."), + FieldType::EsriFieldTypeRaster => todo!(), + FieldType::EsriFieldTypeGeometry => todo!(), + } + }); + }); + }); + + let (keys, vals): (Vec, Vec) = hm.into_iter() + .map(|(k, (_, col))| { + (k, col) + }).unzip(); + + + let res = List::from_names_and_values(&keys, &vals).unwrap(); + + if x.geometryType.is_some() { + return list!(res, geometry = res_geoms).into(); + } + + res.into() +} + + +// TODO: handle missing / empty geometries +// fn as_sfc(x: Vec>>, geom_type: &str) -> Robj { +// match geom_type { +// "esriGeometryPoint" => { +// x.into_iter() +// .map(|g| g.unwrap().as_sfg()) +// .collect::() +// .into_robj() +// .set_class(&["sfc_POINT", "sfc"]) +// .unwrap() +// }, +// "esriGeometryMultipoint" => { +// x.into_iter() +// .map(|g| g.unwrap().as_sfg()) +// .collect::() +// .into_robj() +// .set_class(&["sfc_MULTIPOINT", "sfc"]) +// .unwrap() +// }, +// "esriGeometryPolyline" => { +// x.into_iter() +// .map(|g| g.unwrap().as_sfg()) +// .collect::() +// .into_robj() +// .set_class(&["sfc_MULTILINESTRING", "sfc"]) +// .unwrap() +// }, +// "esriGeometryPolygon" => { +// x.into_iter() +// .map(|g| g.unwrap().as_sfg()) +// .collect::() +// .into_robj() +// .set_class(&["sfc_POLYGON", "sfc"]) +// .unwrap() +// }, +// _ => unimplemented!() +// } +// } + +extendr_module! { + mod sfc; +} \ No newline at end of file