diff --git a/Cargo.lock b/Cargo.lock index 9fd932258..8f62c424c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "arrow" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45aef0d9cf9a039bf6cd1acc451b137aca819977b0928dece52bd92811b640ba" +checksum = "a9ba0d7248932f4e2a12fb37f0a2e3ec82b3bdedbac2a1dce186e036843b8f8c" dependencies = [ "arrow-arith", "arrow-array", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03675e42d1560790f3524800e41403b40d0da1c793fe9528929fde06d8c7649a" +checksum = "d60afcdc004841a5c8d8da4f4fa22d64eb19c0c01ef4bcedd77f175a7cf6e38f" dependencies = [ "arrow-array", "arrow-buffer", @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "arrow-array" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2bf348cf9f02a5975c5962c7fa6dee107a2009a7b41ac5fb1a027e12dc033f" +checksum = "7f16835e8599dbbb1659fd869d865254c4cf32c6c2bb60b6942ac9fc36bfa5da" dependencies = [ "ahash", "arrow-buffer", @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "arrow-buffer" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3092e37715f168976012ce52273c3989b5793b0db5f06cbaa246be25e5f0924d" +checksum = "1a1f34f0faae77da6b142db61deba2cb6d60167592b178be317b341440acba80" dependencies = [ "bytes", "half", @@ -163,9 +163,9 @@ dependencies = [ [[package]] name = "arrow-cast" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce1018bb710d502f9db06af026ed3561552e493e989a79d0d0f5d9cf267a785" +checksum = "450e4abb5775bca0740bec0bcf1b1a5ae07eff43bd625661c4436d8e8e4540c4" dependencies = [ "arrow-array", "arrow-buffer", @@ -176,16 +176,16 @@ dependencies = [ "base64 0.22.1", "chrono", "half", - "lexical-core", + "lexical-core 1.0.2", "num", "ryu", ] [[package]] name = "arrow-csv" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd178575f45624d045e4ebee714e246a05d9652e41363ee3f57ec18cca97f740" +checksum = "d3a4e4d63830a341713e35d9a42452fbc6241d5f42fa5cf6a4681b8ad91370c4" dependencies = [ "arrow-array", "arrow-buffer", @@ -196,15 +196,15 @@ dependencies = [ "csv", "csv-core", "lazy_static", - "lexical-core", + "lexical-core 1.0.2", "regex", ] [[package]] name = "arrow-data" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ac0c4ee79150afe067dc4857154b3ee9c1cd52b5f40d59a77306d0ed18d65" +checksum = "2b1e618bbf714c7a9e8d97203c806734f012ff71ae3adc8ad1b075689f540634" dependencies = [ "arrow-buffer", "arrow-schema", @@ -214,9 +214,9 @@ dependencies = [ [[package]] name = "arrow-ipc" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb307482348a1267f91b0912e962cd53440e5de0f7fb24c5f7b10da70b38c94a" +checksum = "f98e983549259a2b97049af7edfb8f28b8911682040e99a94e4ceb1196bd65c2" dependencies = [ "arrow-array", "arrow-buffer", @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "arrow-json" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24805ba326758effdd6f2cbdd482fcfab749544f21b134701add25b33f474e6" +checksum = "b198b9c6fcf086501730efbbcb483317b39330a116125af7bb06467d04b352a3" dependencies = [ "arrow-array", "arrow-buffer", @@ -242,7 +242,7 @@ dependencies = [ "chrono", "half", "indexmap", - "lexical-core", + "lexical-core 1.0.2", "num", "serde", "serde_json", @@ -250,9 +250,9 @@ dependencies = [ [[package]] name = "arrow-ord" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644046c479d80ae8ed02a7f1e1399072ea344ca6a7b0e293ab2d5d9ed924aa3b" +checksum = "2427f37b4459a4b9e533045abe87a5183a5e0995a3fc2c2fd45027ae2cc4ef3f" dependencies = [ "arrow-array", "arrow-buffer", @@ -265,9 +265,9 @@ dependencies = [ [[package]] name = "arrow-row" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a29791f8eb13b340ce35525b723f5f0df17ecb955599e11f65c2a94ab34e2efb" +checksum = "15959657d92e2261a7a323517640af87f5afd9fd8a6492e424ebee2203c567f6" dependencies = [ "ahash", "arrow-array", @@ -279,18 +279,18 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85320a3a2facf2b2822b57aa9d6d9d55edb8aee0b6b5d3b8df158e503d10858" +checksum = "fbf0388a18fd7f7f3fe3de01852d30f54ed5182f9004db700fbe3ba843ed2794" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "arrow-select" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc7e6b582e23855fd1625ce46e51647aa440c20ea2e71b1d748e0839dd73cba" +checksum = "b83e5723d307a38bf00ecd2972cd078d1339c7fd3eb044f609958a9a24463f3a" dependencies = [ "ahash", "arrow-array", @@ -302,9 +302,9 @@ dependencies = [ [[package]] name = "arrow-string" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0775b6567c66e56ded19b87a954b6b1beffbdd784ef95a3a2b03f59570c1d230" +checksum = "7ab3db7c09dd826e74079661d84ed01ed06547cf75d52c2818ef776d0d852305" dependencies = [ "arrow-array", "arrow-buffer", @@ -533,9 +533,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.24" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "jobserver", "libc", @@ -574,9 +574,9 @@ dependencies = [ [[package]] name = "chrono-tz" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" dependencies = [ "chrono", "chrono-tz-build", @@ -585,12 +585,11 @@ dependencies = [ [[package]] name = "chrono-tz-build" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" dependencies = [ "parse-zoneinfo", - "phf", "phf_codegen", ] @@ -1067,9 +1066,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1082,9 +1081,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1092,15 +1091,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1120,15 +1119,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1137,21 +1136,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1273,7 +1272,7 @@ dependencies = [ "half", "http-range-client", "indexmap", - "lexical-core", + "lexical-core 0.8.5", "num_enum", "object_store", "parquet", @@ -1366,9 +1365,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -1600,7 +1599,7 @@ dependencies = [ "http", "hyper", "hyper-util", - "rustls 0.23.13", + "rustls 0.23.14", "rustls-native-certs", "rustls-pki-types", "tokio", @@ -1694,9 +1693,9 @@ checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-terminal" @@ -1790,11 +1789,24 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", + "lexical-parse-float 0.8.5", + "lexical-parse-integer 0.8.6", + "lexical-util 0.8.5", + "lexical-write-float 0.8.5", + "lexical-write-integer 0.8.5", +] + +[[package]] +name = "lexical-core" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431c65b318a590c1de6b8fd6e72798c92291d27762d94c9e6c37ed7a73d8458" +dependencies = [ + "lexical-parse-float 1.0.2", + "lexical-parse-integer 1.0.2", + "lexical-util 1.0.3", + "lexical-write-float 1.0.2", + "lexical-write-integer 1.0.2", ] [[package]] @@ -1803,8 +1815,19 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" dependencies = [ - "lexical-parse-integer", - "lexical-util", + "lexical-parse-integer 0.8.6", + "lexical-util 0.8.5", + "static_assertions", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb17a4bdb9b418051aa59d41d65b1c9be5affab314a872e5ad7f06231fb3b4e0" +dependencies = [ + "lexical-parse-integer 1.0.2", + "lexical-util 1.0.3", "static_assertions", ] @@ -1814,7 +1837,17 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" dependencies = [ - "lexical-util", + "lexical-util 0.8.5", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df98f4a4ab53bf8b175b363a34c7af608fe31f93cc1fb1bf07130622ca4ef61" +dependencies = [ + "lexical-util 1.0.3", "static_assertions", ] @@ -1827,14 +1860,34 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "lexical-util" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85314db53332e5c192b6bca611fb10c114a80d1b831ddac0af1e9be1b9232ca0" +dependencies = [ + "static_assertions", +] + [[package]] name = "lexical-write-float" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" dependencies = [ - "lexical-util", - "lexical-write-integer", + "lexical-util 0.8.5", + "lexical-write-integer 0.8.5", + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e7c3ad4e37db81c1cbe7cf34610340adc09c322871972f74877a712abc6c809" +dependencies = [ + "lexical-util 1.0.3", + "lexical-write-integer 1.0.2", "static_assertions", ] @@ -1844,7 +1897,17 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" dependencies = [ - "lexical-util", + "lexical-util 0.8.5", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb89e9f6958b83258afa3deed90b5de9ef68eef090ad5086c791cd2345610162" +dependencies = [ + "lexical-util 1.0.3", "static_assertions", ] @@ -2119,9 +2182,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] @@ -2158,12 +2221,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -2249,9 +2309,9 @@ dependencies = [ [[package]] name = "parquet" -version = "53.0.0" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0fbf928021131daaa57d334ca8e3904fe9ae22f73c56244fc7db9b04eedc3d8" +checksum = "310c46a70a3ba90d98fec39fa2da6d9d731e544191da6fb56c9d199484d0dd3e" dependencies = [ "ahash", "arrow-array", @@ -2449,12 +2509,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - [[package]] name = "powerfmt" version = "0.2.0" @@ -2491,9 +2545,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -2545,7 +2599,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.13", + "rustls 0.23.14", "socket2", "thiserror", "tokio", @@ -2562,7 +2616,7 @@ dependencies = [ "rand", "ring", "rustc-hash 2.0.0", - "rustls 0.23.13", + "rustls 0.23.14", "slab", "thiserror", "tinyvec", @@ -2717,7 +2771,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.13", + "rustls 0.23.14", "rustls-native-certs", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -2844,9 +2898,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "once_cell", "ring", @@ -2931,9 +2985,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -3619,7 +3673,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.13", + "rustls 0.23.14", "rustls-pki-types", "tokio", ] diff --git a/fixtures/flatgeobuf/poly00.fgb b/fixtures/flatgeobuf/poly00.fgb new file mode 100644 index 000000000..e88c870d0 Binary files /dev/null and b/fixtures/flatgeobuf/poly00.fgb differ diff --git a/fixtures/flatgeobuf/poly01.fgb b/fixtures/flatgeobuf/poly01.fgb new file mode 100644 index 000000000..cdae61d3a Binary files /dev/null and b/fixtures/flatgeobuf/poly01.fgb differ diff --git a/js/Cargo.lock b/js/Cargo.lock index a8ce64820..dd544bf23 100644 --- a/js/Cargo.lock +++ b/js/Cargo.lock @@ -1034,7 +1034,7 @@ dependencies = [ [[package]] name = "geoarrow" -version = "0.3.0" +version = "0.4.0-beta.1" dependencies = [ "arrow", "arrow-array", diff --git a/js/tests/js/flatgeobuf.test.ts b/js/tests/js/flatgeobuf.test.ts index 3c45566c8..c5f8c50e2 100644 --- a/js/tests/js/flatgeobuf.test.ts +++ b/js/tests/js/flatgeobuf.test.ts @@ -18,6 +18,6 @@ it("read FlatGeobuf", () => { const geometryFieldMetadata = geometryField.metadata; console.log(geometryFieldMetadata); expect(geometryFieldMetadata.get("ARROW:extension:name")).toStrictEqual( - "geoarrow.polygon" + "geoarrow.multipolygon" ); }); diff --git a/python/tests/io/test_flatgeobuf.py b/python/tests/io/test_flatgeobuf.py index 7a90e7e89..4dee1d92d 100644 --- a/python/tests/io/test_flatgeobuf.py +++ b/python/tests/io/test_flatgeobuf.py @@ -1,9 +1,12 @@ from io import BytesIO -from geoarrow.rust.io import read_flatgeobuf, write_flatgeobuf -from geoarrow.rust.core import to_geopandas +import geodatasets import geopandas as gpd +import pyarrow as pa import pytest +import shapely +from geoarrow.rust.core import from_geopandas, geometry_col, to_geopandas +from geoarrow.rust.io import read_flatgeobuf, write_flatgeobuf from geopandas.testing import assert_geodataframe_equal from tests.utils import FIXTURES_DIR @@ -13,7 +16,8 @@ def test_read_flatgeobuf(): path = FIXTURES_DIR / "flatgeobuf" / "countries.fgb" table = read_flatgeobuf(path) assert len(table) == 179 - # assert isinstance(gars.geometry_col(table), gars.ChunkedMultiPolygonArray) + # hacky + assert "MultiPolygon" in geometry_col(table).type.__repr__() def test_read_flatgeobuf_file_object(): @@ -21,7 +25,8 @@ def test_read_flatgeobuf_file_object(): with open(path, "rb") as f: table = read_flatgeobuf(f) assert len(table) == 179 - # assert isinstance(gars.geometry_col(table), gars.ChunkedMultiPolygonArray) + # hacky + assert "MultiPolygon" in geometry_col(table).type.__repr__() def test_round_trip_flatgeobuf(): @@ -29,12 +34,70 @@ def test_round_trip_flatgeobuf(): table = read_flatgeobuf(path) buf = BytesIO() - write_flatgeobuf(table, buf) + write_flatgeobuf(table, buf, write_index=False) buf.seek(0) table_back = read_flatgeobuf(buf) assert table == table_back # type: ignore +def test_round_trip_polygon(): + geom = shapely.geometry.shape( + { + "type": "Polygon", + "coordinates": [ + [ + [-118.4765625, 33.92578125], + [-118.125, 33.92578125], + [-118.125, 34.1015625], + [-118.4765625, 34.1015625], + [-118.4765625, 33.92578125], + ], + [ + [-118.24447631835938, 34.0521240234375], + [-118.24310302734375, 34.0521240234375], + [-118.24310302734375, 34.053497314453125], + [-118.24447631835938, 34.053497314453125], + [-118.24447631835938, 34.0521240234375], + ], + ], + } + ) + polys = [geom] * 3 + gdf = gpd.GeoDataFrame({"col1": ["a", "b", "c"]}, geometry=polys, crs="EPSG:4326") + table = from_geopandas(gdf) + + buf = BytesIO() + write_flatgeobuf(table, buf, write_index=False) + buf.seek(0) + table_back = read_flatgeobuf(buf) + assert pa.table(table) == pa.table(table_back) + + +def test_round_trip_3d_points(): + points = shapely.points([1, 2, 3], [4, 5, 6], [7, 8, 9]) + gdf = gpd.GeoDataFrame({"col1": ["a", "b", "c"]}, geometry=points, crs="EPSG:4326") + table = from_geopandas(gdf) + + buf = BytesIO() + write_flatgeobuf(table, buf, write_index=False) + buf.seek(0) + table_back = read_flatgeobuf(buf) + + assert pa.table(table) == pa.table(table_back) + + +def test_round_trip_multilinestring(): + gdf = gpd.read_file(geodatasets.get_path("eea.large_rivers")) + table = from_geopandas(gdf) + + buf = BytesIO() + write_flatgeobuf(table, buf, write_index=False) + buf.seek(0) + table_back = read_flatgeobuf(buf) + + assert pa.table(table) == pa.table(table_back) + + @pytest.mark.xfail(reason="fix propagate CRS") def test_matches_pyogrio(): path = FIXTURES_DIR / "flatgeobuf" / "countries.fgb" diff --git a/src/io/flatgeobuf/reader/async.rs b/src/io/flatgeobuf/reader/async.rs index 260d47ee6..75390e951 100644 --- a/src/io/flatgeobuf/reader/async.rs +++ b/src/io/flatgeobuf/reader/async.rs @@ -1,12 +1,14 @@ use std::sync::Arc; use flatgeobuf::{GeometryType, HttpFgbReader}; +use geozero::{FeatureProcessor, FeatureProperties}; use http_range_client::AsyncBufferedHttpRangeClient; use object_store::path::Path; use object_store::ObjectStore; use crate::algorithm::native::DowncastTable; use crate::array::*; +use crate::datatypes::Dimension; use crate::error::{GeoArrowError, Result}; use crate::io::flatgeobuf::reader::common::{infer_schema, FlatGeobufReaderOptions}; use crate::io::flatgeobuf::reader::object_store_reader::ObjectStoreWrapper; @@ -31,11 +33,12 @@ pub async fn read_flatgeobuf_async( let reader = HttpFgbReader::new(async_client).await.unwrap(); let header = reader.header(); - if header.has_m() | header.has_t() | header.has_tm() | header.has_z() { + if header.has_m() | header.has_t() | header.has_tm() { return Err(GeoArrowError::General( - "Only XY dimensions are supported".to_string(), + "Only XY and XYZ dimensions are supported".to_string(), )); } + let has_z = header.has_z(); let schema = infer_schema(header); let geometry_type = header.geometry_type(); @@ -58,41 +61,89 @@ pub async fn read_flatgeobuf_async( Default::default(), ); - match geometry_type { - GeometryType::Point => { - let mut builder = GeoTableBuilder::>::new_with_options(options); + macro_rules! impl_read { + ($builder:ty, $geom_type:ty, $dim:expr) => {{ + let mut builder = GeoTableBuilder::<$builder>::new_with_options(options); + while let Some(feature) = selection.next().await? { + feature.process_properties(&mut builder)?; + builder.properties_end()?; + + let geom: Option> = feature + .geometry() + .map(|g| <$geom_type>::new(g, $dim)) + .map(|g| g.into()); + builder.push_geometry(geom.as_ref())?; + + builder.feature_end(0)?; + } selection.process_features(&mut builder).await?; builder.finish() + }}; + } + + match (geometry_type, has_z) { + (GeometryType::Point, false) => { + impl_read!(PointBuilder<2>, super::core::Point, Dimension::XY) } - GeometryType::LineString => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder).await?; - builder.finish() + (GeometryType::LineString, false) => { + impl_read!(LineStringBuilder<2>, super::core::LineString, Dimension::XY) } - GeometryType::Polygon => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder).await?; - builder.finish() + (GeometryType::Polygon, false) => { + impl_read!(PolygonBuilder<2>, super::core::Polygon, Dimension::XY) } - GeometryType::MultiPoint => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder).await?; - builder.finish() + (GeometryType::MultiPoint, false) => { + impl_read!(MultiPointBuilder<2>, super::core::MultiPoint, Dimension::XY) } - GeometryType::MultiLineString => { + (GeometryType::MultiLineString, false) => impl_read!( + MultiLineStringBuilder<2>, + super::core::MultiLineString, + Dimension::XY + ), + (GeometryType::MultiPolygon, false) => impl_read!( + MultiPolygonBuilder<2>, + super::core::MultiPolygon, + Dimension::XY + ), + (GeometryType::Unknown, false) => { let mut builder = - GeoTableBuilder::>::new_with_options(options); + GeoTableBuilder::>::new_with_options(options); selection.process_features(&mut builder).await?; - builder.finish() + let table = builder.finish()?; + table.downcast(true) } - GeometryType::MultiPolygon => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder).await?; - builder.finish() + (GeometryType::Point, true) => { + impl_read!(PointBuilder<3>, super::core::Point, Dimension::XYZ) } - GeometryType::Unknown => { + (GeometryType::LineString, true) => { + impl_read!( + LineStringBuilder<3>, + super::core::LineString, + Dimension::XYZ + ) + } + (GeometryType::Polygon, true) => { + impl_read!(PolygonBuilder<3>, super::core::Polygon, Dimension::XYZ) + } + (GeometryType::MultiPoint, true) => { + impl_read!( + MultiPointBuilder<3>, + super::core::MultiPoint, + Dimension::XYZ + ) + } + (GeometryType::MultiLineString, true) => impl_read!( + MultiLineStringBuilder<3>, + super::core::MultiLineString, + Dimension::XYZ + ), + (GeometryType::MultiPolygon, true) => impl_read!( + MultiPolygonBuilder<3>, + super::core::MultiPolygon, + Dimension::XYZ + ), + (GeometryType::Unknown, true) => { let mut builder = - GeoTableBuilder::>::new_with_options(options); + GeoTableBuilder::>::new_with_options(options); selection.process_features(&mut builder).await?; let table = builder.finish()?; table.downcast(true) diff --git a/src/io/flatgeobuf/reader/core.rs b/src/io/flatgeobuf/reader/core.rs index c22e3ab6a..802f3e85a 100644 --- a/src/io/flatgeobuf/reader/core.rs +++ b/src/io/flatgeobuf/reader/core.rs @@ -10,7 +10,11 @@ use crate::geo_traits::{ pub(super) struct Point<'a> { geom: flatgeobuf::Geometry<'a>, dim: Dimension, - offset: usize, + /// The coordinate offset + /// + /// Note each coord_offset points to an xy coordinate pair, and must be multiplied by 2 to get + /// the buffer coord_offset + coord_offset: usize, } impl<'a> Point<'a> { @@ -18,7 +22,7 @@ impl<'a> Point<'a> { Self { geom, dim, - offset: 0, + coord_offset: 0, } } } @@ -32,19 +36,19 @@ impl<'a> PointTrait for Point<'a> { fn nth_unchecked(&self, n: usize) -> Self::T { match n { - 0 => self.geom.xy().unwrap().get(self.offset), - 1 => self.geom.xy().unwrap().get(self.offset + 1), - 2 => self.geom.z().unwrap().get(self.offset), + 0 => self.geom.xy().unwrap().get(self.coord_offset * 2), + 1 => self.geom.xy().unwrap().get((self.coord_offset * 2) + 1), + 2 => self.geom.z().unwrap().get(self.coord_offset), _ => panic!("Unexpected dim {n}"), } } fn x(&self) -> Self::T { - self.geom.xy().unwrap().get(self.offset) + self.geom.xy().unwrap().get(self.coord_offset * 2) } fn y(&self) -> Self::T { - self.geom.xy().unwrap().get(self.offset + 1) + self.geom.xy().unwrap().get((self.coord_offset * 2) + 1) } } @@ -57,19 +61,19 @@ impl<'a> CoordTrait for Point<'a> { fn nth_unchecked(&self, n: usize) -> Self::T { match n { - 0 => self.geom.xy().unwrap().get(self.offset), - 1 => self.geom.xy().unwrap().get(self.offset + 1), - 2 => self.geom.z().unwrap().get(self.offset), + 0 => self.geom.xy().unwrap().get(self.coord_offset * 2), + 1 => self.geom.xy().unwrap().get((self.coord_offset * 2) + 1), + 2 => self.geom.z().unwrap().get(self.coord_offset), _ => panic!("Unexpected dim {n}"), } } fn x(&self) -> Self::T { - self.geom.xy().unwrap().get(self.offset) + self.geom.xy().unwrap().get(self.coord_offset * 2) } fn y(&self) -> Self::T { - self.geom.xy().unwrap().get(self.offset + 1) + self.geom.xy().unwrap().get((self.coord_offset * 2) + 1) } } @@ -78,9 +82,9 @@ pub(super) struct LineString<'a> { geom: flatgeobuf::Geometry<'a>, dim: Dimension, - /// This offset will be non-zero when the LineString is a reference onto an external geometry, + /// This coord_offset will be non-zero when the LineString is a reference onto an external geometry, /// e.g. a Polygon - offset: usize, + coord_offset: usize, /// This length cannot be inferred from the underlying buffer when this LineString is a /// reference on e.g. a Polygon @@ -93,7 +97,7 @@ impl<'a> LineString<'a> { Self { geom, dim, - offset: 0, + coord_offset: 0, length, } } @@ -115,7 +119,7 @@ impl<'a> LineStringTrait for LineString<'a> { Point { geom: self.geom, dim: self.dim, - offset: self.offset + i, + coord_offset: self.coord_offset + i, } } } @@ -154,7 +158,7 @@ impl<'a> PolygonTrait for Polygon<'a> { Some(LineString { geom: self.geom, dim: self.dim, - offset: 0, + coord_offset: 0, length: exterior_end.try_into().unwrap(), }) } else { @@ -169,8 +173,8 @@ impl<'a> PolygonTrait for Polygon<'a> { LineString { geom: self.geom, dim: self.dim, - offset: start.try_into().unwrap(), - length: end.try_into().unwrap(), + coord_offset: start.try_into().unwrap(), + length: (end - start).try_into().unwrap(), } } } @@ -180,9 +184,9 @@ pub(super) struct MultiPoint<'a> { geom: flatgeobuf::Geometry<'a>, dim: Dimension, - /// This offset will be non-zero when the MultiPoint is a reference onto an external geometry, + /// This coord_offset will be non-zero when the MultiPoint is a reference onto an external geometry, /// e.g. a GeometryCollection - offset: usize, + coord_offset: usize, /// This length is not inferred from the underlying buffer because this MultiPoint could be a /// reference on e.g. a GeometryCollection @@ -195,7 +199,7 @@ impl<'a> MultiPoint<'a> { Self { geom, dim, - offset: 0, + coord_offset: 0, length, } } @@ -217,7 +221,7 @@ impl<'a> MultiPointTrait for MultiPoint<'a> { Point { geom: self.geom, dim: self.dim, - offset: self.offset + i, + coord_offset: self.coord_offset + i, } } } @@ -244,9 +248,9 @@ impl<'a> MultiLineStringTrait for MultiLineString<'a> { fn num_lines(&self) -> usize { if let Some(ends) = self.geom.ends() { - ends.len() - 1 + ends.len() } else { - 0 + 1 } } @@ -257,8 +261,8 @@ impl<'a> MultiLineStringTrait for MultiLineString<'a> { LineString { geom: self.geom, dim: self.dim, - offset: start.try_into().unwrap(), - length: end.try_into().unwrap(), + coord_offset: start.try_into().unwrap(), + length: (end - start).try_into().unwrap(), } } else { assert_eq!(i, 0); @@ -332,6 +336,48 @@ impl<'a> Geometry<'a> { } } +impl<'a> From> for Geometry<'a> { + fn from(value: Point<'a>) -> Self { + Self::Point(value) + } +} + +impl<'a> From> for Geometry<'a> { + fn from(value: LineString<'a>) -> Self { + Self::LineString(value) + } +} + +impl<'a> From> for Geometry<'a> { + fn from(value: Polygon<'a>) -> Self { + Self::Polygon(value) + } +} + +impl<'a> From> for Geometry<'a> { + fn from(value: MultiPoint<'a>) -> Self { + Self::MultiPoint(value) + } +} + +impl<'a> From> for Geometry<'a> { + fn from(value: MultiLineString<'a>) -> Self { + Self::MultiLineString(value) + } +} + +impl<'a> From> for Geometry<'a> { + fn from(value: MultiPolygon<'a>) -> Self { + Self::MultiPolygon(value) + } +} + +impl<'a> From> for Geometry<'a> { + fn from(value: GeometryCollection<'a>) -> Self { + Self::GeometryCollection(value) + } +} + impl<'a> GeometryTrait for Geometry<'a> { type T = f64; type Point<'b> = Point<'a> where Self: 'b; diff --git a/src/io/flatgeobuf/reader/sync.rs b/src/io/flatgeobuf/reader/sync.rs index a8a3fbb98..f43281c65 100644 --- a/src/io/flatgeobuf/reader/sync.rs +++ b/src/io/flatgeobuf/reader/sync.rs @@ -21,12 +21,14 @@ use crate::algorithm::native::DowncastTable; use crate::array::*; +use crate::datatypes::Dimension; use crate::error::{GeoArrowError, Result}; use crate::io::flatgeobuf::reader::common::{infer_schema, FlatGeobufReaderOptions}; use crate::io::geozero::array::MixedGeometryStreamBuilder; use crate::io::geozero::table::{GeoTableBuilder, GeoTableBuilderOptions}; use crate::table::Table; -use flatgeobuf::{FgbReader, GeometryType}; +use flatgeobuf::{FallibleStreamingIterator, FgbReader, GeometryType}; +use geozero::{FeatureProcessor, FeatureProperties}; use std::io::{Read, Seek}; /// Read a FlatGeobuf file to a Table @@ -37,11 +39,12 @@ pub fn read_flatgeobuf( let reader = FgbReader::open(file)?; let header = reader.header(); - if header.has_m() | header.has_t() | header.has_tm() | header.has_z() { + if header.has_m() | header.has_t() | header.has_tm() { return Err(GeoArrowError::General( - "Only XY dimensions are supported".to_string(), + "Only XY and XYZ dimensions are supported".to_string(), )); } + let has_z = header.has_z(); let schema = infer_schema(header); let geometry_type = header.geometry_type(); @@ -64,44 +67,94 @@ pub fn read_flatgeobuf( Default::default(), ); - match geometry_type { - GeometryType::Point => { - let mut builder = GeoTableBuilder::>::new_with_options(options); + macro_rules! impl_read { + ($builder:ty, $geom_type:ty, $dim:expr) => {{ + let mut builder = GeoTableBuilder::<$builder>::new_with_options(options); + while let Some(feature) = selection.next()? { + feature.process_properties(&mut builder)?; + builder.properties_end()?; + + let geom: Option> = feature + .geometry() + .map(|g| <$geom_type>::new(g, $dim)) + .map(|g| g.into()); + builder.push_geometry(geom.as_ref())?; + + builder.feature_end(0)?; + } selection.process_features(&mut builder)?; builder.finish() + }}; + } + + match (geometry_type, has_z) { + (GeometryType::Point, false) => { + impl_read!(PointBuilder<2>, super::core::Point, Dimension::XY) } - GeometryType::LineString => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder)?; - builder.finish() + (GeometryType::LineString, false) => { + impl_read!(LineStringBuilder<2>, super::core::LineString, Dimension::XY) } - GeometryType::Polygon => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder)?; - builder.finish() + (GeometryType::Polygon, false) => { + impl_read!(PolygonBuilder<2>, super::core::Polygon, Dimension::XY) } - GeometryType::MultiPoint => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder)?; - builder.finish() + (GeometryType::MultiPoint, false) => { + impl_read!(MultiPointBuilder<2>, super::core::MultiPoint, Dimension::XY) } - GeometryType::MultiLineString => { + (GeometryType::MultiLineString, false) => impl_read!( + MultiLineStringBuilder<2>, + super::core::MultiLineString, + Dimension::XY + ), + (GeometryType::MultiPolygon, false) => impl_read!( + MultiPolygonBuilder<2>, + super::core::MultiPolygon, + Dimension::XY + ), + (GeometryType::Unknown, false) => { let mut builder = - GeoTableBuilder::>::new_with_options(options); + GeoTableBuilder::>::new_with_options(options); selection.process_features(&mut builder)?; - builder.finish() + let table = builder.finish()?; + table.downcast(true) } - GeometryType::MultiPolygon => { - let mut builder = GeoTableBuilder::>::new_with_options(options); - selection.process_features(&mut builder)?; - builder.finish() + (GeometryType::Point, true) => { + impl_read!(PointBuilder<3>, super::core::Point, Dimension::XYZ) + } + (GeometryType::LineString, true) => { + impl_read!( + LineStringBuilder<3>, + super::core::LineString, + Dimension::XYZ + ) + } + (GeometryType::Polygon, true) => { + impl_read!(PolygonBuilder<3>, super::core::Polygon, Dimension::XYZ) } - GeometryType::Unknown => { + (GeometryType::MultiPoint, true) => { + impl_read!( + MultiPointBuilder<3>, + super::core::MultiPoint, + Dimension::XYZ + ) + } + (GeometryType::MultiLineString, true) => impl_read!( + MultiLineStringBuilder<3>, + super::core::MultiLineString, + Dimension::XYZ + ), + (GeometryType::MultiPolygon, true) => impl_read!( + MultiPolygonBuilder<3>, + super::core::MultiPolygon, + Dimension::XYZ + ), + (GeometryType::Unknown, true) => { let mut builder = - GeoTableBuilder::>::new_with_options(options); + GeoTableBuilder::>::new_with_options(options); selection.process_features(&mut builder)?; let table = builder.finish()?; - table.downcast(true) + // TODO: 3d downcasting not implemented + // table.downcast(true) + Ok(table) } // TODO: Parse into a GeometryCollection array and then downcast to a single-typed array if possible. geom_type => Err(GeoArrowError::NotYetImplemented(format!( @@ -116,6 +169,10 @@ mod test { use std::fs::File; use std::io::BufReader; + use arrow_schema::DataType; + + use crate::datatypes::NativeType; + use super::*; #[test] @@ -131,4 +188,54 @@ mod test { ); let _table = read_flatgeobuf(&mut filein, Default::default()).unwrap(); } + + #[test] + fn test_poly() { + let mut filein = BufReader::new(File::open("fixtures/flatgeobuf/poly00.fgb").unwrap()); + let table = read_flatgeobuf(&mut filein, Default::default()).unwrap(); + + let geom_col = table.geometry_column(None).unwrap(); + assert!(matches!(geom_col.data_type(), NativeType::Polygon(_, _))); + + let (batches, schema) = table.into_inner(); + assert_eq!(batches[0].num_rows(), 10); + assert!(matches!( + schema.field_with_name("AREA").unwrap().data_type(), + DataType::Float64 + )); + assert!(matches!( + schema.field_with_name("EAS_ID").unwrap().data_type(), + DataType::Int64 + )); + assert!(matches!( + schema.field_with_name("PRFEDEA").unwrap().data_type(), + DataType::Utf8 + )); + } + + #[ignore = "fails on JSON columns"] + #[test] + fn test_all_datatypes() { + let mut filein = + BufReader::new(File::open("fixtures/flatgeobuf/alldatatypes.fgb").unwrap()); + let table = read_flatgeobuf(&mut filein, Default::default()).unwrap(); + + let _geom_col = table.geometry_column(None).unwrap(); + // assert!(matches!(geom_col.data_type(), NativeType::Polygon(_, _))); + + // let (batches, schema) = table.into_inner(); + // assert_eq!(batches[0].num_rows(), 10); + // assert!(matches!( + // schema.field_with_name("AREA").unwrap().data_type(), + // DataType::Float64 + // )); + // assert!(matches!( + // schema.field_with_name("EAS_ID").unwrap().data_type(), + // DataType::Int64 + // )); + // assert!(matches!( + // schema.field_with_name("PRFEDEA").unwrap().data_type(), + // DataType::Utf8 + // )); + } } diff --git a/src/io/flatgeobuf/writer.rs b/src/io/flatgeobuf/writer.rs index 9d5f5bae3..298c2146e 100644 --- a/src/io/flatgeobuf/writer.rs +++ b/src/io/flatgeobuf/writer.rs @@ -3,6 +3,7 @@ use std::io::Write; use flatgeobuf::{FgbWriter, FgbWriterOptions}; use geozero::GeozeroDatasource; +use crate::datatypes::{Dimension, NativeType}; use crate::error::Result; use crate::io::stream::RecordBatchReader; use crate::schema::GeoSchemaExt; @@ -24,17 +25,22 @@ pub fn write_flatgeobuf_with_options>( stream: S, writer: W, name: &str, - options: FgbWriterOptions, + mut options: FgbWriterOptions, ) -> Result<()> { let mut stream = stream.into(); - let mut fgb = - FgbWriter::create_with_options(name, infer_flatgeobuf_geometry_type(&stream)?, options)?; + + let (geometry_type, has_z) = infer_flatgeobuf_geometry_type(&stream)?; + options.has_z = has_z; + + let mut fgb = FgbWriter::create_with_options(name, geometry_type, options)?; stream.process(&mut fgb)?; fgb.write(writer)?; Ok(()) } -fn infer_flatgeobuf_geometry_type(stream: &RecordBatchReader) -> Result { +fn infer_flatgeobuf_geometry_type( + stream: &RecordBatchReader, +) -> Result<(flatgeobuf::GeometryType, bool)> { let schema = stream.schema()?; let fields = &schema.fields; let geom_col_idxs = schema.as_ref().geometry_columns(); @@ -43,22 +49,46 @@ fn infer_flatgeobuf_geometry_type(stream: &RecordBatchReader) -> Result flatgeobuf::GeometryType::Point, - "geoarrow.linestring" => flatgeobuf::GeometryType::LineString, - "geoarrow.polygon" => flatgeobuf::GeometryType::Polygon, - "geoarrow.multipoint" => flatgeobuf::GeometryType::MultiPoint, - "geoarrow.multilinestring" => flatgeobuf::GeometryType::MultiLineString, - "geoarrow.multipolygon" => flatgeobuf::GeometryType::MultiPolygon, - "geoarrow.geometry" => flatgeobuf::GeometryType::Unknown, - "geoarrow.geometrycollection" => flatgeobuf::GeometryType::GeometryCollection, - _ => todo!(), - }; - Ok(geometry_type) - } else { - todo!() - } + let geo_data_type = NativeType::try_from(geometry_field.as_ref())?; + + use NativeType::*; + let (geometry_type, has_z) = match geo_data_type { + Point(_, dim) => ( + flatgeobuf::GeometryType::Point, + matches!(dim, Dimension::XYZ), + ), + LineString(_, dim) => ( + flatgeobuf::GeometryType::LineString, + matches!(dim, Dimension::XYZ), + ), + Polygon(_, dim) => ( + flatgeobuf::GeometryType::Polygon, + matches!(dim, Dimension::XYZ), + ), + MultiPoint(_, dim) => ( + flatgeobuf::GeometryType::MultiPoint, + matches!(dim, Dimension::XYZ), + ), + MultiLineString(_, dim) => ( + flatgeobuf::GeometryType::MultiLineString, + matches!(dim, Dimension::XYZ), + ), + MultiPolygon(_, dim) => ( + flatgeobuf::GeometryType::MultiPolygon, + matches!(dim, Dimension::XYZ), + ), + Mixed(_, dim) | Rect(dim) => ( + flatgeobuf::GeometryType::Unknown, + matches!(dim, Dimension::XYZ), + ), + GeometryCollection(_, dim) => ( + flatgeobuf::GeometryType::GeometryCollection, + matches!(dim, Dimension::XYZ), + ), + // TODO: how to know when WKB has 3d geometries? + // WKB | LargeWKB => (flatgeobuf::GeometryType::Unknown, false), + }; + Ok((geometry_type, has_z)) } #[cfg(test)] @@ -86,4 +116,23 @@ mod test { dbg!(new_table); // dbg!(output_buffer); } + + #[test] + fn test_write_z() { + let table = point::table_z(); + + let mut output_buffer = Vec::new(); + let writer = BufWriter::new(&mut output_buffer); + write_flatgeobuf(&table, writer, "name").unwrap(); + + let mut reader = Cursor::new(output_buffer); + let new_table = read_flatgeobuf(&mut reader, Default::default()).unwrap(); + + // TODO: it looks like it's getting read back in backwards row order! + let batch = &new_table.batches()[0]; + let arr = batch.column(0); + dbg!(arr); + dbg!(new_table); + // dbg!(output_buffer); + } } diff --git a/src/io/geozero/table/builder/anyvalue.rs b/src/io/geozero/table/builder/anyvalue.rs index b9ebbad6d..9e1b7b929 100644 --- a/src/io/geozero/table/builder/anyvalue.rs +++ b/src/io/geozero/table/builder/anyvalue.rs @@ -138,11 +138,12 @@ impl AnyBuilder { DataType::Float32 => Float32(Float32Builder::with_capacity(capacity)), DataType::Float64 => Float64(Float64Builder::with_capacity(capacity)), DataType::Utf8 => String(StringBuilder::with_capacity(capacity, 0)), + DataType::Binary => Binary(BinaryBuilder::with_capacity(capacity, 0)), DataType::Timestamp(_time_unit, tz) => DateTime(( TimestampMicrosecondBuilder::with_capacity(capacity), tz.clone(), )), - _ => todo!(), + _ => todo!("Unsupported type {data_type}"), } } diff --git a/src/io/geozero/table/builder/table.rs b/src/io/geozero/table/builder/table.rs index 1a9692662..63cab377d 100644 --- a/src/io/geozero/table/builder/table.rs +++ b/src/io/geozero/table/builder/table.rs @@ -5,11 +5,11 @@ use arrow_array::RecordBatch; use arrow_schema::Schema; use geozero::{FeatureProcessor, GeomProcessor, PropertyProcessor}; -use crate::algorithm::native::DowncastTable; use crate::array::metadata::ArrayMetadata; use crate::array::CoordType; use crate::chunked_array::ChunkedNativeArrayDyn; use crate::error::{GeoArrowError, Result}; +use crate::geo_traits::GeometryTrait; use crate::io::geozero::table::builder::properties::PropertiesBatchBuilder; use crate::table::Table; use crate::trait_::{GeometryArrayBuilder, NativeArray}; @@ -156,6 +156,10 @@ impl GeoTableBuilder { &mut self.prop_builder } + pub fn push_geometry(&mut self, value: Option<&impl GeometryTrait>) -> Result<()> { + self.geom_builder.push_geometry(value) + } + fn flush_batch(&mut self) -> geozero::error::Result<()> { let next_schema = self.prop_builder.schema(); let coord_type = self.geom_builder.coord_type(); @@ -215,7 +219,9 @@ impl GeoTableBuilder { let geom_field = geom_col.extension_field(); table.append_column(geom_field, geom_col.array_refs())?; - table.downcast(false) + Ok(table) + // TODO: 3d downcasting not yet supported + // table.downcast(false) } } diff --git a/src/test/point.rs b/src/test/point.rs index 8f48ae8a6..a2f2cb649 100644 --- a/src/test/point.rs +++ b/src/test/point.rs @@ -110,3 +110,28 @@ pub(crate) fn table() -> Table { Table::try_new(vec![batch], schema).unwrap() } + +pub(crate) fn table_z() -> Table { + let point_array = point_z_array(); + let u8_array = properties::u8_array(); + let string_array = properties::string_array(); + + let fields = vec![ + Arc::new(Field::new("u8", DataType::UInt8, true)), + Arc::new(Field::new("string", DataType::Utf8, true)), + point_array.extension_field(), + ]; + let schema = Arc::new(Schema::new(fields)); + + let batch = RecordBatch::try_new( + schema.clone(), + vec![ + Arc::new(u8_array), + Arc::new(string_array), + point_array.into_array_ref(), + ], + ) + .unwrap(); + + Table::try_new(vec![batch], schema).unwrap() +}