diff --git a/.github/actions/test/test.sh b/.github/actions/test/test.sh index 7403f681d8..86f05f4c41 100755 --- a/.github/actions/test/test.sh +++ b/.github/actions/test/test.sh @@ -10,6 +10,9 @@ function print_headline() { print_headline "Install cargo-llvm-cov" just backend install +print_headline "Run build gdalsource-process" +just backend build-gdalsource-process || exit 1 + print_headline "Run Tests & Generate Code Coverage" just backend test-coverage || exit 1 diff --git a/geoengine/Cargo.lock b/geoengine/Cargo.lock index 9ea201bd76..5ceecf1f17 100644 --- a/geoengine/Cargo.lock +++ b/geoengine/Cargo.lock @@ -405,6 +405,15 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -433,7 +442,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", - "anstyle-parse", + "anstyle-parse 0.2.7", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse 1.0.0", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -456,6 +480,15 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + [[package]] name = "anstyle-query" version = "1.1.5" @@ -1265,7 +1298,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -1324,13 +1357,23 @@ dependencies = [ "clap_derive", ] +[[package]] +name = "clap-verbosity-flag" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34c77f67047557f62582784fd7482884697731b2932c7d37ced54bce2312e1e2" +dependencies = [ + "clap", + "log", +] + [[package]] name = "clap_builder" version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "clap_lex", "strsim", @@ -1364,63 +1407,12 @@ dependencies = [ ] [[package]] -name = "codspeed" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c2eb3388ebe26b5a0ab6bf4969d9c4840143d7f6df07caa3cc851b0606cef6" -dependencies = [ - "anyhow", - "cc", - "colored", - "getrandom 0.2.17", - "glob", - "libc", - "nix", - "serde", - "serde_json", - "statrs", -] - -[[package]] -name = "codspeed-criterion-compat" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e270597a1d1e183f86d1cc9f94f0133654ee3daf201c17903ee29363555dd7" -dependencies = [ - "clap", - "codspeed", - "codspeed-criterion-compat-walltime", - "colored", - "futures", - "regex", - "tokio", -] - -[[package]] -name = "codspeed-criterion-compat-walltime" -version = "4.3.0" +name = "cobs" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c2613d2fac930fe34456be76f9124ee0800bb9db2e7fd2d6c65b9ebe98a292" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "codspeed", - "criterion-plot", - "futures", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "tokio", - "walkdir", + "thiserror 2.0.18", ] [[package]] @@ -1435,16 +1427,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "colored" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" -dependencies = [ - "lazy_static", - "windows-sys 0.59.0", -] - [[package]] name = "config" version = "0.15.19" @@ -1607,13 +1589,13 @@ dependencies = [ [[package]] name = "cql2" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b72642b4c3243fb843c3beb0ec9d0406eecfaa7e01d172e7cdfa437605c884" +checksum = "563cecfd7f529013d5297a18eda58a1186fe95340058d7538fb7f421b16b6871" dependencies = [ - "geo 0.31.0", + "geo", "geo-types", - "geojson", + "geojson 0.24.2", "geozero", "jiff", "json_dotpath", @@ -1640,14 +1622,39 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "page_size", + "plotters", + "regex", + "serde", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + [[package]] name = "criterion-plot" -version = "0.5.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" dependencies = [ "cast", - "itertools 0.10.5", + "itertools 0.13.0", ] [[package]] @@ -1699,6 +1706,16 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crs-bounds" +version = "0.9.2" + +[[package]] +name = "crs-definitions" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5229c5b63bdc8e24d60e5c4b6115fbb4d11a8978661b4ecc57d6068ddb073983" + [[package]] name = "crunchy" version = "0.2.4" @@ -2003,6 +2020,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -2030,12 +2068,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] -name = "earcutr" -version = "0.4.3" +name = "earcut" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01" +checksum = "88459a2a8e3a514b6e6de38cf3aaa9250a894cb098f74a932db77fcc8341b6d0" dependencies = [ - "itertools 0.11.0", "num-traits", ] @@ -2113,6 +2150,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -2122,6 +2171,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "anstream 1.0.0", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equator" version = "0.4.2" @@ -2327,11 +2399,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "float_eq" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a80e3145d8ad11ba0995949bbcf48b9df2be62772b3d351ef017dff6ecb853" + [[package]] name = "float_next_after" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" +checksum = "37007738a80ea34f969af54a3390dd72cacdef654974cfd449c9f6f72dbaac10" [[package]] name = "fluent-uri" @@ -2405,6 +2483,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures" version = "0.3.32" @@ -2517,6 +2601,15 @@ dependencies = [ "semver", ] +[[package]] +name = "ge" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0935f33e11f4ae284b7167a24ab870adf2b146cdbe0b093516c3d4a4fc42ba8" +dependencies = [ + "rand 0.3.23", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -2548,36 +2641,19 @@ dependencies = [ [[package]] name = "geo" -version = "0.31.0" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc1a1678e54befc9b4bcab6cd43b8e7f834ae8ea121118b0fd8c42747675b4a" +checksum = "30eb1fdc57c1e5cfd11826fe0caec4b9dc7901f3758263bb506228d88c8d9e9a" dependencies = [ - "earcutr", + "earcut", "float_next_after", "geo-types", "geographiclib-rs", "i_overlay", "log", "num-traits", - "robust", - "rstar 0.12.2", - "spade", -] - -[[package]] -name = "geo" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3901269ec6d4f6068d3f09e5f02f995bd076398dcd1dfec407cd230b02d11b" -dependencies = [ - "earcutr", - "float_next_after", - "geo-types", - "geographiclib-rs", - "i_overlay", - "log", - "num-traits", - "rand 0.8.5", + "rand 0.10.0", + "rand_pcg", "robust", "rstar 0.12.2", "sif-itree", @@ -2595,9 +2671,9 @@ dependencies = [ [[package]] name = "geo-types" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f8647af4005fa11da47cd56252c6ef030be8fa97bdbf355e7dfb6348f0a82c" +checksum = "94776032c45f950d30a13af6113c2ad5625316c9abfbccee4dd5a6695f8fe0f5" dependencies = [ "approx", "num-traits", @@ -2608,6 +2684,27 @@ dependencies = [ "rstar 0.8.4", "rstar 0.9.3", "serde", + "spade", +] + +[[package]] +name = "geodesy" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef2bd803c1d8af10b606c25679c045abc04f36256955edc26f5952018b6afb7" +dependencies = [ + "anyhow", + "byteorder", + "clap", + "clap-verbosity-flag", + "dirs", + "env_logger", + "float_eq", + "log", + "memmap2", + "thiserror 1.0.69", + "uuid", + "wasm-bindgen", ] [[package]] @@ -2633,15 +2730,19 @@ dependencies = [ "arrow-array", "arrow-ord", "arrow-schema", + "bytemuck", "bytes", "chrono", - "codspeed-criterion-compat", + "criterion", + "crs-bounds", + "crs-definitions", "fallible-iterator", "float-cmp", "futures", "gdal", - "geo 0.32.0", - "geojson", + "geo", + "geodesy", + "geojson 0.24.2", "image", "num", "num-traits", @@ -2657,6 +2758,7 @@ dependencies = [ "snafu", "strum", "tempfile", + "tracing", "uuid", "wkt 0.14.0", ] @@ -2665,7 +2767,7 @@ dependencies = [ name = "geoengine-expression" version = "0.9.2" dependencies = [ - "geo 0.32.0", + "geo", "geoengine-expression-deps", "indoc", "libloading", @@ -2686,7 +2788,7 @@ dependencies = [ name = "geoengine-expression-deps" version = "0.0.0" dependencies = [ - "geo 0.32.0", + "geo", "geo-types", ] @@ -2710,19 +2812,23 @@ dependencies = [ "async-stream", "async-trait", "bb8-postgres", + "bytemuck", "bytes", "chrono", - "codspeed-criterion-compat", + "criterion", "csv", "float-cmp", "futures", "gdal", "gdal-sys", - "geo 0.32.0", + "ge", + "geo", "geoengine-datatypes", "geoengine-expression", "httptest", + "ipc-channel", "itertools 0.14.0", + "lazy_static", "libloading", "lru", "lz4_flex", @@ -2740,7 +2846,9 @@ dependencies = [ "rayon", "rustc-hash", "serde", + "serde_bytes", "serde_json", + "serial_test", "snafu", "stream-cancel", "strum", @@ -2785,13 +2893,13 @@ dependencies = [ "futures", "futures-util", "gdal", - "geo 0.32.0", + "geo", "geoengine-api-client", "geoengine-datatypes", "geoengine-expression", "geoengine-macros", "geoengine-operators", - "geojson", + "geojson 0.24.2", "httptest", "itertools 0.14.0", "mime", @@ -2872,6 +2980,20 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "geojson" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c094bfc76ea34d02eee00833254945b70491d79a9c0b050abed6eaa799ffb" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror 2.0.18", + "tinyvec", +] + [[package]] name = "geozero" version = "0.14.0" @@ -2879,7 +3001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5f28f34864745eb2f123c990c6ffd92c1584bd39439b3f27ff2a0f4ea5b309b" dependencies = [ "geo-types", - "geojson", + "geojson 0.24.2", "log", "serde_json", "thiserror 1.0.69", @@ -2949,12 +3071,6 @@ dependencies = [ "weezl", ] -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "group" version = "0.13.0" @@ -3408,24 +3524,27 @@ dependencies = [ [[package]] name = "i_float" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010025c2c532c8d82e42d0b8bb5184afa449fa6f06c709ea9adcb16c49ae405b" +checksum = "813145bb0ad5b60f55cbbf3c74cdceda1c0a9d253b35c4cc36ae0df7887cb78f" dependencies = [ "libm", ] [[package]] name = "i_key_sort" -version = "0.6.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9190f86706ca38ac8add223b2aed8b1330002b5cdbbce28fb58b10914d38fc27" +checksum = "d73d122b937fca067feb0ad74f62388920272b27c356d4df2d0cfdd59e044cf0" +dependencies = [ + "rayon", +] [[package]] name = "i_overlay" -version = "4.0.7" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413183068e6e0289e18d7d0a1f661b81546e6918d5453a44570b9ab30cbed1b3" +checksum = "8dd314b4668e2b3a12508f2e125558c82a6c0a8636fa5107a900f79ce414e450" dependencies = [ "i_float", "i_key_sort", @@ -3436,18 +3555,18 @@ dependencies = [ [[package]] name = "i_shape" -version = "1.14.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea154b742f7d43dae2897fcd5ead86bc7b5eefcedd305a7ebf9f69d44d61082" +checksum = "bfa9eac533d7509a8ab87672b60ac610c17240f9ea4851d26227689fdfe349c8" dependencies = [ "i_float", ] [[package]] name = "i_tree" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e6d558e6d4c7b82bc51d9c771e7a927862a161a7d87bf2b0541450e0e20915" +checksum = "4804bdc1dc124eb7e1aa9e144ecc04096bcf787a10a15fa44af682b51f0f6cce" [[package]] name = "iana-time-zone" @@ -3461,7 +3580,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -3714,6 +3833,25 @@ dependencies = [ "rustversion", ] +[[package]] +name = "ipc-channel" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "347ef783e380784658248bdb0b87ca1293e3e3df3fc7dee061e129aa5f013d61" +dependencies = [ + "crossbeam-channel", + "libc", + "mio", + "postcard", + "rand 0.9.2", + "rustc-hash", + "serde_core", + "tempfile", + "thiserror 2.0.18", + "uuid", + "windows", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -3730,17 +3868,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is-terminal" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.61.2", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -3758,18 +3885,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -4032,7 +4159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -4232,6 +4359,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -4776,6 +4912,12 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "2.10.1" @@ -4884,6 +5026,16 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -4904,7 +5056,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -5150,6 +5302,34 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "png" version = "0.18.1" @@ -5190,6 +5370,18 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "postgres-derive" version = "0.4.7" @@ -5657,6 +5849,29 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -5710,6 +5925,21 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -5735,6 +5965,15 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.0", +] + [[package]] name = "rav1e" version = "0.8.1" @@ -5811,6 +6050,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "recursive" version = "0.1.1" @@ -5849,6 +6097,17 @@ dependencies = [ "bitflags 2.11.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -6459,6 +6718,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -6849,14 +7118,14 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stac" -version = "0.16.5" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e74a40494b22d1f2a373fc84147725234db1910c89cd038c0afc550f55d3ab" +checksum = "5bf191f5a998a139913f92ae953d40b5f2276c3320c081ec09337619ed214104" dependencies = [ "bytes", "chrono", "cql2", - "geojson", + "geojson 1.0.0", "indexmap 2.13.0", "log", "mime", @@ -6881,11 +7150,11 @@ dependencies = [ [[package]] name = "stac-extensions" -version = "0.1.5" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875ee11e4364dff329ecd38615afeea641bca24e4993559f3ebce3814236c05c" +checksum = "da8479723905c42a03e5d2a15dfa32bf06daf213d2028735f085d4f1a846a34b" dependencies = [ - "geojson", + "geojson 1.0.0", "indexmap 2.13.0", "serde", "serde_json", @@ -6911,16 +7180,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "statrs" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3fe7c28c6512e766b0874335db33c94ad7b8f9054228ae1c2abd47ce7d335e" -dependencies = [ - "approx", - "num-traits", -] - [[package]] name = "stream-cancel" version = "0.8.2" @@ -7196,6 +7455,7 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ + "serde", "tinyvec_macros", ] @@ -8278,6 +8538,41 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -8286,9 +8581,20 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] @@ -8313,21 +8619,46 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + [[package]] name = "windows-registry" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -8336,7 +8667,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -8345,7 +8685,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -8381,7 +8730,22 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -8406,7 +8770,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -8417,6 +8781,21 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -8429,6 +8808,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -8441,6 +8826,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -8465,6 +8856,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -8477,6 +8874,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -8489,6 +8892,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -8501,6 +8910,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/geoengine/Cargo.toml b/geoengine/Cargo.toml index 9d6d6b5fb9..c67ee45dec 100644 --- a/geoengine/Cargo.toml +++ b/geoengine/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["datatypes", "expression", "macros", "operators", "services"] +members = ["crs-bounds","datatypes", "expression", "macros", "operators", "services"] exclude = [ "expression/deps-workspace", # Buggy, cf. https://github.com/rust-lang/cargo/issues/6745 ".scripts", @@ -95,13 +95,14 @@ bb8-postgres = { version = "0.9", features = [ "with-chrono-0_4", "with-serde_json-1", ] } +bytemuck = "1.25.0" bytes = "1.11" # for postgres-types impls chrono = { version = "0.4", features = ["serde"] } clap = { version = "4.5", features = ["derive"] } clap_derive = "4.5" config = "0.15" convert_case = "0.11" -criterion = { package = "codspeed-criterion-compat", version = "4.3", features = ["async_tokio"], default-features = false } +criterion = { default-features = false, version = "0.8" } csv = "1.4" erased-serde = "0.4" fallible-iterator = "0.2" # only for postgres-protocol @@ -112,10 +113,13 @@ futures-util = "0.3" gdal = "0.19" gdal-sys = "0.12" # when changing geo version also adapt the Cargo.toml in the expression "deps-workspace"! -geo = "0.32.0" -geo-types = "0.7.18" # important for compatibility when linking expressions +geo = "0.33.1" +geo-types = "0.7.19" # important for compatibility when linking expressions geojson = { version = "0.24", features = ["geo-types"] } +geodesy = {version = "0.15"} +crs-definitions = { version = "0.4.0", features = ["proj4"] } httptest = "0.16" +ipc-channel = "0.22.0" image = "0.25" indoc = "2.0" itertools = "0.14" @@ -167,14 +171,15 @@ reqwest = { version = "0.12", features = [ ] } # has to match with `oauth` rustc-hash = { version = "2.1", default-features = false } serde = { version = "1.0", features = ["derive", "rc"] } +serde_bytes = "0.11" serde_json = "1.0" serde_urlencoded = "0.7" serde_with = "3.17" serial_test = "3.4" sha2 = "0.10" snafu = "0.9" -stac = "0.16" -stac-extensions = "0.1.5" +stac = "0.17" +stac-extensions = "0.1.11" stream-cancel = "0.8" strum = { version = "0.28", features = ["derive"] } strum_macros = "0.28" diff --git a/geoengine/Settings-default.toml b/geoengine/Settings-default.toml index 4cb1fd26e3..5067009e36 100644 --- a/geoengine/Settings-default.toml +++ b/geoengine/Settings-default.toml @@ -98,6 +98,11 @@ provider_defs_path = "./test_data/provider_defs" layer_defs_path = "./test_data/layer_defs" layer_collection_defs_path = "./test_data/layer_collection_defs" +[gdal_process_pool] +number_of_processes = 32 +max_active_processes = 16 +max_dataset_processes = 8 + [gdal] # TODO: find good default # Use 0 for `ALL_CPUS` option or a number >0 for a specific number of threads. diff --git a/geoengine/Settings-test.toml b/geoengine/Settings-test.toml index dedd81d4a3..f2c236960d 100644 --- a/geoengine/Settings-test.toml +++ b/geoengine/Settings-test.toml @@ -40,3 +40,8 @@ enabled = false size_in_mb = 1_000 # 1 GB # storage limit for collecting query results before insertion into the cache in landing_zone_ratio = 0.1 # 10% of total cache size + +[gdal_process_pool] +number_of_processes = 2 +max_active_processes = 2 +max_dataset_processes = 2 \ No newline at end of file diff --git a/geoengine/crs-bounds/Cargo.toml b/geoengine/crs-bounds/Cargo.toml new file mode 100644 index 0000000000..879912b1c6 --- /dev/null +++ b/geoengine/crs-bounds/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "crs-bounds" +description = "A library for static bounding boxes of coordinate reference systems (CRS). Constants are generated from Projs database." +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true +license-file.workspace = true +documentation.workspace = true +repository.workspace = true + +[dependencies] + +[lints] +workspace = true + +[features] +default = ["metadata"] +code = [] +name = [] +metadata = ["code", "name"] diff --git a/geoengine/crs-bounds/src/epsg/epsg_registry.rs b/geoengine/crs-bounds/src/epsg/epsg_registry.rs new file mode 100644 index 0000000000..f53a901491 --- /dev/null +++ b/geoengine/crs-bounds/src/epsg/epsg_registry.rs @@ -0,0 +1,5864 @@ +macro_rules! build_epsg_registry { + ($( + $name:ident | $code:literal | $string_name:literal | $crs_type:ident | $wgs84:tt | $native_id:ident $( ( $($native_tt:tt)* ) )? | + )*) => { + $( + pub const $name: super::EpsgBounds = super::EpsgBounds { + #[cfg(feature = "metadata")] + code: $code, + #[cfg(feature = "metadata")] + name: $string_name, + crs_type: super::CrsType::$crs_type, + wgs84_bounds: $wgs84, + native_bounds: $native_id $( ( $($native_tt)* ) )?, + }; + )* + + pub const fn get_epsg_bounds(code: u16) -> Option<&'static super::EpsgBounds> { + match code { + $( $code => Some(&$name), )* + _ => None, + } + } + } +} + +build_epsg_registry! { + EPSG_10158|10158|"S34J-IRF"|Geographic2d|[8.0, 54.67, 11.29, 57.8]|Some([8.0, 54.67, 11.29, 57.8])| + EPSG_10160|10160|"S34J reconstruction east-orientated"|Projected|[8.0, 54.67, 11.29, 57.8]|Some([-363228.355, 26316.583, -150968.133, 377309.466])| + EPSG_10175|10175|"DoPw22-IRF"|Geographic2d|[-4.51, 52.45, -3.8, 53.01]|Some([-4.51, 52.45, -3.8, 53.01])| + EPSG_10178|10178|"IGS20"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_10183|10183|"DoPw22 Grid"|Projected|[-4.51, 52.45, -3.8, 53.01]|Some([40384.288, 94446.977, 88655.154, 156825.059])| + EPSG_10185|10185|"ShAb07-IRF"|Geographic2d|[-4.16, 52.37, -2.6, 52.77]|Some([-4.16, 52.37, -2.6, 52.77])| + EPSG_10188|10188|"ShAb07 Grid"|Projected|[-4.16, 52.37, -2.6, 52.77]|Some([854.611, -1025.702, 107105.939, 43793.025])| + EPSG_10191|10191|"CNH22-IRF"|Geographic2d|[-4.71, 53.02, -2.28, 53.46]|Some([-4.71, 53.02, -2.28, 53.46])| + EPSG_10194|10194|"CNH22 Grid"|Projected|[-4.71, 53.02, -2.28, 53.46]|Some([131353.876, 469634.53, 294414.843, 519293.669])| + EPSG_10196|10196|"CWS13-IRF"|Geographic2d|[-3.16, 52.5, -2.65, 53.26]|Some([-3.16, 52.5, -2.65, 53.26])| + EPSG_10199|10199|"CWS13 Grid"|Projected|[-3.16, 52.5, -2.65, 53.26]|Some([182011.448, 100481.923, 216645.637, 185089.303])| + EPSG_10204|10204|"DIBA15-IRF"|Geographic2d|[-1.46, 51.57, -1.15, 52.11]|Some([-1.46, 51.57, -1.15, 52.11])| + EPSG_10207|10207|"DIBA15 Grid"|Projected|[-1.46, 51.57, -1.15, 52.11]|Some([500529.403, 293584.161, 522022.964, 353678.955])| + EPSG_10209|10209|"GWPBS22-IRF"|Geographic2d|[-4.26, 51.25, -0.1, 52.06]|Some([-4.26, 51.25, -0.1, 52.06])| + EPSG_10212|10212|"GWPBS22 Grid"|Projected|[-4.26, 51.25, -0.1, 52.06]|Some([56451.627, 148945.821, 346860.907, 242117.489])| + EPSG_10214|10214|"GWWAB22-IRF"|Geographic2d|[-3.6, 51.35, -3.12, 51.81]|Some([-3.6, 51.35, -3.12, 51.81])| + EPSG_10217|10217|"GWWAB22 Grid"|Projected|[-3.6, 51.35, -3.12, 51.81]|Some([102670.788, 160175.255, 136439.277, 211674.9])| + EPSG_10219|10219|"GWWWA22-IRF"|Geographic2d|[-5.16, 51.55, -3.6, 52.06]|Some([-5.16, 51.55, -3.6, 52.06])| + EPSG_10222|10222|"GWWWA22 Grid"|Projected|[-5.16, 51.55, -3.6, 52.06]|Some([-5215.259, 182749.663, 103697.952, 242022.502])| + EPSG_10224|10224|"MALS09-IRF"|Geographic2d|[-2.26, 51.45, -0.05, 52.91]|Some([-2.26, 51.45, -0.05, 52.91])| + EPSG_10227|10227|"MALS09 Grid"|Projected|[-2.26, 51.45, -0.05, 52.91]|Some([98099.968, 91240.246, 251729.258, 254272.581])| + EPSG_10229|10229|"OxWo08-IRF"|Geographic2d|[-2.31, 51.65, -1.15, 52.26]|Some([-2.31, 51.65, -1.15, 52.26])| + EPSG_10235|10235|"OxWo08 Grid"|Projected|[-2.31, 51.65, -1.15, 52.26]|Some([92572.361, 88493.476, 172858.341, 156541.337])| + EPSG_10237|10237|"SYC20-IRF"|Geographic2d|[-2.91, 52.65, -2.3, 53.16]|Some([-2.91, 52.65, -2.3, 53.16])| + EPSG_10240|10240|"SYC20 Grid"|Projected|[-2.91, 52.65, -2.3, 53.16]|Some([89114.006, 92803.344, 130398.07, 149603.31])| + EPSG_10249|10249|"S34S-IRF"|Geographic2d|[10.79, 54.51, 12.87, 56.79]|Some([10.79, 54.51, 12.87, 56.79])| + EPSG_10250|10250|"S34S reconstruction east-orientated"|Projected|[10.79, 54.51, 12.87, 56.79]|Some([-184657.913, 8585.684, -48406.753, 265148.665])| + EPSG_10252|10252|"S45B-IRF"|Geographic2d|[14.59, 54.94, 15.25, 55.38]|Some([14.59, 54.94, 15.25, 55.38])| + EPSG_10254|10254|"S45B reconstruction east-orientated"|Projected|[14.59, 54.94, 15.25, 55.38]|Some([-69180.999, 30883.835, -26879.885, 79928.626])| + EPSG_10256|10256|"GS-IRF"|Geographic2d|[8.0, 54.51, 12.87, 57.8]|Some([8.0, 54.51, 12.87, 57.8])| + EPSG_10258|10258|"GS reconstruction east-orientated"|Projected|[8.0, 54.51, 12.87, 57.8]|Some([-154002.691, -54548.099, 161415.514, 314433.586])| + EPSG_10260|10260|"GSB-IRF"|Geographic2d|[14.59, 54.94, 15.25, 55.38]|Some([14.59, 54.94, 15.25, 55.38])| + EPSG_10262|10262|"GSB reconstruction east-orientated"|Projected|[14.59, 54.94, 15.25, 55.38]|Some([-40477.219, -1064.626, 1820.941, 47968.034])| + EPSG_10265|10265|"KK-IRF"|Geographic2d|[12.23, 55.51, 12.73, 55.82]|Some([12.23, 55.51, 12.73, 55.82])| + EPSG_10266|10266|"KK reconstruction east-orientated"|Projected|[12.23, 55.51, 12.73, 55.82]|Some([116068.345, 58340.22, 148563.548, 93789.478])| + EPSG_10268|10268|"Ostenfeld-IRF"|Geographic2d|[8.37, 54.8, 10.16, 55.47]|Some([8.37, 54.8, 10.16, 55.47])| + EPSG_10270|10270|"Ostenfeld reconstruction"|Projected|[8.37, 54.8, 10.16, 55.47]|Some([-55493.787, 36897.586, 59609.085, 111867.676])| + EPSG_10272|10272|"SMITB20-IRF"|Geographic2d|[-4.11, 50.65, -3.6, 50.86]|Some([-4.11, 50.65, -3.6, 50.86])| + EPSG_10275|10275|"SMITB20 Grid"|Projected|[-4.11, 50.65, -3.6, 50.86]|Some([92305.313, 90965.054, 128374.776, 114358.409])| + EPSG_10277|10277|"RBEPP12-IRF"|Geographic2d|[-5.63, 50.05, -0.85, 51.71]|Some([-5.63, 50.05, -0.85, 51.71])| + EPSG_10280|10280|"RBEPP12 Grid"|Projected|[-5.63, 50.05, -0.85, 51.71]|Some([201944.462, 128775.958, 544252.949, 316136.671])| + EPSG_10284|10284|"ETRS89/DREF91/2016"|Geographic2d|[3.34, 47.27, 15.04, 55.92]|Some([3.34, 47.27, 15.04, 55.92])| + EPSG_10285|10285|"ETRS89/DREF91/2016 / 3-degree Gauss-Kruger zone 3"|Projected|[8.33, 53.36, 10.41, 53.99]|Some([3455393.394, 5914684.905, 3593871.499, 5985723.555])| + EPSG_10286|10286|"ETRS89/DREF91/2016 / UTM zone 31N (N-zE)"|Projected|[3.34, 50.97, 6.01, 55.92]|Some([31521248.793, 5646543.808, 31711326.613, 6201269.317])| + EPSG_10287|10287|"ETRS89/DREF91/2016 / UTM zone 31N (zE-N)"|Projected|[3.34, 50.97, 6.01, 55.92]|Some([31521248.793, 5646543.808, 31711326.613, 6201269.317])| + EPSG_10288|10288|"ETRS89/DREF91/2016 / UTM zone 32N (N-zE)"|Projected|[6.0, 47.27, 12.01, 55.47]|Some([32273082.89, 5235168.953, 32727673.446, 6151213.241])| + EPSG_10289|10289|"ETRS89/DREF91/2016 / UTM zone 32N (zE-N)"|Projected|[6.0, 47.27, 12.01, 55.47]|Some([32273082.89, 5235168.953, 32727673.446, 6151213.241])| + EPSG_10290|10290|"ETRS89/DREF91/2016 / UTM zone 33N (N-zE)"|Projected|[11.57, 47.46, 15.04, 55.03]|Some([33241491.904, 5256285.079, 33503014.825, 6103509.128])| + EPSG_10291|10291|"ETRS89/DREF91/2016 / UTM zone 33N (zE-N)"|Projected|[11.57, 47.46, 15.04, 55.03]|Some([33241491.904, 5256285.079, 33503014.825, 6103509.128])| + EPSG_10299|10299|"RGSH2020"|Geographic2d|[-8.67, 18.97, 11.99, 38.8]|Some([-8.67, 18.97, 11.99, 38.8])| + EPSG_10305|10305|"LKS-2020"|Geographic2d|[19.06, 55.67, 28.24, 58.09]|Some([19.06, 55.67, 28.24, 58.09])| + EPSG_10306|10306|"LKS-2020 / Latvia TM"|Projected|[19.06, 55.67, 28.24, 58.09]|Some([189423.143, 169353.349, 766599.759, 449393.558])| + EPSG_10307|10307|"RGNC91-93 (lon-lat)"|Geographic2d|[156.25, -26.45, 174.28, -14.83]|Some([156.25, -26.45, 174.28, -14.83])| + EPSG_10310|10310|"RGNC15"|Geographic2d|[156.25, -26.45, 174.28, -14.83]|Some([156.25, -26.45, 174.28, -14.83])| + EPSG_10312|10312|"RGNC15 (lon-lat)"|Geographic2d|[156.25, -26.45, 174.28, -14.83]|Some([156.25, -26.45, 174.28, -14.83])| + EPSG_10314|10314|"RGNC15 / Lambert New Caledonia 2015"|Projected|[163.54, -22.73, 168.19, -19.5]|Some([2141647.059, 2161815.207, 2629999.1, 2521442.55])| + EPSG_10315|10315|"RGNC15 / UTM zone 57S"|Projected|[156.25, -26.03, 162.01, -15.34]|Some([204728.62, 7117519.258, 823209.236, 8304066.981])| + EPSG_10316|10316|"RGNC15 / UTM zone 58S"|Projected|[162.0, -26.45, 168.0, -14.83]|Some([177095.101, 7070989.316, 822904.899, 8360476.621])| + EPSG_10317|10317|"RGNC15 / UTM zone 59S"|Projected|[168.0, -25.95, 174.28, -19.75]|Some([185580.248, 7125735.751, 843789.449, 7816181.82])| + EPSG_10328|10328|"BH_ETRS89"|Geographic2d|[15.74, 42.56, 19.62, 45.27]|Some([15.74, 42.56, 19.62, 45.27])| + EPSG_10329|10329|"BH_ETRS89 / TM"|Projected|[15.74, 42.56, 19.62, 45.27]|Some([314422.327, 4713371.404, 633023.25, 5016934.536])| + EPSG_10345|10345|"Hughes 1980"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_10346|10346|"NSIDC Authalic Sphere"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_10414|10414|"NAD83(CSRS)v8"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_10448|10448|"GDA94 / ALB94"|Projected|[117.55, -35.21, 118.22, -34.75]|Some([19478.155, 102040.094, 80827.064, 153124.743])| + EPSG_10449|10449|"GDA94 / BIO94"|Projected|[114.9, -22.2, 115.59, -20.21]|Some([23422.081, 144006.143, 95532.826, 364380.643])| + EPSG_10450|10450|"GDA94 / BRO94"|Projected|[122.08, -18.09, 122.62, -16.75]|Some([22987.925, 199027.25, 80566.319, 347354.297])| + EPSG_10451|10451|"GDA94 / BCG94"|Projected|[115.18, -33.75, 115.87, -33.4]|Some([26432.657, 163998.177, 90622.761, 202903.832])| + EPSG_10452|10452|"GDA94 / CARN94"|Projected|[113.33, -25.5, 114.0, -23.0]|Some([15484.011, 128520.461, 84174.244, 405467.611])| + EPSG_10453|10453|"GDA94 / COL94"|Projected|[115.73, -34.67, 116.4, -33.25]|Some([21051.241, 161844.483, 83489.111, 319455.354])| + EPSG_10454|10454|"GDA94 / ESP94"|Projected|[121.56, -34.5, 122.2, -33.33]|Some([19896.219, 130806.638, 79483.082, 260632.219])| + EPSG_10455|10455|"GDA94 / EXM94"|Projected|[113.75, -22.42, 114.25, -21.75]|Some([17243.117, 169651.396, 68964.465, 243875.992])| + EPSG_10456|10456|"GDA94 / GCG94"|Projected|[114.51, -29.1, 115.0, -28.48]|Some([42818.966, 129555.066, 90801.523, 198343.233])| + EPSG_10457|10457|"GDA94 / GOLD94"|Projected|[121.0, -32.25, 121.84, -28.75]|Some([11161.027, 130138.944, 93210.379, 518281.641])| + EPSG_10458|10458|"GDA94 / JCG94"|Projected|[114.83, -30.74, 115.34, -29.08]|Some([35070.83, 147786.135, 84726.645, 331853.657])| + EPSG_10459|10459|"GDA94 / KALB94"|Projected|[113.9, -28.5, 114.75, -27.16]|Some([13839.358, 446025.304, 98087.917, 594598.828])| + EPSG_10460|10460|"GDA94 / KAR94"|Projected|[116.58, -20.92, 117.25, -20.25]|Some([13083.282, 135742.53, 83085.706, 209959.741])| + EPSG_10461|10461|"GDA94 / KUN94"|Projected|[128.5, -16.75, 129.0, -14.75]|Some([23080.874, 147312.489, 76919.126, 368645.366])| + EPSG_10462|10462|"GDA94 / LCG94"|Projected|[115.15, -31.49, 115.62, -30.71]|Some([29244.998, 164664.302, 74267.397, 251171.761])| + EPSG_10463|10463|"GDA94 / MRCG94"|Projected|[114.96, -34.42, 115.24, -33.51]|Some([30798.094, 139709.235, 56813.574, 240667.962])| + EPSG_10464|10464|"GDA94 / PCG94"|Projected|[115.44, -33.42, 116.09, -31.33]|Some([14151.118, 100610.565, 76014.192, 332441.045])| + EPSG_10465|10465|"GDA94 / PHG94"|Projected|[118.25, -20.79, 118.97, -20.1]|Some([13396.48, 100126.166, 88695.171, 176560.222])| + EPSG_10468|10468|"COV23-IRF"|Geographic2d|[-1.85, 52.3, -1.3, 52.5]|Some([-1.85, 52.3, -1.3, 52.5])| + EPSG_10471|10471|"COV23 Grid"|Projected|[-1.85, 52.3, -1.3, 52.5]|Some([96422.629, 91067.558, 133942.479, 113364.84])| + EPSG_10475|10475|"BBT2000"|Geographic2d|[11.04, 46.45, 11.91, 47.33]|Some([11.04, 46.45, 11.91, 47.33])| + EPSG_10477|10477|"BBT2000 / BBT-TM"|Projected|[11.04, 46.45, 11.91, 47.33]|Some([-17536.925, 40988.97, 49315.779, 138944.996])| + EPSG_10481|10481|"NAD83 / TWDB GM"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([2727003.332, 17712959.067, 7062843.4, 21655958.31])| + EPSG_10516|10516|"NAD83(2011) / Adjusted Jackson (ftUS)"|Projected|[-91.17, 44.07, -90.31, 44.6]|Some([2973.651, 15183.551, 229018.589, 208863.826])| + EPSG_10571|10571|"ETRF2020"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_10592|10592|"WGS 84 / GLANCE Africa"|Projected|[-31.68, -43.89, 79.3, 38.8]|Some([-5566117.733, -5675627.135, 6317413.023, 4392458.062])| + EPSG_10594|10594|"WGS 84 / GLANCE Asia"|Projected|[31.45, -10.8, -168.43, 83.67]|Some([-7778834.474, -5941398.388, 9606856.811, 4880592.238])| + EPSG_10596|10596|"WGS 84 / GLANCE Europe"|Projected|[-42.52, 29.24, 51.73, 83.67]|Some([-5470880.646, -2837974.676, 3064548.905, 3516634.227])| + EPSG_10598|10598|"WGS 84 / GLANCE North America"|Projected|[-180.0, 7.98, 180.0, 90.0]|Some([-8881538.441, -4546274.25, 8891967.378, 11106265.888])| + EPSG_10601|10601|"WGS 84 / GLANCE Oceania"|Projected|[72.37, -60.56, -121.05, 21.57]|Some([-6696130.412, -7444667.493, 10120981.53, 3977334.425])| + EPSG_10603|10603|"WGS 84 / GLANCE South America"|Projected|[-124.82, -59.87, -14.58, 30.31]|Some([-6905459.877, -5867575.98, 4977436.819, 4884754.986])| + EPSG_10606|10606|"WGS 84 (G2296)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_10622|10622|"NAD83(2011) / San Francisco SFO-B18 (ftUS)"|Projected|[-122.43, 37.58, -122.33, 37.66]|Some([-14504.076, -20644.887, 24703.754, 18632.597])| + EPSG_10623|10623|"ECML14-IRF"|Geographic2d|[-3.45, 51.45, 0.05, 56.1]|Some([-3.45, 51.45, 0.05, 56.1])| + EPSG_10626|10626|"ECML14 Grid"|Projected|[-3.45, 51.45, 0.05, 56.1]|Some([-20579.386, 1690.987, 222719.778, 520781.591])| + EPSG_10628|10628|"WC05-IRF"|Geographic2d|[-4.33, 51.4, -0.04, 55.92]|Some([-4.33, 51.4, -0.04, 55.92])| + EPSG_10632|10632|"WC05 Grid"|Projected|[-4.33, 51.4, -0.04, 55.92]|Some([58196.931, 173844.294, 356732.824, 679060.711])| + EPSG_10665|10665|"SIRGAS 2000 / Porto Alegre TM"|Projected|[-51.3, -30.27, -51.01, -29.93]|Some([271033.901, 1649934.482, 299037.765, 1687662.752])| + EPSG_10671|10671|"RGM23"|Geographic2d|[43.68, -14.49, 46.7, -11.33]|Some([43.68, -14.49, 46.7, -11.33])| + EPSG_10673|10673|"RGM23 (lon-lat)"|Geographic2d|[43.68, -14.49, 46.7, -11.33]|Some([43.68, -14.49, 46.7, -11.33])| + EPSG_10674|10674|"RGM23 / UTM zone 38S"|Projected|[43.68, -14.49, 46.7, -11.33]|Some([355949.066, 8397400.8, 685530.186, 8747531.889])| + EPSG_10731|10731|"ETRS89/DREF91/2016 / UTM zone 31N"|Projected|[3.34, 50.97, 6.01, 55.92]|Some([521248.793, 5646543.808, 711326.613, 6201269.317])| + EPSG_10732|10732|"ETRS89/DREF91/2016 / UTM zone 32N"|Projected|[6.0, 47.27, 12.01, 55.47]|Some([273082.89, 5235168.953, 727673.446, 6151213.241])| + EPSG_10733|10733|"ETRS89/DREF91/2016 / UTM zone 33N"|Projected|[11.57, 47.46, 15.04, 55.03]|Some([241491.904, 5256285.079, 503014.825, 6103509.128])| + EPSG_11114|11114|"MAGNA-SIRGAS 2018 / Colombia Far West zone"|Projected|[-79.1, 1.23, -78.58, 2.48]|Some([1108719.72, 627793.716, 1166682.737, 766087.352])| + EPSG_11115|11115|"MAGNA-SIRGAS 2018 / Colombia West zone"|Projected|[-78.59, 0.03, -75.58, 10.21]|Some([831610.468, 495084.452, 1166720.89, 1621237.667])| + EPSG_11116|11116|"MAGNA-SIRGAS 2018 / Colombia Bogota zone"|Projected|[-75.59, -2.51, -72.58, 11.82]|Some([831610.822, 214126.751, 1166720.539, 1799385.556])| + EPSG_11117|11117|"MAGNA-SIRGAS 2018 / Colombia East Central zone"|Projected|[-72.59, -4.23, -69.58, 12.52]|Some([831611.873, 23865.991, 1166719.5, 1876846.867])| + EPSG_11118|11118|"MAGNA-SIRGAS 2018 / Colombia East zone"|Projected|[-69.59, -2.25, -66.87, 6.31]|Some([831610.472, 242886.613, 1134429.147, 1189762.013])| + EPSG_2000|2000|"Anguilla 1957 / British West Indies Grid"|Projected|[-63.22, 18.11, -62.92, 18.33]|Some([270804.301, 2002777.905, 302666.921, 2027303.139])| + EPSG_20002|20002|"MWC18 Grid"|Projected|[-3.15, 53.09, -2.1, 53.65]|Some([131777.621, 87809.2, 202124.743, 150300.707])| + EPSG_20004|20004|"Pulkovo 1995 / Gauss-Kruger zone 4"|Projected|[19.57, 54.32, 22.87, 55.32]|Some([4407078.563, 6021665.634, 4621806.593, 6134579.544])| + EPSG_20005|20005|"Pulkovo 1995 / Gauss-Kruger zone 5"|Projected|[26.61, 55.69, 30.0, 69.47]|Some([5475597.854, 6174169.652, 5688768.501, 7712859.806])| + EPSG_20006|20006|"Pulkovo 1995 / Gauss-Kruger zone 6"|Projected|[30.0, 50.34, 36.0, 70.02]|Some([6286571.495, 5578774.416, 6713669.567, 7774141.155])| + EPSG_20007|20007|"Pulkovo 1995 / Gauss-Kruger zone 7"|Projected|[36.0, 43.18, 42.01, 69.23]|Some([7256199.043, 4782814.756, 7744842.424, 7686109.75])| + EPSG_20008|20008|"Pulkovo 1995 / Gauss-Kruger zone 8"|Projected|[42.0, 41.19, 48.0, 80.91]|Some([8248412.074, 4561763.64, 8751801.579, 8988214.079])| + EPSG_20009|20009|"Pulkovo 1995 / Gauss-Kruger zone 9"|Projected|[48.0, 41.39, 54.0, 81.4]|Some([9249172.224, 4583968.22, 9751024.511, 9042848.809])| + EPSG_2001|2001|"Antigua 1943 / British West Indies Grid"|Projected|[-61.95, 16.94, -61.61, 17.22]|Some([405547.46, 1872662.165, 441754.249, 1903675.412])| + EPSG_20010|20010|"Pulkovo 1995 / Gauss-Kruger zone 10"|Projected|[54.0, 50.47, 60.0, 81.91]|Some([10287125.015, 5593200.007, 10713053.479, 9099715.968])| + EPSG_20011|20011|"Pulkovo 1995 / Gauss-Kruger zone 11"|Projected|[60.0, 50.66, 66.0, 81.77]|Some([11287969.906, 5614328.533, 11712187.439, 9084093.545])| + EPSG_20012|20012|"Pulkovo 1995 / Gauss-Kruger zone 12"|Projected|[66.0, 54.1, 72.0, 77.07]|Some([12303840.534, 5997105.171, 12696294.246, 8559974.898])| + EPSG_20013|20013|"Pulkovo 1995 / Gauss-Kruger zone 13"|Projected|[72.0, 53.17, 78.0, 79.71]|Some([13299462.038, 5893592.9, 13700648.159, 8854348.472])| + EPSG_20014|20014|"Pulkovo 1995 / Gauss-Kruger zone 14"|Projected|[78.0, 50.69, 84.0, 81.03]|Some([14288069.003, 5617650.359, 14712015.183, 9001547.007])| + EPSG_20015|20015|"Pulkovo 1995 / Gauss-Kruger zone 15"|Projected|[84.0, 49.07, 90.0, 81.27]|Some([15280836.423, 5437460.987, 15719220.874, 9028308.266])| + EPSG_20016|20016|"Pulkovo 1995 / Gauss-Kruger zone 16"|Projected|[90.0, 49.89, 96.0, 81.35]|Some([16284455.314, 5528658.105, 16715574.669, 9037227.52])| + EPSG_20017|20017|"Pulkovo 1995 / Gauss-Kruger zone 17"|Projected|[96.0, 49.73, 102.0, 81.32]|Some([17283729.146, 5510861.296, 17716273.034, 9033880.745])| + EPSG_20018|20018|"Pulkovo 1995 / Gauss-Kruger zone 18"|Projected|[102.0, 49.64, 108.0, 79.48]|Some([18283315.282, 5500851.549, 18716659.022, 8828687.74])| + EPSG_20019|20019|"Pulkovo 1995 / Gauss-Kruger zone 19"|Projected|[108.0, 49.14, 114.0, 76.81]|Some([19281089.45, 5445244.604, 19718857.157, 8530968.069])| + EPSG_2002|2002|"Dominica 1945 / British West Indies Grid"|Projected|[-61.55, 15.14, -61.2, 15.69]|Some([447253.926, 1673134.169, 484981.987, 1734074.761])| + EPSG_20020|20020|"Pulkovo 1995 / Gauss-Kruger zone 20"|Projected|[114.0, 49.51, 120.0, 75.96]|Some([20282711.075, 5486396.75, 20717208.456, 8436200.139])| + EPSG_20021|20021|"Pulkovo 1995 / Gauss-Kruger zone 21"|Projected|[120.0, 51.51, 126.0, 74.0]|Some([21291693.161, 5708876.884, 21708200.296, 8217692.742])| + EPSG_20022|20022|"Pulkovo 1995 / Gauss-Kruger zone 22"|Projected|[126.0, 42.25, 132.0, 73.61]|Some([22252346.86, 4679467.39, 22747520.469, 8174222.464])| + EPSG_20023|20023|"Pulkovo 1995 / Gauss-Kruger zone 23"|Projected|[132.0, 42.63, 138.0, 76.15]|Some([23253827.78, 4721683.193, 23746015.795, 8457401.206])| + EPSG_20024|20024|"Pulkovo 1995 / Gauss-Kruger zone 24"|Projected|[138.0, 45.84, 144.0, 76.27]|Some([24266855.775, 5078372.857, 24732965.973, 8470788.751])| + EPSG_20025|20025|"Pulkovo 1995 / Gauss-Kruger zone 25"|Projected|[144.0, 43.6, 150.0, 76.82]|Some([25257666.355, 4829457.316, 25742134.921, 8532121.18])| + EPSG_20026|20026|"Pulkovo 1995 / Gauss-Kruger zone 26"|Projected|[150.0, 45.77, 156.0, 76.26]|Some([26266543.731, 5070607.003, 26733239.622, 8469693.393])| + EPSG_20027|20027|"Pulkovo 1995 / Gauss-Kruger zone 27"|Projected|[156.0, 50.27, 162.0, 77.2]|Some([27286021.454, 5570973.351, 27713746.544, 8574512.53])| + EPSG_20028|20028|"Pulkovo 1995 / Gauss-Kruger zone 28"|Projected|[162.0, 54.47, 168.0, 70.03]|Some([28305401.414, 6038335.34, 28694353.711, 7775249.265])| + EPSG_20029|20029|"Pulkovo 1995 / Gauss-Kruger zone 29"|Projected|[168.0, 54.45, 174.0, 70.19]|Some([29305300.935, 6036119.536, 29694443.48, 7793092.753])| + EPSG_2003|2003|"Grenada 1953 / British West Indies Grid"|Projected|[-61.84, 11.94, -61.35, 12.57]|Some([417233.903, 1319691.302, 470769.746, 1389407.953])| + EPSG_20030|20030|"Pulkovo 1995 / Gauss-Kruger zone 30"|Projected|[174.0, 61.65, 180.0, 71.59]|Some([30340912.923, 6838054.256, 30658824.391, 7949139.888])| + EPSG_20031|20031|"Pulkovo 1995 / Gauss-Kruger zone 31"|Projected|[-180.0, 64.35, -174.0, 71.65]|Some([31354950.736, 7139013.854, 31644781.793, 7955840.475])| + EPSG_20032|20032|"Pulkovo 1995 / Gauss-Kruger zone 32"|Projected|[-174.0, 64.2, -168.97, 67.18]|Some([32354160.628, 7122304.172, 32598473.704, 7457718.368])| + EPSG_20033|20033|"MWC18-IRF"|Geographic2d|[-3.15, 53.09, -2.1, 53.65]|Some([-3.15, 53.09, -2.1, 53.65])| + EPSG_2004|2004|"Montserrat 1958 / British West Indies Grid"|Projected|[-62.29, 16.62, -62.08, 16.87]|Some([368751.084, 1836949.084, 391157.581, 1864622.694])| + EPSG_20041|20041|"SIRGAS-Chile 2021"|Geographic2d|[-113.21, -59.87, -65.72, -17.5]|Some([-113.21, -59.87, -65.72, -17.5])| + EPSG_20042|20042|"SIRGAS-Chile 2021 / UTM zone 12S"|Projected|[-113.21, -30.54, -108.0, -23.72]|Some([274696.813, 6617544.618, 805884.966, 7376771.981])| + EPSG_20046|20046|"MAGNA-SIRGAS 2018"|Geographic2d|[-84.77, -4.23, -66.87, 15.51]|Some([-84.77, -4.23, -66.87, 15.51])| + EPSG_20047|20047|"GDA2020 / BCSG2020"|Projected|[152.37, -29.36, 153.69, -24.64]|Some([86217.179, 49074.18, 219857.56, 572261.061])| + EPSG_20048|20048|"SIRGAS-Chile 2021 / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, -18.35]|Some([182927.612, 3415992.79, 818130.08, 7971091.108])| + EPSG_20049|20049|"SIRGAS-Chile 2021 / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, -17.5]|Some([181409.073, 3359262.29, 818590.927, 8065119.517])| + EPSG_2005|2005|"St. Kitts 1955 / British West Indies Grid"|Projected|[-62.92, 17.06, -62.5, 17.46]|Some([302015.896, 1885830.679, 346820.853, 1930244.599])| + EPSG_20050|20050|"NAD83(2011) / Amtrak NECCS21 (ft)"|Projected|[-78.51, 38.64, -70.48, 42.74]|Some([212012.333, 700900.743, 2506137.558, 2226434.38])| + EPSG_2006|2006|"St. Lucia 1955 / British West Indies Grid"|Projected|[-61.13, 13.66, -60.82, 14.16]|Some([493943.142, 1509820.279, 527672.049, 1565263.641])| + EPSG_2007|2007|"St. Vincent 45 / British West Indies Grid"|Projected|[-61.52, 12.54, -61.07, 13.44]|Some([451624.951, 1385887.305, 500697.544, 1485546.728])| + EPSG_2009|2009|"NAD27(CGQ77) / SCoPQ zone 3"|Projected|[-60.0, 50.2, -57.1, 52.01]|Some([197712.553, 5562319.026, 404748.548, 5764724.15])| + EPSG_2010|2010|"NAD27(CGQ77) / SCoPQ zone 4"|Projected|[-63.0, 47.16, -60.0, 52.01]|Some([191065.839, 5224293.42, 418534.161, 5764724.15])| + EPSG_2011|2011|"NAD27(CGQ77) / SCoPQ zone 5"|Projected|[-66.0, 47.95, -63.0, 60.42]|Some([192762.832, 5312117.883, 416837.168, 6700932.417])| + EPSG_2012|2012|"NAD27(CGQ77) / SCoPQ zone 6"|Projected|[-69.0, 47.31, -66.0, 59.0]|Some([191386.388, 5240968.005, 418213.612, 6542773.358])| + EPSG_2013|2013|"NAD27(CGQ77) / SCoPQ zone 7"|Projected|[-72.0, 45.01, -69.0, 61.8]|Some([186558.64, 4985339.826, 423041.36, 6854666.992])| + EPSG_20135|20135|"Adindan / UTM zone 35N"|Projected|[23.99, 4.21, 30.01, 22.01]|Some([165747.634, 465132.728, 834131.177, 2436785.642])| + EPSG_20136|20136|"Adindan / UTM zone 36N"|Projected|[29.99, 3.49, 36.0, 22.24]|Some([165450.671, 385547.131, 833280.493, 2462271.878])| + EPSG_20137|20137|"Adindan / UTM zone 37N"|Projected|[36.0, 3.4, 42.0, 22.01]|Some([166511.808, 375599.644, 833297.689, 2436771.277])| + EPSG_20138|20138|"Adindan / UTM zone 38N"|Projected|[42.0, 4.11, 47.99, 12.85]|Some([166767.27, 454080.002, 831903.606, 1422240.565])| + EPSG_2014|2014|"NAD27(CGQ77) / SCoPQ zone 8"|Projected|[-75.0, 44.99, -72.0, 62.53]|Some([186517.489, 4983117.431, 423082.511, 6936002.286])| + EPSG_2015|2015|"NAD27(CGQ77) / SCoPQ zone 9"|Projected|[-78.0, 45.37, -75.0, 62.62]|Some([187301.823, 5025344.296, 422298.177, 6946030.487])| + EPSG_2016|2016|"NAD27(CGQ77) / SCoPQ zone 10"|Projected|[-79.85, 46.23, -78.0, 62.45]|Some([277802.29, 5120921.09, 420503.952, 6927088.432])| + EPSG_2017|2017|"NAD27(76) / MTM zone 8"|Projected|[-75.0, 44.98, -74.35, 45.65]|Some([186496.919, 4982357.747, 238546.972, 5057555.17])| + EPSG_2018|2018|"NAD27(76) / MTM zone 9"|Projected|[-78.0, 43.63, -75.0, 46.25]|Some([183753.536, 4832013.071, 425846.464, 5124237.571])| + EPSG_2019|2019|"NAD27(76) / MTM zone 10"|Projected|[-81.0, 42.26, -77.99, 47.33]|Some([181038.607, 4679834.546, 429386.487, 5244297.192])| + EPSG_2020|2020|"NAD27(76) / MTM zone 11"|Projected|[-83.6, 41.67, -81.0, 46.0]|Some([213200.802, 4614309.507, 429708.775, 5096452.431])| + EPSG_2021|2021|"NAD27(76) / MTM zone 12"|Projected|[-82.5, 46.0, -79.5, 55.21]|Some([188613.61, 5095358.281, 420986.39, 6120808.007])| + EPSG_2022|2022|"NAD27(76) / MTM zone 13"|Projected|[-85.5, 46.0, -82.5, 55.59]|Some([188613.61, 5095358.281, 420986.39, 6163104.874])| + EPSG_2023|2023|"NAD27(76) / MTM zone 14"|Projected|[-88.5, 47.17, -85.5, 56.7]|Some([191087.185, 5225405.045, 418512.815, 6286670.454])| + EPSG_2024|2024|"NAD27(76) / MTM zone 15"|Projected|[-91.5, 47.97, -88.5, 56.9]|Some([192806.073, 5314341.446, 416793.927, 6308936.755])| + EPSG_20249|20249|"AGD66 / AMG zone 49"|Projected|[109.23, -37.84, 114.0, -17.19]|Some([311624.046, 5807560.942, 818988.071, 8099275.498])| + EPSG_2025|2025|"NAD27(76) / MTM zone 16"|Projected|[-94.5, 48.06, -91.5, 55.2]|Some([193000.831, 5324347.58, 416599.169, 6119694.965])| + EPSG_20250|20250|"AGD66 / AMG zone 50"|Projected|[114.0, -38.53, 120.0, -12.61]|Some([173902.614, 5730968.196, 825823.128, 8605836.555])| + EPSG_20251|20251|"AGD66 / AMG zone 51"|Projected|[120.0, -38.07, 126.01, -10.46]|Some([171409.204, 5781987.292, 829416.37, 8843568.776])| + EPSG_20252|20252|"AGD66 / AMG zone 52"|Projected|[125.99, -37.38, 132.0, -9.1]|Some([168971.009, 5858558.281, 829665.002, 8993931.081])| + EPSG_20253|20253|"AGD66 / AMG zone 53"|Projected|[132.0, -40.71, 138.01, -8.88]|Some([169875.679, 5488908.933, 830970.61, 9018251.402])| + EPSG_20254|20254|"AGD66 / AMG zone 54"|Projected|[138.0, -46.63, 144.01, -2.53]|Some([166344.21, 4831377.203, 834645.784, 9720354.562])| + EPSG_20255|20255|"AGD66 / AMG zone 55"|Projected|[144.0, -47.2, 150.01, -1.3]|Some([166105.785, 4768033.726, 835008.225, 9856310.197])| + EPSG_20256|20256|"AGD66 / AMG zone 56"|Projected|[150.0, -46.44, 156.0, -2.32]|Some([166292.667, 4852500.442, 833707.333, 9743568.003])| + EPSG_20257|20257|"AGD66 / AMG zone 57"|Projected|[156.0, -35.13, 162.01, -14.08]|Some([175908.264, 6108196.954, 824979.799, 8443248.341])| + EPSG_20258|20258|"AGD66 / AMG zone 58"|Projected|[162.0, -34.22, 163.2, -27.25]|Some([202849.639, 6209178.862, 334109.654, 6984397.106])| + EPSG_2026|2026|"NAD27(76) / MTM zone 17"|Projected|[-95.16, 48.69, -94.5, 53.24]|Some([360877.826, 5394735.488, 415228.108, 5901572.485])| + EPSG_2027|2027|"NAD27(76) / UTM zone 15N"|Projected|[-95.16, 48.03, -90.0, 56.2]|Some([338966.063, 5319418.515, 723653.084, 6232176.105])| + EPSG_2028|2028|"NAD27(76) / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 56.9]|Some([268160.788, 5106051.321, 731839.212, 6310050.36])| + EPSG_2029|2029|"NAD27(76) / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 55.37]|Some([250247.335, 4612924.732, 749752.665, 6139847.988])| + EPSG_2030|2030|"NAD27(76) / UTM zone 18N"|Projected|[-78.0, 43.63, -74.35, 46.25]|Some([257975.63, 4830564.166, 552437.489, 5125981.942])| + EPSG_2031|2031|"NAD27(CGQ77) / UTM zone 17N"|Projected|[-79.85, 46.23, -78.0, 62.45]|Some([559343.706, 5120027.168, 731335.153, 6927705.425])| + EPSG_2032|2032|"NAD27(CGQ77) / UTM zone 18N"|Projected|[-78.0, 44.99, -72.0, 62.62]|Some([263505.831, 4981622.346, 736494.169, 6946630.602])| + EPSG_2033|2033|"NAD27(CGQ77) / UTM zone 19N"|Projected|[-72.0, 45.01, -66.0, 61.8]|Some([263588.164, 4983844.075, 736411.836, 6855347.635])| + EPSG_2034|2034|"NAD27(CGQ77) / UTM zone 20N"|Projected|[-66.0, 47.16, -60.0, 60.42]|Some([272605.725, 5222725.975, 727394.275, 6701743.485])| + EPSG_20349|20349|"AGD84 / AMG zone 49"|Projected|[109.23, -37.84, 114.0, -17.19]|Some([311622.828, 5807560.31, 818986.822, 8099274.625])| + EPSG_2035|2035|"NAD27(CGQ77) / UTM zone 21N"|Projected|[-60.0, 50.2, -57.1, 52.01]|Some([285902.566, 5560653.906, 493136.242, 5766182.157])| + EPSG_20350|20350|"AGD84 / AMG zone 50"|Projected|[114.0, -38.53, 120.0, -12.61]|Some([173901.365, 5730967.569, 825821.851, 8605835.653])| + EPSG_20351|20351|"AGD84 / AMG zone 51"|Projected|[120.0, -38.07, 126.01, -10.46]|Some([171407.939, 5781986.652, 829415.081, 8843567.856])| + EPSG_20352|20352|"AGD84 / AMG zone 52"|Projected|[125.99, -37.05, 132.01, -9.37]|Some([169221.993, 5895180.342, 830511.703, 8964079.153])| + EPSG_20353|20353|"AGD84 / AMG zone 53"|Projected|[132.0, -36.14, 138.0, -25.99]|Some([199527.485, 5996182.643, 800214.924, 7125251.666])| + EPSG_20354|20354|"AGD84 / AMG zone 54"|Projected|[138.0, -38.13, 144.01, -9.86]|Some([170805.413, 5775302.526, 830046.811, 8909898.989])| + EPSG_20355|20355|"AGD84 / AMG zone 55"|Projected|[144.0, -29.01, 150.0, -14.01]|Some([175791.522, 6787009.688, 823977.3, 8450993.746])| + EPSG_20356|20356|"AGD84 / AMG zone 56"|Projected|[150.0, -29.19, 153.61, -22.0]|Some([190126.112, 6767047.11, 562859.452, 7566988.496])| + EPSG_2039|2039|"Israel 1993 / Israeli TM Grid"|Projected|[34.17, 29.45, 35.69, 33.28]|Some([119097.335, 373609.826, 266563.36, 798747.066])| + EPSG_2040|2040|"Locodjo 1965 / UTM zone 30N"|Projected|[-6.0, 4.92, -2.48, 10.46]|Some([167240.076, 543771.474, 557653.021, 1157730.025])| + EPSG_2041|2041|"Abidjan 1987 / UTM zone 30N"|Projected|[-6.0, 4.92, -2.48, 10.46]|Some([167198.495, 543355.19, 557605.81, 1157371.948])| + EPSG_2042|2042|"Locodjo 1965 / UTM zone 29N"|Projected|[-8.61, 4.29, -6.0, 10.74]|Some([542643.14, 474150.189, 833053.313, 1188726.145])| + EPSG_2043|2043|"Abidjan 1987 / UTM zone 29N"|Projected|[-8.61, 4.29, -6.0, 10.74]|Some([542609.878, 473727.008, 833015.265, 1188370.281])| + EPSG_20436|20436|"Ain el Abd / UTM zone 36N"|Projected|[34.51, 26.82, 36.0, 29.38]|Some([646661.977, 2967387.316, 798319.997, 3253826.574])| + EPSG_20437|20437|"Ain el Abd / UTM zone 37N"|Projected|[36.0, 16.59, 42.0, 32.16]|Some([179954.368, 1834190.807, 820232.386, 3562107.258])| + EPSG_20438|20438|"Ain el Abd / UTM zone 38N"|Projected|[42.0, 16.37, 48.01, 31.15]|Some([179564.9, 1809852.202, 821635.737, 3450114.437])| + EPSG_20439|20439|"Ain el Abd / UTM zone 39N"|Projected|[47.99, 17.94, 54.01, 30.04]|Some([181164.415, 1983534.206, 818910.331, 3327021.12])| + EPSG_2044|2044|"Hanoi 1972 / Gauss-Kruger zone 18"|Projected|[102.14, 8.33, 108.0, 23.4]|Some([18184831.357, 921165.776, 18830609.77, 2592073.854])| + EPSG_20440|20440|"Ain el Abd / UTM zone 40N"|Projected|[54.0, 19.66, 55.67, 22.77]|Some([185413.5, 2174387.686, 363469.993, 2521167.235])| + EPSG_2045|2045|"Hanoi 1972 / Gauss-Kruger zone 19"|Projected|[108.0, 10.43, 109.53, 21.56]|Some([19171376.706, 1153811.313, 19347725.999, 2388115.333])| + EPSG_2046|2046|"Hartebeesthoek94 / Lo15"|Projected|[14.35, -23.15, 14.6, -22.68]|Some([40963.792, 2509155.535, 66796.711, 2561297.787])| + EPSG_2047|2047|"Hartebeesthoek94 / Lo17"|Projected|[16.45, -33.1, 18.0, -28.03]|Some([-98337.421, 3101766.474, 54084.505, 3664283.476])| + EPSG_2048|2048|"Hartebeesthoek94 / Lo19"|Projected|[17.99, -34.88, 20.0, -28.38]|Some([-98017.403, 3140554.317, 98997.633, 3861745.602])| + EPSG_2049|2049|"Hartebeesthoek94 / Lo21"|Projected|[19.99, -34.88, 22.01, -24.76]|Some([-102160.215, 2739469.104, 102160.215, 3861745.602])| + EPSG_20499|20499|"Ain el Abd / Bahrain Grid"|Projected|[50.39, 25.53, 50.85, 26.34]|Some([438748.487, 2823629.615, 485069.209, 2913466.338])| + EPSG_2050|2050|"Hartebeesthoek94 / Lo23"|Projected|[22.0, -34.26, 24.01, -25.26]|Some([-101747.356, 2794855.633, 100739.89, 3792963.473])| + EPSG_2051|2051|"Hartebeesthoek94 / Lo25"|Projected|[24.0, -34.26, 26.01, -24.71]|Some([-102201.074, 2733930.666, 101189.114, 3792963.473])| + EPSG_2052|2052|"Hartebeesthoek94 / Lo27"|Projected|[26.0, -33.83, 28.0, -22.92]|Some([-102586.508, 2535678.103, 102586.508, 3745254.667])| + EPSG_2053|2053|"Hartebeesthoek94 / Lo29"|Projected|[27.99, -33.03, 30.01, -22.13]|Some([-104203.338, 2448195.527, 104203.338, 3656528.534])| + EPSG_20538|20538|"Afgooye / UTM zone 38N"|Projected|[42.0, 0.0, 48.0, 11.52]|Some([166108.238, -45.044, 834061.4, 1275137.847])| + EPSG_20539|20539|"Afgooye / UTM zone 39N"|Projected|[48.0, 4.44, 51.47, 12.03]|Some([167090.207, 490714.884, 552212.7, 1331601.255])| + EPSG_2054|2054|"Hartebeesthoek94 / Lo31"|Projected|[29.99, -31.38, 32.02, -22.22]|Some([-105168.154, 2458161.474, 104137.016, 3473555.714])| + EPSG_2055|2055|"Hartebeesthoek94 / Lo33"|Projected|[31.95, -28.94, 32.95, -26.8]|Some([4874.757, 2965470.883, 104404.866, 3203073.152])| + EPSG_2056|2056|"CH1903+ / LV95"|Projected|[5.96, 45.82, 10.49, 47.81]|Some([2485071.575, 1074261.453, 2837119.805, 1299941.786])| + EPSG_2057|2057|"Rassadiran / Nakhl e Taqi"|Projected|[52.5, 27.39, 52.71, 27.61]|Some([648126.953, 3030815.209, 668895.858, 3055195.617])| + EPSG_2058|2058|"ED50(ED77) / UTM zone 38N"|Projected|[44.03, 30.99, 48.0, 39.78]|Some([407386.343, 3428670.612, 786492.523, 4407825.394])| + EPSG_2059|2059|"ED50(ED77) / UTM zone 39N"|Projected|[48.0, 25.47, 54.0, 39.71]|Some([198325.758, 2817160.866, 801640.257, 4400054.535])| + EPSG_2060|2060|"ED50(ED77) / UTM zone 40N"|Projected|[54.0, 25.32, 60.0, 38.29]|Some([197937.359, 2800548.287, 801999.48, 4242428.082])| + EPSG_2061|2061|"ED50(ED77) / UTM zone 41N"|Projected|[60.0, 25.02, 63.34, 37.06]|Some([197181.588, 2767326.93, 534258.161, 4105915.397])| + EPSG_2062|2062|"Madrid 1870 (Madrid) / Spain LCC"|Projected|[-9.37, 35.95, 3.39, 43.82]|Some([86996.576, 150650.202, 1238677.013, 1046721.762])| + EPSG_2065|2065|"S-JTSK (Ferro) / Krovak"|Projected|[12.09, 47.73, 22.56, 51.06]|Some([911041.249, 159523.535, 1353306.148, 951555.938])| + EPSG_2066|2066|"Mount Dillon / Tobago Grid"|Projected|[-60.9, 11.08, -60.44, 11.41]|Some([71273.473, 85329.414, 321116.569, 266834.077])| + EPSG_2067|2067|"Naparima 1955 / UTM zone 20N"|Projected|[-61.98, 9.99, -60.85, 10.9]|Some([611302.373, 1104296.464, 735519.639, 1205572.585])| + EPSG_2068|2068|"ELD79 / Libya zone 5"|Projected|[9.31, 25.37, 10.01, 30.49]|Some([229839.692, 2806987.297, 301725.851, 3374724.032])| + EPSG_2069|2069|"ELD79 / Libya zone 6"|Projected|[10.0, 23.51, 12.0, 33.23]|Some([97947.589, 2600946.595, 302202.662, 3678529.339])| + EPSG_2070|2070|"ELD79 / Libya zone 7"|Projected|[12.0, 22.8, 14.0, 33.06]|Some([97403.227, 2522322.996, 302737.676, 3659674.39])| + EPSG_2071|2071|"ELD79 / Libya zone 8"|Projected|[14.0, 22.61, 16.0, 32.79]|Some([97256.732, 2501283.426, 302874.651, 3629730.242])| + EPSG_2072|2072|"ELD79 / Libya zone 9"|Projected|[16.0, 22.51, 18.01, 31.34]|Some([97177.762, 2490209.815, 303972.848, 3468954.822])| + EPSG_2073|2073|"ELD79 / Libya zone 10"|Projected|[18.0, 21.54, 20.0, 32.17]|Some([96469.751, 2382810.216, 303642.125, 3560975.045])| + EPSG_2074|2074|"ELD79 / Libya zone 11"|Projected|[20.0, 20.54, 22.0, 33.0]|Some([95770.911, 2272102.154, 304331.0, 3653015.688])| + EPSG_2075|2075|"ELD79 / Libya zone 12"|Projected|[22.0, 19.5, 24.0, 32.97]|Some([95077.838, 2156979.395, 305013.985, 3649687.756])| + EPSG_2076|2076|"ELD79 / Libya zone 13"|Projected|[24.0, 19.99, 25.21, 32.15]|Some([95392.661, 2211217.474, 222016.1, 3558754.619])| + EPSG_2077|2077|"ELD79 / UTM zone 32N"|Projected|[9.31, 23.51, 12.0, 33.23]|Some([528963.421, 2600201.468, 806455.908, 3680994.784])| + EPSG_2078|2078|"ELD79 / UTM zone 33N"|Projected|[12.0, 22.51, 18.01, 33.06]|Some([191419.689, 2489463.522, 809741.148, 3662160.156])| + EPSG_2079|2079|"ELD79 / UTM zone 34N"|Projected|[17.99, 19.5, 24.0, 33.0]|Some([184090.586, 2156332.808, 814960.841, 3655501.404])| + EPSG_20790|20790|"Lisbon (Lisbon) / Portuguese National Grid"|Projected|[-9.56, 36.95, -6.19, 42.16]|Some([72899.47, -1736.381, 373081.618, 578544.383])| + EPSG_20791|20791|"Lisbon (Lisbon) / Portuguese Grid"|Projected|[-9.56, 36.95, -6.19, 42.16]|Some([-127100.53, -301736.381, 173081.618, 278544.383])| + EPSG_2080|2080|"ELD79 / UTM zone 35N"|Projected|[24.0, 19.99, 25.21, 32.15]|Some([186087.074, 2211553.94, 331223.928, 3561192.579])| + EPSG_2081|2081|"Chos Malal 1914 / Argentina 2"|Projected|[-70.51, -43.41, -67.49, -31.91]|Some([2357162.618, 5192823.839, 2642837.382, 6470362.62])| + EPSG_2082|2082|"Pampa del Castillo / Argentina 2"|Projected|[-70.5, -50.34, -67.49, -42.49]|Some([2376674.926, 4422418.608, 2624147.258, 5296139.115])| + EPSG_20822|20822|"Aratu / UTM zone 22S"|Projected|[-53.38, -35.71, -47.99, -25.01]|Some([259738.561, 6044206.178, 803732.07, 7234113.257])| + EPSG_20823|20823|"Aratu / UTM zone 23S"|Projected|[-48.01, -33.5, -41.99, 0.0]|Some([164893.96, 6289387.723, 834994.266, 10000147.595])| + EPSG_20824|20824|"Aratu / UTM zone 24S"|Projected|[-42.01, -26.35, -36.0, 0.01]|Some([164782.299, 7082033.345, 833848.676, 10001254.424])| + EPSG_2083|2083|"Hito XVIII 1963 / Argentina 2"|Projected|[-68.64, -54.9, -67.5, -52.59]|Some([2523008.729, 3915027.203, 2601568.268, 4173100.962])| + EPSG_2084|2084|"Hito XVIII 1963 / UTM zone 19S"|Projected|[-68.62, -54.61, -66.0, -51.65]|Some([524456.899, 3944337.198, 707542.135, 4277701.925])| + EPSG_2087|2087|"ELD79 / TM 12 NE"|Projected|[9.31, 22.61, 15.0, 33.23]|Some([223537.412, 2500534.774, 808496.125, 3680992.592])| + EPSG_2088|2088|"Carthage / TM 11 NE"|Projected|[7.81, 33.22, 13.67, 38.41]|Some([202626.9, 3675221.653, 748781.939, 4255673.849])| + EPSG_2089|2089|"Yemen NGN96 / UTM zone 38N"|Projected|[42.0, 11.57, 48.01, 17.95]|Some([172775.816, 1279005.804, 828315.859, 1987236.648])| + EPSG_2090|2090|"Yemen NGN96 / UTM zone 39N"|Projected|[48.0, 9.45, 54.01, 19.0]|Some([170532.232, 1044603.543, 830566.955, 2103539.422])| + EPSG_20904|20904|"GSK-2011 / Gauss-Kruger zone 4"|Projected|[19.57, 54.32, 22.87, 55.32]|Some([4406950.384, 6021534.095, 4621677.405, 6134447.534])| + EPSG_20905|20905|"GSK-2011 / Gauss-Kruger zone 5"|Projected|[26.61, 55.69, 30.0, 69.47]|Some([5475472.475, 6174048.599, 5688642.395, 7712733.726])| + EPSG_20906|20906|"GSK-2011 / Gauss-Kruger zone 6"|Projected|[30.0, 50.34, 36.0, 70.02]|Some([6286451.463, 5578665.587, 6713548.537, 7774027.091])| + EPSG_20907|20907|"GSK-2011 / Gauss-Kruger zone 7"|Projected|[36.0, 43.18, 42.01, 69.23]|Some([7256085.155, 4782716.52, 7744727.943, 7686007.83])| + EPSG_20908|20908|"GSK-2011 / Gauss-Kruger zone 8"|Projected|[42.0, 41.19, 48.0, 80.91]|Some([8248305.221, 4561673.517, 8751694.779, 8988120.516])| + EPSG_20909|20909|"GSK-2011 / Gauss-Kruger zone 9"|Projected|[48.0, 41.39, 54.0, 81.4]|Some([9249073.502, 4583885.419, 9750926.498, 9042766.032])| + EPSG_20910|20910|"GSK-2011 / Gauss-Kruger zone 10"|Projected|[54.0, 50.47, 60.0, 81.91]|Some([10287035.296, 5593126.377, 10712964.704, 9099643.058])| + EPSG_20911|20911|"GSK-2011 / Gauss-Kruger zone 11"|Projected|[60.0, 50.66, 66.0, 81.77]|Some([11287890.576, 5614261.963, 11712109.424, 9084029.65])| + EPSG_20912|20912|"GSK-2011 / Gauss-Kruger zone 12"|Projected|[66.0, 54.1, 72.0, 77.07]|Some([12303772.461, 5997046.137, 12696227.539, 8559919.66])| + EPSG_20913|20913|"GSK-2011 / Gauss-Kruger zone 13"|Projected|[72.0, 53.17, 78.0, 79.71]|Some([13299406.092, 5893538.837, 13700593.908, 8854299.414])| + EPSG_20914|20914|"GSK-2011 / Gauss-Kruger zone 14"|Projected|[78.0, 50.69, 84.0, 81.03]|Some([14288025.835, 5617599.224, 14711974.165, 9001502.994])| + EPSG_20915|20915|"GSK-2011 / Gauss-Kruger zone 15"|Projected|[84.0, 49.07, 90.0, 81.27]|Some([15280806.528, 5437411.923, 15719193.472, 9028268.14])| + EPSG_20916|20916|"GSK-2011 / Gauss-Kruger zone 16"|Projected|[90.0, 49.89, 96.0, 81.35]|Some([16284439.065, 5528611.515, 16715560.935, 9037189.911])| + EPSG_20917|20917|"GSK-2011 / Gauss-Kruger zone 17"|Projected|[96.0, 49.73, 102.0, 81.32]|Some([17283726.765, 5510815.453, 17716273.235, 9033844.244])| + EPSG_20918|20918|"GSK-2011 / Gauss-Kruger zone 18"|Projected|[102.0, 49.64, 108.0, 79.48]|Some([18283326.841, 5500805.385, 18716673.159, 8828651.173])| + EPSG_20919|20919|"GSK-2011 / Gauss-Kruger zone 19"|Projected|[108.0, 49.14, 114.0, 76.81]|Some([19281114.87, 5445196.746, 19718885.13, 8530929.936])| + EPSG_20920|20920|"GSK-2011 / Gauss-Kruger zone 20"|Projected|[114.0, 49.51, 120.0, 75.96]|Some([20282750.124, 5486346.674, 20717249.876, 8436158.883])| + EPSG_20921|20921|"GSK-2011 / Gauss-Kruger zone 21"|Projected|[120.0, 51.51, 126.0, 74.0]|Some([21291745.447, 5708824.37, 21708254.553, 8217647.003])| + EPSG_20922|20922|"GSK-2011 / Gauss-Kruger zone 22"|Projected|[126.0, 42.25, 132.0, 73.61]|Some([22252411.963, 4679405.432, 22747588.037, 8174170.974])| + EPSG_20923|20923|"GSK-2011 / Gauss-Kruger zone 23"|Projected|[132.0, 42.63, 138.0, 76.15]|Some([23253904.979, 4721616.526, 23746095.021, 8457342.587])| + EPSG_20924|20924|"GSK-2011 / Gauss-Kruger zone 24"|Projected|[138.0, 45.84, 144.0, 76.27]|Some([24266944.199, 5078301.557, 24733055.801, 8470721.893])| + EPSG_20925|20925|"GSK-2011 / Gauss-Kruger zone 25"|Projected|[144.0, 43.6, 150.0, 76.82]|Some([25257765.226, 4829378.647, 25742234.774, 8532044.917])| + EPSG_20926|20926|"GSK-2011 / Gauss-Kruger zone 26"|Projected|[150.0, 45.77, 156.0, 76.26]|Some([26266651.864, 5070521.232, 26733348.136, 8469606.947])| + EPSG_20927|20927|"GSK-2011 / Gauss-Kruger zone 27"|Projected|[156.0, 50.27, 162.0, 77.2]|Some([27286137.549, 5570879.142, 27713862.451, 8574414.681])| + EPSG_20928|20928|"GSK-2011 / Gauss-Kruger zone 28"|Projected|[162.0, 54.47, 168.0, 70.03]|Some([28305524.172, 6038230.946, 28694475.828, 7775141.541])| + EPSG_20929|20929|"GSK-2011 / Gauss-Kruger zone 29"|Projected|[168.0, 54.45, 174.0, 70.19]|Some([29305429.275, 6036004.675, 29694570.725, 7792972.878])| + EPSG_2093|2093|"Hanoi 1972 / GK 106 NE"|Projected|[105.49, 9.03, 107.58, 11.04]|Some([443878.664, 998631.455, 673693.65, 1221409.664])| + EPSG_20930|20930|"GSK-2011 / Gauss-Kruger zone 30"|Projected|[174.0, 61.65, 180.0, 71.59]|Some([30341044.863, 6837925.413, 30658955.137, 7949006.869])| + EPSG_20931|20931|"GSK-2011 / Gauss-Kruger zone 31"|Projected|[-180.0, 64.35, -174.0, 71.65]|Some([31355085.142, 7138871.538, 31644914.858, 7955694.423])| + EPSG_20932|20932|"GSK-2011 / Gauss-Kruger zone 32"|Projected|[-174.0, 64.2, -168.97, 67.18]|Some([32354296.301, 7122149.425, 32598608.014, 7457561.705])| + EPSG_20934|20934|"Arc 1950 / UTM zone 34S"|Projected|[19.99, -26.88, 24.0, -10.86]|Some([389639.526, 7023617.67, 828041.432, 8799799.102])| + EPSG_20935|20935|"Arc 1950 / UTM zone 35S"|Projected|[24.0, -25.84, 30.0, -8.31]|Some([169511.344, 7138895.443, 830487.052, 9081718.695])| + EPSG_20936|20936|"Arc 1950 / UTM zone 36S"|Projected|[30.0, -22.42, 35.93, -8.19]|Some([169394.226, 7517897.353, 822864.896, 9094984.7])| + EPSG_2094|2094|"WGS 72BE / TM 106 NE"|Projected|[106.54, 7.99, 110.0, 11.15]|Some([558937.756, 883229.123, 941129.343, 1235516.446])| + EPSG_2095|2095|"Bissau / UTM zone 28N"|Projected|[-16.77, 10.87, -13.64, 12.69]|Some([306325.12, 1201582.381, 648450.446, 1403484.552])| + EPSG_2096|2096|"Korean 1985 / East Belt"|Projected|[128.0, 34.49, 129.65, 38.64]|Some([108353.549, 110208.823, 259910.427, 571208.969])| + EPSG_2097|2097|"Korean 1985 / Central Belt"|Projected|[126.0, 33.96, 128.0, 38.33]|Some([107761.284, 51418.711, 292621.854, 536799.894])| + EPSG_2098|2098|"Korean 1985 / West Belt"|Projected|[124.53, 33.99, 126.0, 38.04]|Some([156749.105, 54747.459, 292573.369, 504609.7])| + EPSG_2099|2099|"Qatar 1948 / Qatar Grid"|Projected|[50.69, 24.55, 51.68, 26.2]|Some([92767.188, 7796.4, 193068.996, 190907.912])| + EPSG_2100|2100|"GGRS87 / Greek Grid"|Projected|[19.57, 34.88, 28.3, 41.75]|Some([94874.708, 3859448.359, 892934.126, 4631226.435])| + EPSG_21004|21004|"GSK-2011 / Gauss-Kruger CM 21E"|Projected|[19.57, 54.32, 22.87, 55.32]|Some([406950.384, 6021534.095, 621677.405, 6134447.534])| + EPSG_21005|21005|"GSK-2011 / Gauss-Kruger CM 27E"|Projected|[26.61, 55.69, 30.0, 69.47]|Some([475472.475, 6174048.599, 688642.395, 7712733.726])| + EPSG_21006|21006|"GSK-2011 / Gauss-Kruger CM 33E"|Projected|[30.0, 50.34, 36.0, 70.02]|Some([286451.463, 5578665.587, 713548.537, 7774027.091])| + EPSG_21007|21007|"GSK-2011 / Gauss-Kruger CM 39E"|Projected|[36.0, 43.18, 42.01, 69.23]|Some([256085.155, 4782716.52, 744727.943, 7686007.83])| + EPSG_21008|21008|"GSK-2011 / Gauss-Kruger CM 45E"|Projected|[42.0, 41.19, 48.0, 80.91]|Some([248305.221, 4561673.517, 751694.779, 8988120.516])| + EPSG_21009|21009|"GSK-2011 / Gauss-Kruger CM 51E"|Projected|[48.0, 41.39, 54.0, 81.4]|Some([249073.502, 4583885.419, 750926.498, 9042766.032])| + EPSG_2101|2101|"Lake / Maracaibo Grid M1"|Projected|[-72.25, 10.0, -71.5, 11.0]|Some([-70652.718, -71119.937, 11580.375, 39565.77])| + EPSG_21010|21010|"GSK-2011 / Gauss-Kruger CM 57E"|Projected|[54.0, 50.47, 60.0, 81.91]|Some([287035.296, 5593126.377, 712964.704, 9099643.058])| + EPSG_21011|21011|"GSK-2011 / Gauss-Kruger CM 63E"|Projected|[60.0, 50.66, 66.0, 81.77]|Some([287890.576, 5614261.963, 712109.424, 9084029.65])| + EPSG_21012|21012|"GSK-2011 / Gauss-Kruger CM 69E"|Projected|[66.0, 54.1, 72.0, 77.07]|Some([303772.461, 5997046.137, 696227.539, 8559919.66])| + EPSG_21013|21013|"GSK-2011 / Gauss-Kruger CM 75E"|Projected|[72.0, 53.17, 78.0, 79.71]|Some([299406.092, 5893538.837, 700593.908, 8854299.414])| + EPSG_21014|21014|"GSK-2011 / Gauss-Kruger CM 81E"|Projected|[78.0, 50.69, 84.0, 81.03]|Some([288025.835, 5617599.224, 711974.165, 9001502.994])| + EPSG_21015|21015|"GSK-2011 / Gauss-Kruger CM 87E"|Projected|[84.0, 49.07, 90.0, 81.27]|Some([280806.528, 5437411.923, 719193.472, 9028268.14])| + EPSG_21016|21016|"GSK-2011 / Gauss-Kruger CM 93E"|Projected|[90.0, 49.89, 96.0, 81.35]|Some([284439.065, 5528611.515, 715560.935, 9037189.911])| + EPSG_21017|21017|"GSK-2011 / Gauss-Kruger CM 99E"|Projected|[96.0, 49.73, 102.0, 81.32]|Some([283726.765, 5510815.453, 716273.235, 9033844.244])| + EPSG_21018|21018|"GSK-2011 / Gauss-Kruger CM 105E"|Projected|[102.0, 49.64, 108.0, 79.48]|Some([283326.841, 5500805.385, 716673.159, 8828651.173])| + EPSG_21019|21019|"GSK-2011 / Gauss-Kruger CM 111E"|Projected|[108.0, 49.14, 114.0, 76.81]|Some([281114.87, 5445196.746, 718885.13, 8530929.936])| + EPSG_2102|2102|"Lake / Maracaibo Grid"|Projected|[-72.25, 10.0, -71.5, 11.0]|Some([129347.282, 128880.063, 211580.375, 239565.77])| + EPSG_21020|21020|"GSK-2011 / Gauss-Kruger CM 117E"|Projected|[114.0, 49.51, 120.0, 75.96]|Some([282750.124, 5486346.674, 717249.876, 8436158.883])| + EPSG_21021|21021|"GSK-2011 / Gauss-Kruger CM 123E"|Projected|[120.0, 51.51, 126.0, 74.0]|Some([291745.447, 5708824.37, 708254.553, 8217647.003])| + EPSG_21022|21022|"GSK-2011 / Gauss-Kruger CM 129E"|Projected|[126.0, 42.25, 132.0, 73.61]|Some([252411.963, 4679405.432, 747588.037, 8174170.974])| + EPSG_21023|21023|"GSK-2011 / Gauss-Kruger CM 135E"|Projected|[132.0, 42.63, 138.0, 76.15]|Some([253904.979, 4721616.526, 746095.021, 8457342.587])| + EPSG_21024|21024|"GSK-2011 / Gauss-Kruger CM 141E"|Projected|[138.0, 45.84, 144.0, 76.27]|Some([266944.199, 5078301.557, 733055.801, 8470721.893])| + EPSG_21025|21025|"GSK-2011 / Gauss-Kruger CM 147E"|Projected|[144.0, 43.6, 150.0, 76.82]|Some([257765.226, 4829378.647, 742234.774, 8532044.917])| + EPSG_21026|21026|"GSK-2011 / Gauss-Kruger CM 153E"|Projected|[150.0, 45.77, 156.0, 76.26]|Some([266651.864, 5070521.232, 733348.136, 8469606.947])| + EPSG_21027|21027|"GSK-2011 / Gauss-Kruger CM 159E"|Projected|[156.0, 50.27, 162.0, 77.2]|Some([286137.549, 5570879.142, 713862.451, 8574414.681])| + EPSG_21028|21028|"GSK-2011 / Gauss-Kruger CM 165E"|Projected|[162.0, 54.47, 168.0, 70.03]|Some([305524.172, 6038230.946, 694475.828, 7775141.541])| + EPSG_21029|21029|"GSK-2011 / Gauss-Kruger CM 171E"|Projected|[168.0, 54.45, 174.0, 70.19]|Some([305429.275, 6036004.675, 694570.725, 7792972.878])| + EPSG_2103|2103|"Lake / Maracaibo Grid M3"|Projected|[-72.25, 10.0, -71.5, 11.0]|Some([429347.282, 428880.063, 511580.375, 539565.77])| + EPSG_21030|21030|"GSK-2011 / Gauss-Kruger CM 177E"|Projected|[174.0, 61.65, 180.0, 71.59]|Some([341044.863, 6837925.413, 658955.137, 7949006.869])| + EPSG_21031|21031|"GSK-2011 / Gauss-Kruger CM 177W"|Projected|[-180.0, 64.35, -174.0, 71.65]|Some([355085.142, 7138871.538, 644914.858, 7955694.423])| + EPSG_21032|21032|"GSK-2011 / Gauss-Kruger CM 171W"|Projected|[-174.0, 64.2, -168.97, 67.18]|Some([354296.301, 7122149.425, 598608.014, 7457561.705])| + EPSG_21035|21035|"Arc 1960 / UTM zone 35S"|Projected|[29.34, -6.81, 30.0, 0.0]|Some([758569.07, 9246532.335, 833909.569, 10000302.295])| + EPSG_21036|21036|"Arc 1960 / UTM zone 36S"|Projected|[29.99, -11.61, 36.0, 0.01]|Some([164826.397, 8715141.936, 833893.685, 10001409.131])| + EPSG_21037|21037|"Arc 1960 / UTM zone 37S"|Projected|[36.0, -11.75, 41.6, 0.0]|Some([165924.801, 8699651.448, 789317.212, 10000299.293])| + EPSG_2104|2104|"Lake / Maracaibo La Rosa Grid"|Projected|[-71.5, 10.0, -71.17, 10.51]|Some([-5481.98, -41573.056, 30718.942, 14868.836])| + EPSG_2105|2105|"NZGD2000 / Mount Eden 2000"|Projected|[171.99, -39.01, 176.12, -34.1]|Some([143998.08, 559909.377, 525103.263, 1108378.158])| + EPSG_2106|2106|"NZGD2000 / Bay of Plenty 2000"|Projected|[175.75, -39.13, 177.23, -37.22]|Some([336441.42, 647768.691, 467799.172, 860055.956])| + EPSG_2107|2107|"NZGD2000 / Poverty Bay 2000"|Projected|[176.73, -39.04, 178.63, -37.49]|Some([297804.048, 753232.723, 465837.093, 925920.462])| + EPSG_2108|2108|"NZGD2000 / Hawkes Bay 2000"|Projected|[175.8, -40.57, 178.07, -38.87]|Some([324183.293, 697001.458, 521187.763, 886687.913])| + EPSG_2109|2109|"NZGD2000 / Taranaki 2000"|Projected|[173.68, -39.78, 175.44, -38.4]|Some([352149.668, 727748.073, 505893.362, 881654.615])| + EPSG_21095|21095|"Arc 1960 / UTM zone 35N"|Projected|[29.71, 0.0, 30.0, 0.86]|Some([801565.562, 302.218, 833909.569, 95488.996])| + EPSG_21096|21096|"Arc 1960 / UTM zone 36N"|Projected|[29.99, 0.0, 36.0, 4.63]|Some([164826.39, 298.925, 833893.693, 512773.243])| + EPSG_21097|21097|"Arc 1960 / UTM zone 37N"|Projected|[36.0, 0.0, 41.91, 4.49]|Some([165924.801, 298.88, 823852.622, 497270.478])| + EPSG_2110|2110|"NZGD2000 / Tuhirangi 2000"|Projected|[174.88, -39.55, 176.33, -38.87]|Some([334043.215, 795529.821, 459881.752, 871298.87])| + EPSG_2111|2111|"NZGD2000 / Wanganui 2000"|Projected|[174.4, -40.97, 176.27, -39.46]|Some([306357.906, 718581.91, 467296.657, 886820.294])| + EPSG_2112|2112|"NZGD2000 / Wairarapa 2000"|Projected|[175.01, -41.67, 176.55, -40.29]|Some([345815.843, 716897.599, 476765.089, 870545.052])| + EPSG_2113|2113|"NZGD2000 / Wellington 2000"|Projected|[174.52, -41.5, 175.36, -40.91]|Some([378399.347, 777746.62, 449169.082, 843435.206])| + EPSG_2114|2114|"NZGD2000 / Collingwood 2000"|Projected|[172.16, -41.22, 173.13, -40.44]|Some([356564.883, 743760.865, 438862.983, 830506.721])| + EPSG_21148|21148|"Batavia / UTM zone 48S"|Projected|[105.06, -7.79, 108.0, -4.07]|Some([506427.485, 9137790.43, 832950.124, 9550183.663])| + EPSG_21149|21149|"Batavia / UTM zone 49S"|Projected|[108.0, -8.67, 114.0, -4.27]|Some([166831.08, 9040372.461, 832949.865, 9528075.512])| + EPSG_2115|2115|"NZGD2000 / Nelson 2000"|Projected|[172.4, -42.18, 174.08, -40.66]|Some([323960.556, 699030.346, 466032.297, 868236.064])| + EPSG_21150|21150|"Batavia / UTM zone 50S"|Projected|[114.0, -8.91, 117.01, -5.33]|Some([167429.71, 9013802.855, 501107.842, 9410914.334])| + EPSG_2116|2116|"NZGD2000 / Karamea 2000"|Projected|[171.96, -41.49, 172.7, -40.75]|Some([387425.991, 777588.162, 449920.815, 859938.469])| + EPSG_2117|2117|"NZGD2000 / Buller 2000"|Projected|[171.27, -42.19, 172.41, -41.42]|Some([373991.455, 757521.226, 469294.458, 843377.348])| + EPSG_2118|2118|"NZGD2000 / Grey 2000"|Projected|[171.15, -42.74, 172.75, -41.5]|Some([366624.692, 754158.087, 500219.504, 892590.243])| + EPSG_2119|2119|"NZGD2000 / Amuri 2000"|Projected|[171.88, -42.95, 173.55, -42.09]|Some([306510.051, 770373.73, 444676.382, 866524.954])| + EPSG_2120|2120|"NZGD2000 / Marlborough 2000"|Projected|[172.95, -42.65, 174.46, -40.85]|Some([328158.854, 676848.31, 455491.133, 877123.413])| + EPSG_21207|21207|"GSK-2011 / GSK 3GK zone 7"|Projected|[19.57, 54.32, 22.5, 55.32]|Some([7156950.384, 6021534.66, 7347604.168, 6133879.395])| + EPSG_21208|21208|"GSK-2011 / GSK 3GK zone 8"|Projected|[22.5, 54.34, 22.87, 55.07]|Some([8152443.186, 6024349.188, 8177814.197, 6106050.936])| + EPSG_21209|21209|"GSK-2011 / GSK 3GK zone 9"|Projected|[26.61, 56.05, 28.51, 68.93]|Some([9225698.317, 6214129.668, 9344087.314, 7650361.313])| + EPSG_2121|2121|"NZGD2000 / Hokitika 2000"|Projected|[170.39, -43.23, 171.89, -42.41]|Some([351455.879, 761393.698, 474931.438, 852889.172])| + EPSG_21210|21210|"GSK-2011 / GSK 3GK zone 10"|Projected|[28.5, 52.85, 31.5, 69.85]|Some([10148948.613, 5857927.174, 10351051.387, 7752954.876])| + EPSG_21211|21211|"GSK-2011 / GSK 3GK zone 11"|Projected|[31.5, 51.24, 34.5, 70.02]|Some([11145250.218, 5678785.402, 11354749.782, 7771915.31])| + EPSG_21212|21212|"GSK-2011 / GSK 3GK zone 12"|Projected|[34.5, 44.61, 37.51, 69.39]|Some([12130929.978, 4941604.086, 12369863.825, 7701661.56])| + EPSG_21213|21213|"GSK-2011 / GSK 3GK zone 13"|Projected|[37.5, 43.33, 40.5, 68.8]|Some([13128344.324, 4799381.163, 13371655.676, 7635853.999])| + EPSG_21214|21214|"GSK-2011 / GSK 3GK zone 14"|Projected|[40.5, 42.87, 43.5, 68.74]|Some([14127429.951, 4748277.612, 14372570.049, 7629162.926])| + EPSG_21215|21215|"GSK-2011 / GSK 3GK zone 15"|Projected|[43.5, 41.89, 46.5, 80.8]|Some([15125508.496, 4639418.564, 15374491.504, 8974815.264])| + EPSG_21216|21216|"GSK-2011 / GSK 3GK zone 16"|Projected|[46.5, 41.19, 49.5, 80.91]|Some([16124158.402, 4561673.517, 16375841.598, 8987094.467])| + EPSG_21217|21217|"GSK-2011 / GSK 3GK zone 17"|Projected|[49.5, 42.36, 52.5, 81.22]|Some([17126425.477, 4691624.144, 17373574.523, 9021699.865])| + EPSG_21218|21218|"GSK-2011 / GSK 3GK zone 18"|Projected|[52.5, 50.52, 55.5, 81.41]|Some([18143623.11, 5598688.306, 18356376.89, 9042909.889])| + EPSG_21219|21219|"GSK-2011 / GSK 3GK zone 19"|Projected|[55.5, 50.53, 58.5, 81.89]|Some([19143645.593, 5599800.698, 19356354.407, 9096493.97])| + EPSG_2122|2122|"NZGD2000 / Okarito 2000"|Projected|[169.21, -43.85, 170.89, -43.0]|Some([314313.689, 717247.627, 451302.933, 812220.235])| + EPSG_21220|21220|"GSK-2011 / GSK 3GK zone 20"|Projected|[58.5, 50.47, 61.5, 81.91]|Some([20143510.743, 5593126.377, 20356489.257, 9098726.667])| + EPSG_21221|21221|"GSK-2011 / GSK 3GK zone 21"|Projected|[61.5, 51.03, 64.5, 81.77]|Some([21144773.909, 5655422.723, 21355226.091, 9083097.837])| + EPSG_21222|21222|"GSK-2011 / GSK 3GK zone 22"|Projected|[64.5, 54.31, 67.5, 81.25]|Some([22152372.16, 6020420.986, 22347627.84, 9025048.803])| + EPSG_21223|21223|"GSK-2011 / GSK 3GK zone 23"|Projected|[67.5, 54.85, 70.5, 77.07]|Some([23153654.745, 6080531.499, 23346345.255, 8558485.326])| + EPSG_21224|21224|"GSK-2011 / GSK 3GK zone 24"|Projected|[70.5, 53.43, 73.5, 73.57]|Some([24150300.843, 5922474.731, 24349699.157, 8167927.777])| + EPSG_21225|21225|"GSK-2011 / GSK 3GK zone 25"|Projected|[73.5, 53.47, 76.5, 79.71]|Some([25150394.482, 5926926.52, 25349605.518, 8853143.441])| + EPSG_21226|21226|"GSK-2011 / GSK 3GK zone 26"|Projected|[76.5, 51.49, 79.5, 81.03]|Some([26145819.102, 5706599.213, 26354180.898, 9000490.041])| + EPSG_21227|21227|"GSK-2011 / GSK 3GK zone 27"|Projected|[79.5, 50.7, 82.5, 81.03]|Some([27144028.308, 5618711.648, 27355971.692, 9000490.041])| + EPSG_21228|21228|"GSK-2011 / GSK 3GK zone 28"|Projected|[82.5, 49.58, 85.5, 77.56]|Some([28141524.368, 5494132.093, 28358475.632, 8613171.762])| + EPSG_21229|21229|"GSK-2011 / GSK 3GK zone 29"|Projected|[85.5, 49.07, 88.5, 77.16]|Some([29140397.972, 5437411.923, 29359602.028, 8568529.632])| + EPSG_2123|2123|"NZGD2000 / Jacksons Bay 2000"|Projected|[168.02, -44.4, 170.01, -43.67]|Some([352730.447, 752125.61, 513223.351, 834196.763])| + EPSG_21230|21230|"GSK-2011 / GSK 3GK zone 30"|Projected|[88.5, 49.44, 91.5, 81.28]|Some([30141214.295, 5478561.349, 30358785.705, 9028397.746])| + EPSG_21231|21231|"GSK-2011 / GSK 3GK zone 31"|Projected|[91.5, 50.16, 94.5, 81.26]|Some([31142815.894, 5558643.49, 31357184.106, 9026165.117])| + EPSG_21232|21232|"GSK-2011 / GSK 3GK zone 32"|Projected|[94.5, 49.73, 97.5, 81.35]|Some([32141857.314, 5510815.453, 32358142.686, 9036211.965])| + EPSG_21233|21233|"GSK-2011 / GSK 3GK zone 33"|Projected|[97.5, 49.79, 100.5, 80.9]|Some([33141990.702, 5517488.918, 33358009.298, 8985978.173])| + EPSG_21234|21234|"GSK-2011 / GSK 3GK zone 34"|Projected|[100.5, 50.17, 103.5, 79.71]|Some([34142838.259, 5559755.812, 34357161.741, 8853143.441])| + EPSG_21235|21235|"GSK-2011 / GSK 3GK zone 35"|Projected|[103.5, 50.13, 106.5, 79.21]|Some([35142748.818, 5555306.535, 35357251.182, 8797333.196])| + EPSG_21236|21236|"GSK-2011 / GSK 3GK zone 36"|Projected|[106.5, 49.25, 109.5, 78.4]|Some([36140794.529, 5457430.232, 36359205.471, 8706924.212])| + EPSG_21237|21237|"GSK-2011 / GSK 3GK zone 37"|Projected|[109.5, 49.14, 112.5, 76.81]|Some([37140552.059, 5445196.746, 37359447.941, 8529468.81])| + EPSG_21238|21238|"GSK-2011 / GSK 3GK zone 38"|Projected|[112.5, 49.49, 115.5, 76.7]|Some([38141324.96, 5484122.286, 38358675.04, 8517192.757])| + EPSG_21239|21239|"GSK-2011 / GSK 3GK zone 39"|Projected|[115.5, 49.51, 118.5, 74.43]|Some([39141369.25, 5486346.674, 39358630.75, 8263882.952])| + EPSG_2124|2124|"NZGD2000 / Mount Pleasant 2000"|Projected|[171.11, -43.96, 173.38, -42.69]|Some([267490.448, 757680.499, 453517.798, 900047.265])| + EPSG_21240|21240|"GSK-2011 / GSK 3GK zone 40"|Projected|[118.5, 49.87, 121.5, 73.63]|Some([40142168.738, 5526386.98, 40357831.262, 8174622.089])| + EPSG_21241|21241|"GSK-2011 / GSK 3GK zone 41"|Projected|[121.5, 53.18, 124.5, 74.0]|Some([41149716.712, 5894651.733, 41350283.288, 8215904.466])| + EPSG_21242|21242|"GSK-2011 / GSK 3GK zone 42"|Projected|[124.5, 49.88, 127.5, 74.0]|Some([42142191.008, 5527499.247, 42357808.992, 8215904.466])| + EPSG_21243|21243|"GSK-2011 / GSK 3GK zone 43"|Projected|[127.5, 42.67, 130.5, 73.59]|Some([43127034.872, 4726059.962, 43372965.128, 8170159.21])| + EPSG_21244|21244|"GSK-2011 / GSK 3GK zone 44"|Projected|[130.5, 42.25, 133.5, 71.99]|Some([44126210.113, 4679405.432, 44373789.887, 7991657.462])| + EPSG_21245|21245|"GSK-2011 / GSK 3GK zone 45"|Projected|[133.5, 42.74, 136.5, 75.9]|Some([45127172.978, 4733836.051, 45372827.022, 8427915.408])| + EPSG_21246|21246|"GSK-2011 / GSK 3GK zone 46"|Projected|[136.5, 44.76, 139.5, 76.27]|Some([46131236.922, 4958272.916, 46368763.078, 8469205.51])| + EPSG_21247|21247|"GSK-2011 / GSK 3GK zone 47"|Projected|[139.5, 45.84, 142.5, 76.23]|Some([47133470.971, 5078301.557, 47366529.029, 8464741.659])| + EPSG_21248|21248|"GSK-2011 / GSK 3GK zone 48"|Projected|[142.5, 43.61, 145.5, 75.98]|Some([48128904.763, 4830489.692, 48371095.237, 8436842.898])| + EPSG_21249|21249|"GSK-2011 / GSK 3GK zone 49"|Projected|[145.5, 43.6, 148.5, 76.76]|Some([49128884.697, 4829378.647, 49371115.303, 8523888.773])| + EPSG_2125|2125|"NZGD2000 / Gawler 2000"|Projected|[170.68, -44.25, 172.26, -43.13]|Some([344623.889, 743896.089, 473186.963, 868728.491])| + EPSG_21250|21250|"GSK-2011 / GSK 3GK zone 50"|Projected|[148.5, 45.21, 151.5, 76.82]|Some([50132162.661, 5008282.079, 50367837.339, 8530584.819])| + EPSG_21251|21251|"GSK-2011 / GSK 3GK zone 51"|Projected|[151.5, 46.72, 154.5, 76.26]|Some([51135322.157, 5176119.522, 51364677.843, 8468089.546])| + EPSG_21252|21252|"GSK-2011 / GSK 3GK zone 52"|Projected|[154.5, 49.02, 157.5, 77.2]|Some([52140288.011, 5431851.393, 52359711.989, 8572993.788])| + EPSG_21253|21253|"GSK-2011 / GSK 3GK zone 53"|Projected|[157.5, 51.36, 160.5, 71.12]|Some([53145523.032, 5692135.882, 53354476.968, 7894608.584])| + EPSG_21254|21254|"GSK-2011 / GSK 3GK zone 54"|Projected|[160.5, 54.34, 163.5, 70.98]|Some([54152443.186, 6023760.318, 54347556.814, 7878992.308])| + EPSG_21255|21255|"GSK-2011 / GSK 3GK zone 55"|Projected|[163.5, 54.69, 166.5, 69.82]|Some([55153273.813, 6062720.414, 55346726.187, 7749608.953])| + EPSG_21256|21256|"GSK-2011 / GSK 3GK zone 56"|Projected|[166.5, 54.45, 169.5, 70.07]|Some([56152703.845, 6036004.675, 56347296.155, 7777491.973])| + EPSG_21257|21257|"GSK-2011 / GSK 3GK zone 57"|Projected|[169.5, 59.86, 172.5, 70.19]|Some([57165951.376, 6638474.726, 57334048.624, 7790876.083])| + EPSG_21258|21258|"GSK-2011 / GSK 3GK zone 58"|Projected|[172.5, 60.99, 175.5, 70.02]|Some([58168818.185, 6764378.707, 58331181.815, 7771915.31])| + EPSG_21259|21259|"GSK-2011 / GSK 3GK zone 59"|Projected|[175.5, 62.09, 178.5, 71.1]|Some([59171639.615, 6886960.527, 59328360.385, 7892377.674])| + EPSG_2126|2126|"NZGD2000 / Timaru 2000"|Projected|[169.82, -44.98, 171.55, -43.35]|Some([299689.744, 735018.339, 439952.785, 916881.026])| + EPSG_21260|21260|"GSK-2011 / GSK 3GK zone 60"|Projected|[178.5, 62.24, -178.5, 71.65]|Some([60172026.646, 6903677.764, 60327973.354, 7953729.324])| + EPSG_21261|21261|"GSK-2011 / GSK 3GK zone 61"|Projected|[-178.5, 64.74, -175.5, 71.61]|Some([61178554.837, 7182350.963, 61321445.163, 7949267.273])| + EPSG_21262|21262|"GSK-2011 / GSK 3GK zone 62"|Projected|[-175.5, 64.2, -172.5, 67.78]|Some([62177132.636, 7122149.301, 62322867.364, 7522111.898])| + EPSG_21263|21263|"GSK-2011 / GSK 3GK zone 63"|Projected|[-172.5, 64.35, -169.57, 67.06]|Some([63177527.039, 7138872.003, 63319091.339, 7441831.363])| + EPSG_21264|21264|"GSK-2011 / GSK 3GK zone 64"|Projected|[-169.22, 65.7, -168.97, 65.86]|Some([64193959.092, 7289730.783, 64205717.516, 7307768.81])| + EPSG_2127|2127|"NZGD2000 / Lindis Peak 2000"|Projected|[168.62, -45.4, 170.24, -43.71]|Some([331694.921, 725747.045, 462260.364, 913894.37])| + EPSG_2128|2128|"NZGD2000 / Mount Nicholas 2000"|Projected|[167.72, -45.58, 169.11, -44.29]|Some([345836.965, 750050.096, 456779.182, 893654.509])| + EPSG_2129|2129|"NZGD2000 / Mount York 2000"|Projected|[166.37, -46.33, 168.21, -44.53]|Some([291210.6, 713905.159, 437470.13, 914867.805])| + EPSG_21291|21291|"Barbados 1938 / British West Indies Grid"|Projected|[-59.71, 13.0, -59.37, 13.39]|Some([647800.275, 1437668.121, 685091.002, 1481190.236])| + EPSG_21292|21292|"Barbados 1938 / Barbados National Grid"|Projected|[-59.71, 13.0, -59.37, 13.39]|Some([13517.243, 55177.152, 50401.781, 98331.188])| + EPSG_2130|2130|"NZGD2000 / Observation Point 2000"|Projected|[169.77, -45.82, 171.24, -44.61]|Some([331865.561, 799209.425, 448554.043, 934042.021])| + EPSG_21307|21307|"GSK-2011 / GSK 3GK CM 21E"|Projected|[19.57, 54.32, 22.5, 55.32]|Some([156950.384, 6021534.66, 347604.168, 6133879.395])| + EPSG_21308|21308|"GSK-2011 / GSK 3GK CM 24E"|Projected|[22.5, 54.34, 22.87, 55.07]|Some([152443.186, 6024349.188, 177814.197, 6106050.936])| + EPSG_21309|21309|"GSK-2011 / GSK 3GK CM 27E"|Projected|[26.61, 56.05, 28.51, 68.93]|Some([225698.317, 6214129.668, 344087.314, 7650361.313])| + EPSG_2131|2131|"NZGD2000 / North Taieri 2000"|Projected|[168.64, -46.73, 170.87, -45.23]|Some([271018.521, 702141.139, 446134.964, 870171.271])| + EPSG_21310|21310|"GSK-2011 / GSK 3GK CM 30E"|Projected|[28.5, 52.85, 31.5, 69.85]|Some([148948.613, 5857927.174, 351051.387, 7752954.876])| + EPSG_21311|21311|"GSK-2011 / GSK 3GK CM 33E"|Projected|[31.5, 51.24, 34.5, 70.02]|Some([145250.218, 5678785.402, 354749.782, 7771915.31])| + EPSG_21312|21312|"GSK-2011 / GSK 3GK CM 36E"|Projected|[34.5, 44.61, 37.51, 69.39]|Some([130929.978, 4941604.086, 369863.825, 7701661.56])| + EPSG_21313|21313|"GSK-2011 / GSK 3GK CM 39E"|Projected|[37.5, 43.33, 40.5, 68.8]|Some([128344.324, 4799381.163, 371655.676, 7635853.999])| + EPSG_21314|21314|"GSK-2011 / GSK 3GK CM 42E"|Projected|[40.5, 42.87, 43.5, 68.74]|Some([127429.951, 4748277.612, 372570.049, 7629162.926])| + EPSG_21315|21315|"GSK-2011 / GSK 3GK CM 45E"|Projected|[43.5, 41.89, 46.5, 80.8]|Some([125508.496, 4639418.564, 374491.504, 8974815.264])| + EPSG_21316|21316|"GSK-2011 / GSK 3GK CM 48E"|Projected|[46.5, 41.19, 49.5, 80.91]|Some([124158.402, 4561673.517, 375841.598, 8987094.467])| + EPSG_21317|21317|"GSK-2011 / GSK 3GK CM 51E"|Projected|[49.5, 42.36, 52.5, 81.22]|Some([126425.477, 4691624.144, 373574.523, 9021699.865])| + EPSG_21318|21318|"GSK-2011 / GSK 3GK CM 54E"|Projected|[52.5, 50.52, 55.5, 81.41]|Some([143623.11, 5598688.306, 356376.89, 9042909.889])| + EPSG_21319|21319|"GSK-2011 / GSK 3GK CM 57E"|Projected|[55.5, 50.53, 58.5, 81.89]|Some([143645.593, 5599800.698, 356354.407, 9096493.97])| + EPSG_2132|2132|"NZGD2000 / Bluff 2000"|Projected|[167.29, -47.33, 168.97, -45.33]|Some([317469.721, 718308.217, 449169.772, 941161.243])| + EPSG_21320|21320|"GSK-2011 / GSK 3GK CM 60E"|Projected|[58.5, 50.47, 61.5, 81.91]|Some([143510.743, 5593126.377, 356489.257, 9098726.667])| + EPSG_21321|21321|"GSK-2011 / GSK 3GK CM 63E"|Projected|[61.5, 51.03, 64.5, 81.77]|Some([144773.909, 5655422.723, 355226.091, 9083097.837])| + EPSG_21322|21322|"GSK-2011 / GSK 3GK CM 66E"|Projected|[64.5, 54.31, 67.5, 81.25]|Some([152372.16, 6020420.986, 347627.84, 9025048.803])| + EPSG_21323|21323|"GSK-2011 / GSK 3GK CM 69E"|Projected|[67.5, 54.85, 70.5, 77.07]|Some([153654.745, 6080531.499, 346345.255, 8558485.326])| + EPSG_21324|21324|"GSK-2011 / GSK 3GK CM 72E"|Projected|[70.5, 53.43, 73.5, 73.57]|Some([150300.843, 5922474.731, 349699.157, 8167927.777])| + EPSG_21325|21325|"GSK-2011 / GSK 3GK CM 75E"|Projected|[73.5, 53.47, 76.5, 79.71]|Some([150394.482, 5926926.52, 349605.518, 8853143.441])| + EPSG_21326|21326|"GSK-2011 / GSK 3GK CM 78E"|Projected|[76.5, 51.49, 79.5, 81.03]|Some([145819.102, 5706599.213, 354180.898, 9000490.041])| + EPSG_21327|21327|"GSK-2011 / GSK 3GK CM 81E"|Projected|[79.5, 50.7, 82.5, 81.03]|Some([144028.308, 5618711.648, 355971.692, 9000490.041])| + EPSG_21328|21328|"GSK-2011 / GSK 3GK CM 84E"|Projected|[82.5, 49.58, 85.5, 77.56]|Some([141524.368, 5494132.093, 358475.632, 8613171.762])| + EPSG_21329|21329|"GSK-2011 / GSK 3GK CM 87E"|Projected|[85.5, 49.07, 88.5, 77.16]|Some([140397.972, 5437411.923, 359602.028, 8568529.632])| + EPSG_2133|2133|"NZGD2000 / UTM zone 58S"|Projected|[162.0, -55.89, 168.0, -39.68]|Some([242707.61, 3802095.009, 757292.39, 5607758.669])| + EPSG_21330|21330|"GSK-2011 / GSK 3GK CM 90E"|Projected|[88.5, 49.44, 91.5, 81.28]|Some([141214.295, 5478561.349, 358785.705, 9028397.746])| + EPSG_21331|21331|"GSK-2011 / GSK 3GK CM 93E"|Projected|[91.5, 50.16, 94.5, 81.26]|Some([142815.894, 5558643.49, 357184.106, 9026165.117])| + EPSG_21332|21332|"GSK-2011 / GSK 3GK CM 96E"|Projected|[94.5, 49.73, 97.5, 81.35]|Some([141857.314, 5510815.453, 358142.686, 9036211.965])| + EPSG_21333|21333|"GSK-2011 / GSK 3GK CM 99E"|Projected|[97.5, 49.79, 100.5, 80.9]|Some([141990.702, 5517488.918, 358009.298, 8985978.173])| + EPSG_21334|21334|"GSK-2011 / GSK 3GK CM 102E"|Projected|[100.5, 50.17, 103.5, 79.71]|Some([142838.259, 5559755.812, 357161.741, 8853143.441])| + EPSG_21335|21335|"GSK-2011 / GSK 3GK CM 105E"|Projected|[103.5, 50.13, 106.5, 79.21]|Some([142748.818, 5555306.535, 357251.182, 8797333.196])| + EPSG_21336|21336|"GSK-2011 / GSK 3GK CM 108E"|Projected|[106.5, 49.25, 109.5, 78.4]|Some([140794.529, 5457430.232, 359205.471, 8706924.212])| + EPSG_21337|21337|"GSK-2011 / GSK 3GK CM 111E"|Projected|[109.5, 49.14, 112.5, 76.81]|Some([140552.059, 5445196.746, 359447.941, 8529468.81])| + EPSG_21338|21338|"GSK-2011 / GSK 3GK CM 114E"|Projected|[112.5, 49.49, 115.5, 76.7]|Some([141324.96, 5484122.286, 358675.04, 8517192.757])| + EPSG_21339|21339|"GSK-2011 / GSK 3GK CM 117E"|Projected|[115.5, 49.51, 118.5, 74.43]|Some([141369.25, 5486346.674, 358630.75, 8263882.952])| + EPSG_2134|2134|"NZGD2000 / UTM zone 59S"|Projected|[168.0, -55.95, 174.0, -30.78]|Some([212883.494, 3795420.664, 787116.506, 6594779.177])| + EPSG_21340|21340|"GSK-2011 / GSK 3GK CM 120E"|Projected|[118.5, 49.87, 121.5, 73.63]|Some([142168.738, 5526386.98, 357831.262, 8174622.089])| + EPSG_21341|21341|"GSK-2011 / GSK 3GK CM 123E"|Projected|[121.5, 53.18, 124.5, 74.0]|Some([149716.712, 5894651.733, 350283.288, 8215904.466])| + EPSG_21342|21342|"GSK-2011 / GSK 3GK CM 126E"|Projected|[124.5, 49.88, 127.5, 74.0]|Some([142191.008, 5527499.247, 357808.992, 8215904.466])| + EPSG_21343|21343|"GSK-2011 / GSK 3GK CM 129E"|Projected|[127.5, 42.67, 130.5, 73.59]|Some([127034.872, 4726059.962, 372965.128, 8170159.21])| + EPSG_21344|21344|"GSK-2011 / GSK 3GK CM 132E"|Projected|[130.5, 42.25, 133.5, 71.99]|Some([126210.113, 4679405.432, 373789.887, 7991657.462])| + EPSG_21345|21345|"GSK-2011 / GSK 3GK CM 135E"|Projected|[133.5, 42.74, 136.5, 75.9]|Some([127172.978, 4733836.051, 372827.022, 8427915.408])| + EPSG_21346|21346|"GSK-2011 / GSK 3GK CM 138E"|Projected|[136.5, 44.76, 139.5, 76.27]|Some([131236.922, 4958272.916, 368763.078, 8469205.51])| + EPSG_21347|21347|"GSK-2011 / GSK 3GK CM 141E"|Projected|[139.5, 45.84, 142.5, 76.23]|Some([133470.971, 5078301.557, 366529.029, 8464741.659])| + EPSG_21348|21348|"GSK-2011 / GSK 3GK CM 144E"|Projected|[142.5, 43.61, 145.5, 75.98]|Some([128904.763, 4830489.692, 371095.237, 8436842.898])| + EPSG_21349|21349|"GSK-2011 / GSK 3GK CM 147E"|Projected|[145.5, 43.6, 148.5, 76.76]|Some([128884.697, 4829378.647, 371115.303, 8523888.773])| + EPSG_2135|2135|"NZGD2000 / UTM zone 60S"|Projected|[174.0, -54.32, 180.0, -26.42]|Some([200759.474, 3976723.319, 799240.526, 7077802.461])| + EPSG_21350|21350|"GSK-2011 / GSK 3GK CM 150E"|Projected|[148.5, 45.21, 151.5, 76.82]|Some([132162.661, 5008282.079, 367837.339, 8530584.819])| + EPSG_21351|21351|"GSK-2011 / GSK 3GK CM 153E"|Projected|[151.5, 46.72, 154.5, 76.26]|Some([135322.157, 5176119.522, 364677.843, 8468089.546])| + EPSG_21352|21352|"GSK-2011 / GSK 3GK CM 156E"|Projected|[154.5, 49.02, 157.5, 77.2]|Some([140288.011, 5431851.393, 359711.989, 8572993.788])| + EPSG_21353|21353|"GSK-2011 / GSK 3GK CM 159E"|Projected|[157.5, 51.36, 160.5, 71.12]|Some([145523.032, 5692135.882, 354476.968, 7894608.584])| + EPSG_21354|21354|"GSK-2011 / GSK 3GK CM 162E"|Projected|[160.5, 54.34, 163.5, 70.98]|Some([152443.186, 6023760.318, 347556.814, 7878992.308])| + EPSG_21355|21355|"GSK-2011 / GSK 3GK CM 165E"|Projected|[163.5, 54.69, 166.5, 69.82]|Some([153273.813, 6062720.414, 346726.187, 7749608.953])| + EPSG_21356|21356|"GSK-2011 / GSK 3GK CM 168E"|Projected|[166.5, 54.45, 169.5, 70.07]|Some([152703.845, 6036004.675, 347296.155, 7777491.973])| + EPSG_21357|21357|"GSK-2011 / GSK 3GK CM 171E"|Projected|[169.5, 59.86, 172.5, 70.19]|Some([165951.376, 6638474.726, 334048.624, 7790876.083])| + EPSG_21358|21358|"GSK-2011 / GSK 3GK CM 174E"|Projected|[172.5, 60.99, 175.5, 70.02]|Some([168818.185, 6764378.707, 331181.815, 7771915.31])| + EPSG_21359|21359|"GSK-2011 / GSK 3GK CM 177E"|Projected|[175.5, 62.09, 178.5, 71.1]|Some([171639.615, 6886960.527, 328360.385, 7892377.674])| + EPSG_2136|2136|"Accra / Ghana National Grid"|Projected|[-3.25, 4.67, 1.23, 11.16]|Some([80855.657, 184.557, 1711663.278, 2357568.198])| + EPSG_21360|21360|"GSK-2011 / GSK 3GK CM 180E"|Projected|[178.5, 62.24, -178.5, 71.65]|Some([172026.646, 6903677.764, 327973.354, 7953729.324])| + EPSG_21361|21361|"GSK-2011 / GSK 3GK CM 177W"|Projected|[-178.5, 64.74, -175.5, 71.61]|Some([178554.837, 7182350.963, 321445.163, 7949267.273])| + EPSG_21362|21362|"GSK-2011 / GSK 3GK CM 174W"|Projected|[-175.5, 64.2, -172.5, 67.78]|Some([177132.636, 7122149.301, 322867.364, 7522111.898])| + EPSG_21363|21363|"GSK-2011 / GSK 3GK CM 171W"|Projected|[-172.5, 64.35, -169.57, 67.06]|Some([177527.039, 7138872.003, 319091.339, 7441831.363])| + EPSG_21364|21364|"GSK-2011 / GSK 3GK CM 168W"|Projected|[-169.22, 65.7, -168.97, 65.86]|Some([193959.092, 7289730.783, 205717.516, 7307768.81])| + EPSG_2137|2137|"Accra / TM 1 NW"|Projected|[-3.79, 1.4, 2.1, 6.06]|Some([189472.044, 154417.225, 844979.9, 670493.219])| + EPSG_2138|2138|"NAD27(CGQ77) / Quebec Lambert"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([-893301.768, 110382.065, 897203.745, 2112812.285])| + EPSG_21413|21413|"Beijing 1954 / Gauss-Kruger zone 13"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([13374663.303, 3921260.898, 13773412.178, 4552759.619])| + EPSG_21414|21414|"Beijing 1954 / Gauss-Kruger zone 14"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([14206111.89, 3227060.674, 14791940.888, 5237335.439])| + EPSG_21415|21415|"Beijing 1954 / Gauss-Kruger zone 15"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([15202998.726, 3023142.02, 15797001.274, 5454076.57])| + EPSG_21416|21416|"Beijing 1954 / Gauss-Kruger zone 16"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([16204045.915, 3066359.345, 16796941.116, 5311790.155])| + EPSG_21417|21417|"Beijing 1954 / Gauss-Kruger zone 17"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([17188248.264, 2337511.888, 17812791.62, 4787202.354])| + EPSG_21418|21418|"Beijing 1954 / Gauss-Kruger zone 18"|Projected|[102.0, 21.53, 108.0, 42.47]|Some([18189093.408, 2381801.202, 18810857.638, 4708290.022])| + EPSG_21419|21419|"Beijing 1954 / Gauss-Kruger zone 19"|Projected|[108.0, 18.11, 114.0, 45.11]|Some([19182310.655, 2003192.605, 19817640.633, 5001638.028])| + EPSG_21420|21420|"Beijing 1954 / Gauss-Kruger zone 20"|Projected|[114.0, 22.14, 120.0, 51.52]|Some([20190358.217, 2449346.515, 20809588.653, 5714306.627])| + EPSG_21421|21421|"Beijing 1954 / Gauss-Kruger zone 21"|Projected|[120.0, 26.34, 126.0, 53.56]|Some([21200428.116, 2914555.258, 21799571.884, 5941235.827])| + EPSG_21422|21422|"Beijing 1954 / Gauss-Kruger zone 22"|Projected|[126.0, 40.89, 132.0, 52.79]|Some([22247154.322, 4528437.525, 22752845.678, 5855574.461])| + EPSG_21423|21423|"Beijing 1954 / Gauss-Kruger zone 23"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([23263537.792, 4987280.706, 23482968.991, 5367350.289])| + EPSG_21453|21453|"Beijing 1954 / Gauss-Kruger CM 75E"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([374663.303, 3921260.898, 773412.178, 4552759.619])| + EPSG_21454|21454|"Beijing 1954 / Gauss-Kruger CM 81E"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([206111.89, 3227060.674, 791940.888, 5237335.439])| + EPSG_21455|21455|"Beijing 1954 / Gauss-Kruger CM 87E"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([202998.726, 3023142.02, 797001.274, 5454076.57])| + EPSG_21456|21456|"Beijing 1954 / Gauss-Kruger CM 93E"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([204045.915, 3066359.345, 796941.116, 5311790.155])| + EPSG_21457|21457|"Beijing 1954 / Gauss-Kruger CM 99E"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([188248.264, 2337511.888, 812791.62, 4787202.354])| + EPSG_21458|21458|"Beijing 1954 / Gauss-Kruger CM 105E"|Projected|[102.0, 21.53, 108.0, 42.47]|Some([189093.408, 2381801.202, 810857.638, 4708290.022])| + EPSG_21459|21459|"Beijing 1954 / Gauss-Kruger CM 111E"|Projected|[108.0, 18.11, 114.0, 45.11]|Some([182310.655, 2003192.605, 817640.633, 5001638.028])| + EPSG_21460|21460|"Beijing 1954 / Gauss-Kruger CM 117E"|Projected|[114.0, 22.14, 120.0, 51.52]|Some([190358.217, 2449346.515, 809588.653, 5714306.627])| + EPSG_21461|21461|"Beijing 1954 / Gauss-Kruger CM 123E"|Projected|[120.0, 26.34, 126.0, 53.56]|Some([200428.116, 2914555.258, 799571.884, 5941235.827])| + EPSG_21462|21462|"Beijing 1954 / Gauss-Kruger CM 129E"|Projected|[126.0, 40.89, 132.0, 52.79]|Some([247154.322, 4528437.525, 752845.678, 5855574.461])| + EPSG_21463|21463|"Beijing 1954 / Gauss-Kruger CM 135E"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([263537.792, 4987280.706, 482968.991, 5367350.289])| + EPSG_21500|21500|"BD50 (Brussels) / Belge Lambert 50"|Projected|[2.5, 49.5, 6.4, 51.51]|Some([14686.823, 20753.799, 297193.855, 246277.538])| + EPSG_2154|2154|"RGF93 v1 / Lambert-93"|Projected|[-9.86, 41.15, 10.38, 51.56]|Some([-378305.81, 6005281.901, 1320649.571, 7235612.725])| + EPSG_2157|2157|"IRENET95 / Irish Transverse Mercator"|Projected|[-10.56, 51.39, -5.34, 55.43]|Some([421849.813, 515251.594, 785108.095, 968015.387])| + EPSG_2158|2158|"IRENET95 / UTM zone 29N"|Projected|[-10.56, 51.39, -5.34, 55.43]|Some([391458.608, 5693195.833, 754623.489, 6148736.507])| + EPSG_2159|2159|"Sierra Leone 1924 / New Colony Grid"|Projected|[-13.34, 8.32, -13.13, 8.55]|Some([15664.393, 600462.068, 91819.622, 684175.57])| + EPSG_2160|2160|"Sierra Leone 1924 / New War Office Grid"|Projected|[-13.34, 8.32, -13.13, 8.55]|Some([315664.393, 1200462.068, 391819.622, 1284175.57])| + EPSG_2161|2161|"Sierra Leone 1968 / UTM zone 28N"|Projected|[-13.35, 7.15, -12.0, 9.94]|Some([680909.221, 790560.4, 831414.577, 1100171.01])| + EPSG_2162|2162|"Sierra Leone 1968 / UTM zone 29N"|Projected|[-12.0, 6.88, -10.26, 10.0]|Some([168422.923, 760569.245, 361908.226, 1106813.199])| + EPSG_2164|2164|"Locodjo 1965 / TM 5 NW"|Projected|[-7.55, 1.02, -3.11, 5.19]|Some([216194.222, 112730.384, 710318.661, 574183.323])| + EPSG_2165|2165|"Abidjan 1987 / TM 5 NW"|Projected|[-7.55, 1.02, -3.11, 5.19]|Some([216157.276, 112275.377, 710272.478, 573770.844])| + EPSG_2169|2169|"LUREF / Luxembourg TM"|Projected|[5.73, 49.44, 6.53, 50.19]|Some([48225.166, 56133.744, 106245.562, 139644.912])| + EPSG_2172|2172|"Pulkovo 1942(58) / Poland zone II"|Projected|[19.0, 51.33, 23.95, 54.51]|Some([4428704.828, 5620013.047, 4773672.098, 5976708.134])| + EPSG_2173|2173|"Pulkovo 1942(58) / Poland zone III"|Projected|[14.14, 52.16, 20.0, 54.89]|Some([3304881.711, 5840665.873, 3705803.314, 6148524.342])| + EPSG_2174|2174|"Pulkovo 1942(58) / Poland zone IV"|Projected|[14.14, 49.39, 19.09, 53.34]|Some([3519274.764, 5373337.396, 3878667.292, 5815713.366])| + EPSG_2175|2175|"Pulkovo 1942(58) / Poland zone V"|Projected|[18.33, 49.39, 19.67, 51.34]|Some([191508.524, 773040.981, 288789.533, 990189.404])| + EPSG_2176|2176|"ETRF2000-PL / CS2000/15"|Projected|[14.14, 50.26, 16.5, 55.35]|Some([5438679.954, 5569338.391, 5606952.079, 6136746.813])| + EPSG_2177|2177|"ETRF2000-PL / CS2000/18"|Projected|[16.5, 49.39, 19.5, 55.93]|Some([6391112.089, 5472579.481, 6608887.911, 6201308.275])| + EPSG_2178|2178|"ETRF2000-PL / CS2000/21"|Projected|[19.5, 49.09, 22.5, 54.55]|Some([7390450.407, 5439217.735, 7609549.593, 6047705.958])| + EPSG_21780|21780|"CH1903 (Bern) / LV03C"|Projected|[5.96, 45.82, 10.49, 47.81]|Some([-114928.425, -125738.547, 237119.805, 99941.786])| + EPSG_21781|21781|"CH1903 / LV03"|Projected|[5.96, 45.82, 10.49, 47.81]|Some([485071.575, 74261.453, 837119.805, 299941.786])| + EPSG_21782|21782|"CH1903 / LV03C-G"|Projected|[9.47, 47.05, 9.64, 47.28]|Some([153675.75, 12994.342, 167250.692, 38903.182])| + EPSG_2179|2179|"ETRF2000-PL / CS2000/24"|Projected|[22.5, 49.0, 24.15, 54.41]|Some([8390252.492, 5429209.659, 8510974.922, 6032124.941])| + EPSG_2180|2180|"ETRF2000-PL / CS92"|Projected|[14.14, 49.0, 24.15, 55.93]|Some([144693.28, 125837.018, 876500.359, 908411.193])| + EPSG_21818|21818|"Bogota 1975 / UTM zone 18N"|Projected|[-77.37, 7.9, -72.0, 13.68]|Some([238316.098, 873556.359, 830447.423, 1514665.25])| + EPSG_2188|2188|"Azores Occidental 1939 / UTM zone 25N"|Projected|[-31.34, 39.3, -31.02, 39.77]|Some([642551.649, 4351315.691, 671109.002, 4404038.792])| + EPSG_2189|2189|"Azores Central 1948 / UTM zone 26N"|Projected|[-28.9, 38.32, -26.97, 39.14]|Some([333802.334, 4241405.955, 502520.915, 4334117.4])| + EPSG_21896|21896|"Bogota 1975 / Colombia West zone"|Projected|[-79.1, 0.03, -75.58, 10.21]|Some([774827.739, 495077.905, 1166736.483, 1621538.404])| + EPSG_21897|21897|"Bogota 1975 / Colombia Bogota zone"|Projected|[-75.59, -2.51, -72.58, 11.82]|Some([831612.987, 214123.05, 1166725.996, 1799383.719])| + EPSG_21898|21898|"Bogota 1975 / Colombia East Central zone"|Projected|[-72.59, -4.23, -69.58, 12.52]|Some([831603.946, 23859.672, 1166714.827, 1876852.77])| + EPSG_21899|21899|"Bogota 1975 / Colombia East zone"|Projected|[-69.59, -2.25, -66.87, 6.31]|Some([831592.838, 242884.535, 1134415.24, 1189768.563])| + EPSG_2190|2190|"Azores Oriental 1940 / UTM zone 26N"|Projected|[-25.92, 36.87, -24.72, 37.96]|Some([594841.35, 4080960.32, 703193.694, 4203794.86])| + EPSG_2193|2193|"NZGD2000 / New Zealand Transverse Mercator 2000"|Projected|[166.37, -47.33, 178.63, -34.1]|Some([987942.452, 4736810.693, 2119620.661, 6226752.366])| + EPSG_2195|2195|"NAD83(HARN) / UTM zone 2S"|Projected|[-170.88, -14.59, -168.09, -14.11]|Some([512925.157, 8385013.991, 814222.967, 8440104.877])| + EPSG_2196|2196|"ETRS89 / Kp2000 Jutland"|Projected|[8.0, 54.67, 11.29, 57.8]|Some([103231.08, 6060192.101, 315475.728, 6410093.958])| + EPSG_2197|2197|"ETRS89 / Kp2000 Zealand"|Projected|[10.79, 54.51, 12.87, 56.79]|Some([421632.143, 6042382.316, 556347.795, 6296867.169])| + EPSG_2198|2198|"ETRS89 / Kp2000 Bornholm"|Projected|[14.59, 54.94, 15.25, 55.38]|Some([873723.345, 6090550.964, 916022.38, 6139611.105])| + EPSG_2200|2200|"ATS77 / New Brunswick Stereographic (ATS77)"|Projected|[-69.05, 44.56, -63.7, 48.07]|Some([97385.342, 584384.178, 522474.858, 978289.647])| + EPSG_2201|2201|"REGVEN / UTM zone 18N"|Projected|[-73.38, 7.02, -71.99, 11.62]|Some([676616.652, 776269.04, 832592.864, 1286272.451])| + EPSG_2202|2202|"REGVEN / UTM zone 19N"|Projected|[-72.0, 0.73, -66.0, 15.64]|Some([166048.419, 80686.977, 833951.581, 1731386.661])| + EPSG_2203|2203|"REGVEN / UTM zone 20N"|Projected|[-66.0, 0.64, -58.95, 16.75]|Some([166042.177, 70739.292, 951013.852, 1856304.26])| + EPSG_22032|22032|"Camacupa 1948 / UTM zone 32S"|Projected|[8.2, -17.26, 12.0, -6.03]|Some([411464.862, 8089369.263, 832476.282, 9333540.112])| + EPSG_22033|22033|"Camacupa 1948 / UTM zone 33S"|Projected|[12.0, -17.44, 18.0, -5.82]|Some([167728.645, 8069435.011, 832271.355, 9356751.966])| + EPSG_2204|2204|"NAD27 / Tennessee"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([709174.76, 214010.37, 3302776.461, 861190.343])| + EPSG_2205|2205|"NAD83 / Kentucky North"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([349218.279, 23310.027, 656953.319, 184641.099])| + EPSG_2206|2206|"ED50 / 3-degree Gauss-Kruger zone 9"|Projected|[25.62, 36.5, 28.5, 42.11]|Some([9376400.774, 4041209.978, 9634432.986, 4665132.655])| + EPSG_2207|2207|"ED50 / 3-degree Gauss-Kruger zone 10"|Projected|[28.5, 36.06, 31.5, 41.46]|Some([10364885.695, 3992383.259, 10635183.101, 4592933.198])| + EPSG_2208|2208|"ED50 / 3-degree Gauss-Kruger zone 11"|Projected|[31.5, 35.97, 34.5, 42.07]|Some([11364725.397, 3982395.538, 11635330.294, 4660686.616])| + EPSG_2209|2209|"ED50 / 3-degree Gauss-Kruger zone 12"|Projected|[34.5, 35.81, 37.5, 42.15]|Some([12364446.192, 3964641.197, 12635596.157, 4669571.728])| + EPSG_22091|22091|"Camacupa 1948 / TM 11.30 SE"|Projected|[10.83, -6.59, 11.67, -6.03]|Some([426184.058, 9271755.473, 519143.184, 9333709.647])| + EPSG_22092|22092|"Camacupa 1948 / TM 12 SE"|Projected|[8.2, -17.26, 13.86, -6.01]|Some([79152.236, 8087867.956, 705880.75, 9335920.727])| + EPSG_2210|2210|"ED50 / 3-degree Gauss-Kruger zone 13"|Projected|[37.5, 36.66, 40.5, 41.19]|Some([13365899.705, 4058959.72, 13634129.422, 4562941.708])| + EPSG_2211|2211|"ED50 / 3-degree Gauss-Kruger zone 14"|Projected|[40.5, 37.02, 43.5, 41.6]|Some([14366520.303, 4098910.107, 14633495.314, 4608478.343])| + EPSG_2212|2212|"ED50 / 3-degree Gauss-Kruger zone 15"|Projected|[43.5, 36.97, 44.83, 41.02]|Some([15366426.001, 4093374.223, 15485702.86, 4544059.891])| + EPSG_2213|2213|"ETRS89 / TM 30 NE"|Projected|[28.64, 43.44, 31.41, 45.2]|Some([389942.319, 4809678.237, 614103.965, 5006135.24])| + EPSG_2215|2215|"Manoca 1962 / UTM zone 32N"|Projected|[8.45, 2.16, 10.4, 4.99]|Some([438979.649, 238788.111, 655829.74, 551766.673])| + EPSG_2216|2216|"Qornoq 1927 / UTM zone 22N"|Projected|[-54.0, 60.63, -48.0, 73.05]|Some([335676.792, 6721894.585, 663883.984, 8108826.92])| + EPSG_2217|2217|"Qornoq 1927 / UTM zone 23N"|Projected|[-48.0, 59.74, -42.52, 62.05]|Some([331173.69, 6622795.066, 639174.027, 6883717.562])| + EPSG_22171|22171|"POSGAR 98 / Argentina 1"|Projected|[-73.59, -52.0, -70.5, -36.16]|Some([1356924.253, 4237428.116, 1634976.536, 5998668.443])| + EPSG_22172|22172|"POSGAR 98 / Argentina 2"|Projected|[-70.5, -54.9, -67.49, -24.08]|Some([2347455.132, 3914823.252, 2653561.991, 7337816.363])| + EPSG_22173|22173|"POSGAR 98 / Argentina 3"|Projected|[-67.5, -55.11, -64.49, -21.78]|Some([3344856.074, 3891448.065, 3656178.393, 7592525.578])| + EPSG_22174|22174|"POSGAR 98 / Argentina 4"|Projected|[-64.5, -54.91, -61.5, -21.99]|Some([4345083.097, 3913723.952, 4654916.903, 7569272.554])| + EPSG_22175|22175|"POSGAR 98 / Argentina 5"|Projected|[-61.51, -39.06, -58.5, -23.37]|Some([5345603.797, 5675715.397, 5653373.55, 7416451.898])| + EPSG_22176|22176|"POSGAR 98 / Argentina 6"|Projected|[-58.5, -38.59, -55.49, -24.84]|Some([6348368.071, 5727895.023, 6652642.961, 7253635.022])| + EPSG_22177|22177|"POSGAR 98 / Argentina 7"|Projected|[-55.5, -28.11, -53.65, -25.49]|Some([7349170.006, 6890424.501, 7535191.255, 7181630.899])| + EPSG_22181|22181|"POSGAR 94 / Argentina 1"|Projected|[-73.59, -52.0, -70.5, -36.16]|Some([1356924.253, 4237428.116, 1634976.536, 5998668.443])| + EPSG_22182|22182|"POSGAR 94 / Argentina 2"|Projected|[-70.5, -54.9, -67.49, -24.08]|Some([2347455.132, 3914823.252, 2653561.991, 7337816.363])| + EPSG_22183|22183|"POSGAR 94 / Argentina 3"|Projected|[-67.5, -55.11, -64.49, -21.78]|Some([3344856.074, 3891448.065, 3656178.393, 7592525.578])| + EPSG_22184|22184|"POSGAR 94 / Argentina 4"|Projected|[-64.5, -54.91, -61.5, -21.99]|Some([4345083.097, 3913723.952, 4654916.903, 7569272.554])| + EPSG_22185|22185|"POSGAR 94 / Argentina 5"|Projected|[-61.51, -39.06, -58.5, -23.37]|Some([5345603.797, 5675715.397, 5653373.55, 7416451.898])| + EPSG_22186|22186|"POSGAR 94 / Argentina 6"|Projected|[-58.5, -38.59, -55.49, -24.84]|Some([6348368.071, 5727895.023, 6652642.961, 7253635.022])| + EPSG_22187|22187|"POSGAR 94 / Argentina 7"|Projected|[-55.5, -28.11, -53.65, -25.49]|Some([7349170.006, 6890424.501, 7535191.255, 7181630.899])| + EPSG_2219|2219|"ATS77 / UTM zone 19N"|Projected|[-69.0, 43.64, -66.0, 48.07]|Some([500000.0, 4831888.165, 741977.198, 5328433.235])| + EPSG_22191|22191|"Campo Inchauspe / Argentina 1"|Projected|[-73.59, -52.0, -70.5, -36.16]|Some([1357022.428, 4237624.694, 1635075.79, 5998870.294])| + EPSG_22192|22192|"Campo Inchauspe / Argentina 2"|Projected|[-70.5, -52.43, -67.49, -24.08]|Some([2347543.734, 4189903.039, 2653652.26, 7338026.895])| + EPSG_22193|22193|"Campo Inchauspe / Argentina 3"|Projected|[-67.5, -49.05, -64.49, -21.78]|Some([3344935.142, 4565883.404, 3656259.108, 7592739.213])| + EPSG_22194|22194|"Campo Inchauspe / Argentina 4"|Projected|[-64.5, -43.14, -61.49, -21.99]|Some([4345152.499, 5222793.915, 4656020.711, 7569487.529])| + EPSG_22195|22195|"Campo Inchauspe / Argentina 5"|Projected|[-61.51, -39.06, -58.5, -23.37]|Some([5345663.367, 5675926.576, 5653434.32, 7416667.537])| + EPSG_22196|22196|"Campo Inchauspe / Argentina 6"|Projected|[-58.5, -38.59, -55.49, -24.84]|Some([6348417.638, 5728108.079, 6652693.502, 7253851.259])| + EPSG_22197|22197|"Campo Inchauspe / Argentina 7"|Projected|[-55.5, -28.11, -53.65, -25.49]|Some([7349209.388, 6890640.77, 7535231.148, 7181847.92])| + EPSG_2220|2220|"ATS77 / UTM zone 20N"|Projected|[-66.0, 43.41, -59.73, 47.98]|Some([257100.824, 4806347.37, 764761.387, 5319251.224])| + EPSG_22207|22207|"NAD83(CSRS)v2 / UTM zone 7N"|Projected|[-141.01, 52.05, -138.0, 72.53]|Some([499314.26, 5766599.426, 705699.252, 8050561.199])| + EPSG_22208|22208|"NAD83(CSRS)v2 / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276483.897, 5322969.01, 723516.103, 8818432.315])| + EPSG_22209|22209|"NAD83(CSRS)v2 / UTM zone 9N"|Projected|[-132.0, 46.52, -126.0, 80.93]|Some([269894.217, 5151825.698, 730105.783, 8986755.526])| + EPSG_22210|22210|"NAD83(CSRS)v2 / UTM zone 10N"|Projected|[-126.0, 48.13, -120.0, 81.8]|Some([276787.323, 5330749.349, 723212.677, 9083741.173])| + EPSG_22211|22211|"NAD83(CSRS)v2 / UTM zone 11N"|Projected|[-120.0, 48.99, -114.0, 83.5]|Some([280542.341, 5426344.13, 719457.659, 9273261.909])| + EPSG_22212|22212|"NAD83(CSRS)v2 / UTM zone 12N"|Projected|[-114.0, 48.99, -108.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22213|22213|"NAD83(CSRS)v2 / UTM zone 13N"|Projected|[-108.0, 48.99, -102.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22214|22214|"NAD83(CSRS)v2 / UTM zone 14N"|Projected|[-102.0, 48.99, -96.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22215|22215|"NAD83(CSRS)v2 / UTM zone 15N"|Projected|[-96.0, 48.03, -90.0, 84.0]|Some([276353.96, 5319634.608, 723646.04, 9329005.182])| + EPSG_22216|22216|"NAD83(CSRS)v2 / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 84.0]|Some([268167.8, 5106269.364, 731832.2, 9329005.182])| + EPSG_22217|22217|"NAD83(CSRS)v2 / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 84.0]|Some([250254.167, 4613137.759, 749745.833, 9329005.182])| + EPSG_22218|22218|"NAD83(CSRS)v2 / UTM zone 18N"|Projected|[-78.0, 43.63, -72.0, 84.0]|Some([257982.558, 4830779.091, 742017.442, 9329005.182])| + EPSG_22219|22219|"NAD83(CSRS)v2 / UTM zone 19N"|Projected|[-72.0, 40.8, -66.0, 84.0]|Some([246917.185, 4516555.551, 753082.815, 9329005.182])| + EPSG_2222|2222|"NAD83 / Arizona East (ft)"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([218115.411, 120028.214, 1051776.963, 2190595.217])| + EPSG_22220|22220|"NAD83(CSRS)v2 / UTM zone 20N"|Projected|[-66.0, 40.04, -60.0, 84.0]|Some([244050.009, 4432196.843, 755949.991, 9329005.182])| + EPSG_22221|22221|"NAD83(CSRS)v2 / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.645, 4267951.154, 761403.355, 9329005.182])| + EPSG_22222|22222|"NAD83(CSRS)v2 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242040.228, 4372264.52, 758819.79, 6393731.856])| + EPSG_22229|22229|"RGSH2020 / UTM zone 29N"|Projected|[-8.67, 25.73, -6.0, 29.85]|Some([531875.582, 2845824.986, 801002.277, 3305943.351])| + EPSG_2223|2223|"NAD83 / Arizona Central (ft)"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([252464.84, 120027.575, 1161066.659, 2190286.329])| + EPSG_22230|22230|"RGSH2020 / UTM zone 30N"|Projected|[-6.0, 21.82, 0.0, 37.01]|Some([189844.938, 2412903.699, 810155.062, 4100190.449])| + EPSG_22231|22231|"RGSH2020 / UTM zone 31N"|Projected|[0.0, 18.97, 6.01, 38.77]|Some([184079.154, 2097508.031, 816974.684, 4295557.36])| + EPSG_22232|22232|"RGSH2020 / UTM zone 32N"|Projected|[6.0, 19.6, 11.99, 38.8]|Some([185287.054, 2167218.305, 813663.158, 4298858.795])| + EPSG_22234|22234|"Cape / UTM zone 34S"|Projected|[19.99, -26.88, 24.0, -17.99]|Some([393065.778, 7023619.989, 817731.3, 8011092.823])| + EPSG_22235|22235|"Cape / UTM zone 35S"|Projected|[24.0, -25.84, 29.38, -17.78]|Some([181894.368, 7138897.961, 752328.782, 8034324.684])| + EPSG_22239|22239|"NAD83(CSRS)v2 / PEI Stereographic"|Projected|[-64.49, 45.9, -61.9, 47.09]|Some([284369.899, 649937.094, 485365.777, 783292.439])| + EPSG_2224|2224|"NAD83 / Arizona West (ft)"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([371574.223, 381938.138, 1081101.152, 2185694.073])| + EPSG_22240|22240|"NAD83(CSRS)v2 / NB Stereographic"|Projected|[-69.05, 44.56, -63.7, 48.07]|Some([2297385.279, 7284384.11, 2722474.928, 7678289.703])| + EPSG_22243|22243|"NAD83(CSRS)v2 / SCoPQ zone 3"|Projected|[-60.0, 50.2, -57.1, 52.01]|Some([197716.075, 5562537.973, 404745.261, 5764942.283])| + EPSG_22244|22244|"NAD83(CSRS)v2 / SCoPQ zone 4"|Projected|[-63.0, 47.16, -60.0, 52.01]|Some([191069.356, 5224512.09, 418530.644, 5764942.283])| + EPSG_22245|22245|"NAD83(CSRS)v2 / SCoPQ zone 5"|Projected|[-66.0, 47.95, -63.0, 60.42]|Some([192766.354, 5312336.817, 416833.646, 6701138.05])| + EPSG_22246|22246|"NAD83(CSRS)v2 / SCoPQ zone 6"|Projected|[-69.0, 47.31, -66.0, 59.0]|Some([191389.906, 5241186.736, 418210.094, 6542982.089])| + EPSG_22247|22247|"NAD83(CSRS)v2 / SCoPQ zone 7"|Projected|[-72.0, 45.01, -69.0, 61.8]|Some([186562.13, 4985557.091, 423037.87, 6854869.25])| + EPSG_22248|22248|"NAD83(CSRS)v2 / SCoPQ zone 8"|Projected|[-75.0, 44.99, -72.0, 62.53]|Some([186520.979, 4983334.678, 423079.021, 6936202.617])| + EPSG_22249|22249|"NAD83(CSRS)v2 / SCoPQ zone 9"|Projected|[-78.0, 45.37, -75.0, 62.62]|Some([187305.319, 5025561.867, 422294.681, 6946230.574])| + EPSG_2225|2225|"NAD83 / California zone 1 (ftUS)"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([5871181.062, 1733933.063, 7128170.827, 2624901.545])| + EPSG_22250|22250|"NAD83(CSRS)v2 / SCoPQ zone 10"|Projected|[-79.85, 46.23, -78.0, 62.45]|Some([277803.109, 5121139.276, 420500.444, 6927288.98])| + EPSG_2226|2226|"NAD83 / California zone 2 (ftUS)"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([5968210.696, 1769106.414, 7270330.95, 2557837.816])| + EPSG_22262|22262|"NAD83(CSRS)v2 / Alberta 3TM ref merid 111 W"|Projected|[-112.5, 48.99, -109.98, 60.0]|Some([-109766.957, 5427972.754, 74642.162, 6654356.188])| + EPSG_22263|22263|"NAD83(CSRS)v2 / Alberta 3TM ref merid 114 W"|Projected|[-115.5, 48.99, -112.5, 60.0]|Some([-109766.957, 5427972.684, 109766.957, 6654356.188])| + EPSG_22264|22264|"NAD83(CSRS)v2 / Alberta 3TM ref merid 117 W"|Projected|[-118.5, 50.77, -115.5, 60.0]|Some([-105803.257, 5625936.476, 105803.257, 6654356.188])| + EPSG_22265|22265|"NAD83(CSRS)v2 / Alberta 3TM ref merid 120 W"|Projected|[-120.0, 52.88, -118.5, 60.0]|Some([0.0, 5860680.032, 100971.61, 6654356.188])| + EPSG_2227|2227|"NAD83 / California zone 3 (ftUS)"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([5823176.279, 1724173.279, 7344103.224, 2456022.577])| + EPSG_22275|22275|"Cape / Lo15"|Projected|[14.35, -23.15, 14.6, -22.68]|Some([40964.858, 2508944.861, 66798.428, 2561083.606])| + EPSG_22277|22277|"Cape / Lo17"|Projected|[16.45, -33.1, 18.0, -28.03]|Some([-98401.398, 3101474.667, 54021.238, 3663994.43])| + EPSG_22279|22279|"Cape / Lo19"|Projected|[17.99, -34.88, 20.0, -28.38]|Some([-98075.674, 3140261.685, 98940.23, 3861456.352])| + EPSG_2228|2228|"NAD83 / California zone 4 (ftUS)"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([5668908.533, 1803028.335, 7564123.003, 2475594.923])| + EPSG_22281|22281|"Cape / Lo21"|Projected|[19.99, -34.88, 22.01, -24.76]|Some([-102212.67, 2739173.995, 102108.492, 3861455.254])| + EPSG_22283|22283|"Cape / Lo23"|Projected|[22.0, -34.26, 24.01, -25.26]|Some([-101793.981, 2794559.994, 100693.961, 3792671.843])| + EPSG_22285|22285|"Cape / Lo25"|Projected|[24.0, -34.26, 26.01, -24.71]|Some([-102241.798, 2733634.186, 101149.024, 3792670.991])| + EPSG_22287|22287|"Cape / Lo27"|Projected|[26.0, -33.83, 28.0, -22.92]|Some([-102621.255, 2535380.53, 102552.283, 3744961.259])| + EPSG_22289|22289|"Cape / Lo29"|Projected|[27.99, -33.03, 30.01, -22.13]|Some([-104232.092, 2447897.325, 104175.042, 3656234.174])| + EPSG_2229|2229|"NAD83 / California zone 5 (ftUS)"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([5509907.499, 1371068.148, 7754824.418, 2503353.939])| + EPSG_22291|22291|"Cape / Lo31"|Projected|[29.99, -31.38, 32.02, -22.22]|Some([-105190.903, 2457862.957, 104114.705, 3473260.205])| + EPSG_22293|22293|"Cape / Lo33"|Projected|[31.95, -28.94, 32.95, -26.8]|Some([4858.267, 2965173.378, 104388.665, 3202776.376])| + EPSG_2230|2230|"NAD83 / California zone 6 (ftUS)"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([5976051.325, 1772620.398, 7125708.965, 2341861.472])| + EPSG_22307|22307|"NAD83(CSRS)v3 / UTM zone 7N"|Projected|[-141.01, 52.05, -138.0, 72.53]|Some([499314.26, 5766599.426, 705699.252, 8050561.199])| + EPSG_22308|22308|"NAD83(CSRS)v3 / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276483.897, 5322969.01, 723516.103, 8818432.315])| + EPSG_22309|22309|"NAD83(CSRS)v3 / UTM zone 9N"|Projected|[-132.0, 46.52, -126.0, 80.93]|Some([269894.217, 5151825.698, 730105.783, 8986755.526])| + EPSG_2231|2231|"NAD83 / Colorado North (ftUS)"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([1996488.183, 1082572.159, 3975337.797, 1630521.443])| + EPSG_22310|22310|"NAD83(CSRS)v3 / UTM zone 10N"|Projected|[-126.0, 48.13, -120.0, 81.8]|Some([276787.323, 5330749.349, 723212.677, 9083741.173])| + EPSG_22311|22311|"NAD83(CSRS)v3 / UTM zone 11N"|Projected|[-120.0, 48.99, -114.0, 83.5]|Some([280542.341, 5426344.13, 719457.659, 9273261.909])| + EPSG_22312|22312|"NAD83(CSRS)v3 / UTM zone 12N"|Projected|[-114.0, 48.99, -108.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22313|22313|"NAD83(CSRS)v3 / UTM zone 13N"|Projected|[-108.0, 48.99, -102.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22314|22314|"NAD83(CSRS)v3 / UTM zone 14N"|Projected|[-102.0, 48.99, -96.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22315|22315|"NAD83(CSRS)v3 / UTM zone 15N"|Projected|[-96.0, 48.03, -90.0, 84.0]|Some([276353.96, 5319634.608, 723646.04, 9329005.182])| + EPSG_22316|22316|"NAD83(CSRS)v3 / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 84.0]|Some([268167.8, 5106269.364, 731832.2, 9329005.182])| + EPSG_22317|22317|"NAD83(CSRS)v3 / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 84.0]|Some([250254.167, 4613137.759, 749745.833, 9329005.182])| + EPSG_22318|22318|"NAD83(CSRS)v3 / UTM zone 18N"|Projected|[-78.0, 43.63, -72.0, 84.0]|Some([257982.558, 4830779.091, 742017.442, 9329005.182])| + EPSG_22319|22319|"NAD83(CSRS)v3 / UTM zone 19N"|Projected|[-72.0, 40.8, -66.0, 84.0]|Some([246917.185, 4516555.551, 753082.815, 9329005.182])| + EPSG_2232|2232|"NAD83 / Colorado Central (ftUS)"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([1976275.555, 1111693.626, 3994982.248, 1841443.624])| + EPSG_22320|22320|"NAD83(CSRS)v3 / UTM zone 20N"|Projected|[-66.0, 40.04, -60.0, 84.0]|Some([244050.009, 4432196.843, 755949.991, 9329005.182])| + EPSG_22321|22321|"NAD83(CSRS)v3 / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.645, 4267951.154, 761403.355, 9329005.182])| + EPSG_22322|22322|"NAD83(CSRS)v3 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242040.228, 4372264.52, 758819.79, 6393731.856])| + EPSG_2233|2233|"NAD83 / Colorado South (ftUS)"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([1960286.099, 1114096.419, 4010522.005, 1752502.494])| + EPSG_22332|22332|"Carthage / UTM zone 32N"|Projected|[7.81, 33.22, 13.67, 38.41]|Some([389062.026, 3675218.967, 935316.007, 4261191.13])| + EPSG_22337|22337|"NAD83(CSRS)v3 / MTM NS 1997 zone 5"|Projected|[-66.28, 43.41, -63.0, 46.02]|Some([15355838.948, 4807789.174, 15621483.709, 5099339.87])| + EPSG_22338|22338|"NAD83(CSRS)v3 / MTM NS 1997 zone 4"|Projected|[-63.0, 44.64, -59.73, 47.08]|Some([14381003.192, 4944444.039, 14640416.322, 5217139.633])| + EPSG_2234|2234|"NAD83 / Connecticut (ftUS)"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([729403.211, 553439.369, 1267835.713, 944836.621])| + EPSG_22348|22348|"NAD83(CSRS)v3 / MTM zone 8"|Projected|[-75.0, 44.98, -74.35, 45.65]|Some([186500.409, 4982574.975, 238548.955, 5057772.925])| + EPSG_22349|22349|"NAD83(CSRS)v3 / MTM zone 9"|Projected|[-78.0, 43.63, -75.0, 46.25]|Some([183757.001, 4832228.905, 425842.999, 5124455.736])| + EPSG_2235|2235|"NAD83 / Delaware (ftUS)"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([546367.548, 160236.055, 784106.588, 674114.41])| + EPSG_22350|22350|"NAD83(CSRS)v3 / MTM zone 10"|Projected|[-81.0, 42.26, -77.99, 47.33]|Some([181042.039, 4680048.55, 429383.032, 5244515.896])| + EPSG_22351|22351|"NAD83(CSRS)v3 / MTM zone 11"|Projected|[-83.6, 41.67, -81.0, 46.0]|Some([213203.307, 4614522.598, 429705.359, 5096670.435])| + EPSG_22352|22352|"NAD83(CSRS)v3 / MTM zone 12"|Projected|[-82.5, 46.0, -79.5, 55.21]|Some([188617.115, 5095576.318, 420982.885, 6121023.073])| + EPSG_22353|22353|"NAD83(CSRS)v3 / MTM zone 13"|Projected|[-85.5, 46.0, -82.5, 55.59]|Some([188617.115, 5095576.318, 420982.885, 6163319.436])| + EPSG_22354|22354|"NAD83(CSRS)v3 / MTM zone 14"|Projected|[-88.5, 47.17, -85.5, 56.7]|Some([191090.702, 5225623.719, 418509.298, 6286883.372])| + EPSG_22355|22355|"NAD83(CSRS)v3 / MTM zone 15"|Projected|[-91.5, 47.97, -88.5, 56.9]|Some([192809.596, 5314560.385, 416790.404, 6309149.351])| + EPSG_22356|22356|"NAD83(CSRS)v3 / MTM zone 16"|Projected|[-94.5, 48.06, -91.5, 55.2]|Some([193004.354, 5324566.54, 416595.646, 6119910.044])| + EPSG_22357|22357|"NAD83(CSRS)v3 / MTM zone 17"|Projected|[-95.16, 48.69, -94.5, 53.24]|Some([360875.873, 5394954.536, 415224.583, 5901789.692])| + EPSG_2236|2236|"NAD83 / Florida East (ftUS)"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([213589.61, 27858.86, 998906.144, 2364275.146])| + EPSG_2237|2237|"NAD83 / Florida West (ftUS)"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([217029.83, 703825.629, 941268.688, 1916926.67])| + EPSG_2238|2238|"NAD83 / Florida North (ftUS)"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([969966.9, 76375.368, 2753327.28, 744477.307])| + EPSG_2239|2239|"NAD83 / Georgia East (ftUS)"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([245110.367, 130920.93, 1096662.0, 1705427.168])| + EPSG_22391|22391|"Carthage / Nord Tunisie"|Projected|[8.18, 34.65, 11.37, 37.4]|Some([342291.525, 150096.155, 634694.663, 456494.987])| + EPSG_22392|22392|"Carthage / Sud Tunisie"|Projected|[7.49, 30.23, 11.59, 34.66]|Some([267733.588, -40625.465, 662795.941, 453181.234])| + EPSG_2240|2240|"NAD83 / Georgia West (ftUS)"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([1842577.296, 225477.311, 2666701.982, 1825735.918])| + EPSG_22407|22407|"NAD83(CSRS)v4 / UTM zone 7N"|Projected|[-141.01, 52.05, -138.0, 72.53]|Some([499314.26, 5766599.426, 705699.252, 8050561.199])| + EPSG_22408|22408|"NAD83(CSRS)v4 / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276483.897, 5322969.01, 723516.103, 8818432.315])| + EPSG_22409|22409|"NAD83(CSRS)v4 / UTM zone 9N"|Projected|[-132.0, 46.52, -126.0, 80.93]|Some([269894.217, 5151825.698, 730105.783, 8986755.526])| + EPSG_2241|2241|"NAD83 / Idaho East (ftUS)"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([364381.553, 117818.241, 962450.615, 1125811.597])| + EPSG_22410|22410|"NAD83(CSRS)v4 / UTM zone 10N"|Projected|[-126.0, 48.13, -120.0, 81.8]|Some([276787.323, 5330749.349, 723212.677, 9083741.173])| + EPSG_22411|22411|"NAD83(CSRS)v4 / UTM zone 11N"|Projected|[-120.0, 48.99, -114.0, 83.5]|Some([280542.341, 5426344.13, 719457.659, 9273261.909])| + EPSG_22412|22412|"NAD83(CSRS)v4 / UTM zone 12N"|Projected|[-114.0, 48.99, -108.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22413|22413|"NAD83(CSRS)v4 / UTM zone 13N"|Projected|[-108.0, 48.99, -102.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22414|22414|"NAD83(CSRS)v4 / UTM zone 14N"|Projected|[-102.0, 48.99, -96.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22415|22415|"NAD83(CSRS)v4 / UTM zone 15N"|Projected|[-96.0, 48.03, -90.0, 84.0]|Some([276353.96, 5319634.608, 723646.04, 9329005.182])| + EPSG_22416|22416|"NAD83(CSRS)v4 / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 84.0]|Some([268167.8, 5106269.364, 731832.2, 9329005.182])| + EPSG_22417|22417|"NAD83(CSRS)v4 / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 84.0]|Some([250254.167, 4613137.759, 749745.833, 9329005.182])| + EPSG_22418|22418|"NAD83(CSRS)v4 / UTM zone 18N"|Projected|[-78.0, 43.63, -72.0, 84.0]|Some([257982.558, 4830779.091, 742017.442, 9329005.182])| + EPSG_22419|22419|"NAD83(CSRS)v4 / UTM zone 19N"|Projected|[-72.0, 40.8, -66.0, 84.0]|Some([246917.185, 4516555.551, 753082.815, 9329005.182])| + EPSG_2242|2242|"NAD83 / Idaho Central (ftUS)"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([1287011.305, 117817.469, 2001977.692, 1472978.482])| + EPSG_22420|22420|"NAD83(CSRS)v4 / UTM zone 20N"|Projected|[-66.0, 40.04, -60.0, 84.0]|Some([244050.009, 4432196.843, 755949.991, 9329005.182])| + EPSG_22421|22421|"NAD83(CSRS)v4 / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.645, 4267951.154, 761403.355, 9329005.182])| + EPSG_22422|22422|"NAD83(CSRS)v4 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242040.228, 4372264.52, 758819.79, 6393731.856])| + EPSG_2243|2243|"NAD83 / Idaho West (ftUS)"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([2219614.274, 117816.887, 3013407.856, 2680905.614])| + EPSG_2246|2246|"NAD83 / Kentucky North (ftUS)"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([1145726.971, 76476.313, 2155354.347, 605776.672])| + EPSG_22462|22462|"NAD83(CSRS)v4 / Alberta 3TM ref merid 111 W"|Projected|[-112.5, 48.99, -109.98, 60.0]|Some([-109766.957, 5427972.754, 74642.162, 6654356.188])| + EPSG_22463|22463|"NAD83(CSRS)v4 / Alberta 3TM ref merid 114 W"|Projected|[-115.5, 48.99, -112.5, 60.0]|Some([-109766.957, 5427972.684, 109766.957, 6654356.188])| + EPSG_22464|22464|"NAD83(CSRS)v4 / Alberta 3TM ref merid 117 W"|Projected|[-118.5, 50.77, -115.5, 60.0]|Some([-105803.257, 5625936.476, 105803.257, 6654356.188])| + EPSG_22465|22465|"NAD83(CSRS)v4 / Alberta 3TM ref merid 120 W"|Projected|[-120.0, 52.88, -118.5, 60.0]|Some([0.0, 5860680.032, 100971.61, 6654356.188])| + EPSG_2247|2247|"NAD83 / Kentucky South (ftUS)"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([517692.837, 1697457.607, 2757265.541, 2331367.827])| + EPSG_2248|2248|"NAD83 / Maryland (ftUS)"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([594549.654, 110480.0, 1897538.739, 761032.921])| + EPSG_2249|2249|"NAD83 / Massachusetts Mainland (ftUS)"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([107979.867, 2628256.293, 1105693.337, 3155656.2])| + EPSG_2250|2250|"NAD83 / Massachusetts Island (ftUS)"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([1527568.495, 69228.329, 1808311.967, 186415.188])| + EPSG_2251|2251|"NAD83 / Michigan North (ft)"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([25363418.889, 108192.459, 27166154.088, 1309391.423])| + EPSG_2252|2252|"NAD83 / Michigan Central (ft)"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([18973981.902, 176218.125, 20238613.052, 960520.258])| + EPSG_22521|22521|"Corrego Alegre 1970-72 / UTM zone 21S"|Projected|[-58.16, -31.91, -54.0, -17.99]|Some([377266.68, 6465614.248, 817805.295, 8010924.078])| + EPSG_22522|22522|"Corrego Alegre 1970-72 / UTM zone 22S"|Projected|[-54.0, -33.78, -48.0, -15.0]|Some([177403.535, 6258196.615, 822704.88, 8341679.929])| + EPSG_22523|22523|"Corrego Alegre 1970-72 / UTM zone 23S"|Projected|[-48.0, -25.29, -42.0, -15.0]|Some([177376.232, 7199567.838, 822677.162, 8341681.026])| + EPSG_22524|22524|"Corrego Alegre 1970-72 / UTM zone 24S"|Projected|[-42.0, -22.96, -36.0, -2.68]|Some([166384.628, 7457773.2, 833613.159, 9703781.824])| + EPSG_22525|22525|"Corrego Alegre 1970-72 / UTM zone 25S"|Projected|[-36.0, -10.1, -34.74, -4.99]|Some([167252.923, 8882026.5, 309306.165, 9448191.2])| + EPSG_2253|2253|"NAD83 / Michigan South (ft)"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([12349527.498, 69247.199, 13734275.183, 1003855.027])| + EPSG_2254|2254|"NAD83 / Mississippi East (ftUS)"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([624474.212, 185465.023, 1219524.147, 2006468.92])| + EPSG_2255|2255|"NAD83 / Mississippi West (ftUS)"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([1884030.919, 545527.105, 2598419.639, 2007131.139])| + EPSG_2256|2256|"NAD83 / Montana (ft)"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([251084.45, 36513.623, 3396283.855, 1801595.017])| + EPSG_2257|2257|"NAD83 / New Mexico East (ftUS)"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([111468.841, 363740.204, 957771.658, 2186265.545])| + EPSG_2258|2258|"NAD83 / New Mexico Central (ftUS)"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([1180523.49, 283711.114, 2078556.121, 2186656.1])| + EPSG_2259|2259|"NAD83 / New Mexico West (ftUS)"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([2340084.161, 120030.862, 3195615.082, 2186845.616])| + EPSG_2260|2260|"NAD83 / New York East (ftUS)"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([113306.679, 745481.654, 843291.664, 2257245.523])| + EPSG_22607|22607|"NAD83(CSRS)v6 / UTM zone 7N"|Projected|[-141.01, 52.05, -138.0, 72.53]|Some([499314.26, 5766599.426, 705699.252, 8050561.199])| + EPSG_22608|22608|"NAD83(CSRS)v6 / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276483.897, 5322969.01, 723516.103, 8818432.315])| + EPSG_22609|22609|"NAD83(CSRS)v6 / UTM zone 9N"|Projected|[-132.0, 46.52, -126.0, 80.93]|Some([269894.217, 5151825.698, 730105.783, 8986755.526])| + EPSG_2261|2261|"NAD83 / New York Central (ftUS)"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([503053.371, 725009.395, 1234324.252, 1610722.52])| + EPSG_22610|22610|"NAD83(CSRS)v6 / UTM zone 10N"|Projected|[-126.0, 48.13, -120.0, 81.8]|Some([276787.323, 5330749.349, 723212.677, 9083741.173])| + EPSG_22611|22611|"NAD83(CSRS)v6 / UTM zone 11N"|Projected|[-120.0, 48.99, -114.0, 83.5]|Some([280542.341, 5426344.13, 719457.659, 9273261.909])| + EPSG_22612|22612|"NAD83(CSRS)v6 / UTM zone 12N"|Projected|[-114.0, 48.99, -108.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22613|22613|"NAD83(CSRS)v6 / UTM zone 13N"|Projected|[-108.0, 48.99, -102.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22614|22614|"NAD83(CSRS)v6 / UTM zone 14N"|Projected|[-102.0, 48.99, -96.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22615|22615|"NAD83(CSRS)v6 / UTM zone 15N"|Projected|[-96.0, 48.03, -90.0, 84.0]|Some([276353.96, 5319634.608, 723646.04, 9329005.182])| + EPSG_22616|22616|"NAD83(CSRS)v6 / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 84.0]|Some([268167.8, 5106269.364, 731832.2, 9329005.182])| + EPSG_22617|22617|"NAD83(CSRS)v6 / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 84.0]|Some([250254.167, 4613137.759, 749745.833, 9329005.182])| + EPSG_22618|22618|"NAD83(CSRS)v6 / UTM zone 18N"|Projected|[-78.0, 43.63, -72.0, 84.0]|Some([257982.558, 4830779.091, 742017.442, 9329005.182])| + EPSG_22619|22619|"NAD83(CSRS)v6 / UTM zone 19N"|Projected|[-72.0, 40.8, -66.0, 84.0]|Some([246917.185, 4516555.551, 753082.815, 9329005.182])| + EPSG_2262|2262|"NAD83 / New York West (ftUS)"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([825699.68, 725009.927, 1480851.543, 1328725.001])| + EPSG_22620|22620|"NAD83(CSRS)v6 / UTM zone 20N"|Projected|[-66.0, 40.04, -60.0, 84.0]|Some([244050.009, 4432196.843, 755949.991, 9329005.182])| + EPSG_22621|22621|"NAD83(CSRS)v6 / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.645, 4267951.154, 761403.355, 9329005.182])| + EPSG_22622|22622|"NAD83(CSRS)v6 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242040.228, 4372264.52, 758819.79, 6393731.856])| + EPSG_2263|2263|"NAD83 / New York Long Island (ftUS)"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([911908.377, 110510.822, 1596307.976, 420506.527])| + EPSG_22639|22639|"NAD83(CSRS)v6 / PEI Stereographic"|Projected|[-64.49, 45.9, -61.9, 47.09]|Some([284369.899, 649937.094, 485365.777, 783292.439])| + EPSG_2264|2264|"NAD83 / North Carolina (ftUS)"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([381761.488, 29120.552, 3099350.865, 1075706.457])| + EPSG_22641|22641|"NAD83(CSRS)v6 / MTM zone 1"|Projected|[-54.5, 46.56, -52.54, 49.89]|Some([189795.031, 5157817.897, 340068.384, 5529138.193])| + EPSG_22642|22642|"NAD83(CSRS)v6 / MTM zone 2"|Projected|[-57.5, 46.81, -54.49, 54.71]|Some([190324.464, 5185606.301, 420038.695, 6065387.551])| + EPSG_22643|22643|"NAD83(CSRS)v6 / MTM zone 3"|Projected|[-59.48, 47.5, -57.5, 50.54]|Some([230970.997, 5262308.632, 380135.705, 5600831.044])| + EPSG_22644|22644|"NAD83(CSRS)v6 / MTM zone 4"|Projected|[-63.0, 52.0, -60.0, 58.92]|Some([201796.114, 5762767.216, 407803.886, 6534072.854])| + EPSG_22645|22645|"NAD83(CSRS)v6 / MTM zone 5"|Projected|[-66.0, 51.58, -63.0, 60.52]|Some([200834.78, 5716041.278, 408765.22, 6712276.997])| + EPSG_22646|22646|"NAD83(CSRS)v6 / MTM zone 6"|Projected|[-67.81, 52.05, -66.0, 55.34]|Some([283535.695, 5768330.083, 407689.068, 6135492.604])| + EPSG_22648|22648|"NAD83(CSRS)v6 / MTM zone 8"|Projected|[-75.0, 44.98, -74.35, 45.65]|Some([186500.409, 4982574.975, 238548.955, 5057772.925])| + EPSG_22649|22649|"NAD83(CSRS)v6 / MTM zone 9"|Projected|[-78.0, 43.63, -75.0, 46.25]|Some([183757.001, 4832228.905, 425842.999, 5124455.736])| + EPSG_2265|2265|"NAD83 / North Dakota North (ft)"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([1080444.859, 54719.74, 2881420.035, 754225.239])| + EPSG_22650|22650|"NAD83(CSRS)v6 / MTM zone 10"|Projected|[-81.0, 42.26, -77.99, 47.33]|Some([181042.039, 4680048.55, 429383.032, 5244515.896])| + EPSG_22651|22651|"NAD83(CSRS)v6 / MTM zone 11"|Projected|[-83.6, 41.67, -81.0, 46.0]|Some([213203.307, 4614522.598, 429705.359, 5096670.435])| + EPSG_22652|22652|"NAD83(CSRS)v6 / MTM zone 12"|Projected|[-82.5, 46.0, -79.5, 55.21]|Some([188617.115, 5095576.318, 420982.885, 6121023.073])| + EPSG_22653|22653|"NAD83(CSRS)v6 / MTM zone 13"|Projected|[-85.5, 46.0, -82.5, 55.59]|Some([188617.115, 5095576.318, 420982.885, 6163319.436])| + EPSG_22654|22654|"NAD83(CSRS)v6 / MTM zone 14"|Projected|[-88.5, 47.17, -85.5, 56.7]|Some([191090.702, 5225623.719, 418509.298, 6286883.372])| + EPSG_22655|22655|"NAD83(CSRS)v6 / MTM zone 15"|Projected|[-91.5, 47.97, -88.5, 56.9]|Some([192809.596, 5314560.385, 416790.404, 6309149.351])| + EPSG_22656|22656|"NAD83(CSRS)v6 / MTM zone 16"|Projected|[-94.5, 48.06, -91.5, 55.2]|Some([193004.354, 5324566.54, 416595.646, 6119910.044])| + EPSG_22657|22657|"NAD83(CSRS)v6 / MTM zone 17"|Projected|[-95.16, 48.69, -94.5, 53.24]|Some([360875.873, 5394954.536, 415224.583, 5901789.692])| + EPSG_2266|2266|"NAD83 / North Dakota South (ft)"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([1065407.067, 96039.809, 2973276.77, 813394.098])| + EPSG_2267|2267|"NAD83 / Oklahoma North (ftUS)"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([476488.914, 98315.947, 3037010.129, 769305.456])| + EPSG_2268|2268|"NAD83 / Oklahoma South (ftUS)"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([1359609.223, 104330.196, 3058257.314, 832905.32])| + EPSG_2269|2269|"NAD83 / Oregon North (ft)"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([7235769.707, 103310.859, 9263144.52, 970945.557])| + EPSG_2270|2270|"NAD83 / Oregon South (ft)"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([3806773.909, 114198.733, 5899922.402, 1080736.333])| + EPSG_22707|22707|"NAD83(CSRS)v7 / UTM zone 7N"|Projected|[-141.01, 52.05, -138.0, 72.53]|Some([499314.26, 5766599.426, 705699.252, 8050561.199])| + EPSG_22708|22708|"NAD83(CSRS)v7 / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276483.897, 5322969.01, 723516.103, 8818432.315])| + EPSG_22709|22709|"NAD83(CSRS)v7 / UTM zone 9N"|Projected|[-132.0, 46.52, -126.0, 80.93]|Some([269894.217, 5151825.698, 730105.783, 8986755.526])| + EPSG_2271|2271|"NAD83 / Pennsylvania North (ftUS)"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([1196594.385, 157892.487, 2815345.245, 875632.155])| + EPSG_22710|22710|"NAD83(CSRS)v7 / UTM zone 10N"|Projected|[-126.0, 48.13, -120.0, 81.8]|Some([276787.323, 5330749.349, 723212.677, 9083741.173])| + EPSG_22711|22711|"NAD83(CSRS)v7 / UTM zone 11N"|Projected|[-120.0, 48.99, -114.0, 83.5]|Some([280542.341, 5426344.13, 719457.659, 9273261.909])| + EPSG_22712|22712|"NAD83(CSRS)v7 / UTM zone 12N"|Projected|[-114.0, 48.99, -108.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22713|22713|"NAD83(CSRS)v7 / UTM zone 13N"|Projected|[-108.0, 48.99, -102.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22714|22714|"NAD83(CSRS)v7 / UTM zone 14N"|Projected|[-102.0, 48.99, -96.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22715|22715|"NAD83(CSRS)v7 / UTM zone 15N"|Projected|[-96.0, 48.03, -90.0, 84.0]|Some([276353.96, 5319634.608, 723646.04, 9329005.182])| + EPSG_22716|22716|"NAD83(CSRS)v7 / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 84.0]|Some([268167.8, 5106269.364, 731832.2, 9329005.182])| + EPSG_22717|22717|"NAD83(CSRS)v7 / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 84.0]|Some([250254.167, 4613137.759, 749745.833, 9329005.182])| + EPSG_22718|22718|"NAD83(CSRS)v7 / UTM zone 18N"|Projected|[-78.0, 43.63, -72.0, 84.0]|Some([257982.558, 4830779.091, 742017.442, 9329005.182])| + EPSG_22719|22719|"NAD83(CSRS)v7 / UTM zone 19N"|Projected|[-72.0, 40.8, -66.0, 84.0]|Some([246917.185, 4516555.551, 753082.815, 9329005.182])| + EPSG_2272|2272|"NAD83 / Pennsylvania South (ftUS)"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([1186458.044, 137223.18, 2820843.01, 687058.943])| + EPSG_22720|22720|"NAD83(CSRS)v7 / UTM zone 20N"|Projected|[-66.0, 40.04, -60.0, 84.0]|Some([244050.009, 4432196.843, 755949.991, 9329005.182])| + EPSG_22721|22721|"NAD83(CSRS)v7 / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.645, 4267951.154, 761403.355, 9329005.182])| + EPSG_22722|22722|"NAD83(CSRS)v7 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242040.228, 4372264.52, 758819.79, 6393731.856])| + EPSG_2273|2273|"NAD83 / South Carolina (ft)"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([1268683.052, 78847.627, 2768495.59, 1237555.103])| + EPSG_22739|22739|"NAD83(CSRS)v7 / PEI Stereographic"|Projected|[-64.49, 45.9, -61.9, 47.09]|Some([284369.899, 649937.094, 485365.777, 783292.439])| + EPSG_2274|2274|"NAD83 / Tennessee (ftUS)"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([677677.177, 235397.768, 3271294.763, 882581.658])| + EPSG_2275|2275|"NAD83 / Texas North (ftUS)"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([194034.147, 3390028.382, 1112258.712, 4194289.858])| + EPSG_2276|2276|"NAD83 / Texas North Central (ftUS)"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([547686.318, 6581073.199, 3367564.088, 7651634.987])| + EPSG_22762|22762|"NAD83(CSRS)v7 / Alberta 3TM ref merid 111 W"|Projected|[-112.5, 48.99, -109.98, 60.0]|Some([-109766.957, 5427972.754, 74642.162, 6654356.188])| + EPSG_22763|22763|"NAD83(CSRS)v7 / Alberta 3TM ref merid 114 W"|Projected|[-115.5, 48.99, -112.5, 60.0]|Some([-109766.957, 5427972.684, 109766.957, 6654356.188])| + EPSG_22764|22764|"NAD83(CSRS)v7 / Alberta 3TM ref merid 117 W"|Projected|[-118.5, 50.77, -115.5, 60.0]|Some([-105803.257, 5625936.476, 105803.257, 6654356.188])| + EPSG_22765|22765|"NAD83(CSRS)v7 / Alberta 3TM ref merid 120 W"|Projected|[-120.0, 52.88, -118.5, 60.0]|Some([0.0, 5860680.032, 100971.61, 6654356.188])| + EPSG_2277|2277|"NAD83 / Texas Central (ftUS)"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([290307.569, 9883770.564, 4463335.649, 10854275.232])| + EPSG_22770|22770|"Deir ez Zor / Syria Lambert"|Projected|[35.04, 32.31, 42.38, 37.3]|Some([82372.781, 40541.143, 773589.034, 605219.103])| + EPSG_2278|2278|"NAD83 / Texas South Central (ftUS)"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([28737.767, 13103938.798, 3662735.393, 14203231.984])| + EPSG_22780|22780|"Deir ez Zor / Levant Stereographic"|Projected|[35.04, 32.31, 42.38, 37.3]|Some([-387008.418, -209491.72, 304004.375, 351513.924])| + EPSG_2279|2279|"NAD83 / Texas South (ftUS)"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([425010.511, 16463541.159, 1527042.22, 17332399.035])| + EPSG_2280|2280|"NAD83 / Utah North (ft)"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([934625.219, 3359781.761, 2323990.957, 3901836.502])| + EPSG_22807|22807|"NAD83(CSRS)v8 / UTM zone 7N"|Projected|[-141.01, 52.05, -138.0, 72.53]|Some([499314.26, 5766599.426, 705699.252, 8050561.199])| + EPSG_22808|22808|"NAD83(CSRS)v8 / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276483.897, 5322969.01, 723516.103, 8818432.315])| + EPSG_22809|22809|"NAD83(CSRS)v8 / UTM zone 9N"|Projected|[-132.0, 46.52, -126.0, 80.93]|Some([269894.217, 5151825.698, 730105.783, 8986755.526])| + EPSG_2281|2281|"NAD83 / Utah Central (ft)"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([910491.089, 6618750.698, 2344593.227, 7572222.758])| + EPSG_22810|22810|"NAD83(CSRS)v8 / UTM zone 10N"|Projected|[-126.0, 48.13, -120.0, 81.8]|Some([276787.323, 5330749.349, 723212.677, 9083741.173])| + EPSG_22811|22811|"NAD83(CSRS)v8 / UTM zone 11N"|Projected|[-120.0, 48.99, -114.0, 83.5]|Some([280542.341, 5426344.13, 719457.659, 9273261.909])| + EPSG_22812|22812|"NAD83(CSRS)v8 / UTM zone 12N"|Projected|[-114.0, 48.99, -108.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22813|22813|"NAD83(CSRS)v8 / UTM zone 13N"|Projected|[-108.0, 48.99, -102.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22814|22814|"NAD83(CSRS)v8 / UTM zone 14N"|Projected|[-102.0, 48.99, -96.0, 84.0]|Some([280542.341, 5426344.13, 719457.659, 9329005.182])| + EPSG_22815|22815|"NAD83(CSRS)v8 / UTM zone 15N"|Projected|[-96.0, 48.03, -90.0, 84.0]|Some([276353.96, 5319634.608, 723646.04, 9329005.182])| + EPSG_22816|22816|"NAD83(CSRS)v8 / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 84.0]|Some([268167.8, 5106269.364, 731832.2, 9329005.182])| + EPSG_22817|22817|"NAD83(CSRS)v8 / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 84.0]|Some([250254.167, 4613137.759, 749745.833, 9329005.182])| + EPSG_22818|22818|"NAD83(CSRS)v8 / UTM zone 18N"|Projected|[-78.0, 43.63, -72.0, 84.0]|Some([257982.558, 4830779.091, 742017.442, 9329005.182])| + EPSG_22819|22819|"NAD83(CSRS)v8 / UTM zone 19N"|Projected|[-72.0, 40.8, -66.0, 84.0]|Some([246917.185, 4516555.551, 753082.815, 9329005.182])| + EPSG_2282|2282|"NAD83 / Utah South (ft)"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([895695.776, 9960255.725, 2358865.912, 10549177.844])| + EPSG_22820|22820|"NAD83(CSRS)v8 / UTM zone 20N"|Projected|[-66.0, 40.04, -60.0, 84.0]|Some([244050.009, 4432196.843, 755949.991, 9329005.182])| + EPSG_22821|22821|"NAD83(CSRS)v8 / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.645, 4267951.154, 761403.355, 9329005.182])| + EPSG_22822|22822|"NAD83(CSRS)v8 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242040.228, 4372264.52, 758819.79, 6393731.856])| + EPSG_2283|2283|"NAD83 / Virginia North (ftUS)"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([11031977.038, 6599298.223, 12058136.392, 7220874.863])| + EPSG_2284|2284|"NAD83 / Virginia South (ftUS)"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([9961814.003, 3356101.914, 12419949.373, 4030434.647])| + EPSG_2285|2285|"NAD83 / Washington North (ftUS)"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([654926.968, 29190.59, 2590236.007, 772224.407])| + EPSG_2286|2286|"NAD83 / Washington South (ftUS)"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([641400.92, 75380.599, 2560080.681, 854063.654])| + EPSG_2287|2287|"NAD83 / Wisconsin North (ftUS)"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([1225865.459, 74145.175, 2469645.826, 794687.121])| + EPSG_2288|2288|"NAD83 / Wisconsin Central (ftUS)"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([1207874.319, 53469.789, 2955328.583, 739108.439])| + EPSG_2289|2289|"NAD83 / Wisconsin South (ftUS)"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([1582728.415, 174956.315, 2791156.407, 863858.997])| + EPSG_2290|2290|"ATS77 / Prince Edward Isl. Stereographic (ATS77)"|Projected|[-64.49, 45.9, -61.9, 47.09]|Some([584369.935, 249937.142, 785365.75, 383292.445])| + EPSG_2294|2294|"ATS77 / MTM Nova Scotia zone 4"|Projected|[-63.0, 44.64, -59.73, 47.08]|Some([4381003.229, 4944442.471, 4640416.278, 5217137.979])| + EPSG_2295|2295|"ATS77 / MTM Nova Scotia zone 5"|Projected|[-66.28, 43.41, -63.0, 46.02]|Some([5355838.993, 4807787.648, 5621483.671, 5099338.252])| + EPSG_22991|22991|"Egypt 1907 / Blue Belt"|Projected|[33.0, 21.97, 36.95, 31.36]|Some([93230.888, 210352.705, 501259.178, 1252489.495])| + EPSG_22992|22992|"Egypt 1907 / Red Belt"|Projected|[29.0, 21.99, 34.27, 33.82]|Some([408265.604, -77434.085, 952656.334, 1238372.303])| + EPSG_22993|22993|"Egypt 1907 / Purple Belt"|Projected|[24.7, 28.18, 29.0, 31.68]|Some([473954.842, -1744.341, 896260.806, 388536.171])| + EPSG_22994|22994|"Egypt 1907 / Extended Purple Belt"|Projected|[24.99, 21.99, 29.01, 28.19]|Some([492238.249, 312561.638, 907441.338, 1001001.074])| + EPSG_23028|23028|"ED50 / UTM zone 28N"|Projected|[-16.1, 48.43, -12.0, 56.57]|Some([418745.818, 5364326.116, 722027.726, 6273790.16])| + EPSG_23029|23029|"ED50 / UTM zone 29N"|Projected|[-12.0, 36.13, -6.0, 62.41]|Some([230140.734, 3998576.546, 770103.482, 6923688.842])| + EPSG_23030|23030|"ED50 / UTM zone 30N"|Projected|[-6.0, 35.26, 0.01, 80.49]|Some([227184.683, 3902083.028, 773953.021, 8937972.613])| + EPSG_23031|23031|"ED50 / UTM zone 31N"|Projected|[0.0, 38.56, 6.0, 82.45]|Some([238693.456, 4268156.085, 761499.418, 9156428.267])| + EPSG_23032|23032|"ED50 / UTM zone 32N"|Projected|[6.0, 36.53, 12.0, 84.33]|Some([231482.228, 4042925.462, 768684.075, 9366014.079])| + EPSG_23033|23033|"ED50 / UTM zone 33N"|Projected|[12.0, 34.49, 18.01, 84.42]|Some([224555.78, 3816548.112, 776418.861, 9376047.049])| + EPSG_23034|23034|"ED50 / UTM zone 34N"|Projected|[17.99, 33.59, 24.01, 84.54]|Some([220642.267, 3716754.813, 779357.733, 9389420.106])| + EPSG_23035|23035|"ED50 / UTM zone 35N"|Projected|[24.0, 25.71, 30.01, 84.73]|Some([198934.655, 2843741.509, 802069.475, 9410598.654])| + EPSG_23036|23036|"ED50 / UTM zone 36N"|Projected|[30.0, 29.19, 36.0, 84.7]|Some([208253.309, 3229082.663, 791766.924, 9407245.561])| + EPSG_23037|23037|"ED50 / UTM zone 37N"|Projected|[36.0, 29.18, 42.0, 79.07]|Some([208251.56, 3228098.423, 791781.589, 8779676.39])| + EPSG_23038|23038|"ED50 / UTM zone 38N"|Projected|[42.0, 36.97, 44.83, 41.6]|Some([232932.388, 4091736.873, 485834.794, 4609895.943])| + EPSG_2308|2308|"Batavia / TM 109 SE"|Projected|[105.77, -6.89, 110.01, -4.07]|Some([141153.363, 9237246.316, 611974.294, 9550179.64])| + EPSG_2309|2309|"WGS 84 / TM 116 SE"|Projected|[112.8, -8.46, 117.01, -6.8]|Some([146228.356, 9063398.923, 611608.015, 9248360.26])| + EPSG_23090|23090|"ED50 / TM 0 N"|Projected|[-5.05, 51.03, 3.4, 62.03]|Some([146062.362, 5653374.216, 738492.015, 6888029.052])| + EPSG_23095|23095|"ED50 / TM 5 NE"|Projected|[2.53, 51.44, 6.37, 55.77]|Some([328424.909, 5698964.286, 595307.445, 6183453.481])| + EPSG_2310|2310|"WGS 84 / TM 132 SE"|Projected|[131.89, -2.94, 133.82, -1.97]|Some([487766.933, 9674873.971, 702435.731, 9782254.936])| + EPSG_2311|2311|"WGS 84 / TM 6 NE"|Projected|[2.66, 1.92, 8.49, 6.38]|Some([128337.383, 212218.529, 777007.744, 706408.931])| + EPSG_2312|2312|"Garoua / UTM zone 33N"|Projected|[12.9, 8.92, 14.19, 9.87]|Some([269202.864, 986092.361, 411312.372, 1091750.378])| + EPSG_2313|2313|"Kousseri / UTM zone 33N"|Projected|[14.17, 11.7, 15.09, 12.77]|Some([409625.51, 1293176.075, 509888.273, 1411641.045])| + EPSG_2314|2314|"Trinidad 1903 / Trinidad Grid (ftCla)"|Projected|[-62.09, 9.83, -60.0, 11.51]|Some([11209.422, -8817.614, 763388.963, 601966.616])| + EPSG_2315|2315|"Campo Inchauspe / UTM zone 19S"|Projected|[-68.62, -54.61, -66.0, -51.65]|Some([524632.635, 3944346.986, 707621.96, 4277698.269])| + EPSG_2316|2316|"Campo Inchauspe / UTM zone 20S"|Projected|[-66.0, -54.93, -61.49, -51.36]|Some([291215.213, 3908762.162, 605201.605, 4310025.105])| + EPSG_2317|2317|"PSAD56 / ICN Regional"|Projected|[-73.38, 0.64, -59.8, 12.25]|Some([176281.746, 407599.155, 1692381.997, 1697488.134])| + EPSG_2318|2318|"Ain el Abd / Aramco Lambert"|Projected|[34.51, 16.37, 55.67, 32.16]|Some([-1441011.105, -959705.424, 820316.431, 841326.633])| + EPSG_2319|2319|"ED50 / TM27"|Projected|[25.62, 36.5, 28.5, 42.11]|Some([376400.774, 4041209.978, 634432.986, 4665132.655])| + EPSG_2320|2320|"ED50 / TM30"|Projected|[28.5, 36.06, 31.5, 41.46]|Some([364885.695, 3992383.259, 635183.101, 4592933.198])| + EPSG_2321|2321|"ED50 / TM33"|Projected|[31.5, 35.97, 34.5, 42.07]|Some([364725.397, 3982395.538, 635330.294, 4660686.616])| + EPSG_2322|2322|"ED50 / TM36"|Projected|[34.5, 35.81, 37.5, 42.15]|Some([364446.192, 3964641.197, 635596.157, 4669571.728])| + EPSG_2323|2323|"ED50 / TM39"|Projected|[37.5, 36.66, 40.5, 41.19]|Some([365899.705, 4058959.72, 634129.422, 4562941.708])| + EPSG_23239|23239|"Fahud / UTM zone 39N"|Projected|[51.99, 16.59, 54.0, 19.67]|Some([603506.512, 1834214.965, 819866.358, 2177489.709])| + EPSG_2324|2324|"ED50 / TM42"|Projected|[40.5, 37.02, 43.5, 41.6]|Some([366520.303, 4098910.107, 633495.314, 4608478.343])| + EPSG_23240|23240|"Fahud / UTM zone 40N"|Projected|[54.0, 16.89, 59.91, 26.42]|Some([180072.987, 1867149.887, 809749.198, 2925444.083])| + EPSG_2325|2325|"ED50 / TM45"|Projected|[43.5, 36.97, 44.83, 41.02]|Some([366426.001, 4093374.223, 485702.86, 4544059.891])| + EPSG_2326|2326|"Hong Kong 1980 Grid System"|Projected|[113.76, 22.13, 114.51, 22.58]|Some([793259.701, 799071.107, 870635.999, 848961.791])| + EPSG_2327|2327|"Xian 1980 / Gauss-Kruger zone 13"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([13374665.346, 3921193.212, 13773407.722, 4552717.342])| + EPSG_2328|2328|"Xian 1980 / Gauss-Kruger zone 14"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([14206116.694, 3227004.803, 14791936.115, 5237245.647])| + EPSG_2329|2329|"Xian 1980 / Gauss-Kruger zone 15"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([15203003.585, 3023089.637, 15796996.415, 5453983.174])| + EPSG_2330|2330|"Xian 1980 / Gauss-Kruger zone 16"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([16204050.756, 3066306.222, 16796936.259, 5311699.124])| + EPSG_23301|23301|"NAD83(2011) / ICS83-Freeport (ftUS)"|Projected|[-90.66, 41.92, -89.39, 42.51]|Some([1610760.918, 652960.094, 1956414.589, 868775.653])| + EPSG_23302|23302|"NAD83(2011) / ICS83-Rockford (ftUS)"|Projected|[-89.69, 41.58, -88.93, 42.51]|Some([2701612.532, 612247.609, 2909554.473, 951471.835])| + EPSG_23303|23303|"NAD83(2011) / ICS83-Aurora (ftUS)"|Projected|[-88.95, 41.45, -88.19, 42.5]|Some([3649629.715, 564876.559, 3857988.361, 947841.876])| + EPSG_23304|23304|"NAD83(2011) / ICS83-Chicago (ftUS)"|Projected|[-88.27, 41.46, -87.52, 42.5]|Some([4628167.267, 568519.898, 4833751.346, 947867.941])| + EPSG_23305|23305|"NAD83(2011) / ICS83-Moline (ftUS)"|Projected|[-91.08, 41.32, -90.15, 41.79]|Some([5609143.251, 671191.417, 5864615.779, 842820.112])| + EPSG_23306|23306|"NAD83(2011) / ICS83-Sterling (ftUS)"|Projected|[-90.44, 41.14, -89.62, 41.94]|Some([6618570.52, 605603.612, 6844447.801, 897409.843])| + EPSG_23307|23307|"NAD83(2011) / ICS83-Ottawa (ftUS)"|Projected|[-89.87, 40.92, -88.24, 41.64]|Some([7516372.258, 616540.435, 7966864.07, 879951.143])| + EPSG_23308|23308|"NAD83(2011) / ICS83-Joliet (ftUS)"|Projected|[-88.27, 40.99, -87.52, 41.73]|Some([8619458.008, 652310.873, 8826519.248, 922317.535])| + EPSG_23309|23309|"NAD83(2011) / ICS83-Monmouth (ftUS)"|Projected|[-91.19, 40.62, -90.43, 41.34]|Some([9583609.315, 645014.896, 9794600.313, 907630.879])| + EPSG_2331|2331|"Xian 1980 / Gauss-Kruger zone 17"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([17188253.376, 2337471.289, 17812786.491, 4787120.08])| + EPSG_23310|23310|"NAD83(2011) / ICS83-Galesburg (ftUS)"|Projected|[-90.46, 40.18, -89.63, 41.24]|Some([129404.216, 1559147.095, 361333.488, 1945696.355])| + EPSG_23311|23311|"NAD83(2011) / ICS83-Peoria (ftUS)"|Projected|[-89.99, 40.31, -89.26, 40.98]|Some([1283174.454, 1681888.26, 1486770.513, 1926233.236])| + EPSG_23312|23312|"NAD83(2011) / ICS83-Eureka (ftUS)"|Projected|[-89.64, 40.59, -88.92, 41.15]|Some([2661567.007, 1570694.302, 2861542.781, 1774958.785])| + EPSG_23313|23313|"NAD83(2011) / ICS83-Bloomington (ftUS)"|Projected|[-89.27, 40.28, -88.45, 40.76]|Some([3655809.507, 1658849.114, 3884610.033, 1834004.974])| + EPSG_23314|23314|"NAD83(2011) / ICS83-Pontiac (ftUS)"|Projected|[-88.94, 40.61, -88.23, 41.12]|Some([4648711.157, 1633340.301, 4845852.48, 1819398.659])| + EPSG_23315|23315|"NAD83(2011) / ICS83-Watseka (ftUS)"|Projected|[-88.46, 40.39, -87.52, 41.02]|Some([5598927.656, 1607840.147, 5860786.681, 1837783.954])| + EPSG_23316|23316|"NAD83(2011) / ICS83-Quincy (ftUS)"|Projected|[-91.52, 39.75, -90.9, 40.64]|Some([6650079.051, 1584555.173, 6824416.091, 1908981.475])| + EPSG_23317|23317|"NAD83(2011) / ICS83-Macomb (ftUS)"|Projected|[-90.92, 39.83, -90.19, 40.64]|Some([7620123.667, 1595494.112, 7825154.122, 1890850.121])| + EPSG_23318|23318|"NAD83(2011) / ICS83-Lincoln (ftUS)"|Projected|[-90.59, 39.87, -89.14, 40.44]|Some([8538248.37, 1636997.907, 8945261.592, 1845628.262])| + EPSG_23319|23319|"NAD83(2011) / ICS83-Decatur (ftUS)"|Projected|[-89.22, 39.21, -88.46, 40.29]|Some([9558983.724, 1551690.325, 9774346.449, 1945389.33])| + EPSG_2332|2332|"Xian 1980 / Gauss-Kruger zone 18"|Projected|[102.0, 21.53, 108.0, 42.47]|Some([18189098.506, 2381759.84, 18810901.494, 4708209.07])| + EPSG_23320|23320|"NAD83(2011) / ICS83-Champaign (ftUS)"|Projected|[-88.47, 39.86, -87.52, 40.5]|Some([196050.369, 2716353.776, 462757.043, 2949871.993])| + EPSG_23321|23321|"NAD83(2011) / ICS83-Jacksonville (ftUS)"|Projected|[-91.38, 39.39, -89.92, 39.88]|Some([1026536.251, 2727290.965, 1439199.746, 2906735.385])| + EPSG_23322|23322|"NAD83(2011) / ICS83-Springfield (ftUS)"|Projected|[-90.0, 39.52, -89.21, 39.98]|Some([2230257.108, 2803217.503, 2453133.738, 2971088.682])| + EPSG_23323|23323|"NAD83(2011) / ICS83-Charleston (ftUS)"|Projected|[-88.48, 39.37, -87.53, 39.89]|Some([3637289.86, 2654005.709, 3905882.871, 2843787.573])| + EPSG_23324|23324|"NAD83(2011) / ICS83-Jerseyville (ftUS)"|Projected|[-90.94, 38.86, -90.14, 39.41]|Some([4631700.308, 2679863.683, 4859517.858, 2880497.757])| + EPSG_23325|23325|"NAD83(2011) / ICS83-Carlinville (ftUS)"|Projected|[-90.63, 38.99, -89.69, 39.53]|Some([5604557.464, 2643085.314, 5871757.48, 2840138.469])| + EPSG_23326|23326|"NAD83(2011) / ICS83-Taylorville (ftUS)"|Projected|[-89.71, 38.99, -89.02, 39.83]|Some([6637881.376, 2618061.629, 6834016.433, 2924264.342])| + EPSG_23327|23327|"NAD83(2011) / ICS83-Effingham (ftUS)"|Projected|[-89.64, 38.73, -88.36, 39.22]|Some([7527415.084, 2675871.36, 7892584.916, 2854980.616])| + EPSG_23328|23328|"NAD83(2011) / ICS83-Robinson (ftUS)"|Projected|[-88.48, 38.84, -87.51, 39.49]|Some([8557270.945, 2661300.538, 8833577.55, 2898430.775])| + EPSG_23329|23329|"NAD83(2011) / ICS83-Belleville (ftUS)"|Projected|[-90.38, 38.08, -89.59, 39.0]|Some([9568614.926, 2577704.944, 9796020.772, 2913031.486])| + EPSG_2333|2333|"Xian 1980 / Gauss-Kruger zone 19"|Projected|[108.0, 18.11, 114.0, 45.11]|Some([19182364.581, 2003157.779, 19817635.419, 5001552.167])| + EPSG_23330|23330|"NAD83(2011) / ICS83-Mount Vernon (ftUS)"|Projected|[-89.71, 38.12, -88.69, 38.83]|Some([166887.575, 3652818.979, 460342.614, 3911884.208])| + EPSG_23331|23331|"NAD83(2011) / ICS83-Olney (ftUS)"|Projected|[-88.71, 38.23, -87.49, 38.92]|Some([1086130.057, 3656458.755, 1436596.263, 3908433.229])| + EPSG_23332|23332|"NAD83(2011) / ICS83-Carbondale (ftUS)"|Projected|[-90.21, 37.56, -87.91, 38.26]|Some([2029755.495, 3649190.49, 2696474.577, 3906545.954])| + EPSG_23333|23333|"NAD83(2011) / ICS83-Metropolis (ftUS)"|Projected|[-89.53, 36.97, -88.06, 37.61]|Some([3457945.335, 3755255.007, 3887404.814, 3989372.119])| + EPSG_2334|2334|"Xian 1980 / Gauss-Kruger zone 20"|Projected|[114.0, 22.14, 120.0, 51.52]|Some([20190416.423, 2449303.989, 20809583.577, 5714208.919])| + EPSG_2335|2335|"Xian 1980 / Gauss-Kruger zone 21"|Projected|[120.0, 26.34, 126.0, 53.56]|Some([21200433.019, 2914504.736, 21799566.981, 5941134.37])| + EPSG_2336|2336|"Xian 1980 / Gauss-Kruger zone 22"|Projected|[126.0, 40.89, 132.0, 52.79]|Some([22247158.432, 4528359.591, 22752841.568, 5855474.418])| + EPSG_2337|2337|"Xian 1980 / Gauss-Kruger zone 23"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([23263541.627, 4987195.083, 23482969.267, 5367258.334])| + EPSG_2338|2338|"Xian 1980 / Gauss-Kruger CM 75E"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([374665.346, 3921193.212, 773407.722, 4552717.342])| + EPSG_2339|2339|"Xian 1980 / Gauss-Kruger CM 81E"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([206116.694, 3227004.803, 791936.115, 5237245.647])| + EPSG_2340|2340|"Xian 1980 / Gauss-Kruger CM 87E"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([203003.585, 3023089.637, 796996.415, 5453983.174])| + EPSG_2341|2341|"Xian 1980 / Gauss-Kruger CM 93E"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([204050.756, 3066306.222, 796936.259, 5311699.124])| + EPSG_2342|2342|"Xian 1980 / Gauss-Kruger CM 99E"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([188253.376, 2337471.289, 812786.491, 4787120.08])| + EPSG_2343|2343|"Xian 1980 / Gauss-Kruger CM 105E"|Projected|[102.0, 21.53, 108.0, 42.47]|Some([189098.506, 2381759.84, 810901.494, 4708209.07])| + EPSG_2344|2344|"Xian 1980 / Gauss-Kruger CM 111E"|Projected|[108.0, 18.11, 114.0, 45.11]|Some([182364.581, 2003157.779, 817635.419, 5001552.167])| + EPSG_2345|2345|"Xian 1980 / Gauss-Kruger CM 117E"|Projected|[114.0, 22.14, 120.0, 51.52]|Some([190416.423, 2449303.989, 809583.577, 5714208.919])| + EPSG_2346|2346|"Xian 1980 / Gauss-Kruger CM 123E"|Projected|[120.0, 26.34, 126.0, 53.56]|Some([200433.019, 2914504.736, 799566.981, 5941134.37])| + EPSG_2347|2347|"Xian 1980 / Gauss-Kruger CM 129E"|Projected|[126.0, 40.89, 132.0, 52.79]|Some([247158.432, 4528359.591, 752841.568, 5855474.418])| + EPSG_2348|2348|"Xian 1980 / Gauss-Kruger CM 135E"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([263541.627, 4987195.083, 482969.267, 5367258.334])| + EPSG_2349|2349|"Xian 1980 / 3-degree Gauss-Kruger zone 25"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([25375272.438, 3964463.241, 25635574.194, 4502789.699])| + EPSG_2350|2350|"Xian 1980 / 3-degree Gauss-Kruger zone 26"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([26356780.682, 3434302.016, 26643219.318, 4633844.823])| + EPSG_2351|2351|"Xian 1980 / 3-degree Gauss-Kruger zone 27"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([27355189.656, 3314572.352, 27645775.859, 5083859.294])| + EPSG_2352|2352|"Xian 1980 / 3-degree Gauss-Kruger zone 28"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([28352803.436, 3127256.808, 28647196.564, 5233910.91])| + EPSG_2353|2353|"Xian 1980 / 3-degree Gauss-Kruger zone 29"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([29352176.007, 3076279.612, 29647823.993, 5450731.684])| + EPSG_2354|2354|"Xian 1980 / 3-degree Gauss-Kruger zone 30"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([30350541.527, 3023089.637, 30649458.473, 5366233.536])| + EPSG_2355|2355|"Xian 1980 / 3-degree Gauss-Kruger zone 31"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([31352054.362, 3066306.212, 31647945.638, 5000488.84])| + EPSG_2356|2356|"Xian 1980 / 3-degree Gauss-Kruger zone 32"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([32352762.227, 3123932.114, 32648219.484, 4930492.519])| + EPSG_2357|2357|"Xian 1980 / 3-degree Gauss-Kruger zone 33"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([33344482.25, 2370687.503, 33655517.75, 4737151.761])| + EPSG_2358|2358|"Xian 1980 / 3-degree Gauss-Kruger zone 34"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([34344166.497, 2337471.281, 34655833.503, 4729375.425])| + EPSG_2359|2359|"Xian 1980 / 3-degree Gauss-Kruger zone 35"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([35345642.998, 2489168.479, 35655386.217, 4676068.945])| + EPSG_2360|2360|"Xian 1980 / 3-degree Gauss-Kruger zone 36"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([36341298.753, 2012012.359, 36659759.453, 4704950.678])| + EPSG_2361|2361|"Xian 1980 / 3-degree Gauss-Kruger zone 37"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([37341226.506, 2003157.779, 37658773.494, 4998266.159])| + EPSG_2362|2362|"Xian 1980 / 3-degree Gauss-Kruger zone 38"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([38344577.806, 2380652.601, 38655422.194, 5036052.734])| + EPSG_2363|2363|"Xian 1980 / 3-degree Gauss-Kruger zone 39"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([39345754.229, 2500242.252, 39654245.771, 5528581.54])| + EPSG_2364|2364|"Xian 1980 / 3-degree Gauss-Kruger zone 40"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([40347872.175, 2702917.288, 40652127.825, 5912397.962])| + EPSG_2365|2365|"Xian 1980 / 3-degree Gauss-Kruger zone 41"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([41352748.5, 3122823.872, 41647251.5, 5937993.196])| + EPSG_2366|2366|"Xian 1980 / 3-degree Gauss-Kruger zone 42"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([42372262.407, 4450628.033, 42627737.593, 5897931.5])| + EPSG_2367|2367|"Xian 1980 / 3-degree Gauss-Kruger zone 43"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([43374503.705, 4581666.698, 43625496.295, 5569734.309])| + EPSG_2368|2368|"Xian 1980 / 3-degree Gauss-Kruger zone 44"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([44376543.073, 4698291.564, 44623456.927, 5417370.163])| + EPSG_2369|2369|"Xian 1980 / 3-degree Gauss-Kruger zone 45"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([45383491.788, 5079441.547, 45482969.267, 5363995.113])| + EPSG_2370|2370|"Xian 1980 / 3-degree Gauss-Kruger CM 75E"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([375272.438, 3964463.241, 635574.194, 4502789.699])| + EPSG_23700|23700|"HD72 / EOV"|Projected|[16.11, 45.74, 22.9, 48.58]|Some([421391.21, 43912.191, 949769.508, 366660.881])| + EPSG_2371|2371|"Xian 1980 / 3-degree Gauss-Kruger CM 78E"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([356780.682, 3434302.016, 643219.318, 4633844.823])| + EPSG_2372|2372|"Xian 1980 / 3-degree Gauss-Kruger CM 81E"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([355189.656, 3314572.352, 645775.859, 5083859.294])| + EPSG_2373|2373|"Xian 1980 / 3-degree Gauss-Kruger CM 84E"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([352803.436, 3127256.808, 647196.564, 5233910.91])| + EPSG_2374|2374|"Xian 1980 / 3-degree Gauss-Kruger CM 87E"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([352176.007, 3076279.612, 647823.993, 5450731.684])| + EPSG_2375|2375|"Xian 1980 / 3-degree Gauss-Kruger CM 90E"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([350541.527, 3023089.637, 649458.473, 5366233.536])| + EPSG_2376|2376|"Xian 1980 / 3-degree Gauss-Kruger CM 93E"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([352054.362, 3066306.212, 647945.638, 5000488.84])| + EPSG_2377|2377|"Xian 1980 / 3-degree Gauss-Kruger CM 96E"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([352762.227, 3123932.114, 648219.484, 4930492.519])| + EPSG_2378|2378|"Xian 1980 / 3-degree Gauss-Kruger CM 99E"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([344482.25, 2370687.503, 655517.75, 4737151.761])| + EPSG_2379|2379|"Xian 1980 / 3-degree Gauss-Kruger CM 102E"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([344166.497, 2337471.281, 655833.503, 4729375.425])| + EPSG_2380|2380|"Xian 1980 / 3-degree Gauss-Kruger CM 105E"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([345642.998, 2489168.479, 655386.217, 4676068.945])| + EPSG_2381|2381|"Xian 1980 / 3-degree Gauss-Kruger CM 108E"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([341298.753, 2012012.359, 659759.453, 4704950.678])| + EPSG_2382|2382|"Xian 1980 / 3-degree Gauss-Kruger CM 111E"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([341226.506, 2003157.779, 658773.494, 4998266.159])| + EPSG_2383|2383|"Xian 1980 / 3-degree Gauss-Kruger CM 114E"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([344577.806, 2380652.601, 655422.194, 5036052.734])| + EPSG_23830|23830|"DGN95 / Indonesia TM-3 zone 46.2"|Projected|[95.16, 2.55, 96.0, 5.97]|Some([273069.324, 1781956.883, 366817.423, 2160312.502])| + EPSG_23831|23831|"DGN95 / Indonesia TM-3 zone 47.1"|Projected|[96.0, -1.81, 99.0, 5.42]|Some([33018.609, 1299810.89, 366981.391, 2099476.14])| + EPSG_23832|23832|"DGN95 / Indonesia TM-3 zone 47.2"|Projected|[99.0, -3.57, 102.0, 3.71]|Some([33018.381, 1105148.319, 366981.619, 1910336.457])| + EPSG_23833|23833|"DGN95 / Indonesia TM-3 zone 48.1"|Projected|[102.0, -5.99, 105.0, 1.68]|Some([33018.561, 837475.246, 366981.439, 1685810.807])| + EPSG_23834|23834|"DGN95 / Indonesia TM-3 zone 48.2"|Projected|[105.0, -7.79, 108.0, 4.11]|Some([33018.777, 638365.829, 366981.223, 1954578.932])| + EPSG_23835|23835|"DGN95 / Indonesia TM-3 zone 49.1"|Projected|[108.0, -8.31, 111.0, 4.25]|Some([33018.359, 580842.562, 366981.641, 1970063.893])| + EPSG_23836|23836|"DGN95 / Indonesia TM-3 zone 49.2"|Projected|[111.0, -8.67, 114.0, 1.59]|Some([33018.621, 541017.949, 366981.379, 1675856.607])| + EPSG_23837|23837|"DGN95 / Indonesia TM-3 zone 50.1"|Projected|[114.0, -9.15, 117.01, 4.37]|Some([33018.73, 487912.782, 368094.738, 1983338.982])| + EPSG_23838|23838|"DGN95 / Indonesia TM-3 zone 50.2"|Projected|[117.0, -10.15, 120.0, 4.36]|Some([33018.259, 377286.907, 366981.741, 1982230.686])| + EPSG_23839|23839|"DGN95 / Indonesia TM-3 zone 51.1"|Projected|[120.0, -10.98, 123.0, 1.4]|Some([33018.921, 285458.885, 366981.079, 1654842.212])| + EPSG_2384|2384|"Xian 1980 / 3-degree Gauss-Kruger CM 117E"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([345754.229, 2500242.252, 654245.771, 5528581.54])| + EPSG_23840|23840|"DGN95 / Indonesia TM-3 zone 51.2"|Projected|[123.0, -10.92, 126.0, 3.84]|Some([33018.49, 292097.208, 366981.51, 1924715.218])| + EPSG_23841|23841|"DGN95 / Indonesia TM-3 zone 52.1"|Projected|[126.0, -8.32, 129.0, 4.59]|Some([33018.779, 579736.332, 366981.221, 2007670.45])| + EPSG_23842|23842|"DGN95 / Indonesia TM-3 zone 52.2"|Projected|[129.0, -8.41, 132.0, 0.1]|Some([33018.51, 569780.238, 366981.49, 1511060.138])| + EPSG_23843|23843|"DGN95 / Indonesia TM-3 zone 53.1"|Projected|[132.0, -7.3, 135.0, -0.29]|Some([33020.383, 692569.244, 366979.617, 1467936.664])| + EPSG_23844|23844|"DGN95 / Indonesia TM-3 zone 53.2"|Projected|[135.0, -8.49, 138.0, -0.58]|Some([33026.76, 560930.341, 366973.24, 1435873.311])| + EPSG_23845|23845|"DGN95 / Indonesia TM-3 zone 54.1"|Projected|[138.0, -9.19, 141.01, -1.49]|Some([33074.366, 483487.657, 368038.73, 1335260.431])| + EPSG_23846|23846|"ID74 / UTM zone 46N"|Projected|[95.16, 2.55, 96.0, 5.97]|Some([739092.045, 282053.455, 833626.308, 660792.272])| + EPSG_23847|23847|"ID74 / UTM zone 47N"|Projected|[96.0, 0.0, 102.0, 5.42]|Some([165995.578, -2.652, 833953.781, 599912.551])| + EPSG_23848|23848|"ID74 / UTM zone 48N"|Projected|[102.0, 0.0, 108.0, 4.11]|Some([165994.259, -3.142, 833952.513, 454909.978])| + EPSG_23849|23849|"ID74 / UTM zone 49N"|Projected|[108.0, 0.0, 114.0, 4.25]|Some([165992.991, -3.488, 833951.31, 470405.748])| + EPSG_2385|2385|"Xian 1980 / 3-degree Gauss-Kruger CM 120E"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([347872.175, 2702917.288, 652127.825, 5912397.962])| + EPSG_23850|23850|"ID74 / UTM zone 50N"|Projected|[114.0, 0.0, 120.0, 4.37]|Some([165991.787, -3.685, 833950.185, 483687.961])| + EPSG_23851|23851|"ID74 / UTM zone 51N"|Projected|[120.0, 0.0, 125.71, 3.84]|Some([165990.662, -3.733, 801638.941, 425023.653])| + EPSG_23852|23852|"ID74 / UTM zone 52N"|Projected|[126.55, 0.0, 131.0, 4.59]|Some([227260.991, -3.728, 722564.87, 507806.552])| + EPSG_2386|2386|"Xian 1980 / 3-degree Gauss-Kruger CM 123E"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([352748.5, 3122823.872, 647251.5, 5937993.196])| + EPSG_23866|23866|"DGN95 / UTM zone 46N"|Projected|[92.01, 0.0, 96.0, 7.79]|Some([389832.268, 0.0, 833978.557, 862257.189])| + EPSG_23867|23867|"DGN95 / UTM zone 47N"|Projected|[96.0, 0.0, 102.0, 7.49]|Some([166021.443, 0.0, 833978.557, 829048.09])| + EPSG_23868|23868|"DGN95 / UTM zone 48N"|Projected|[102.0, 0.0, 108.0, 6.94]|Some([166021.443, 0.0, 833978.557, 768165.583])| + EPSG_23869|23869|"DGN95 / UTM zone 49N"|Projected|[108.0, 0.0, 114.0, 7.37]|Some([166021.443, 0.0, 833978.557, 815764.543])| + EPSG_2387|2387|"Xian 1980 / 3-degree Gauss-Kruger CM 126E"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([372262.407, 4450628.033, 627737.593, 5897931.5])| + EPSG_23870|23870|"DGN95 / UTM zone 50N"|Projected|[114.0, 0.0, 120.0, 4.37]|Some([166021.443, 0.0, 833978.557, 483690.46])| + EPSG_23871|23871|"DGN95 / UTM zone 51N"|Projected|[120.0, 0.0, 126.0, 5.48]|Some([166021.443, 0.0, 833978.557, 606554.937])| + EPSG_23872|23872|"DGN95 / UTM zone 52N"|Projected|[126.0, 0.0, 132.0, 6.68]|Some([166021.443, 0.0, 833978.557, 739385.121])| + EPSG_23877|23877|"DGN95 / UTM zone 47S"|Projected|[96.0, -8.86, 102.0, 0.0]|Some([166021.443, 9019294.169, 833978.557, 10000000.0])| + EPSG_23878|23878|"DGN95 / UTM zone 48S"|Projected|[102.0, -10.73, 108.01, 0.0]|Some([166021.443, 8812262.988, 835092.849, 10000000.0])| + EPSG_23879|23879|"DGN95 / UTM zone 49S"|Projected|[108.0, -12.07, 114.0, 0.0]|Some([166021.443, 8663916.572, 833978.557, 10000000.0])| + EPSG_2388|2388|"Xian 1980 / 3-degree Gauss-Kruger CM 129E"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([374503.705, 4581666.698, 625496.295, 5569734.309])| + EPSG_23880|23880|"DGN95 / UTM zone 50S"|Projected|[114.0, -13.06, 120.01, 0.0]|Some([166021.443, 8554289.996, 835092.849, 10000000.0])| + EPSG_23881|23881|"DGN95 / UTM zone 51S"|Projected|[120.0, -13.95, 126.01, 0.01]|Some([166021.448, 8455742.409, 835092.844, 10001106.838])| + EPSG_23882|23882|"DGN95 / UTM zone 52S"|Projected|[126.0, -9.45, 132.0, 0.0]|Some([166021.443, 8953979.307, 833978.557, 10000000.0])| + EPSG_23883|23883|"DGN95 / UTM zone 53S"|Projected|[132.0, -10.06, 138.01, 0.0]|Some([166021.443, 8886438.653, 835092.849, 10000000.0])| + EPSG_23884|23884|"DGN95 / UTM zone 54S"|Projected|[138.0, -10.84, 141.46, 0.0]|Some([166021.443, 8800095.483, 551187.037, 10000000.0])| + EPSG_23887|23887|"ID74 / UTM zone 47S"|Projected|[98.24, -3.57, 102.0, 0.0]|Some([415403.1, 9604855.596, 833953.781, 9999997.729])| + EPSG_23888|23888|"ID74 / UTM zone 48S"|Projected|[102.0, -7.79, 108.0, 0.0]|Some([165994.259, 9137738.252, 833952.513, 9999997.348])| + EPSG_23889|23889|"ID74 / UTM zone 49S"|Projected|[108.0, -8.67, 114.0, 0.0]|Some([165992.991, 9040322.275, 833951.31, 9999996.858])| + EPSG_2389|2389|"Xian 1980 / 3-degree Gauss-Kruger CM 132E"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([376543.073, 4698291.564, 623456.927, 5417370.163])| + EPSG_23890|23890|"ID74 / UTM zone 50S"|Projected|[114.0, -10.15, 120.0, 0.0]|Some([165991.787, 8876479.021, 833950.185, 9999996.512])| + EPSG_23891|23891|"ID74 / UTM zone 51S"|Projected|[120.0, -10.98, 126.0, 0.0]|Some([165990.662, 8784589.262, 833949.15, 9999996.315])| + EPSG_23892|23892|"ID74 / UTM zone 52S"|Projected|[126.0, -8.41, 132.0, 0.0]|Some([165989.627, 9069103.515, 833948.217, 9999996.369])| + EPSG_23893|23893|"ID74 / UTM zone 53S"|Projected|[132.0, -8.49, 138.0, -0.29]|Some([165992.961, 9060247.426, 833943.154, 9967942.689])| + EPSG_23894|23894|"ID74 / UTM zone 54S"|Projected|[138.0, -9.19, 141.01, -1.49]|Some([166100.343, 8982756.025, 501079.798, 9835306.237])| + EPSG_2390|2390|"Xian 1980 / 3-degree Gauss-Kruger CM 135E"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([383491.788, 5079441.547, 482969.267, 5363995.113])| + EPSG_2391|2391|"KKJ / Finland zone 1"|Projected|[19.5, 59.76, 22.5, 69.33]|Some([1415885.568, 6627483.644, 1584487.541, 7695113.038])| + EPSG_2392|2392|"KKJ / Finland zone 2"|Projected|[22.5, 59.75, 25.5, 68.9]|Some([2415851.964, 6626360.402, 2584504.306, 7647149.121])| + EPSG_2393|2393|"KKJ / Finland Uniform Coordinate System"|Projected|[25.5, 60.18, 28.51, 70.09]|Some([3416929.226, 6674258.824, 3583964.707, 7779866.56])| + EPSG_2394|2394|"KKJ / Finland zone 4"|Projected|[28.5, 60.94, 31.5, 69.81]|Some([4418851.112, 6758931.247, 4581470.193, 7748619.834])| + EPSG_23946|23946|"Indian 1954 / UTM zone 46N"|Projected|[92.2, 15.66, 96.01, 27.14]|Some([414272.851, 1731188.725, 822667.042, 3005265.723])| + EPSG_23947|23947|"Indian 1954 / UTM zone 47N"|Projected|[95.99, 5.63, 102.01, 28.55]|Some([166560.915, 621998.703, 833824.289, 3161562.44])| + EPSG_23948|23948|"Indian 1954 / UTM zone 48N"|Projected|[102.0, 6.02, 105.64, 18.44]|Some([168281.609, 665105.425, 571248.714, 2041167.519])| + EPSG_2395|2395|"South Yemen / Gauss-Kruger zone 8"|Projected|[43.37, 12.54, 48.01, 17.95]|Some([8322872.587, 1386748.111, 8827309.883, 1987955.202])| + EPSG_2396|2396|"South Yemen / Gauss-Kruger zone 9"|Projected|[48.0, 13.94, 53.14, 19.0]|Some([9175711.337, 1541632.811, 9731327.293, 2104285.019])| + EPSG_2397|2397|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 3"|Projected|[9.92, 50.35, 10.5, 51.56]|Some([3563928.4, 5580334.374, 3606886.635, 5715606.438])| + EPSG_2398|2398|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 4"|Projected|[10.5, 48.97, 13.5, 54.74]|Some([4390176.295, 5426386.82, 4609946.512, 6069468.31])| + EPSG_2399|2399|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 5"|Projected|[13.5, 46.54, 16.5, 54.72]|Some([5384939.346, 5156201.335, 5615183.77, 6067236.771])| + EPSG_2401|2401|"Beijing 1954 / 3-degree Gauss-Kruger zone 25"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([25375270.405, 3964531.66, 25635576.403, 4502867.203])| + EPSG_2402|2402|"Beijing 1954 / 3-degree Gauss-Kruger zone 26"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([26356778.343, 3434361.425, 26643221.657, 4633924.528])| + EPSG_2403|2403|"Beijing 1954 / 3-degree Gauss-Kruger zone 27"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([27355187.289, 3314629.719, 27645778.241, 5083946.531])| + EPSG_2404|2404|"Beijing 1954 / 3-degree Gauss-Kruger zone 28"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([28352801.029, 3127310.974, 28647198.971, 5234000.648])| + EPSG_24047|24047|"Indian 1975 / UTM zone 47N"|Projected|[97.34, 5.63, 102.01, 20.46]|Some([316655.451, 621957.13, 833992.277, 2264917.993])| + EPSG_24048|24048|"Indian 1975 / UTM zone 48N"|Projected|[102.0, 6.02, 105.64, 18.44]|Some([168449.364, 665058.055, 571416.026, 2041120.239])| + EPSG_2405|2405|"Beijing 1954 / 3-degree Gauss-Kruger zone 29"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([29352173.589, 3076332.906, 29647826.411, 5450825.027])| + EPSG_2406|2406|"Beijing 1954 / 3-degree Gauss-Kruger zone 30"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([30350539.081, 3023142.02, 30649460.919, 5366325.476])| + EPSG_2407|2407|"Beijing 1954 / 3-degree Gauss-Kruger zone 31"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([31352051.942, 3066359.335, 31647948.058, 5000574.685])| + EPSG_2408|2408|"Beijing 1954 / 3-degree Gauss-Kruger zone 32"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([32352759.819, 3123986.223, 32648221.908, 4930577.194])| + EPSG_2409|2409|"Beijing 1954 / 3-degree Gauss-Kruger zone 33"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([33344479.7, 2370728.674, 33655520.3, 4737233.2])| + EPSG_2410|2410|"Beijing 1954 / 3-degree Gauss-Kruger zone 34"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([34344163.942, 2337511.88, 34655836.058, 4729456.733])| + EPSG_24100|24100|"Jamaica 1875 / Jamaica (Old Grid)"|Projected|[-78.43, 17.64, -76.17, 18.58]|Some([52117.087, 269285.573, 838982.891, 612517.898])| + EPSG_2411|2411|"Beijing 1954 / 3-degree Gauss-Kruger zone 35"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([35345640.468, 2489211.692, 35655388.764, 4676149.359])| + EPSG_2412|2412|"Beijing 1954 / 3-degree Gauss-Kruger zone 36"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([36341296.148, 2012073.398, 36659709.203, 4705031.577])| + EPSG_2413|2413|"Beijing 1954 / 3-degree Gauss-Kruger zone 37"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([37341171.472, 2003192.605, 37658776.101, 4998351.967])| + EPSG_2414|2414|"Beijing 1954 / 3-degree Gauss-Kruger zone 38"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([38344525.745, 2380708.641, 38655367.376, 5036139.173])| + EPSG_2415|2415|"Beijing 1954 / 3-degree Gauss-Kruger zone 39"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([39345694.776, 2500285.656, 39654248.299, 5528676.175])| + EPSG_2416|2416|"Beijing 1954 / 3-degree Gauss-Kruger zone 40"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([40347869.683, 2702964.178, 40652130.317, 5912498.946])| + EPSG_2417|2417|"Beijing 1954 / 3-degree Gauss-Kruger zone 41"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([41352746.092, 3122877.962, 41647253.908, 5938094.602])| + EPSG_2418|2418|"Beijing 1954 / 3-degree Gauss-Kruger zone 42"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([42372260.329, 4450704.66, 42627739.671, 5898032.245])| + EPSG_2419|2419|"Beijing 1954 / 3-degree Gauss-Kruger zone 43"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([43374501.666, 4581745.528, 43625498.334, 5569829.626])| + EPSG_2420|2420|"Beijing 1954 / 3-degree Gauss-Kruger zone 44"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([44376541.067, 4698372.352, 44623458.933, 5417462.952])| + EPSG_24200|24200|"JAD69 / Jamaica National Grid"|Projected|[-78.43, 17.64, -76.17, 18.58]|Some([98138.868, 109866.998, 337965.307, 214492.805])| + EPSG_2421|2421|"Beijing 1954 / 3-degree Gauss-Kruger zone 45"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([45383489.899, 5079528.711, 45482968.991, 5364087.015])| + EPSG_2422|2422|"Beijing 1954 / 3-degree Gauss-Kruger CM 75E"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([375270.405, 3964531.66, 635576.403, 4502867.203])| + EPSG_2423|2423|"Beijing 1954 / 3-degree Gauss-Kruger CM 78E"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([356778.343, 3434361.425, 643221.657, 4633924.528])| + EPSG_2424|2424|"Beijing 1954 / 3-degree Gauss-Kruger CM 81E"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([355187.289, 3314629.719, 645778.241, 5083946.531])| + EPSG_2425|2425|"Beijing 1954 / 3-degree Gauss-Kruger CM 84E"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([352801.029, 3127310.974, 647198.971, 5234000.648])| + EPSG_2426|2426|"Beijing 1954 / 3-degree Gauss-Kruger CM 87E"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([352173.589, 3076332.906, 647826.411, 5450825.027])| + EPSG_2427|2427|"Beijing 1954 / 3-degree Gauss-Kruger CM 90E"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([350539.081, 3023142.02, 649460.919, 5366325.476])| + EPSG_2428|2428|"Beijing 1954 / 3-degree Gauss-Kruger CM 93E"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([352051.942, 3066359.335, 647948.058, 5000574.685])| + EPSG_2429|2429|"Beijing 1954 / 3-degree Gauss-Kruger CM 96E"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([352759.819, 3123986.223, 648221.908, 4930577.194])| + EPSG_2430|2430|"Beijing 1954 / 3-degree Gauss-Kruger CM 99E"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([344479.7, 2370728.674, 655520.3, 4737233.2])| + EPSG_24305|24305|"Kalianpur 1937 / UTM zone 45N"|Projected|[88.01, 21.59, 90.0, 26.64]|Some([600770.35, 2387500.938, 810889.841, 2949781.448])| + EPSG_24306|24306|"Kalianpur 1937 / UTM zone 46N"|Projected|[90.0, 20.52, 92.67, 25.29]|Some([187442.685, 2268766.638, 467098.142, 2800139.392])| + EPSG_2431|2431|"Beijing 1954 / 3-degree Gauss-Kruger CM 102E"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([344163.942, 2337511.88, 655836.058, 4729456.733])| + EPSG_24311|24311|"Kalianpur 1962 / UTM zone 41N"|Projected|[60.86, 24.98, 66.01, 29.87]|Some([283929.588, 2762477.641, 803818.668, 3307925.165])| + EPSG_24312|24312|"Kalianpur 1962 / UTM zone 42N"|Projected|[66.0, 23.64, 72.01, 36.56]|Some([193950.013, 2614114.249, 807110.437, 4050016.383])| + EPSG_24313|24313|"Kalianpur 1962 / UTM zone 43N"|Projected|[72.0, 28.21, 77.83, 37.07]|Some([205629.961, 3120206.443, 777869.405, 4106580.571])| + EPSG_2432|2432|"Beijing 1954 / 3-degree Gauss-Kruger CM 105E"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([345640.468, 2489211.692, 655388.764, 4676149.359])| + EPSG_2433|2433|"Beijing 1954 / 3-degree Gauss-Kruger CM 108E"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([341296.148, 2012073.398, 659709.203, 4705031.577])| + EPSG_2434|2434|"Beijing 1954 / 3-degree Gauss-Kruger CM 111E"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([341171.472, 2003192.605, 658776.101, 4998351.967])| + EPSG_24342|24342|"Kalianpur 1975 / UTM zone 42N"|Projected|[68.13, 20.64, 72.01, 28.22]|Some([409375.667, 2282046.716, 813691.352, 3124987.661])| + EPSG_24343|24343|"Kalianpur 1975 / UTM zone 43N"|Projected|[72.0, 8.02, 78.01, 35.51]|Some([169367.711, 886247.233, 831924.532, 3933512.324])| + EPSG_24344|24344|"Kalianpur 1975 / UTM zone 44N"|Projected|[78.0, 8.29, 84.01, 35.5]|Some([169672.333, 916094.246, 831782.648, 3932394.515])| + EPSG_24345|24345|"Kalianpur 1975 / UTM zone 45N"|Projected|[84.0, 18.18, 90.01, 28.14]|Some([182875.626, 2009826.372, 818695.046, 3116098.527])| + EPSG_24346|24346|"Kalianpur 1975 / UTM zone 46N"|Projected|[90.0, 21.94, 96.01, 29.42]|Some([190439.948, 2425898.905, 811259.681, 3257997.196])| + EPSG_24347|24347|"Kalianpur 1975 / UTM zone 47N"|Projected|[96.0, 27.1, 97.42, 29.47]|Some([202948.012, 2998186.556, 347207.464, 3263496.921])| + EPSG_2435|2435|"Beijing 1954 / 3-degree Gauss-Kruger CM 114E"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([344525.745, 2380708.641, 655367.376, 5036139.173])| + EPSG_2436|2436|"Beijing 1954 / 3-degree Gauss-Kruger CM 117E"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([345694.776, 2500285.656, 654248.299, 5528676.175])| + EPSG_2437|2437|"Beijing 1954 / 3-degree Gauss-Kruger CM 120E"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([347869.683, 2702964.178, 652130.317, 5912498.946])| + EPSG_24370|24370|"Kalianpur 1880 / India zone 0"|Projected|[71.18, 35.58, 77.01, 37.07]|Some([2664461.392, 2120182.957, 3247667.331, 2339165.756])| + EPSG_24371|24371|"Kalianpur 1880 / India zone I"|Projected|[60.86, 28.0, 81.64, 35.59]|Some([2231240.688, 454629.309, 4465703.704, 1460882.104])| + EPSG_24372|24372|"Kalianpur 1880 / India zone IIa"|Projected|[61.59, 21.0, 82.01, 28.01]|Some([1587687.026, 394444.542, 3912373.249, 1306572.652])| + EPSG_24373|24373|"Kalianpur 1880 / India zone IIIa"|Projected|[70.14, 15.0, 87.15, 21.01]|Some([1839658.713, 516134.983, 3841632.741, 1274453.627])| + EPSG_24374|24374|"Kalianpur 1880 / India zone IVa"|Projected|[73.94, 8.02, 80.4, 15.01]|Some([2268717.502, 518806.147, 3048273.345, 1371719.143])| + EPSG_24375|24375|"Kalianpur 1937 / India zone IIb"|Projected|[88.01, 20.52, 92.67, 26.64]|Some([2535242.915, 307272.632, 3022837.691, 987874.979])| + EPSG_24376|24376|"Kalianpur 1962 / India zone I"|Projected|[60.86, 28.0, 77.83, 35.59]|Some([2040158.152, 415703.234, 3710473.212, 1297978.155])| + EPSG_24377|24377|"Kalianpur 1962 / India zone IIa"|Projected|[61.59, 23.64, 71.91, 28.01]|Some([1479437.387, 654900.425, 2537856.069, 1194724.658])| + EPSG_24378|24378|"Kalianpur 1975 / India zone I"|Projected|[70.35, 28.0, 81.64, 35.51]|Some([2956401.921, 418236.2, 4083618.608, 1327112.122])| + EPSG_24379|24379|"Kalianpur 1975 / India zone IIa"|Projected|[68.13, 21.0, 82.01, 28.01]|Some([2131630.958, 360597.853, 3577663.504, 1160977.9])| + EPSG_2438|2438|"Beijing 1954 / 3-degree Gauss-Kruger CM 123E"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([352746.092, 3122877.962, 647253.908, 5938094.602])| + EPSG_24380|24380|"Kalianpur 1975 / India zone IIb"|Projected|[82.0, 21.0, 97.42, 29.47]|Some([1910147.516, 360590.622, 3516480.538, 1322399.198])| + EPSG_24381|24381|"Kalianpur 1975 / India zone IIIa"|Projected|[70.14, 15.0, 87.15, 21.01]|Some([1682201.023, 471813.886, 3513047.277, 1165278.672])| + EPSG_24382|24382|"Kalianpur 1880 / India zone IIb"|Projected|[82.0, 21.0, 101.17, 29.47]|Some([2088764.37, 394474.751, 4271558.361, 1470859.558])| + EPSG_24383|24383|"Kalianpur 1975 / India zone IVa"|Projected|[73.94, 8.02, 80.4, 15.01]|Some([2074587.54, 474198.523, 2787505.184, 1254160.63])| + EPSG_2439|2439|"Beijing 1954 / 3-degree Gauss-Kruger CM 126E"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([372260.329, 4450704.66, 627739.671, 5898032.245])| + EPSG_2440|2440|"Beijing 1954 / 3-degree Gauss-Kruger CM 129E"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([374501.666, 4581745.528, 625498.334, 5569829.626])| + EPSG_2441|2441|"Beijing 1954 / 3-degree Gauss-Kruger CM 132E"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([376541.067, 4698372.352, 623458.933, 5417462.952])| + EPSG_2442|2442|"Beijing 1954 / 3-degree Gauss-Kruger CM 135E"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([383489.899, 5079528.711, 482968.991, 5364087.015])| + EPSG_2443|2443|"JGD2000 / Japan Plane Rectangular CS I"|Projected|[128.17, 26.96, 130.46, 34.74]|Some([-132049.508, -669482.511, 95311.499, 193787.213])| + EPSG_2444|2444|"JGD2000 / Japan Plane Rectangular CS II"|Projected|[129.76, 30.18, 132.05, 33.99]|Some([-119419.183, -312649.517, 101119.969, 110486.304])| + EPSG_2445|2445|"JGD2000 / Japan Plane Rectangular CS III"|Projected|[130.81, 33.72, 133.49, 36.38]|Some([-125737.064, -252913.371, 122647.486, 43016.411])| + EPSG_2446|2446|"JGD2000 / Japan Plane Rectangular CS IV"|Projected|[131.95, 32.69, 134.81, 34.45]|Some([-145349.824, -34375.821, 122842.247, 161904.197])| + EPSG_2447|2447|"JGD2000 / Japan Plane Rectangular CS V"|Projected|[133.13, 34.13, 135.47, 35.71]|Some([-110992.003, -207439.917, 104842.551, -31506.672])| + EPSG_2448|2448|"JGD2000 / Japan Plane Rectangular CS VI"|Projected|[134.86, 33.4, 136.99, 36.33]|Some([-106045.514, -288402.568, 92091.564, 37217.083])| + EPSG_2449|2449|"JGD2000 / Japan Plane Rectangular CS VII"|Projected|[136.22, 34.51, 137.84, 37.58]|Some([-86924.201, -165291.634, 61825.867, 175742.246])| + EPSG_2450|2450|"JGD2000 / Japan Plane Rectangular CS VIII"|Projected|[137.32, 34.54, 139.91, 38.58]|Some([-108311.316, -161963.928, 129424.253, 287250.64])| + EPSG_24500|24500|"Kertau 1968 / Singapore Grid"|Projected|[103.59, 1.13, 104.07, 1.47]|Some([921.475, 12573.671, 54344.475, 50170.818])| + EPSG_2451|2451|"JGD2000 / Japan Plane Rectangular CS IX"|Projected|[138.4, 29.31, 141.11, 37.98]|Some([-139237.374, -741839.214, 124017.003, 220682.801])| + EPSG_2452|2452|"JGD2000 / Japan Plane Rectangular CS X"|Projected|[139.49, 37.73, 142.14, 41.58]|Some([-118410.456, -251974.013, 115178.264, 176312.8])| + EPSG_2453|2453|"JGD2000 / Japan Plane Rectangular CS XI"|Projected|[139.34, 41.34, 141.46, 43.42]|Some([-76161.028, -295460.047, 101269.482, -63724.196])| + EPSG_2454|2454|"JGD2000 / Japan Plane Rectangular CS XII"|Projected|[140.89, 42.15, 143.61, 45.54]|Some([-112401.852, -205503.727, 112401.852, 172018.666])| + EPSG_24547|24547|"Kertau 1968 / UTM zone 47N"|Projected|[99.59, 2.29, 102.01, 6.72]|Some([565327.585, 253124.029, 834949.237, 743815.739])| + EPSG_24548|24548|"Kertau 1968 / UTM zone 48N"|Projected|[102.0, 1.21, 105.82, 7.81]|Some([166305.514, 133736.451, 591437.822, 864464.969])| + EPSG_2455|2455|"JGD2000 / Japan Plane Rectangular CS XIII"|Projected|[142.61, 41.87, 145.87, 44.4]|Some([-136139.903, -236601.142, 134479.611, 45750.328])| + EPSG_2456|2456|"JGD2000 / Japan Plane Rectangular CS XIV"|Projected|[141.2, 24.67, 142.33, 27.8]|Some([-80968.069, -147319.884, 33398.738, 199680.336])| + EPSG_2457|2457|"JGD2000 / Japan Plane Rectangular CS XV"|Projected|[126.63, 26.02, 128.4, 26.91]|Some([-87081.022, 2215.628, 90083.966, 101131.166])| + EPSG_2458|2458|"JGD2000 / Japan Plane Rectangular CS XVI"|Projected|[122.83, 23.98, 125.51, 24.94]|Some([-119061.641, -223738.677, 153665.544, -116567.494])| + EPSG_2459|2459|"JGD2000 / Japan Plane Rectangular CS XVII"|Projected|[131.12, 24.4, 131.38, 26.01]|Some([12011.913, -177218.482, 38541.557, 1163.086])| + EPSG_2460|2460|"JGD2000 / Japan Plane Rectangular CS XVIII"|Projected|[136.02, 20.37, 136.16, 20.48]|Some([2086.316, 40957.476, 16702.411, 53142.346])| + EPSG_24600|24600|"KOC Lambert"|Projected|[46.54, 28.53, 48.48, 30.09]|Some([1648331.871, 727043.022, 1840871.568, 904395.604])| + EPSG_2461|2461|"JGD2000 / Japan Plane Rectangular CS XIX"|Projected|[153.91, 24.22, 154.05, 24.35]|Some([-9141.113, -197159.042, 5078.395, -182758.419])| + EPSG_2462|2462|"Albanian 1987 / Gauss-Kruger zone 4"|Projected|[19.22, 39.64, 21.06, 42.67]|Some([4347329.277, 4389687.45, 4505281.87, 4727729.264])| + EPSG_2463|2463|"Pulkovo 1995 / Gauss-Kruger CM 21E"|Projected|[19.57, 54.32, 22.87, 55.32]|Some([407078.563, 6021665.634, 621806.593, 6134579.544])| + EPSG_2464|2464|"Pulkovo 1995 / Gauss-Kruger CM 27E"|Projected|[26.61, 55.69, 30.0, 69.47]|Some([475597.854, 6174169.652, 688768.501, 7712859.806])| + EPSG_2465|2465|"Pulkovo 1995 / Gauss-Kruger CM 33E"|Projected|[30.0, 50.34, 36.0, 70.02]|Some([286571.495, 5578774.416, 713669.567, 7774141.155])| + EPSG_2466|2466|"Pulkovo 1995 / Gauss-Kruger CM 39E"|Projected|[36.0, 43.18, 42.01, 69.23]|Some([256199.043, 4782814.756, 744842.424, 7686109.75])| + EPSG_2467|2467|"Pulkovo 1995 / Gauss-Kruger CM 45E"|Projected|[42.0, 41.19, 48.0, 80.91]|Some([248412.074, 4561763.64, 751801.579, 8988214.079])| + EPSG_2468|2468|"Pulkovo 1995 / Gauss-Kruger CM 51E"|Projected|[48.0, 41.39, 54.0, 81.4]|Some([249172.224, 4583968.22, 751024.511, 9042848.809])| + EPSG_2469|2469|"Pulkovo 1995 / Gauss-Kruger CM 57E"|Projected|[54.0, 50.47, 60.0, 81.91]|Some([287125.015, 5593200.007, 713053.479, 9099715.968])| + EPSG_2470|2470|"Pulkovo 1995 / Gauss-Kruger CM 63E"|Projected|[60.0, 50.66, 66.0, 81.77]|Some([287969.906, 5614328.533, 712187.439, 9084093.545])| + EPSG_2471|2471|"Pulkovo 1995 / Gauss-Kruger CM 69E"|Projected|[66.0, 54.1, 72.0, 77.07]|Some([303840.534, 5997105.171, 696294.246, 8559974.898])| + EPSG_24718|24718|"La Canoa / UTM zone 18N"|Projected|[-73.38, 7.02, -71.99, 11.62]|Some([676845.111, 776635.603, 832823.379, 1286642.296])| + EPSG_24719|24719|"La Canoa / UTM zone 19N"|Projected|[-72.0, 0.73, -66.0, 12.25]|Some([166254.224, 81046.001, 834162.375, 1356378.026])| + EPSG_2472|2472|"Pulkovo 1995 / Gauss-Kruger CM 75E"|Projected|[72.0, 53.17, 78.0, 79.71]|Some([299462.038, 5893592.9, 700648.159, 8854348.472])| + EPSG_24720|24720|"La Canoa / UTM zone 20N"|Projected|[-66.0, 0.64, -59.8, 11.23]|Some([166227.077, 71094.833, 856432.972, 1243670.514])| + EPSG_2473|2473|"Pulkovo 1995 / Gauss-Kruger CM 81E"|Projected|[78.0, 50.69, 84.0, 81.03]|Some([288069.003, 5617650.359, 712015.183, 9001547.007])| + EPSG_2474|2474|"Pulkovo 1995 / Gauss-Kruger CM 87E"|Projected|[84.0, 49.07, 90.0, 81.27]|Some([280836.423, 5437460.987, 719220.874, 9028308.266])| + EPSG_2475|2475|"Pulkovo 1995 / Gauss-Kruger CM 93E"|Projected|[90.0, 49.89, 96.0, 81.35]|Some([284455.314, 5528658.105, 715574.669, 9037227.52])| + EPSG_2476|2476|"Pulkovo 1995 / Gauss-Kruger CM 99E"|Projected|[96.0, 49.73, 102.0, 81.32]|Some([283729.146, 5510861.296, 716273.034, 9033880.745])| + EPSG_2477|2477|"Pulkovo 1995 / Gauss-Kruger CM 105E"|Projected|[102.0, 49.64, 108.0, 79.48]|Some([283315.282, 5500851.549, 716659.022, 8828687.74])| + EPSG_2478|2478|"Pulkovo 1995 / Gauss-Kruger CM 111E"|Projected|[108.0, 49.14, 114.0, 76.81]|Some([281089.45, 5445244.604, 718857.157, 8530968.069])| + EPSG_2479|2479|"Pulkovo 1995 / Gauss-Kruger CM 117E"|Projected|[114.0, 49.51, 120.0, 75.96]|Some([282711.075, 5486396.75, 717208.456, 8436200.139])| + EPSG_2480|2480|"Pulkovo 1995 / Gauss-Kruger CM 123E"|Projected|[120.0, 51.51, 126.0, 74.0]|Some([291693.161, 5708876.884, 708200.296, 8217692.742])| + EPSG_2481|2481|"Pulkovo 1995 / Gauss-Kruger CM 129E"|Projected|[126.0, 42.25, 132.0, 73.61]|Some([252346.86, 4679467.39, 747520.469, 8174222.464])| + EPSG_24817|24817|"PSAD56 / UTM zone 17N"|Projected|[-80.18, 0.0, -78.0, 1.45]|Some([591467.037, 364.203, 834227.7, 160856.784])| + EPSG_24818|24818|"PSAD56 / UTM zone 18N"|Projected|[-78.0, 0.0, -71.99, 11.62]|Some([166244.291, 366.263, 835322.185, 1286642.296])| + EPSG_24819|24819|"PSAD56 / UTM zone 19N"|Projected|[-72.0, 0.73, -66.0, 12.68]|Some([166254.224, 81046.001, 834162.375, 1403638.764])| + EPSG_2482|2482|"Pulkovo 1995 / Gauss-Kruger CM 135E"|Projected|[132.0, 42.63, 138.0, 76.15]|Some([253827.78, 4721683.193, 746015.795, 8457401.206])| + EPSG_24820|24820|"PSAD56 / UTM zone 20N"|Projected|[-66.0, 0.64, -59.99, 11.23]|Some([166227.077, 71094.789, 835260.616, 1243451.156])| + EPSG_24821|24821|"PSAD56 / UTM zone 21N"|Projected|[-60.0, 1.18, -56.47, 8.58]|Some([166255.224, 130793.56, 559126.93, 950063.935])| + EPSG_2483|2483|"Pulkovo 1995 / Gauss-Kruger CM 141E"|Projected|[138.0, 45.84, 144.0, 76.27]|Some([266855.775, 5078372.857, 732965.973, 8470788.751])| + EPSG_2484|2484|"Pulkovo 1995 / Gauss-Kruger CM 147E"|Projected|[144.0, 43.6, 150.0, 76.82]|Some([257666.355, 4829457.316, 742134.921, 8532121.18])| + EPSG_2485|2485|"Pulkovo 1995 / Gauss-Kruger CM 153E"|Projected|[150.0, 45.77, 156.0, 76.26]|Some([266543.731, 5070607.003, 733239.622, 8469693.393])| + EPSG_2486|2486|"Pulkovo 1995 / Gauss-Kruger CM 159E"|Projected|[156.0, 50.27, 162.0, 77.2]|Some([286021.454, 5570973.351, 713746.544, 8574512.53])| + EPSG_2487|2487|"Pulkovo 1995 / Gauss-Kruger CM 165E"|Projected|[162.0, 54.47, 168.0, 70.03]|Some([305401.414, 6038335.34, 694353.711, 7775249.265])| + EPSG_24877|24877|"PSAD56 / UTM zone 17S"|Projected|[-81.41, -10.53, -78.0, 0.0]|Some([454625.551, 8834782.302, 834227.7, 10000375.861])| + EPSG_24878|24878|"PSAD56 / UTM zone 18S"|Projected|[-78.0, -43.5, -71.99, 0.0]|Some([166244.291, 5179547.397, 835322.185, 10000371.368])| + EPSG_24879|24879|"PSAD56 / UTM zone 19S"|Projected|[-72.0, -43.5, -66.0, -2.14]|Some([166450.824, 5179563.252, 833952.755, 9763843.642])| + EPSG_2488|2488|"Pulkovo 1995 / Gauss-Kruger CM 171E"|Projected|[168.0, 54.45, 174.0, 70.19]|Some([305300.935, 6036119.536, 694443.48, 7793092.753])| + EPSG_24880|24880|"PSAD56 / UTM zone 20S"|Projected|[-66.0, -22.87, -60.0, -9.67]|Some([170897.952, 7468109.196, 829412.371, 8931461.957])| + EPSG_24881|24881|"PSAD56 / UTM zone 21S"|Projected|[-60.0, -20.17, -57.52, -16.27]|Some([179456.474, 7767256.26, 445786.595, 8201514.821])| + EPSG_24882|24882|"PSAD56 / UTM zone 22S"|Projected|[-51.64, -1.05, -48.0, 5.6]|Some([428942.566, 9884148.111, 834137.381, 10620199.393])| + EPSG_2489|2489|"Pulkovo 1995 / Gauss-Kruger CM 177E"|Projected|[174.0, 61.65, 180.0, 71.59]|Some([340912.923, 6838054.256, 658824.391, 7949139.888])| + EPSG_24891|24891|"PSAD56 / Peru west zone"|Projected|[-81.41, -8.32, -79.0, -3.38]|Some([121132.619, 1170370.32, 388930.806, 1716881.425])| + EPSG_24892|24892|"PSAD56 / Peru central zone"|Projected|[-79.0, -16.57, -73.0, -0.03]|Some([386338.938, 256314.356, 1054117.232, 2086902.258])| + EPSG_24893|24893|"PSAD56 / Peru east zone"|Projected|[-73.0, -18.39, -68.67, -2.14]|Some([1046140.385, 55503.4, 1527717.298, 1854014.227])| + EPSG_2490|2490|"Pulkovo 1995 / Gauss-Kruger CM 177W"|Projected|[-180.0, 64.35, -174.0, 71.65]|Some([354950.736, 7139013.854, 644781.793, 7955840.475])| + EPSG_2491|2491|"Pulkovo 1995 / Gauss-Kruger CM 171W"|Projected|[-174.0, 64.2, -168.97, 67.18]|Some([354160.628, 7122304.172, 598473.704, 7457718.368])| + EPSG_2494|2494|"Pulkovo 1942 / Gauss-Kruger CM 21E"|Projected|[19.57, 47.95, 24.0, 59.44]|Some([393311.436, 5313002.348, 724209.475, 6595660.829])| + EPSG_2495|2495|"Pulkovo 1942 / Gauss-Kruger CM 27E"|Projected|[24.0, 45.18, 30.01, 69.47]|Some([264323.516, 5005069.355, 736707.502, 7712882.618])| + EPSG_2496|2496|"Pulkovo 1942 / Gauss-Kruger CM 33E"|Projected|[30.0, 44.32, 36.0, 70.02]|Some([260792.863, 4909489.524, 739442.427, 7774144.124])| + EPSG_2497|2497|"Pulkovo 1942 / Gauss-Kruger CM 39E"|Projected|[36.0, 41.43, 42.0, 69.23]|Some([249337.884, 4588428.31, 750883.149, 7686092.064])| + EPSG_2498|2498|"Pulkovo 1942 / Gauss-Kruger CM 45E"|Projected|[42.0, 38.84, 48.01, 80.91]|Some([239615.436, 4300832.574, 761461.845, 8988222.666])| + EPSG_2499|2499|"Pulkovo 1942 / Gauss-Kruger CM 51E"|Projected|[48.0, 37.34, 54.0, 81.4]|Some([234221.968, 4134327.363, 765968.277, 9042847.212])| + EPSG_2500|2500|"Pulkovo 1942 / Gauss-Kruger CM 57E"|Projected|[54.0, 37.05, 60.0, 81.91]|Some([233192.164, 4102135.436, 766977.869, 9099712.916])| + EPSG_25000|25000|"Leigon / Ghana Metre Grid"|Projected|[-3.79, 1.4, 2.1, 11.16]|Some([-36241.845, -361446.915, 619363.469, 719420.593])| + EPSG_2501|2501|"Pulkovo 1942 / Gauss-Kruger CM 63E"|Projected|[60.0, 35.14, 66.0, 81.77]|Some([226634.717, 3890194.514, 773511.637, 9084089.076])| + EPSG_2502|2502|"Pulkovo 1942 / Gauss-Kruger CM 69E"|Projected|[66.0, 36.67, 72.0, 77.07]|Some([231843.104, 4059952.286, 768280.091, 8559969.162])| + EPSG_2503|2503|"Pulkovo 1942 / Gauss-Kruger CM 75E"|Projected|[72.0, 36.79, 78.0, 79.71]|Some([232247.905, 4073263.623, 767849.342, 8854341.365])| + EPSG_2504|2504|"Pulkovo 1942 / Gauss-Kruger CM 81E"|Projected|[78.0, 41.04, 84.01, 81.03]|Some([247769.313, 4545065.159, 753144.781, 9001547.239])| + EPSG_2505|2505|"Pulkovo 1942 / Gauss-Kruger CM 87E"|Projected|[84.0, 46.82, 90.0, 81.27]|Some([271096.995, 5187278.682, 728947.048, 9028298.761])| + EPSG_2506|2506|"Pulkovo 1942 / Gauss-Kruger CM 93E"|Projected|[90.0, 49.89, 96.0, 81.35]|Some([284449.126, 5528648.758, 715568.137, 9037216.977])| + EPSG_2507|2507|"Pulkovo 1942 / Gauss-Kruger CM 99E"|Projected|[96.0, 49.73, 102.0, 81.32]|Some([283721.763, 5510850.995, 716265.315, 9033869.281])| + EPSG_2508|2508|"Pulkovo 1942 / Gauss-Kruger CM 105E"|Projected|[102.0, 49.64, 108.0, 79.48]|Some([283306.668, 5500840.387, 716650.084, 8828675.512])| + EPSG_2509|2509|"Pulkovo 1942 / Gauss-Kruger CM 111E"|Projected|[108.0, 49.14, 114.0, 76.81]|Some([281079.401, 5445232.723, 718846.794, 8530955.236])| + EPSG_2510|2510|"Pulkovo 1942 / Gauss-Kruger CM 117E"|Projected|[114.0, 49.51, 120.0, 75.96]|Some([282699.881, 5486384.198, 717196.977, 8436186.796])| + EPSG_2511|2511|"Pulkovo 1942 / Gauss-Kruger CM 123E"|Projected|[120.0, 51.51, 126.0, 74.0]|Some([291681.397, 5708863.687, 708188.301, 8217679.061])| + EPSG_2512|2512|"Pulkovo 1942 / Gauss-Kruger CM 129E"|Projected|[126.0, 42.25, 132.0, 73.61]|Some([252330.493, 4679454.411, 747503.78, 8174208.545])| + EPSG_2513|2513|"Pulkovo 1942 / Gauss-Kruger CM 135E"|Projected|[132.0, 42.63, 138.0, 76.15]|Some([253810.29, 4721669.871, 745998.032, 8457387.139])| + EPSG_2514|2514|"Pulkovo 1942 / Gauss-Kruger CM 141E"|Projected|[138.0, 45.84, 144.0, 76.27]|Some([266838.112, 5078359.194, 732948.118, 8470774.745])| + EPSG_2515|2515|"Pulkovo 1942 / Gauss-Kruger CM 147E"|Projected|[144.0, 43.6, 150.0, 76.82]|Some([257646.756, 4829443.722, 742115.157, 8532107.385])| + EPSG_2516|2516|"Pulkovo 1942 / Gauss-Kruger CM 153E"|Projected|[150.0, 45.77, 156.0, 76.26]|Some([266523.655, 5070593.451, 733219.449, 8469679.966])| + EPSG_2517|2517|"Pulkovo 1942 / Gauss-Kruger CM 159E"|Projected|[156.0, 50.27, 162.0, 77.2]|Some([286001.657, 5570960.016, 713726.722, 8574499.614])| + EPSG_2518|2518|"Pulkovo 1942 / Gauss-Kruger CM 165E"|Projected|[162.0, 54.47, 168.0, 70.03]|Some([305381.84, 6038322.476, 694334.165, 7775236.972])| + EPSG_2519|2519|"Pulkovo 1942 / Gauss-Kruger CM 171E"|Projected|[168.0, 54.45, 174.0, 70.19]|Some([305280.311, 6036107.29, 694422.921, 7793081.207])| + EPSG_2520|2520|"Pulkovo 1942 / Gauss-Kruger CM 177E"|Projected|[174.0, 61.65, 180.0, 71.59]|Some([340893.574, 6838042.968, 658805.129, 7949129.236])| + EPSG_2521|2521|"Pulkovo 1942 / Gauss-Kruger CM 177W"|Projected|[-180.0, 64.35, -174.0, 71.65]|Some([354931.327, 7139003.599, 644762.485, 7955830.817])| + EPSG_2522|2522|"Pulkovo 1942 / Gauss-Kruger CM 171W"|Projected|[-174.0, 64.2, -168.97, 67.18]|Some([354140.353, 7122294.961, 598453.528, 7457709.668])| + EPSG_2523|2523|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 7"|Projected|[19.57, 48.24, 22.5, 59.1]|Some([7393910.616, 5345247.776, 7611543.594, 6554912.743])| + EPSG_25231|25231|"Lome / UTM zone 31N"|Projected|[-0.15, 2.91, 2.42, 11.14]|Some([149749.846, 321632.146, 436666.409, 1233177.309])| + EPSG_2524|2524|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 8"|Projected|[22.5, 47.71, 25.5, 59.75]|Some([8387561.932, 5286310.682, 8612688.32, 6627306.779])| + EPSG_2525|2525|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 9"|Projected|[25.49, 45.26, 28.51, 68.93]|Some([9381603.194, 5013960.262, 9618641.882, 7650491.461])| + EPSG_2526|2526|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 10"|Projected|[28.5, 45.18, 31.5, 69.85]|Some([10382220.809, 5005064.057, 10618020.038, 7753078.442])| + EPSG_2527|2527|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 11"|Projected|[31.5, 44.32, 34.5, 70.02]|Some([11380456.274, 4909489.524, 11619779.008, 7772032.116])| + EPSG_2528|2528|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 12"|Projected|[34.5, 44.61, 37.5, 69.39]|Some([12381044.734, 4941709.69, 12619185.09, 7701761.866])| + EPSG_2529|2529|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 13"|Projected|[37.5, 43.07, 40.5, 68.8]|Some([13377937.585, 4770596.572, 13622284.668, 7635957.332])| + EPSG_2530|2530|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 14"|Projected|[40.5, 41.01, 43.51, 68.74]|Some([14373923.3, 4541778.989, 14627136.048, 7629269.331])| + EPSG_2531|2531|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 15"|Projected|[43.5, 38.84, 46.5, 80.8]|Some([15369869.533, 4300832.564, 15630339.274, 8974908.559])| + EPSG_2532|2532|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 16"|Projected|[46.5, 38.31, 49.5, 80.91]|Some([16368904.522, 4241994.537, 16631294.915, 8987181.489])| + EPSG_2533|2533|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 17"|Projected|[49.5, 37.66, 52.5, 81.22]|Some([17367737.006, 4169843.141, 17632453.463, 9021780.89])| + EPSG_2534|2534|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 18"|Projected|[52.5, 37.33, 55.5, 81.41]|Some([18367148.293, 4133213.723, 18633032.237, 9042985.114])| + EPSG_2535|2535|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 19"|Projected|[55.5, 37.64, 58.5, 81.89]|Some([19367691.669, 4167615.843, 19632478.791, 9096563.728])| + EPSG_2536|2536|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 20"|Projected|[58.5, 35.51, 61.5, 81.91]|Some([20363997.252, 3931247.753, 20636160.783, 9098791.09])| + EPSG_2537|2537|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 21"|Projected|[61.5, 35.14, 64.5, 81.77]|Some([21363369.879, 3890194.514, 21636776.472, 9083157.167])| + EPSG_2538|2538|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 22"|Projected|[64.5, 36.27, 67.5, 81.25]|Some([22365280.658, 4015568.354, 22634854.612, 9025103.25])| + EPSG_2539|2539|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 23"|Projected|[67.5, 36.93, 70.5, 77.07]|Some([23366418.364, 4088805.335, 23633705.021, 8558534.679])| + EPSG_25391|25391|"Luzon 1911 / Philippines zone I"|Projected|[116.89, 7.75, 118.0, 9.32]|Some([487713.015, 857018.696, 610156.731, 1030806.756])| + EPSG_25392|25392|"Luzon 1911 / Philippines zone II"|Projected|[118.0, 8.82, 120.07, 11.58]|Some([389833.79, 975353.867, 617560.623, 1280841.319])| + EPSG_25393|25393|"Luzon 1911 / Philippines zone III"|Projected|[119.7, 4.99, 122.21, 19.45]|Some([355822.83, 551829.946, 634039.271, 2151958.438])| + EPSG_25394|25394|"Luzon 1911 / Philippines zone IV"|Projected|[121.74, 6.35, 124.29, 18.58]|Some([360432.226, 702220.189, 642578.553, 2055634.971])| + EPSG_25395|25395|"Luzon 1911 / Philippines zone V"|Projected|[123.73, 5.5, 126.65, 14.15]|Some([359110.593, 608226.201, 682700.812, 1565394.136])| + EPSG_2540|2540|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 24"|Projected|[70.5, 36.67, 73.5, 73.57]|Some([24365959.287, 4059949.544, 24634151.083, 8167972.995])| + EPSG_2541|2541|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 25"|Projected|[73.5, 37.22, 76.5, 79.71]|Some([25366913.688, 4120983.735, 25633183.883, 8853185.272])| + EPSG_2542|2542|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 26"|Projected|[76.5, 40.44, 79.5, 81.03]|Some([26372776.915, 4478438.547, 26627309.595, 9000528.669])| + EPSG_2543|2543|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 27"|Projected|[79.5, 41.82, 82.5, 81.03]|Some([27375407.7, 4631693.934, 27624660.678, 9000525.587])| + EPSG_2544|2544|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 28"|Projected|[82.5, 45.11, 85.5, 77.56]|Some([28381985.611, 4997214.288, 28618071.377, 8613204.194])| + EPSG_2545|2545|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 29"|Projected|[85.5, 47.05, 88.5, 77.16]|Some([29386046.288, 5212847.507, 29613997.941, 8568559.719])| + EPSG_2546|2546|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 30"|Projected|[88.5, 49.44, 91.5, 81.28]|Some([30391230.853, 5478600.283, 30608800.828, 9028426.292])| + EPSG_2547|2547|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 31"|Projected|[91.5, 50.16, 94.5, 81.26]|Some([31392825.342, 5558680.534, 31607192.142, 9026192.066])| + EPSG_2548|2548|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 32"|Projected|[94.5, 49.73, 97.5, 81.35]|Some([32391859.104, 5510851.754, 32608143.021, 9036237.724])| + EPSG_2549|2549|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 33"|Projected|[97.5, 49.79, 100.5, 80.9]|Some([33391984.986, 5517524.415, 33608002.126, 8986003.039])| + EPSG_2551|2551|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 34"|Projected|[100.5, 50.17, 103.5, 79.71]|Some([34392825.139, 5559790.57, 34607147.193, 8853167.719])| + EPSG_2552|2552|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 35"|Projected|[103.5, 50.13, 106.5, 79.21]|Some([35392728.112, 5555341.173, 35607229.056, 8797357.386])| + EPSG_2553|2553|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 36"|Projected|[106.5, 49.25, 109.5, 78.4]|Some([36390765.904, 5457465.676, 36609175.39, 8706948.69])| + EPSG_2554|2554|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 37"|Projected|[109.5, 49.14, 112.5, 76.81]|Some([37390515.865, 5445232.723, 37609410.312, 8529493.96])| + EPSG_2555|2555|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 38"|Projected|[112.5, 49.49, 115.5, 76.7]|Some([38391281.435, 5484158.765, 38608630.14, 8517219.022])| + EPSG_2556|2556|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 39"|Projected|[115.5, 49.51, 118.5, 74.43]|Some([39391318.334, 5486384.198, 39608578.506, 8263910.747])| + EPSG_2557|2557|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 40"|Projected|[118.5, 49.87, 121.5, 73.63]|Some([40392110.662, 5526425.626, 40607771.932, 8174651.778])| + EPSG_2558|2558|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 41"|Projected|[121.5, 53.18, 124.5, 74.0]|Some([41399652.781, 5894690.088, 41600218.349, 8215936.345])| + EPSG_2559|2559|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 42"|Projected|[124.5, 49.88, 127.5, 74.0]|Some([42392118.684, 5527541.5, 42607735.545, 8215938.919])| + EPSG_2560|2560|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 43"|Projected|[127.5, 42.67, 130.5, 73.59]|Some([43376952.858, 4726108.679, 43622881.739, 8170196.599])| + EPSG_2561|2561|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 44"|Projected|[130.5, 42.25, 133.5, 71.99]|Some([44376121.228, 4679456.572, 44623699.719, 7991698.158])| + EPSG_2562|2562|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 45"|Projected|[133.5, 42.74, 136.5, 75.9]|Some([45377077.751, 4733889.339, 45622730.648, 8427959.782])| + EPSG_2563|2563|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 46"|Projected|[136.5, 44.76, 139.5, 76.27]|Some([46381136.126, 4958327.949, 46618661.326, 8469253.902])| + EPSG_2564|2564|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 47"|Projected|[139.5, 45.84, 142.5, 76.23]|Some([47383364.48, 5078359.194, 47616421.74, 8464794.351])| + EPSG_2565|2565|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 48"|Projected|[142.5, 43.61, 145.5, 75.98]|Some([48378791.597, 4830551.372, 48620981.351, 8436900.165])| + EPSG_2566|2566|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 49"|Projected|[145.5, 43.6, 148.5, 76.76]|Some([49378765.936, 4829443.722, 49620995.97, 8523951.081])| + EPSG_2567|2567|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 50"|Projected|[148.5, 45.21, 151.5, 76.82]|Some([50382039.161, 5008350.447, 50617713.443, 8530652.341])| + EPSG_2568|2568|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 51"|Projected|[151.5, 46.72, 154.5, 76.26]|Some([51385194.168, 5176191.633, 51614549.626, 8468162.41])| + EPSG_2569|2569|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 52"|Projected|[154.5, 49.02, 157.5, 77.2]|Some([52390156.123, 5431927.642, 52609580.038, 8573072.605])| + EPSG_2570|2570|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 53"|Projected|[157.5, 51.36, 160.5, 71.12]|Some([53395387.58, 5692216.805, 53604341.601, 7894691.889])| + EPSG_2571|2571|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 54"|Projected|[160.5, 54.34, 163.5, 70.98]|Some([54402304.744, 6023846.519, 54597418.588, 7879081.583])| + EPSG_2572|2572|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 55"|Projected|[163.5, 54.69, 166.5, 69.82]|Some([55403131.725, 6062811.986, 55596584.433, 7749704.112])| + EPSG_2573|2573|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 56"|Projected|[166.5, 54.45, 169.5, 70.07]|Some([56402558.237, 6036101.672, 56597151.003, 7777593.54])| + EPSG_2574|2574|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 57"|Projected|[169.5, 59.86, 172.5, 70.19]|Some([57415804.764, 6638579.125, 57583902.507, 7790984.198])| + EPSG_2575|2575|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 58"|Projected|[172.5, 60.99, 175.5, 70.02]|Some([58418669.297, 6764489.661, 58581033.494, 7772030.015])| + EPSG_2576|2576|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 59"|Projected|[175.5, 62.09, 178.5, 71.1]|Some([59421488.812, 6887078.28, 59578210.212, 7892499.626])| + EPSG_2578|2578|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 61"|Projected|[-178.5, 64.74, -175.5, 71.61]|Some([61428401.531, 7182483.241, 61571292.565, 7949403.446])| + EPSG_2579|2579|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 62"|Projected|[-175.5, 64.2, -172.5, 67.78]|Some([62426977.92, 7122288.034, 62572713.448, 7522253.007])| + EPSG_2580|2580|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 63"|Projected|[-172.5, 64.35, -169.57, 67.06]|Some([63427371.581, 7139017.648, 63568936.729, 7441979.009])| + EPSG_2581|2581|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 64"|Projected|[-169.22, 65.7, -168.97, 65.86]|Some([64443803.842, 7289884.389, 64455562.401, 7307922.569])| + EPSG_2582|2582|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 21E"|Projected|[19.57, 48.24, 22.5, 59.1]|Some([393910.616, 5345247.776, 611543.594, 6554912.743])| + EPSG_25828|25828|"ETRS89 / UTM zone 28N"|Projected|[-16.1, 34.93, -11.99, 72.44]|Some([399535.629, 3865280.383, 774945.079, 8040550.06])| + EPSG_25829|25829|"ETRS89 / UTM zone 29N"|Projected|[-12.0, 34.91, -6.0, 74.13]|Some([225902.081, 3863062.481, 774097.919, 8228847.09])| + EPSG_2583|2583|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 24E"|Projected|[22.5, 47.71, 25.5, 59.75]|Some([387561.932, 5286310.682, 612688.32, 6627306.779])| + EPSG_25830|25830|"ETRS89 / UTM zone 30N"|Projected|[-6.0, 35.26, 0.01, 80.49]|Some([227071.857, 3901876.707, 773838.184, 8937716.117])| + EPSG_25831|25831|"ETRS89 / UTM zone 31N"|Projected|[0.0, 37.0, 6.01, 82.45]|Some([233037.88, 4094872.382, 767852.221, 9156211.355])| + EPSG_25832|25832|"ETRS89 / UTM zone 32N"|Projected|[6.0, 36.53, 12.01, 84.01]|Some([231401.819, 4042735.82, 769493.75, 9330126.13])| + EPSG_25833|25833|"ETRS89 / UTM zone 33N"|Projected|[12.0, 34.79, 18.01, 84.01]|Some([225503.37, 3849755.277, 775411.914, 9330126.13])| + EPSG_25834|25834|"ETRS89 / UTM zone 34N"|Projected|[18.0, 34.76, 24.01, 84.01]|Some([225403.881, 3846428.515, 775511.736, 9330126.13])| + EPSG_25835|25835|"ETRS89 / UTM zone 35N"|Projected|[24.0, 41.24, 30.01, 84.01]|Some([248597.584, 4565399.907, 752240.526, 9330126.13])| + EPSG_25836|25836|"ETRS89 / UTM zone 36N"|Projected|[30.0, 42.56, 36.01, 84.01]|Some([253727.659, 4711955.441, 747093.314, 9330126.13])| + EPSG_25837|25837|"ETRS89 / UTM zone 37N"|Projected|[36.0, 72.99, 38.01, 79.07]|Some([402080.15, 8099631.325, 479045.428, 8779418.626])| + EPSG_2584|2584|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 27E"|Projected|[25.49, 45.26, 28.51, 68.93]|Some([381603.194, 5013960.262, 618641.882, 7650491.461])| + EPSG_2585|2585|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 30E"|Projected|[28.5, 45.18, 31.5, 69.85]|Some([382220.809, 5005064.057, 618020.038, 7753078.442])| + EPSG_2586|2586|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 33E"|Projected|[31.5, 44.32, 34.5, 70.02]|Some([380456.274, 4909489.524, 619779.008, 7772032.116])| + EPSG_2587|2587|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 36E"|Projected|[34.5, 44.61, 37.5, 69.39]|Some([381044.734, 4941709.69, 619185.09, 7701761.866])| + EPSG_2588|2588|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 39E"|Projected|[37.5, 43.07, 40.5, 68.8]|Some([377937.585, 4770596.572, 622284.668, 7635957.332])| + EPSG_25884|25884|"ETRS89 / TM Baltic93"|Projected|[19.02, 53.89, 28.24, 60.0]|Some([172829.274, 5971284.941, 778584.587, 6661868.848])| + EPSG_2589|2589|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 42E"|Projected|[40.5, 41.01, 43.51, 68.74]|Some([373923.3, 4541778.989, 627136.048, 7629269.331])| + EPSG_2590|2590|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 45E"|Projected|[43.5, 38.84, 46.5, 80.8]|Some([369869.533, 4300832.564, 630339.274, 8974908.559])| + EPSG_2591|2591|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 48E"|Projected|[46.5, 38.31, 49.5, 80.91]|Some([368904.522, 4241994.537, 631294.915, 8987181.489])| + EPSG_2592|2592|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 51E"|Projected|[49.5, 37.66, 52.5, 81.22]|Some([367737.006, 4169843.141, 632453.463, 9021780.89])| + EPSG_2593|2593|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 54E"|Projected|[52.5, 37.33, 55.5, 81.41]|Some([367148.293, 4133213.723, 633032.237, 9042985.114])| + EPSG_25932|25932|"Malongo 1987 / UTM zone 32S"|Projected|[10.53, -6.04, 12.37, -5.05]|Some([669297.491, 9331318.009, 873730.373, 9441709.355])| + EPSG_2594|2594|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 57E"|Projected|[55.5, 37.64, 58.5, 81.89]|Some([367691.669, 4167615.843, 632478.791, 9096563.728])| + EPSG_2595|2595|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 60E"|Projected|[58.5, 35.51, 61.5, 81.91]|Some([363997.252, 3931247.753, 636160.783, 9098791.09])| + EPSG_2596|2596|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 63E"|Projected|[61.5, 35.14, 64.5, 81.77]|Some([363369.879, 3890194.514, 636776.472, 9083157.167])| + EPSG_2597|2597|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 66E"|Projected|[64.5, 36.27, 67.5, 81.25]|Some([365280.658, 4015568.354, 634854.612, 9025103.25])| + EPSG_2598|2598|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 69E"|Projected|[67.5, 36.93, 70.5, 77.07]|Some([366418.364, 4088805.335, 633705.021, 8558534.679])| + EPSG_2599|2599|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 72E"|Projected|[70.5, 36.67, 73.5, 73.57]|Some([365959.287, 4059949.544, 634151.083, 8167972.995])| + EPSG_2601|2601|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 75E"|Projected|[73.5, 37.22, 76.5, 79.71]|Some([366913.688, 4120983.735, 633183.883, 8853185.272])| + EPSG_2602|2602|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 78E"|Projected|[76.5, 40.44, 79.5, 81.03]|Some([372776.915, 4478438.547, 627309.595, 9000528.669])| + EPSG_2603|2603|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 81E"|Projected|[79.5, 41.82, 82.5, 81.03]|Some([375407.7, 4631693.934, 624660.678, 9000525.587])| + EPSG_2604|2604|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 84E"|Projected|[82.5, 45.11, 85.5, 77.56]|Some([381985.611, 4997214.288, 618071.377, 8613204.194])| + EPSG_2605|2605|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 87E"|Projected|[85.5, 47.05, 88.5, 77.16]|Some([386046.288, 5212847.507, 613997.941, 8568559.719])| + EPSG_2606|2606|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 90E"|Projected|[88.5, 49.44, 91.5, 81.28]|Some([391230.853, 5478600.283, 608800.828, 9028426.292])| + EPSG_2607|2607|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 93E"|Projected|[91.5, 50.16, 94.5, 81.26]|Some([392825.342, 5558680.534, 607192.142, 9026192.066])| + EPSG_2608|2608|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 96E"|Projected|[94.5, 49.73, 97.5, 81.35]|Some([391859.104, 5510851.754, 608143.021, 9036237.724])| + EPSG_2609|2609|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 99E"|Projected|[97.5, 49.79, 100.5, 80.9]|Some([391984.986, 5517524.415, 608002.126, 8986003.039])| + EPSG_2610|2610|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 102E"|Projected|[100.5, 50.17, 103.5, 79.71]|Some([392825.139, 5559790.57, 607147.193, 8853167.719])| + EPSG_2611|2611|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 105E"|Projected|[103.5, 50.13, 106.5, 79.21]|Some([392728.112, 5555341.173, 607229.056, 8797357.386])| + EPSG_2612|2612|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 108E"|Projected|[106.5, 49.25, 109.5, 78.4]|Some([390765.904, 5457465.676, 609175.39, 8706948.69])| + EPSG_2613|2613|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 111E"|Projected|[109.5, 49.14, 112.5, 76.81]|Some([390515.865, 5445232.723, 609410.312, 8529493.96])| + EPSG_2614|2614|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 114E"|Projected|[112.5, 49.49, 115.5, 76.7]|Some([391281.435, 5484158.765, 608630.14, 8517219.022])| + EPSG_2615|2615|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 117E"|Projected|[115.5, 49.51, 118.5, 74.43]|Some([391318.334, 5486384.198, 608578.506, 8263910.747])| + EPSG_2616|2616|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 120E"|Projected|[118.5, 49.87, 121.5, 73.63]|Some([392110.662, 5526425.626, 607771.932, 8174651.778])| + EPSG_2617|2617|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 123E"|Projected|[121.5, 53.18, 124.5, 74.0]|Some([399652.781, 5894690.088, 600218.349, 8215936.345])| + EPSG_2618|2618|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 126E"|Projected|[124.5, 49.88, 127.5, 74.0]|Some([392118.684, 5527541.5, 607735.545, 8215938.919])| + EPSG_2619|2619|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 129E"|Projected|[127.5, 42.67, 130.5, 73.59]|Some([376952.858, 4726108.679, 622881.739, 8170196.599])| + EPSG_26191|26191|"Merchich / Nord Maroc"|Projected|[-9.85, 31.49, -1.01, 35.97]|Some([77121.417, 99613.582, 916886.295, 605045.105])| + EPSG_26192|26192|"Merchich / Sud Maroc"|Projected|[-13.24, 27.66, -3.59, 31.51]|Some([-273313.626, 74217.003, 678480.146, 526125.586])| + EPSG_26194|26194|"Merchich / Sahara Nord"|Projected|[-15.42, 24.29, -8.66, 27.67]|Some([183609.211, 203694.751, 878252.966, 611914.65])| + EPSG_26195|26195|"Merchich / Sahara Sud"|Projected|[-17.16, 20.71, -12.0, 24.31]|Some([276069.591, 217013.192, 830178.882, 647263.566])| + EPSG_2620|2620|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 132E"|Projected|[130.5, 42.25, 133.5, 71.99]|Some([376121.228, 4679456.572, 623699.719, 7991698.158])| + EPSG_2621|2621|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 135E"|Projected|[133.5, 42.74, 136.5, 75.9]|Some([377077.751, 4733889.339, 622730.648, 8427959.782])| + EPSG_2622|2622|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 138E"|Projected|[136.5, 44.76, 139.5, 76.27]|Some([381136.126, 4958327.949, 618661.326, 8469253.902])| + EPSG_2623|2623|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 141E"|Projected|[139.5, 45.84, 142.5, 76.23]|Some([383364.48, 5078359.194, 616421.74, 8464794.351])| + EPSG_26237|26237|"Massawa / UTM zone 37N"|Projected|[36.44, 12.36, 43.31, 18.1]|Some([221698.191, 1366305.475, 969043.605, 2006532.768])| + EPSG_2624|2624|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 144E"|Projected|[142.5, 43.61, 145.5, 75.98]|Some([378791.597, 4830551.372, 620981.351, 8436900.165])| + EPSG_2625|2625|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 147E"|Projected|[145.5, 43.6, 148.5, 76.76]|Some([378765.936, 4829443.722, 620995.97, 8523951.081])| + EPSG_2626|2626|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 150E"|Projected|[148.5, 45.21, 151.5, 76.82]|Some([382039.161, 5008350.447, 617713.443, 8530652.341])| + EPSG_2627|2627|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 153E"|Projected|[151.5, 46.72, 154.5, 76.26]|Some([385194.168, 5176191.633, 614549.626, 8468162.41])| + EPSG_2628|2628|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 156E"|Projected|[154.5, 49.02, 157.5, 77.2]|Some([390156.123, 5431927.642, 609580.038, 8573072.605])| + EPSG_2629|2629|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 159E"|Projected|[157.5, 51.36, 160.5, 71.12]|Some([395387.58, 5692216.805, 604341.601, 7894691.889])| + EPSG_2630|2630|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 162E"|Projected|[160.5, 54.34, 163.5, 70.98]|Some([402304.744, 6023846.519, 597418.588, 7879081.583])| + EPSG_2631|2631|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 165E"|Projected|[163.5, 54.69, 166.5, 69.82]|Some([403131.725, 6062811.986, 596584.433, 7749704.112])| + EPSG_2632|2632|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 168E"|Projected|[166.5, 54.45, 169.5, 70.07]|Some([402558.237, 6036101.672, 597151.003, 7777593.54])| + EPSG_2633|2633|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 171E"|Projected|[169.5, 59.86, 172.5, 70.19]|Some([415804.764, 6638579.125, 583902.507, 7790984.198])| + EPSG_26331|26331|"Minna / UTM zone 31N"|Projected|[2.66, 1.92, 6.0, 6.14]|Some([462265.938, 212105.271, 833871.565, 679496.718])| + EPSG_26332|26332|"Minna / UTM zone 32N"|Projected|[6.0, 2.61, 7.82, 3.68]|Some([166433.623, 288433.578, 369022.965, 407203.389])| + EPSG_2634|2634|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 174E"|Projected|[172.5, 60.99, 175.5, 70.02]|Some([418669.297, 6764489.661, 581033.494, 7772030.015])| + EPSG_2635|2635|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 177E"|Projected|[175.5, 62.09, 178.5, 71.1]|Some([421488.812, 6887078.28, 578210.212, 7892499.626])| + EPSG_2636|2636|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 180E"|Projected|[178.5, 62.24, -178.5, 71.65]|Some([421873.935, 6903802.069, 577821.352, 7953858.471])| + EPSG_2637|2637|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 177W"|Projected|[-178.5, 64.74, -175.5, 71.61]|Some([428401.531, 7182483.241, 571292.565, 7949403.446])| + EPSG_2638|2638|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 174W"|Projected|[-175.5, 64.2, -172.5, 67.78]|Some([426977.92, 7122288.034, 572713.448, 7522253.007])| + EPSG_2639|2639|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 171W"|Projected|[-172.5, 64.35, -169.57, 67.06]|Some([427371.581, 7139017.648, 568936.729, 7441979.009])| + EPSG_26391|26391|"Minna / Nigeria West Belt"|Projected|[2.69, 3.57, 6.5, 13.9]|Some([29740.083, -47617.321, 453023.776, 1095534.315])| + EPSG_26392|26392|"Minna / Nigeria Mid Belt"|Projected|[6.5, 3.57, 10.51, 13.53]|Some([448432.498, -47617.737, 893943.637, 1054594.036])| + EPSG_26393|26393|"Minna / Nigeria East Belt"|Projected|[10.49, 6.43, 14.65, 13.72]|Some([888096.98, 268571.108, 1348277.675, 1075750.502])| + EPSG_2640|2640|"Pulkovo 1942 / 3-degree Gauss-Kruger CM 168W"|Projected|[-169.22, 65.7, -168.97, 65.86]|Some([443803.842, 7289884.389, 455562.401, 7307922.569])| + EPSG_2641|2641|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 7"|Projected|[19.57, 54.32, 22.5, 55.32]|Some([7407078.563, 6021666.198, 7597733.243, 6134011.412])| + EPSG_2642|2642|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 8"|Projected|[22.5, 54.34, 22.87, 55.07]|Some([8402569.967, 6024475.236, 8427941.126, 6106177.329])| + EPSG_2643|2643|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 9"|Projected|[26.61, 56.05, 28.51, 68.93]|Some([9475823.717, 6214250.857, 9594213.118, 7650487.22])| + EPSG_2644|2644|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 10"|Projected|[28.5, 52.85, 31.5, 69.85]|Some([10399071.477, 5858041.84, 10601174.837, 7753074.968])| + EPSG_2645|2645|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 11"|Projected|[31.5, 51.24, 34.5, 70.02]|Some([11395370.555, 5678894.464, 11604870.604, 7772029.337])| + EPSG_2646|2646|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 12"|Projected|[34.5, 44.61, 37.51, 69.39]|Some([12381047.165, 4941706.878, 12619981.458, 7701769.368])| + EPSG_2647|2647|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 13"|Projected|[37.5, 43.33, 40.5, 68.8]|Some([13378458.368, 4799479.419, 13621770.015, 7635955.933])| + EPSG_2648|2648|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 14"|Projected|[40.5, 42.87, 43.5, 68.74]|Some([14377540.579, 4748371.694, 14622680.808, 7629259.217])| + EPSG_2649|2649|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 15"|Projected|[43.5, 41.89, 46.5, 80.8]|Some([15375615.37, 4639508.678, 15624598.349, 8974908.773])| + EPSG_2650|2650|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 16"|Projected|[46.5, 41.19, 49.5, 80.91]|Some([16374261.236, 4561759.922, 16625944.239, 8987182.437])| + EPSG_2651|2651|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 17"|Projected|[49.5, 42.36, 52.5, 81.22]|Some([17376524.064, 4691706.793, 17623672.763, 9021782.571])| + EPSG_2652|2652|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 18"|Projected|[52.5, 50.52, 55.5, 81.41]|Some([18393717.464, 5598765.734, 18606470.878, 9042987.523])| + EPSG_2653|2653|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 19"|Projected|[55.5, 50.53, 58.5, 81.89]|Some([19393735.078, 5599874.313, 19606443.421, 9096566.86])| + EPSG_2654|2654|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 20"|Projected|[58.5, 50.47, 61.5, 81.91]|Some([20393595.101, 5593196.403, 20606573.045, 9098794.936])| + EPSG_2655|2655|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 21"|Projected|[61.5, 51.03, 64.5, 81.77]|Some([21394852.925, 5655489.165, 21605304.46, 9083161.716])| + EPSG_2656|2656|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 22"|Projected|[64.5, 54.31, 67.5, 81.25]|Some([22402445.705, 6020483.03, 22597700.779, 9025108.489])| + EPSG_2657|2657|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 23"|Projected|[67.5, 54.85, 70.5, 77.07]|Some([23403722.503, 6080590.245, 23596412.361, 8558540.545])| + EPSG_2658|2658|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 24"|Projected|[70.5, 53.43, 73.5, 73.57]|Some([24400362.55, 5922531.222, 24599760.088, 8167979.436])| + EPSG_2659|2659|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 25"|Projected|[73.5, 53.47, 76.5, 79.71]|Some([25400450.014, 5926980.442, 25599660.216, 8853192.482])| + EPSG_2660|2660|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 26"|Projected|[76.5, 51.49, 79.5, 81.03]|Some([26395868.235, 5706651.863, 26604229.046, 9000536.516])| + EPSG_2661|2661|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 27"|Projected|[79.5, 50.7, 82.5, 81.03]|Some([27394070.939, 5618762.777, 27606013.247, 9000534.038])| + EPSG_2662|2662|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 28"|Projected|[82.5, 49.58, 85.5, 77.56]|Some([28391560.364, 5494182.217, 28608510.448, 8613213.181])| + EPSG_2663|2663|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 29"|Projected|[85.5, 49.07, 88.5, 77.16]|Some([29390427.243, 5437460.987, 29609630.053, 8568569.257])| + EPSG_26632|26632|"M'poraloko / UTM zone 32N"|Projected|[9.25, 0.0, 12.0, 2.32]|Some([527914.433, -41.14, 834097.386, 256744.412])| + EPSG_2664|2664|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 30"|Projected|[88.5, 49.44, 91.5, 81.28]|Some([30391236.775, 5478609.07, 30608806.927, 9028436.416])| + EPSG_2665|2665|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 31"|Projected|[91.5, 50.16, 94.5, 81.26]|Some([31392831.521, 5558689.904, 31607198.491, 9026202.694])| + EPSG_2666|2666|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 32"|Projected|[94.5, 49.73, 97.5, 81.35]|Some([32391866.007, 5510861.583, 32608150.095, 9036248.828])| + EPSG_2667|2667|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 33"|Projected|[97.5, 49.79, 100.5, 80.9]|Some([33391992.439, 5517534.721, 33608009.747, 8986014.586])| + EPSG_2668|2668|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 34"|Projected|[100.5, 50.17, 103.5, 79.71]|Some([34392833.034, 5559801.356, 34607155.25, 8853179.671])| + EPSG_2669|2669|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 35"|Projected|[103.5, 50.13, 106.5, 79.21]|Some([35392736.626, 5555352.376, 35607237.729, 8797369.716])| + EPSG_26692|26692|"M'poraloko / UTM zone 32S"|Projected|[7.03, -6.37, 12.01, 2.32]|Some([280862.617, 9294881.159, 835211.68, 10256746.778])| + EPSG_2670|2670|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 36"|Projected|[106.5, 49.25, 109.5, 78.4]|Some([36390775.372, 5457477.2, 36609185.02, 8706961.361])| + EPSG_26701|26701|"NAD27 / UTM zone 1N"|Projected|[-180.0, 47.88, -173.99, 63.21]|Some([275698.158, 5302744.024, 725049.446, 7012338.293])| + EPSG_26702|26702|"NAD27 / UTM zone 2N"|Projected|[-174.0, 48.66, -167.99, 73.05]|Some([279088.487, 5389441.736, 721647.799, 8108355.464])| + EPSG_26703|26703|"NAD27 / UTM zone 3N"|Projected|[-168.0, 49.52, -161.99, 74.29]|Some([282874.264, 5485045.349, 717849.384, 8246534.71])| + EPSG_26704|26704|"NAD27 / UTM zone 4N"|Projected|[-162.0, 50.98, -155.99, 74.71]|Some([289413.566, 5647382.197, 711288.256, 8293340.034])| + EPSG_26705|26705|"NAD27 / UTM zone 5N"|Projected|[-156.0, 52.15, -149.99, 74.71]|Some([294753.548, 5777503.851, 705930.452, 8293340.034])| + EPSG_26706|26706|"NAD27 / UTM zone 6N"|Projected|[-150.0, 54.05, -143.99, 74.13]|Some([303608.004, 5988868.325, 697046.449, 8228704.459])| + EPSG_26707|26707|"NAD27 / UTM zone 7N"|Projected|[-144.0, 53.47, -138.0, 73.59]|Some([300881.532, 5924357.877, 699242.054, 8168512.954])| + EPSG_26708|26708|"NAD27 / UTM zone 8N"|Projected|[-138.0, 52.58, -132.0, 73.04]|Some([296864.267, 5825356.988, 703364.635, 8107203.218])| + EPSG_26709|26709|"NAD27 / UTM zone 9N"|Projected|[-132.0, 49.18, -126.0, 72.03]|Some([281475.826, 5447264.015, 718718.004, 7994645.983])| + EPSG_2671|2671|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 37"|Projected|[109.5, 49.14, 112.5, 76.81]|Some([37390526.001, 5445244.604, 37609420.606, 8529506.925])| + EPSG_26710|26710|"NAD27 / UTM zone 10N"|Projected|[-126.0, 34.4, -119.99, 77.13]|Some([224209.616, 3806317.192, 776796.359, 8562981.783])| + EPSG_26711|26711|"NAD27 / UTM zone 11N"|Projected|[-120.0, 26.93, -114.0, 78.13]|Some([202169.869, 2978488.438, 797988.693, 8674419.814])| + EPSG_26712|26712|"NAD27 / UTM zone 12N"|Projected|[-114.0, 18.66, -108.0, 78.81]|Some([183556.772, 2063010.429, 816558.765, 8750208.666])| + EPSG_26713|26713|"NAD27 / UTM zone 13N"|Projected|[-108.0, 17.86, -102.0, 79.42]|Some([182089.474, 1974497.559, 818000.966, 8818198.828])| + EPSG_26714|26714|"NAD27 / UTM zone 14N"|Projected|[-102.0, 15.59, -96.0, 80.74]|Some([178285.393, 1723384.701, 821778.959, 8965338.63])| + EPSG_26715|26715|"NAD27 / UTM zone 15N"|Projected|[-96.0, 13.63, -90.0, 81.96]|Some([175379.079, 1506713.611, 824622.273, 9101340.067])| + EPSG_26716|26716|"NAD27 / UTM zone 16N"|Projected|[-90.01, 9.27, -84.0, 82.54]|Some([169257.567, 1024502.251, 829629.602, 9166007.204])| + EPSG_26717|26717|"NAD27 / UTM zone 17N"|Projected|[-84.0, 7.98, -78.0, 83.03]|Some([169222.055, 881884.765, 830740.152, 9220628.654])| + EPSG_26718|26718|"NAD27 / UTM zone 18N"|Projected|[-78.0, 18.83, -72.0, 83.16]|Some([183787.386, 2081823.714, 816148.939, 9235125.475])| + EPSG_26719|26719|"NAD27 / UTM zone 19N"|Projected|[-72.0, 33.61, -66.0, 83.17]|Some([221598.782, 3718709.966, 778358.999, 9236245.822])| + EPSG_2672|2672|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 38"|Projected|[112.5, 49.49, 115.5, 76.7]|Some([38391292.074, 5484171.011, 38608640.927, 8517232.269])| + EPSG_26720|26720|"NAD27 / UTM zone 20N"|Projected|[-66.0, 38.21, -59.99, 82.97]|Some([237325.832, 4228909.211, 763549.939, 9213957.848])| + EPSG_26721|26721|"NAD27 / UTM zone 21N"|Projected|[-60.0, 40.57, -54.0, 68.93]|Some([245967.679, 4490816.822, 753877.051, 7649281.7])| + EPSG_26722|26722|"NAD27 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242033.532, 4372055.492, 758826.509, 6393526.854])| + EPSG_26729|26729|"NAD27 / Alabama East"|Projected|[-86.79, 30.99, -84.89, 35.0]|Some([200208.266, 178136.814, 795589.513, 1638517.421])| + EPSG_2673|2673|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 39"|Projected|[115.5, 49.51, 118.5, 74.43]|Some([39391329.608, 5486396.75, 39608589.923, 8263924.202])| + EPSG_26730|26730|"NAD27 / Alabama West"|Projected|[-88.48, 30.14, -86.3, 35.02]|Some([190230.474, 50826.916, 879321.632, 1828372.58])| + EPSG_26731|26731|"NAD27 / Alaska zone 1"|Projected|[-141.0, 54.61, -129.99, 60.35]|Some([1132120.604, 1014225.918, 3465136.721, 3183944.691])| + EPSG_26732|26732|"NAD27 / Alaska zone 2"|Projected|[-144.01, 59.72, -140.98, 70.16]|Some([129356.865, 2089759.81, 688642.954, 5912233.405])| + EPSG_26733|26733|"NAD27 / Alaska zone 3"|Projected|[-148.0, 59.72, -144.0, 70.38]|Some([131219.294, 2089781.848, 869533.36, 5992696.737])| + EPSG_26734|26734|"NAD27 / Alaska zone 4"|Projected|[-152.01, 59.11, -147.99, 70.63]|Some([122655.732, 1866876.668, 878127.743, 6084218.897])| + EPSG_26735|26735|"NAD27 / Alaska zone 5"|Projected|[-156.0, 55.72, -151.86, 71.28]|Some([88120.305, 628399.925, 941545.377, 6322566.223])| + EPSG_26736|26736|"NAD27 / Alaska zone 6"|Projected|[-160.0, 54.89, -155.99, 71.4]|Some([79429.998, 325286.557, 923508.177, 6365971.865])| + EPSG_26737|26737|"NAD27 / Alaska zone 7"|Projected|[-164.01, 54.32, -160.0, 70.74]|Some([270941.258, 116848.018, 1126924.399, 6124541.571])| + EPSG_26738|26738|"NAD27 / Alaska zone 8"|Projected|[-168.26, 54.34, -164.0, 69.05]|Some([18266.269, 124488.511, 927138.189, 5507500.498])| + EPSG_26739|26739|"NAD27 / Alaska zone 9"|Projected|[-173.16, 56.49, -168.0, 65.82]|Some([-38478.163, 909394.507, 1004574.522, 4331950.127])| + EPSG_2674|2674|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 40"|Projected|[118.5, 49.87, 121.5, 73.63]|Some([40392122.449, 5526438.478, 40607783.853, 8174665.432])| + EPSG_26740|26740|"NAD27 / Alaska zone 10"|Projected|[172.42, 51.3, -164.84, 54.34]|Some([361852.868, 109599.817, 5543974.107, 1418017.042])| + EPSG_26741|26741|"NAD27 / California zone I"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([1309814.745, 93565.315, 2566801.317, 984531.699])| + EPSG_26742|26742|"NAD27 / California zone II"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([1406844.113, 128727.598, 2708961.054, 917456.829])| + EPSG_26743|26743|"NAD27 / California zone III"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([1261798.859, 83781.138, 2782721.606, 815628.237])| + EPSG_26744|26744|"NAD27 / California zone IV"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([1107520.161, 162621.999, 3002728.889, 835186.349])| + EPSG_26745|26745|"NAD27 / California zone V"|Projected|[-121.43, 32.76, -114.12, 35.81]|Some([945437.922, -269356.034, 3193421.751, 862925.625])| + EPSG_26746|26746|"NAD27 / California zone VI"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([1414640.875, 132176.854, 2564294.511, 701416.029])| + EPSG_26748|26748|"NAD27 / Arizona East"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([18323.012, 119975.85, 851979.124, 2190529.443])| + EPSG_26749|26749|"NAD27 / Arizona Central"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([52686.68, 119978.575, 961282.81, 2190224.451])| + EPSG_2675|2675|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 41"|Projected|[121.5, 53.18, 124.5, 74.0]|Some([41399663.984, 5894703.386, 41600229.657, 8215950.185])| + EPSG_26750|26750|"NAD27 / Arizona West"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([171810.277, 381891.341, 881332.988, 2185636.587])| + EPSG_26751|26751|"NAD27 / Arkansas North"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([1212262.468, 122502.841, 2709665.313, 798871.849])| + EPSG_26752|26752|"NAD27 / Arkansas South"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([1239771.689, 124883.539, 2490570.964, 894439.296])| + EPSG_26753|26753|"NAD27 / Colorado North"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([996657.707, 82578.315, 2975497.449, 630524.966])| + EPSG_26754|26754|"NAD27 / Colorado Central"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([976445.172, 111691.204, 2995141.809, 841437.697])| + EPSG_26755|26755|"NAD27 / Colorado South"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([960455.784, 114086.355, 3010681.483, 752489.365])| + EPSG_26756|26756|"NAD27 / Connecticut"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([329274.081, 53441.241, 867703.678, 444836.358])| + EPSG_26757|26757|"NAD27 / Delaware"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([390094.727, 160220.046, 627832.434, 674095.511])| + EPSG_26758|26758|"NAD27 / Florida East"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([57368.953, 27713.678, 842682.561, 2364103.115])| + EPSG_26759|26759|"NAD27 / Florida West"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([60819.006, 703663.845, 785053.071, 1916756.991])| + EPSG_2676|2676|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 42"|Projected|[124.5, 49.88, 127.5, 74.0]|Some([42392131.776, 5527554.802, 42607748.757, 8215952.902])| + EPSG_26760|26760|"NAD27 / Florida North"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([1001449.055, 76276.061, 2784798.14, 744373.742])| + EPSG_26766|26766|"NAD27 / Georgia East"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([88900.736, 130833.081, 940447.038, 1705329.599])| + EPSG_26767|26767|"NAD27 / Georgia West"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([45969.583, 225388.221, 870089.081, 1825636.891])| + EPSG_26768|26768|"NAD27 / Idaho East"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([208437.235, 117849.232, 806503.768, 1125838.275])| + EPSG_26769|26769|"NAD27 / Idaho Central"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([146832.18, 117853.355, 861795.729, 1473008.891])| + EPSG_2677|2677|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 43"|Projected|[127.5, 42.67, 130.5, 73.59]|Some([43376969.165, 4726121.688, 43622898.204, 8170210.679])| + EPSG_26770|26770|"NAD27 / Idaho West"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([95199.36, 117857.742, 888990.001, 2680936.587])| + EPSG_26771|26771|"NAD27 / Illinois East"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([226694.097, 143176.724, 883242.738, 2127499.725])| + EPSG_26772|26772|"NAD27 / Illinois West"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([104670.64, 110409.609, 864222.298, 2131246.743])| + EPSG_26773|26773|"NAD27 / Indiana East"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([233742.481, 163834.162, 755657.457, 1556671.136])| + EPSG_26774|26774|"NAD27 / Indiana West"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([206128.16, 98289.651, 743767.183, 1556958.18])| + EPSG_26775|26775|"NAD27 / Iowa North"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([1141982.331, 127565.304, 2912586.986, 750138.863])| + EPSG_26776|26776|"NAD27 / Iowa South"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([1264403.078, 131157.723, 2936256.987, 760897.572])| + EPSG_26777|26777|"NAD27 / Kansas North"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([838815.42, 67983.657, 2978417.307, 636173.841])| + EPSG_26778|26778|"NAD27 / Kansas South"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([963439.981, 117724.162, 3138915.151, 829174.983])| + EPSG_26779|26779|"NAD27 / Kentucky North"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([1505286.796, 76451.493, 2514908.125, 605748.679])| + EPSG_2678|2678|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 44"|Projected|[130.5, 42.25, 133.5, 71.99]|Some([44376138.301, 4679469.727, 44623716.943, 7991712.281])| + EPSG_26780|26780|"NAD27 / Kentucky South"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([877270.216, 57008.473, 3116829.443, 690914.858])| + EPSG_26781|26781|"NAD27 / Louisiana North"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([1513600.687, 66619.026, 2514740.531, 863474.42])| + EPSG_26782|26782|"NAD27 / Louisiana South"|Projected|[-93.94, 27.82, -87.76, 31.07]|Some([1156987.62, -308053.979, 3155641.781, 891385.869])| + EPSG_26783|26783|"NAD27 / Maine East"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([96495.222, 16984.702, 919073.307, 1329954.813])| + EPSG_26784|26784|"NAD27 / Maine West"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([253003.362, 75334.473, 742234.145, 1367258.72])| + EPSG_26785|26785|"NAD27 / Maryland"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([82127.951, 49762.449, 1385109.589, 700311.604])| + EPSG_26786|26786|"NAD27 / Massachusetts Mainland"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([51674.47, 167635.154, 1049382.645, 695032.217])| + EPSG_26787|26787|"NAD27 / Massachusetts Island"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([87001.932, 69234.391, 367743.947, 186420.642])| + EPSG_2679|2679|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 45"|Projected|[133.5, 42.74, 136.5, 75.9]|Some([45377095.276, 4733902.668, 45622748.309, 8427973.983])| + EPSG_26791|26791|"NAD27 / Minnesota North"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([965491.926, 51073.439, 2906651.927, 1076743.951])| + EPSG_26792|26792|"NAD27 / Minnesota Central"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([1328262.944, 102114.15, 2504599.521, 915055.14])| + EPSG_26793|26793|"NAD27 / Minnesota South"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([1243810.305, 178635.718, 2740399.916, 956940.901])| + EPSG_26794|26794|"NAD27 / Mississippi East"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([140238.5, 124766.071, 735290.557, 1945781.378])| + EPSG_26795|26795|"NAD27 / Mississippi West"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([87483.068, 181776.31, 801861.049, 1643358.463])| + EPSG_26796|26796|"NAD27 / Missouri East"|Projected|[-91.97, 35.98, -89.1, 40.61]|Some([65094.033, 53358.815, 914254.978, 1742794.985])| + EPSG_26797|26797|"NAD27 / Missouri Central"|Projected|[-93.79, 36.48, -91.41, 40.61]|Some([120795.365, 235371.206, 820501.281, 1742012.236])| + EPSG_26798|26798|"NAD27 / Missouri West"|Projected|[-95.77, 36.48, -93.48, 40.59]|Some([126690.712, 114038.986, 799941.603, 1613324.386])| + EPSG_26799|26799|"NAD27 / California zone VII"|Projected|[-118.96, 33.66, -117.63, 34.83]|Some([3996265.032, 3988673.96, 4400991.224, 4415203.868])| + EPSG_2680|2680|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 46"|Projected|[136.5, 44.76, 139.5, 76.27]|Some([46381153.588, 4958341.497, 46618678.901, 8469268.092])| + EPSG_2681|2681|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 47"|Projected|[139.5, 45.84, 142.5, 76.23]|Some([47383382.195, 5078372.857, 47616439.552, 8464808.49])| + EPSG_2682|2682|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 48"|Projected|[142.5, 43.61, 145.5, 75.98]|Some([48378810.651, 4830564.964, 48621000.501, 8436914.214])| + EPSG_2683|2683|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 49"|Projected|[145.5, 43.6, 148.5, 76.76]|Some([49378785.58, 4829457.316, 49621015.696, 8523965.003])| + EPSG_2684|2684|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 50"|Projected|[148.5, 45.21, 151.5, 76.82]|Some([50382058.872, 5008364.061, 50617733.217, 8530666.098])| + EPSG_26847|26847|"NAD83 / Maine East (ftUS)"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([580868.792, 77759.255, 1403450.241, 1390734.707])| + EPSG_26848|26848|"NAD83 / Maine West (ftUS)"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([2705905.49, 75321.959, 3195138.806, 1367295.839])| + EPSG_26849|26849|"NAD83 / Minnesota North (ftUS)"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([1590098.143, 379149.477, 3531269.373, 1404855.366])| + EPSG_2685|2685|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 51"|Projected|[151.5, 46.72, 154.5, 76.26]|Some([51385213.973, 5176205.207, 51614569.476, 8468175.967])| + EPSG_26850|26850|"NAD83 / Minnesota Central (ftUS)"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([1952860.877, 430189.984, 3129204.319, 1243135.696])| + EPSG_26851|26851|"NAD83 / Minnesota South (ftUS)"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([1868410.019, 506715.185, 3365008.371, 1285024.882])| + EPSG_26852|26852|"NAD83 / Nebraska (ftUS)"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([503201.832, 57074.252, 2956732.313, 1191417.417])| + EPSG_26853|26853|"NAD83 / West Virginia North (ftUS)"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([1324061.585, 94702.058, 2476086.866, 787403.506])| + EPSG_26854|26854|"NAD83 / West Virginia South (ftUS)"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([1487902.2, 72830.025, 2536467.316, 796046.703])| + EPSG_26855|26855|"NAD83(HARN) / Maine East (ftUS)"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([580868.778, 77755.969, 1403450.074, 1390731.171])| + EPSG_26856|26856|"NAD83(HARN) / Maine West (ftUS)"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([2705905.584, 75318.748, 3195138.808, 1367292.403])| + EPSG_26857|26857|"NAD83(HARN) / Minnesota North (ftUS)"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([1590100.243, 379146.462, 3531271.207, 1404852.22])| + EPSG_26858|26858|"NAD83(HARN) / Minnesota Central (ftUS)"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([1952863.016, 430187.07, 3129206.291, 1243132.67])| + EPSG_26859|26859|"NAD83(HARN) / Minnesota South (ftUS)"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([1868412.158, 506712.362, 3365010.284, 1285021.944])| + EPSG_2686|2686|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 52"|Projected|[154.5, 49.02, 157.5, 77.2]|Some([52390175.767, 5431941.132, 52609599.709, 8573085.917])| + EPSG_26860|26860|"NAD83(HARN) / Nebraska (ftUS)"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([503204.47, 57071.769, 2956734.581, 1191414.772])| + EPSG_26861|26861|"NAD83(HARN) / West Virginia North (ftUS)"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([1324062.543, 94699.27, 2476087.595, 787400.593])| + EPSG_26862|26862|"NAD83(HARN) / West Virginia South (ftUS)"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([1487903.252, 72827.375, 2536468.153, 796043.897])| + EPSG_26863|26863|"NAD83(NSRS2007) / Maine East (ftUS)"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([580868.792, 77759.255, 1403450.241, 1390734.707])| + EPSG_26864|26864|"NAD83(NSRS2007) / Maine West (ftUS)"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([2705905.49, 75321.959, 3195138.806, 1367295.839])| + EPSG_26865|26865|"NAD83(NSRS2007) / Minnesota North (ftUS)"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([1590098.143, 379149.477, 3531269.373, 1404855.366])| + EPSG_26866|26866|"NAD83(NSRS2007) / Minnesota Central (ftUS)"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([1952860.877, 430189.984, 3129204.319, 1243135.696])| + EPSG_26867|26867|"NAD83(NSRS2007) / Minnesota South (ftUS)"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([1868410.019, 506715.185, 3365008.371, 1285024.882])| + EPSG_26868|26868|"NAD83(NSRS2007) / Nebraska (ftUS)"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([503201.832, 57074.252, 2956732.313, 1191417.417])| + EPSG_26869|26869|"NAD83(NSRS2007) / West Virginia North (ftUS)"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([1324061.585, 94702.058, 2476086.866, 787403.506])| + EPSG_2687|2687|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 53"|Projected|[157.5, 51.36, 160.5, 71.12]|Some([53395407.042, 5692230.146, 53604361.073, 7894704.974])| + EPSG_26870|26870|"NAD83(NSRS2007) / West Virginia South (ftUS)"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([1487902.2, 72830.025, 2536467.316, 796046.703])| + EPSG_2688|2688|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 54"|Projected|[160.5, 54.34, 163.5, 70.98]|Some([54402323.809, 6023859.646, 54597437.649, 7879094.374])| + EPSG_2689|2689|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 55"|Projected|[163.5, 54.69, 166.5, 69.82]|Some([55403151.228, 6062824.849, 55596603.921, 7749716.588])| + EPSG_26891|26891|"NAD83(CSRS) / MTM zone 11"|Projected|[-83.6, 41.67, -81.0, 46.0]|Some([213203.661, 4614521.7, 429705.673, 5096669.451])| + EPSG_26892|26892|"NAD83(CSRS) / MTM zone 12"|Projected|[-82.5, 46.0, -79.5, 55.21]|Some([188617.442, 5095575.328, 420983.174, 6121021.942])| + EPSG_26893|26893|"NAD83(CSRS) / MTM zone 13"|Projected|[-85.5, 46.0, -82.5, 55.59]|Some([188617.516, 5095575.346, 420983.249, 6163318.321])| + EPSG_26894|26894|"NAD83(CSRS) / MTM zone 14"|Projected|[-88.5, 47.17, -85.5, 56.7]|Some([191091.176, 5225622.746, 418509.738, 6286882.268])| + EPSG_26895|26895|"NAD83(CSRS) / MTM zone 15"|Projected|[-91.5, 47.97, -88.5, 56.9]|Some([192810.14, 5314559.421, 416790.917, 6309148.27])| + EPSG_26896|26896|"NAD83(CSRS) / MTM zone 16"|Projected|[-94.5, 48.06, -91.5, 55.2]|Some([193004.965, 5324565.598, 416596.228, 6119909.011])| + EPSG_26897|26897|"NAD83(CSRS) / MTM zone 17"|Projected|[-95.16, 48.69, -94.5, 53.24]|Some([360876.533, 5394953.611, 415225.232, 5901788.708])| + EPSG_26898|26898|"NAD83(CSRS) / MTM zone 1"|Projected|[-54.5, 46.56, -52.54, 49.89]|Some([189794.62, 5157816.821, 340067.948, 5529137.064])| + EPSG_26899|26899|"NAD83(CSRS) / MTM zone 2"|Projected|[-57.5, 46.81, -54.49, 54.71]|Some([190324.136, 5185605.22, 420038.327, 6065386.332])| + EPSG_2690|2690|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 56"|Projected|[166.5, 54.45, 169.5, 70.07]|Some([56402578.344, 6036114.244, 56597171.087, 7777605.655])| + EPSG_26901|26901|"NAD83 / UTM zone 1N"|Projected|[-180.0, 47.88, -173.99, 63.21]|Some([275705.201, 5302962.875, 725042.38, 7012536.576])| + EPSG_26902|26902|"NAD83 / UTM zone 2N"|Projected|[-174.0, 48.66, -167.99, 73.05]|Some([279095.534, 5389660.727, 721640.728, 8108518.215])| + EPSG_26903|26903|"NAD83 / UTM zone 3N"|Projected|[-168.0, 49.52, -161.99, 74.29]|Some([282881.311, 5485264.341, 717842.313, 8246692.024])| + EPSG_26904|26904|"NAD83 / UTM zone 4N"|Projected|[-162.0, 15.57, -155.99, 74.71]|Some([178221.766, 1721372.738, 822852.829, 8293495.466])| + EPSG_26905|26905|"NAD83 / UTM zone 5N"|Projected|[-156.0, 15.56, -149.99, 74.71]|Some([178206.058, 1720266.624, 822867.956, 8293495.466])| + EPSG_26906|26906|"NAD83 / UTM zone 6N"|Projected|[-150.0, 54.05, -143.99, 74.13]|Some([303614.943, 5989084.721, 697039.487, 8228862.485])| + EPSG_26907|26907|"NAD83 / UTM zone 7N"|Projected|[-144.0, 52.05, -137.99, 73.59]|Some([294300.748, 5766599.39, 706384.763, 8168689.221])| + EPSG_26908|26908|"NAD83 / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276483.897, 5322969.01, 723516.103, 8818432.315])| + EPSG_26909|26909|"NAD83 / UTM zone 9N"|Projected|[-132.0, 35.38, -126.0, 80.93]|Some([227475.274, 3915184.943, 772524.726, 8986755.526])| + EPSG_2691|2691|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 57"|Projected|[169.5, 59.86, 172.5, 70.19]|Some([57415823.67, 6638591.263, 57583921.381, 7790995.921])| + EPSG_26910|26910|"NAD83 / UTM zone 10N"|Projected|[-126.0, 30.54, -119.99, 81.8]|Some([212172.222, 3378624.203, 788787.633, 9083749.436])| + EPSG_26911|26911|"NAD83 / UTM zone 11N"|Projected|[-120.0, 30.88, -114.0, 83.5]|Some([213181.345, 3416303.043, 786818.655, 9273261.909])| + EPSG_26912|26912|"NAD83 / UTM zone 12N"|Projected|[-114.0, 31.33, -108.0, 84.0]|Some([214532.476, 3466175.159, 785467.524, 9329005.182])| + EPSG_26913|26913|"NAD83 / UTM zone 13N"|Projected|[-108.0, 28.98, -102.0, 84.0]|Some([207672.606, 3205769.791, 792327.394, 9329005.182])| + EPSG_26914|26914|"NAD83 / UTM zone 14N"|Projected|[-102.0, 25.83, -96.0, 84.0]|Some([199250.354, 2856857.634, 800749.646, 9329005.182])| + EPSG_26915|26915|"NAD83 / UTM zone 15N"|Projected|[-96.0, 25.61, -90.0, 84.0]|Some([198695.776, 2832494.965, 801304.224, 9329005.182])| + EPSG_26916|26916|"NAD83 / UTM zone 16N"|Projected|[-90.0, 23.97, -84.0, 84.0]|Some([194701.986, 2650905.127, 805298.014, 9329005.182])| + EPSG_26917|26917|"NAD83 / UTM zone 17N"|Projected|[-84.0, 23.81, -78.0, 84.0]|Some([194325.668, 2633191.154, 805674.332, 9329005.182])| + EPSG_26918|26918|"NAD83 / UTM zone 18N"|Projected|[-78.0, 28.28, -72.0, 84.0]|Some([205723.769, 3128220.038, 794276.231, 9329005.182])| + EPSG_26919|26919|"NAD83 / UTM zone 19N"|Projected|[-72.0, 14.92, -66.0, 84.0]|Some([177229.186, 1649477.67, 822770.814, 9329005.182])| + EPSG_2692|2692|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 58"|Projected|[172.5, 60.99, 175.5, 70.02]|Some([58418688.351, 6764501.387, 58581052.509, 7772041.319])| + EPSG_26920|26920|"NAD83 / UTM zone 20N"|Projected|[-66.0, 15.63, -60.0, 84.0]|Some([178314.76, 1728008.737, 821685.24, 9329005.182])| + EPSG_26921|26921|"NAD83 / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.645, 4267951.154, 761403.355, 9329005.182])| + EPSG_26922|26922|"NAD83 / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242040.228, 4372264.52, 758819.79, 6393731.856])| + EPSG_26923|26923|"NAD83 / UTM zone 23N"|Projected|[-48.0, 39.85, -42.0, 54.47]|Some([243340.254, 4411108.902, 756659.746, 6039959.486])| + EPSG_26929|26929|"NAD83 / Alabama East"|Projected|[-86.79, 30.99, -84.89, 35.0]|Some([108626.368, 54321.743, 290100.077, 499449.41])| + EPSG_2693|2693|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 59"|Projected|[175.5, 62.09, 178.5, 71.1]|Some([59421508.005, 6887089.555, 59578229.362, 7892510.453])| + EPSG_26930|26930|"NAD83 / Alabama West"|Projected|[-88.48, 30.14, -86.3, 35.02]|Some([505580.446, 15519.0, 715617.187, 557319.387])| + EPSG_26931|26931|"NAD83 / Alaska zone 1"|Projected|[-141.0, 54.61, -129.99, 60.35]|Some([345065.001, 308998.437, 1056160.285, 970307.741])| + EPSG_26932|26932|"NAD83 / Alaska zone 2"|Projected|[-144.01, 59.72, -140.98, 70.16]|Some([386918.31, 636898.364, 557389.032, 1801991.227])| + EPSG_26933|26933|"NAD83 / Alaska zone 3"|Projected|[-148.0, 59.72, -144.0, 70.38]|Some([387480.793, 636898.322, 612519.207, 1826509.149])| + EPSG_26934|26934|"NAD83 / Alaska zone 4"|Projected|[-152.01, 59.11, -147.99, 70.63]|Some([384866.006, 568949.725, 615133.994, 1854397.462])| + EPSG_26935|26935|"NAD83 / Alaska zone 5"|Projected|[-156.0, 55.72, -151.86, 71.28]|Some([374335.663, 191454.933, 634459.395, 1927037.849])| + EPSG_26936|26936|"NAD83 / Alaska zone 6"|Projected|[-160.0, 54.89, -155.99, 71.4]|Some([371683.471, 99058.912, 628958.023, 1940259.62])| + EPSG_26937|26937|"NAD83 / Alaska zone 7"|Projected|[-164.01, 54.32, -160.0, 70.74]|Some([369227.276, 35615.004, 630122.198, 1866663.097])| + EPSG_26938|26938|"NAD83 / Alaska zone 8"|Projected|[-168.26, 54.34, -164.0, 69.05]|Some([353035.906, 37841.059, 630059.06, 1678580.299])| + EPSG_26939|26939|"NAD83 / Alaska zone 9"|Projected|[-173.16, 56.49, -168.0, 65.82]|Some([305398.633, 277180.772, 623180.037, 1320354.895])| + EPSG_26940|26940|"NAD83 / Alaska zone 10"|Projected|[172.42, 51.3, -164.84, 54.34]|Some([195687.986, 33405.015, 1775378.814, 432207.098])| + EPSG_26941|26941|"NAD83 / California zone 1"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([1789539.567, 528503.854, 2172670.813, 800071.591])| + EPSG_26942|26942|"NAD83 / California zone 2"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([1819114.258, 539224.713, 2216001.305, 779630.525])| + EPSG_26943|26943|"NAD83 / California zone 3"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([1774907.68, 525529.067, 2238487.139, 748597.178])| + EPSG_26944|26944|"NAD83 / California zone 4"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([1727886.776, 549564.135, 2305549.302, 754562.842])| + EPSG_26945|26945|"NAD83 / California zone 5"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([1679423.165, 417902.407, 2363675.21, 763023.807])| + EPSG_26946|26946|"NAD83 / California zone 6"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([1821504.087, 540295.778, 2171920.436, 713800.804])| + EPSG_26948|26948|"NAD83 / Arizona East"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([66481.577, 36584.599, 320581.618, 667693.422])| + EPSG_26949|26949|"NAD83 / Arizona Central"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([76951.283, 36584.405, 353893.118, 667599.273])| + EPSG_2695|2695|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 61"|Projected|[-178.5, 64.74, -175.5, 71.61]|Some([61428420.796, 7182493.481, 61571311.781, 7949413.264])| + EPSG_26950|26950|"NAD83 / Arizona West"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([113255.823, 116414.744, 329519.631, 666199.554])| + EPSG_26951|26951|"NAD83 / Arkansas North"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([159882.152, 37351.802, 616294.174, 243510.715])| + EPSG_26952|26952|"NAD83 / Arkansas South"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([168267.017, 438081.85, 549513.688, 672644.321])| + EPSG_26953|26953|"NAD83 / Colorado North"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([608530.815, 329968.654, 1211685.384, 496983.93])| + EPSG_26954|26954|"NAD83 / Colorado Central"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([602369.994, 338844.895, 1217673.025, 561273.139])| + EPSG_26955|26955|"NAD83 / Colorado South"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([597496.398, 339577.268, 1222409.552, 534163.828])| + EPSG_26956|26956|"NAD83 / Connecticut"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([222322.543, 168688.657, 386437.098, 287986.778])| + EPSG_26957|26957|"NAD83 / Delaware"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([166533.162, 48840.047, 238996.166, 205470.483])| + EPSG_26958|26958|"NAD83 / Florida East"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([65102.243, 8491.398, 304467.201, 720632.506])| + EPSG_26959|26959|"NAD83 / Florida West"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([66150.824, 214526.481, 286899.27, 584280.418])| + EPSG_2696|2696|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 62"|Projected|[-175.5, 64.2, -172.5, 67.78]|Some([62426997.778, 7122297.782, 62572733.251, 7522262.419])| + EPSG_26960|26960|"NAD83 / Florida North"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([295646.502, 23279.259, 839215.833, 226917.137])| + EPSG_26961|26961|"NAD83 / Hawaii zone 1"|Projected|[-156.1, 18.87, -154.74, 20.33]|Some([436777.595, 4059.061, 580083.54, 165857.58])| + EPSG_26962|26962|"NAD83 / Hawaii zone 2"|Projected|[-157.36, 20.45, -155.93, 21.26]|Some([427654.703, 12916.272, 576868.139, 102770.623])| + EPSG_26963|26963|"NAD83 / Hawaii zone 3"|Projected|[-158.33, 21.2, -157.61, 21.75]|Some([465736.444, 3691.084, 540494.565, 64639.121])| + EPSG_26964|26964|"NAD83 / Hawaii zone 4"|Projected|[-159.85, 21.81, -159.23, 22.29]|Some([463811.095, -2583.174, 527918.143, 50608.923])| + EPSG_26965|26965|"NAD83 / Hawaii zone 5"|Projected|[-160.3, 21.73, -159.99, 22.07]|Some([486206.399, 7013.116, 518277.929, 44671.505])| + EPSG_26966|26966|"NAD83 / Georgia East"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([74709.789, 39904.779, 334263.246, 519815.24])| + EPSG_26967|26967|"NAD83 / Georgia West"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([561618.683, 68725.622, 812812.39, 556485.421])| + EPSG_26968|26968|"NAD83 / Idaho East"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([111063.719, 35911.072, 293355.534, 343148.061])| + EPSG_26969|26969|"NAD83 / Idaho Central"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([392281.83, 35910.836, 610204.021, 448964.739])| + EPSG_2697|2697|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 63"|Projected|[-172.5, 64.35, -169.57, 67.06]|Some([63427391.784, 7139026.845, 63568956.872, 7441987.9])| + EPSG_26970|26970|"NAD83 / Idaho West"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([676539.784, 35910.659, 918488.551, 817141.665])| + EPSG_26971|26971|"NAD83 / Illinois East"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([216692.348, 43649.737, 416809.982, 648476.24])| + EPSG_26972|26972|"NAD83 / Illinois West"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([579494.187, 33660.726, 811007.382, 649616.799])| + EPSG_26973|26973|"NAD83 / Indiana East"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([18848.17, 299944.57, 177929.13, 724484.683])| + EPSG_26974|26974|"NAD83 / Indiana West"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([810427.296, 279966.475, 974300.985, 724572.21])| + EPSG_26975|26975|"NAD83 / Iowa North"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([1238456.484, 1038882.072, 1778141.02, 1228643.996])| + EPSG_26976|26976|"NAD83 / Iowa South"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([275770.616, 39978.785, 785355.693, 231925.011])| + EPSG_26977|26977|"NAD83 / Kansas North"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([46038.237, 20724.498, 698193.85, 193910.164])| + EPSG_26978|26978|"NAD83 / Kansas South"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([84022.727, 435888.473, 747112.606, 652740.363])| + EPSG_2698|2698|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 64"|Projected|[-169.22, 65.7, -168.97, 65.86]|Some([64443823.984, 7289892.837, 64455582.489, 7307930.978])| + EPSG_26980|26980|"NAD83 / Kentucky South"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([157793.092, 517386.113, 840416.218, 710602.335])| + EPSG_26981|26981|"NAD83 / Louisiana North"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([851729.364, 38809.051, 1156879.26, 281692.556])| + EPSG_26982|26982|"NAD83 / Louisiana South"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([745633.391, 38798.174, 1252090.057, 287715.924])| + EPSG_26983|26983|"NAD83 / Maine East"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([177049.162, 23701.068, 427772.489, 423896.787])| + EPSG_26984|26984|"NAD83 / Maine West"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([824761.643, 22958.179, 973880.256, 416752.605])| + EPSG_26985|26985|"NAD83 / Maryland"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([181219.097, 33674.371, 578370.965, 231963.298])| + EPSG_26986|26986|"NAD83 / Massachusetts Mainland"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([32912.329, 801094.12, 337016.003, 961845.933])| + EPSG_26987|26987|"NAD83 / Massachusetts Island"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([465603.809, 21100.837, 551174.59, 56819.463])| + EPSG_26988|26988|"NAD83 / Michigan North"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([7730770.077, 32977.062, 8280243.766, 399102.506])| + EPSG_26989|26989|"NAD83 / Michigan Central"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([5783269.684, 53711.284, 6168729.258, 292766.575])| + EPSG_2699|2699|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 21E"|Projected|[19.57, 54.32, 22.5, 55.32]|Some([407078.563, 6021666.198, 597733.243, 6134011.412])| + EPSG_26990|26990|"NAD83 / Michigan South"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([3764135.981, 21106.546, 4186207.076, 305975.012])| + EPSG_26991|26991|"NAD83 / Minnesota North"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([484662.883, 115564.992, 1076333.058, 428200.772])| + EPSG_26992|26992|"NAD83 / Minnesota Central"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([595233.186, 131122.17, 953783.384, 378908.518])| + EPSG_26993|26993|"NAD83 / Minnesota South"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([569492.513, 154447.097, 1025656.603, 391676.367])| + EPSG_26994|26994|"NAD83 / Mississippi East"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([190340.121, 56529.852, 371711.704, 611572.95])| + EPSG_26995|26995|"NAD83 / Mississippi West"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([574253.773, 166276.994, 791999.89, 611774.795])| + EPSG_26996|26996|"NAD83 / Missouri East"|Projected|[-91.97, 35.98, -89.1, 40.61]|Some([117430.214, 16273.215, 376256.537, 531217.473])| + EPSG_26997|26997|"NAD83 / Missouri Central"|Projected|[-93.79, 36.48, -91.41, 40.61]|Some([384402.583, 71750.784, 597674.639, 530978.613])| + EPSG_26998|26998|"NAD83 / Missouri West"|Projected|[-95.77, 36.48, -93.48, 40.59]|Some([736193.986, 34767.346, 941402.47, 491753.126])| + EPSG_2700|2700|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 24E"|Projected|[22.5, 54.34, 22.87, 55.07]|Some([402569.967, 6024475.236, 427941.126, 6106177.329])| + EPSG_2701|2701|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 27E"|Projected|[26.61, 56.05, 28.51, 68.93]|Some([475823.717, 6214250.857, 594213.118, 7650487.22])| + EPSG_2702|2702|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 30E"|Projected|[28.5, 52.85, 31.5, 69.85]|Some([399071.477, 5858041.84, 601174.837, 7753074.968])| + EPSG_2703|2703|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 33E"|Projected|[31.5, 51.24, 34.5, 70.02]|Some([395370.555, 5678894.464, 604870.604, 7772029.337])| + EPSG_27039|27039|"Nahrwan 1967 / UTM zone 39N"|Projected|[50.55, 22.76, 54.01, 27.05]|Some([453800.66, 2516651.467, 809014.791, 2995300.963])| + EPSG_2704|2704|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 36E"|Projected|[34.5, 44.61, 37.51, 69.39]|Some([381047.165, 4941706.878, 619981.458, 7701769.368])| + EPSG_27040|27040|"Nahrwan 1967 / UTM zone 40N"|Projected|[54.0, 22.63, 57.13, 26.27]|Some([191502.102, 2502145.384, 513234.745, 2908640.206])| + EPSG_2705|2705|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 39E"|Projected|[37.5, 43.33, 40.5, 68.8]|Some([378458.368, 4799479.419, 621770.015, 7635955.933])| + EPSG_2706|2706|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 42E"|Projected|[40.5, 42.87, 43.5, 68.74]|Some([377540.579, 4748371.694, 622680.808, 7629259.217])| + EPSG_2707|2707|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 45E"|Projected|[43.5, 41.89, 46.5, 80.8]|Some([375615.37, 4639508.678, 624598.349, 8974908.773])| + EPSG_2708|2708|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 48E"|Projected|[46.5, 41.19, 49.5, 80.91]|Some([374261.236, 4561759.922, 625944.239, 8987182.437])| + EPSG_2709|2709|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 51E"|Projected|[49.5, 42.36, 52.5, 81.22]|Some([376524.064, 4691706.793, 623672.763, 9021782.571])| + EPSG_2710|2710|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 54E"|Projected|[52.5, 50.52, 55.5, 81.41]|Some([393717.464, 5598765.734, 606470.878, 9042987.523])| + EPSG_2711|2711|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 57E"|Projected|[55.5, 50.53, 58.5, 81.89]|Some([393735.078, 5599874.313, 606443.421, 9096566.86])| + EPSG_2712|2712|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 60E"|Projected|[58.5, 50.47, 61.5, 81.91]|Some([393595.101, 5593196.403, 606573.045, 9098794.936])| + EPSG_27120|27120|"Naparima 1972 / UTM zone 20N"|Projected|[-60.9, 11.08, -60.44, 11.41]|Some([728967.2, 1225448.524, 779505.312, 1262364.095])| + EPSG_2713|2713|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 63E"|Projected|[61.5, 51.03, 64.5, 81.77]|Some([394852.925, 5655489.165, 605304.46, 9083161.716])| + EPSG_2714|2714|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 66E"|Projected|[64.5, 54.31, 67.5, 81.25]|Some([402445.705, 6020483.03, 597700.779, 9025108.489])| + EPSG_2715|2715|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 69E"|Projected|[67.5, 54.85, 70.5, 77.07]|Some([403722.503, 6080590.245, 596412.361, 8558540.545])| + EPSG_2716|2716|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 72E"|Projected|[70.5, 53.43, 73.5, 73.57]|Some([400362.55, 5922531.222, 599760.088, 8167979.436])| + EPSG_2717|2717|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 75E"|Projected|[73.5, 53.47, 76.5, 79.71]|Some([400450.014, 5926980.442, 599660.216, 8853192.482])| + EPSG_2718|2718|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 78E"|Projected|[76.5, 51.49, 79.5, 81.03]|Some([395868.235, 5706651.863, 604229.046, 9000536.516])| + EPSG_2719|2719|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 81E"|Projected|[79.5, 50.7, 82.5, 81.03]|Some([394070.939, 5618762.777, 606013.247, 9000534.038])| + EPSG_2720|2720|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 84E"|Projected|[82.5, 49.58, 85.5, 77.56]|Some([391560.364, 5494182.217, 608510.448, 8613213.181])| + EPSG_27200|27200|"NZGD49 / New Zealand Map Grid"|Projected|[166.37, -47.33, 178.63, -34.1]|Some([1896988.079, 5299420.541, 3031001.671, 6788999.185])| + EPSG_27205|27205|"NZGD49 / Mount Eden Circuit"|Projected|[171.99, -39.01, 176.12, -34.1]|Some([43961.811, 459721.538, 425071.045, 1008197.176])| + EPSG_27206|27206|"NZGD49 / Bay of Plenty Circuit"|Projected|[175.75, -39.13, 177.23, -37.22]|Some([236415.44, 547585.451, 367774.36, 759874.133])| + EPSG_27207|27207|"NZGD49 / Poverty Bay Circuit"|Projected|[176.73, -39.04, 178.63, -37.49]|Some([197776.877, 653064.792, 365811.393, 825754.569])| + EPSG_27208|27208|"NZGD49 / Hawkes Bay Circuit"|Projected|[175.8, -40.57, 178.07, -38.87]|Some([224159.565, 596818.371, 421165.611, 786505.626])| + EPSG_27209|27209|"NZGD49 / Taranaki Circuit"|Projected|[173.68, -39.78, 175.44, -38.4]|Some([252114.079, 627575.372, 405859.042, 781482.355])| + EPSG_2721|2721|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 87E"|Projected|[85.5, 49.07, 88.5, 77.16]|Some([390427.243, 5437460.987, 609630.053, 8568569.257])| + EPSG_27210|27210|"NZGD49 / Tuhirangi Circuit"|Projected|[174.88, -39.55, 176.33, -38.87]|Some([234023.496, 695362.776, 359863.039, 771132.784])| + EPSG_27211|27211|"NZGD49 / Wanganui Circuit"|Projected|[174.4, -40.97, 176.27, -39.46]|Some([206337.883, 618388.56, 367277.869, 786628.68])| + EPSG_27212|27212|"NZGD49 / Wairarapa Circuit"|Projected|[175.01, -41.67, 176.55, -40.29]|Some([245789.389, 616734.965, 376739.582, 770382.988])| + EPSG_27213|27213|"NZGD49 / Wellington Circuit"|Projected|[174.52, -41.5, 175.36, -40.91]|Some([278365.507, 677579.861, 349135.731, 743268.518])| + EPSG_27214|27214|"NZGD49 / Collingwood Circuit"|Projected|[172.16, -41.22, 173.13, -40.44]|Some([256543.922, 643572.125, 338842.611, 730318.857])| + EPSG_27215|27215|"NZGD49 / Nelson Circuit"|Projected|[172.4, -42.18, 174.08, -40.66]|Some([223934.793, 598849.701, 366007.534, 768057.06])| + EPSG_27216|27216|"NZGD49 / Karamea Circuit"|Projected|[171.96, -41.49, 172.7, -40.75]|Some([287402.912, 677418.447, 349898.174, 759768.993])| + EPSG_27217|27217|"NZGD49 / Buller Circuit"|Projected|[171.27, -42.19, 172.41, -41.42]|Some([273968.532, 657359.141, 369272.169, 743215.359])| + EPSG_27218|27218|"NZGD49 / Grey Circuit"|Projected|[171.15, -42.74, 172.75, -41.5]|Some([266610.153, 653979.207, 400205.849, 792411.698])| + EPSG_27219|27219|"NZGD49 / Amuri Circuit"|Projected|[171.88, -42.95, 173.55, -42.09]|Some([206487.0, 670211.225, 344654.199, 766363.659])| + EPSG_2722|2722|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 90E"|Projected|[88.5, 49.44, 91.5, 81.28]|Some([391236.775, 5478609.07, 608806.927, 9028436.416])| + EPSG_27220|27220|"NZGD49 / Marlborough Circuit"|Projected|[172.95, -42.65, 174.46, -40.85]|Some([228134.464, 576661.967, 355467.626, 776938.841])| + EPSG_27221|27221|"NZGD49 / Hokitika Circuit"|Projected|[170.39, -43.23, 171.89, -42.41]|Some([251424.138, 661230.491, 374900.455, 752725.912])| + EPSG_27222|27222|"NZGD49 / Okarito Circuit"|Projected|[169.21, -43.85, 170.89, -43.0]|Some([214297.62, 617074.192, 351287.663, 712047.885])| + EPSG_27223|27223|"NZGD49 / Jacksons Bay Circuit"|Projected|[168.02, -44.4, 170.01, -43.67]|Some([252711.689, 651944.155, 413205.48, 734014.994])| + EPSG_27224|27224|"NZGD49 / Mount Pleasant Circuit"|Projected|[171.11, -43.96, 173.38, -42.69]|Some([167458.449, 657503.724, 353486.91, 799872.373])| + EPSG_27225|27225|"NZGD49 / Gawler Circuit"|Projected|[170.68, -44.25, 172.26, -43.13]|Some([244598.766, 643722.956, 373162.583, 768555.52])| + EPSG_27226|27226|"NZGD49 / Timaru Circuit"|Projected|[169.82, -44.98, 171.55, -43.35]|Some([199678.196, 634865.405, 339942.029, 816729.593])| + EPSG_27227|27227|"NZGD49 / Lindis Peak Circuit"|Projected|[168.62, -45.4, 170.24, -43.71]|Some([231667.19, 625593.672, 362233.35, 813742.525])| + EPSG_27228|27228|"NZGD49 / Mount Nicholas Circuit"|Projected|[167.72, -45.58, 169.11, -44.29]|Some([245828.925, 649882.644, 356771.72, 793487.465])| + EPSG_27229|27229|"NZGD49 / Mount York Circuit"|Projected|[166.37, -46.33, 168.21, -44.53]|Some([191185.585, 613736.368, 337445.858, 814700.843])| + EPSG_2723|2723|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 93E"|Projected|[91.5, 50.16, 94.5, 81.26]|Some([392831.521, 5558689.904, 607198.491, 9026202.694])| + EPSG_27230|27230|"NZGD49 / Observation Point Circuit"|Projected|[169.77, -45.82, 171.24, -44.61]|Some([231836.939, 699039.82, 348526.006, 833873.642])| + EPSG_27231|27231|"NZGD49 / North Taieri Circuit"|Projected|[168.64, -46.73, 170.87, -45.23]|Some([171004.14, 601975.24, 346121.405, 770006.93])| + EPSG_27232|27232|"NZGD49 / Bluff Circuit"|Projected|[167.29, -47.33, 168.97, -45.33]|Some([217459.905, 618131.239, 349160.573, 840985.734])| + EPSG_2724|2724|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 96E"|Projected|[94.5, 49.73, 97.5, 81.35]|Some([391866.007, 5510861.583, 608150.095, 9036248.828])| + EPSG_2725|2725|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 99E"|Projected|[97.5, 49.79, 100.5, 80.9]|Some([391992.439, 5517534.721, 608009.747, 8986014.586])| + EPSG_27258|27258|"NZGD49 / UTM zone 58S"|Projected|[165.87, -47.65, 168.0, -42.59]|Some([565335.907, 4717962.96, 746152.897, 5284072.731])| + EPSG_27259|27259|"NZGD49 / UTM zone 59S"|Projected|[168.0, -47.64, 174.0, -33.89]|Some([222534.86, 4719073.981, 777437.635, 6249776.461])| + EPSG_2726|2726|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 102E"|Projected|[100.5, 50.17, 103.5, 79.71]|Some([392833.034, 5559801.356, 607155.25, 8853179.671])| + EPSG_27260|27260|"NZGD49 / UTM zone 60S"|Projected|[174.0, -44.13, 179.27, -34.24]|Some([223668.587, 5109038.377, 709040.713, 6210969.854])| + EPSG_2727|2727|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 105E"|Projected|[103.5, 50.13, 106.5, 79.21]|Some([392736.626, 5555352.376, 607237.729, 8797369.716])| + EPSG_2728|2728|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 108E"|Projected|[106.5, 49.25, 109.5, 78.4]|Some([390775.372, 5457477.2, 609185.02, 8706961.361])| + EPSG_2729|2729|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 111E"|Projected|[109.5, 49.14, 112.5, 76.81]|Some([390526.001, 5445244.604, 609420.606, 8529506.925])| + EPSG_27291|27291|"NZGD49 / North Island Grid"|Projected|[171.99, -41.67, 178.63, -34.1]|Some([-54317.945, 69032.676, 615905.556, 994444.877])| + EPSG_27292|27292|"NZGD49 / South Island Grid"|Projected|[166.37, -47.33, 174.46, -40.44]|Some([23891.873, 81059.467, 774655.935, 932256.802])| + EPSG_2730|2730|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 114E"|Projected|[112.5, 49.49, 115.5, 76.7]|Some([391292.074, 5484171.011, 608640.927, 8517232.269])| + EPSG_2731|2731|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 117E"|Projected|[115.5, 49.51, 118.5, 74.43]|Some([391329.608, 5486396.75, 608589.923, 8263924.202])| + EPSG_2732|2732|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 120E"|Projected|[118.5, 49.87, 121.5, 73.63]|Some([392122.449, 5526438.478, 607783.853, 8174665.432])| + EPSG_2733|2733|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 123E"|Projected|[121.5, 53.18, 124.5, 74.0]|Some([399663.984, 5894703.386, 600229.657, 8215950.185])| + EPSG_2734|2734|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 126E"|Projected|[124.5, 49.88, 127.5, 74.0]|Some([392131.776, 5527554.802, 607748.757, 8215952.902])| + EPSG_2735|2735|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 129E"|Projected|[127.5, 42.67, 130.5, 73.59]|Some([376969.165, 4726121.688, 622898.204, 8170210.679])| + EPSG_2736|2736|"Tete / UTM zone 36S"|Projected|[30.21, -26.87, 36.0, -11.41]|Some([195567.914, 7024667.872, 827456.363, 8738918.845])| + EPSG_2737|2737|"Tete / UTM zone 37S"|Projected|[35.99, -18.98, 40.9, -10.42]|Some([170445.244, 7898889.623, 708063.035, 8848389.755])| + EPSG_2738|2738|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 132E"|Projected|[130.5, 42.25, 133.5, 71.99]|Some([376138.301, 4679469.727, 623716.943, 7991712.281])| + EPSG_2739|2739|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 135E"|Projected|[133.5, 42.74, 136.5, 75.9]|Some([377095.276, 4733902.668, 622748.309, 8427973.983])| + EPSG_27391|27391|"NGO 1948 (Oslo) / NGO zone I"|Projected|[4.39, 57.92, 7.23, 63.17]|Some([-98492.558, -9001.471, 69809.814, 577043.129])| + EPSG_27392|27392|"NGO 1948 (Oslo) / NGO zone II"|Projected|[7.22, 57.9, 9.56, 64.23]|Some([-69089.748, -11237.48, 69661.584, 694630.589])| + EPSG_27393|27393|"NGO 1948 (Oslo) / NGO zone III"|Projected|[9.55, 58.78, 11.98, 67.58]|Some([-67569.707, 86771.902, 72979.376, 1068185.264])| + EPSG_27394|27394|"NGO 1948 (Oslo) / NGO zone IV"|Projected|[11.97, 59.88, 15.06, 69.1]|Some([-69881.801, 209302.364, 103151.701, 1238269.813])| + EPSG_27395|27395|"NGO 1948 (Oslo) / NGO zone V"|Projected|[15.05, 66.15, 18.89, 70.27]|Some([-82737.03, 908146.91, 90569.497, 1368921.097])| + EPSG_27396|27396|"NGO 1948 (Oslo) / NGO zone VI"|Projected|[18.88, 68.33, 22.89, 70.93]|Some([-82533.112, 1151253.175, 82754.424, 1442513.641])| + EPSG_27397|27397|"NGO 1948 (Oslo) / NGO zone VII"|Projected|[22.88, 68.58, 26.98, 71.24]|Some([-81609.526, 1179121.893, 85530.585, 1477168.952])| + EPSG_27398|27398|"NGO 1948 (Oslo) / NGO zone VIII"|Projected|[26.97, 69.02, 31.32, 71.19]|Some([-83054.98, 1228183.708, 90799.061, 1471798.868])| + EPSG_2740|2740|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 138E"|Projected|[136.5, 44.76, 139.5, 76.27]|Some([381153.588, 4958341.497, 618678.901, 8469268.092])| + EPSG_2741|2741|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 141E"|Projected|[139.5, 45.84, 142.5, 76.23]|Some([383382.195, 5078372.857, 616439.552, 8464808.49])| + EPSG_2742|2742|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 144E"|Projected|[142.5, 43.61, 145.5, 75.98]|Some([378810.651, 4830564.964, 621000.501, 8436914.214])| + EPSG_27429|27429|"Datum 73 / UTM zone 29N"|Projected|[-9.56, 36.95, -6.19, 42.16]|Some([450065.332, 4089308.93, 750140.88, 4671349.287])| + EPSG_2743|2743|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 147E"|Projected|[145.5, 43.6, 148.5, 76.76]|Some([378785.58, 4829457.316, 621015.696, 8523965.003])| + EPSG_2744|2744|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 150E"|Projected|[148.5, 45.21, 151.5, 76.82]|Some([382058.872, 5008364.061, 617733.217, 8530666.098])| + EPSG_2745|2745|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 153E"|Projected|[151.5, 46.72, 154.5, 76.26]|Some([385213.973, 5176205.207, 614569.476, 8468175.967])| + EPSG_2746|2746|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 156E"|Projected|[154.5, 49.02, 157.5, 77.2]|Some([390175.767, 5431941.132, 609599.709, 8573085.917])| + EPSG_2747|2747|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 159E"|Projected|[157.5, 51.36, 160.5, 71.12]|Some([395407.042, 5692230.146, 604361.073, 7894704.974])| + EPSG_2748|2748|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 162E"|Projected|[160.5, 54.34, 163.5, 70.98]|Some([402323.809, 6023859.646, 597437.649, 7879094.374])| + EPSG_2749|2749|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 165E"|Projected|[163.5, 54.69, 166.5, 69.82]|Some([403151.228, 6062824.849, 596603.921, 7749716.588])| + EPSG_27493|27493|"Datum 73 / Modified Portuguese Grid"|Projected|[-9.56, 36.95, -6.19, 42.16]|Some([-127102.779, -301734.29, 173077.539, 278540.724])| + EPSG_2750|2750|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 168E"|Projected|[166.5, 54.45, 169.5, 70.07]|Some([402578.344, 6036114.244, 597171.087, 7777605.655])| + EPSG_27500|27500|"ATF (Paris) / Nord de Guerre"|Projected|[6.84, 47.42, 8.23, 49.07]|Some([432310.739, 68819.162, 537178.939, 252601.05])| + EPSG_2751|2751|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 171E"|Projected|[169.5, 59.86, 172.5, 70.19]|Some([415823.67, 6638591.263, 583921.381, 7790995.921])| + EPSG_2752|2752|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 174E"|Projected|[172.5, 60.99, 175.5, 70.02]|Some([418688.351, 6764501.387, 581052.509, 7772041.319])| + EPSG_2753|2753|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 177E"|Projected|[175.5, 62.09, 178.5, 71.1]|Some([421508.005, 6887089.555, 578229.362, 7892510.453])| + EPSG_2754|2754|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 180E"|Projected|[178.5, 62.24, -178.5, 71.65]|Some([421893.548, 6903812.886, 577840.917, 7953868.801])| + EPSG_2755|2755|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 177W"|Projected|[-178.5, 64.74, -175.5, 71.61]|Some([428420.796, 7182493.481, 571311.781, 7949413.264])| + EPSG_2756|2756|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 174W"|Projected|[-175.5, 64.2, -172.5, 67.78]|Some([426997.778, 7122297.782, 572733.251, 7522262.419])| + EPSG_27561|27561|"NTF (Paris) / Lambert Nord France"|Projected|[-4.87, 48.14, 8.23, 51.14]|Some([64398.201, 48794.111, 1038236.253, 406550.265])| + EPSG_27562|27562|"NTF (Paris) / Lambert Centre France"|Projected|[-4.8, 45.44, 7.63, 48.15]|Some([42305.076, 48844.509, 1013919.298, 374195.249])| + EPSG_27563|27563|"NTF (Paris) / Lambert Sud France"|Projected|[-1.79, 42.33, 7.71, 45.46]|Some([259903.039, 3351.274, 1042726.81, 364839.797])| + EPSG_27564|27564|"NTF (Paris) / Lambert Corse"|Projected|[8.5, 41.31, 9.63, 43.07]|Some([501837.105, 109519.055, 610277.097, 311750.743])| + EPSG_2757|2757|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 171W"|Projected|[-172.5, 64.35, -169.57, 67.06]|Some([427391.784, 7139026.845, 568956.872, 7441987.9])| + EPSG_27571|27571|"NTF (Paris) / Lambert zone I"|Projected|[-4.87, 48.14, 8.23, 51.14]|Some([64398.201, 1048794.111, 1038236.253, 1406550.265])| + EPSG_27572|27572|"NTF (Paris) / Lambert zone II"|Projected|[-4.87, 42.33, 8.23, 51.14]|Some([5168.43, 1702873.868, 1086668.418, 2706243.896])| + EPSG_27573|27573|"NTF (Paris) / Lambert zone III"|Projected|[-1.79, 42.33, 7.71, 45.46]|Some([259903.039, 3003351.274, 1042726.81, 3364839.797])| + EPSG_27574|27574|"NTF (Paris) / Lambert zone IV"|Projected|[8.5, 41.31, 9.63, 43.07]|Some([501837.105, 4109519.055, 610277.097, 4311750.743])| + EPSG_2758|2758|"Pulkovo 1995 / 3-degree Gauss-Kruger CM 168W"|Projected|[-169.22, 65.7, -168.97, 65.86]|Some([443823.984, 7289892.837, 455582.489, 7307930.978])| + EPSG_2759|2759|"NAD83(HARN) / Alabama East"|Projected|[-86.79, 30.99, -84.89, 35.0]|Some([108626.806, 54321.103, 290100.475, 499448.674])| + EPSG_2760|2760|"NAD83(HARN) / Alabama West"|Projected|[-88.48, 30.14, -86.3, 35.02]|Some([505580.93, 15518.387, 715617.624, 557318.658])| + EPSG_2761|2761|"NAD83(HARN) / Arizona East"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([66482.592, 36584.053, 320582.594, 667692.78])| + EPSG_2762|2762|"NAD83(HARN) / Arizona Central"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([76952.329, 36583.867, 353894.123, 667598.654])| + EPSG_2763|2763|"NAD83(HARN) / Arizona West"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([113256.892, 116414.204, 329520.671, 666198.947])| + EPSG_2764|2764|"NAD83(HARN) / Arkansas North"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([159882.773, 37351.102, 616294.707, 243509.972])| + EPSG_2765|2765|"NAD83(HARN) / Arkansas South"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([168267.64, 438081.186, 549514.235, 672643.609])| + EPSG_2766|2766|"NAD83(HARN) / California zone 1"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([1789540.727, 528503.28, 2172671.944, 800070.988])| + EPSG_2767|2767|"NAD83(HARN) / California zone 2"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([1819115.425, 539224.154, 2216002.439, 779629.955])| + EPSG_2768|2768|"NAD83(HARN) / California zone 3"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([1774908.838, 525528.509, 2238488.255, 748596.609])| + EPSG_2769|2769|"NAD83(HARN) / California zone 4"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([1727887.925, 549563.577, 2305550.391, 754562.275])| + EPSG_2770|2770|"NAD83(HARN) / California zone 5"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([1679424.323, 417901.88, 2363676.289, 763023.255])| + EPSG_27700|27700|"OSGB36 / British National Grid"|Projected|[-9.01, 49.75, 2.01, 61.01]|Some([-104728.765, -16627.284, 688806.007, 1256616.321])| + EPSG_27701|27701|"WGS 84 / Equi7 Africa"|Projected|[-31.68, -43.89, 79.3, 38.8]|Some([-328746.637, -337225.812, 12088218.902, 10275625.585])| + EPSG_27702|27702|"WGS 84 / Equi7 Antarctica"|Projected|[-180.0, -90.0, 180.0, -34.47]|Some([-2438013.31, -2784152.26, 9866547.264, 9588185.272])| + EPSG_27703|27703|"WGS 84 / Equi7 Asia"|Projected|[31.45, -10.8, -168.43, 83.67]|Some([-3542679.294, -1587640.07, 15788530.972, 9709655.184])| + EPSG_27704|27704|"WGS 84 / Equi7 Europe"|Projected|[-42.52, 29.24, 51.73, 83.67]|Some([-84889.626, -517149.002, 8561760.722, 5952264.206])| + EPSG_27705|27705|"WGS 84 / Equi7 North America"|Projected|[-180.0, 7.98, 180.0, 90.0]|Some([-1645027.574, -11892.75, 18212027.479, 18218187.692])| + EPSG_27706|27706|"WGS 84 / Equi7 Oceania"|Projected|[72.37, -60.56, -121.05, 21.57]|Some([227152.169, -308053.196, 19197633.98, 12197711.638])| + EPSG_27707|27707|"WGS 84 / Equi7 South America"|Projected|[-124.82, -59.87, -14.58, 30.31]|Some([958.172, -682930.226, 12441372.45, 10624597.219])| + EPSG_2771|2771|"NAD83(HARN) / California zone 6"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([1821505.205, 540295.244, 2171921.511, 713800.24])| + EPSG_2772|2772|"NAD83(HARN) / Colorado North"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([608531.725, 329967.945, 1211686.21, 496983.191])| + EPSG_2773|2773|"NAD83(HARN) / Colorado Central"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([602370.91, 338844.208, 1217673.853, 561272.413])| + EPSG_2774|2774|"NAD83(HARN) / Colorado South"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([597497.32, 339576.6, 1222410.382, 534163.124])| + EPSG_2775|2775|"NAD83(HARN) / Connecticut"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([222322.636, 168687.732, 386437.159, 287985.832])| + EPSG_2776|2776|"NAD83(HARN) / Delaware"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([166533.31, 48839.189, 238996.299, 205469.592])| + EPSG_2777|2777|"NAD83(HARN) / Florida East"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([65102.56, 8490.922, 304467.456, 720631.854])| + EPSG_2778|2778|"NAD83(HARN) / Florida West"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([66151.169, 214525.955, 286899.559, 584279.801])| + EPSG_2779|2779|"NAD83(HARN) / Florida North"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([295646.954, 23278.66, 839216.159, 226916.491])| + EPSG_2780|2780|"NAD83(HARN) / Georgia East"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([74710.135, 39904.144, 334263.531, 519814.495])| + EPSG_2781|2781|"NAD83(HARN) / Georgia West"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([561619.087, 68724.986, 812812.736, 556484.677])| + EPSG_2782|2782|"NAD83(HARN) / Hawaii zone 1"|Projected|[-156.1, 18.87, -154.74, 20.33]|Some([436777.084, 4058.543, 580083.065, 165857.102])| + EPSG_2783|2783|"NAD83(HARN) / Hawaii zone 2"|Projected|[-157.36, 20.45, -155.93, 21.26]|Some([427654.163, 12915.794, 576867.636, 102770.167])| + EPSG_2784|2784|"NAD83(HARN) / Hawaii zone 3"|Projected|[-158.33, 21.2, -157.61, 21.75]|Some([465735.883, 3690.622, 540494.023, 64638.674])| + EPSG_2785|2785|"NAD83(HARN) / Hawaii zone 4"|Projected|[-159.85, 21.81, -159.23, 22.29]|Some([463810.5, -2583.624, 527917.563, 50608.486])| + EPSG_2786|2786|"NAD83(HARN) / Hawaii zone 5"|Projected|[-160.3, 21.73, -159.99, 22.07]|Some([486205.794, 7012.661, 518277.332, 44671.059])| + EPSG_2787|2787|"NAD83(HARN) / Idaho East"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([111064.714, 35910.384, 293356.509, 343147.344])| + EPSG_2788|2788|"NAD83(HARN) / Idaho Central"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([392282.857, 35910.166, 610205.025, 448964.031])| + EPSG_2789|2789|"NAD83(HARN) / Idaho West"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([676540.839, 35910.004, 918489.583, 817140.942])| + EPSG_2790|2790|"NAD83(HARN) / Illinois East"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([216692.846, 43648.967, 416810.442, 648475.358])| + EPSG_2791|2791|"NAD83(HARN) / Illinois West"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([579494.738, 33659.966, 811007.889, 649615.93])| + EPSG_2792|2792|"NAD83(HARN) / Indiana East"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([18848.6, 299943.767, 177929.529, 724483.801])| + EPSG_2793|2793|"NAD83(HARN) / Indiana West"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([810427.764, 279965.682, 974301.422, 724571.336])| + EPSG_2794|2794|"NAD83(HARN) / Iowa North"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([1238457.132, 1038881.237, 1778141.581, 1228643.13])| + EPSG_2795|2795|"NAD83(HARN) / Iowa South"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([275771.26, 39977.977, 785356.252, 231924.171])| + EPSG_2796|2796|"NAD83(HARN) / Kansas North"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([46039.006, 20723.753, 698194.512, 193909.387])| + EPSG_2797|2797|"NAD83(HARN) / Kansas South"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([84023.505, 435887.76, 747113.272, 652739.617])| + EPSG_2798|2798|"NAD83(HARN) / Kentucky North"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([349218.686, 23309.222, 656953.665, 184640.261])| + EPSG_2799|2799|"NAD83(HARN) / Kentucky South"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([157793.575, 517385.343, 840416.564, 710601.527])| + EPSG_2800|2800|"NAD83(HARN) / Louisiana North"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([851729.988, 38808.439, 1156879.821, 281691.895])| + EPSG_2801|2801|"NAD83(HARN) / Louisiana South"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([745634.013, 38797.605, 1252090.569, 287715.298])| + EPSG_2802|2802|"NAD83(HARN) / Maine East"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([177049.158, 23700.067, 427772.438, 423895.709])| + EPSG_2803|2803|"NAD83(HARN) / Maine West"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([824761.671, 22957.2, 973880.256, 416751.558])| + EPSG_2804|2804|"NAD83(HARN) / Maryland"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([181219.326, 33673.53, 578371.112, 231962.42])| + EPSG_2805|2805|"NAD83(HARN) / Massachusetts Mainland"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([32912.406, 801093.18, 337016.021, 961844.967])| + EPSG_2806|2806|"NAD83(HARN) / Massachusetts Island"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([465603.831, 21099.899, 551174.596, 56818.517])| + EPSG_2807|2807|"NAD83(HARN) / Michigan North"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([7730770.573, 32976.125, 8280244.176, 399101.507])| + EPSG_2808|2808|"NAD83(HARN) / Michigan Central"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([5783270.107, 53710.355, 6168729.618, 292765.609])| + EPSG_2809|2809|"NAD83(HARN) / Michigan South"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([3764136.407, 21105.658, 4186207.427, 305974.077])| + EPSG_2810|2810|"NAD83(HARN) / Minnesota North"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([484663.524, 115564.073, 1076333.617, 428199.813])| + EPSG_2811|2811|"NAD83(HARN) / Minnesota Central"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([595233.838, 131121.281, 953783.985, 378907.596])| + EPSG_2812|2812|"NAD83(HARN) / Minnesota South"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([569493.165, 154446.237, 1025657.186, 391675.472])| + EPSG_2813|2813|"NAD83(HARN) / Mississippi East"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([190340.644, 56529.247, 371712.187, 611572.226])| + EPSG_2814|2814|"NAD83(HARN) / Mississippi West"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([574254.337, 166276.37, 792000.408, 611774.078])| + EPSG_2815|2815|"NAD83(HARN) / Missouri East"|Projected|[-91.97, 35.98, -89.1, 40.61]|Some([117430.777, 16272.478, 376257.049, 531216.641])| + EPSG_2816|2816|"NAD83(HARN) / Missouri Central"|Projected|[-93.79, 36.48, -91.41, 40.61]|Some([384403.191, 71750.047, 597675.207, 530977.793])| + EPSG_2817|2817|"NAD83(HARN) / Missouri West"|Projected|[-95.77, 36.48, -93.48, 40.59]|Some([736194.641, 34766.62, 941403.087, 491752.32])| + EPSG_2818|2818|"NAD83(HARN) / Montana"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([76531.52, 11128.611, 1035188.202, 549125.361])| + EPSG_2819|2819|"NAD83(HARN) / Nebraska"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([153377.029, 17395.51, 901214.503, 363143.949])| + EPSG_28191|28191|"Palestine 1923 / Palestine Grid"|Projected|[34.17, 29.18, 39.31, 33.38]|Some([68886.188, -156267.991, 568789.115, 316899.38])| + EPSG_28192|28192|"Palestine 1923 / Palestine Belt"|Projected|[34.17, 29.18, 39.31, 33.38]|Some([68881.905, 843732.009, 569049.626, 1316899.422])| + EPSG_28193|28193|"Palestine 1923 / Israeli CS Grid"|Projected|[34.17, 29.45, 35.69, 33.28]|Some([69099.905, 873606.926, 216561.8, 1298746.247])| + EPSG_2820|2820|"NAD83(HARN) / Nevada East"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([69755.834, 8026622.525, 341811.077, 8805864.774])| + EPSG_2821|2821|"NAD83(HARN) / Nevada Central"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([362660.438, 6138670.311, 651167.127, 6694999.193])| + EPSG_2822|2822|"NAD83(HARN) / Nevada West"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([673827.752, 4244078.869, 941910.098, 4805925.708])| + EPSG_2823|2823|"NAD83(HARN) / New Hampshire"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([226793.87, 21104.478, 384952.104, 312731.649])| + EPSG_28232|28232|"Pointe Noire / UTM zone 32S"|Projected|[8.84, -6.91, 18.65, 3.72]|Some([482122.643, 9225566.268, 1578868.781, 10417395.997])| + EPSG_2824|2824|"NAD83(HARN) / New Jersey"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([54545.258, 4069.239, 203801.464, 281107.127])| + EPSG_2825|2825|"NAD83(HARN) / New Mexico East"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([33976.663, 110867.65, 291930.233, 666374.391])| + EPSG_2826|2826|"NAD83(HARN) / New Mexico Central"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([359825.213, 86474.748, 633546.062, 666493.445])| + EPSG_2827|2827|"NAD83(HARN) / New Mexico West"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([713260.043, 36584.923, 974026.348, 666551.228])| + EPSG_2828|2828|"NAD83(HARN) / New York East"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([34536.091, 227222.346, 257035.916, 688008.811])| + EPSG_2829|2829|"NAD83(HARN) / New York Central"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([153331.175, 220982.373, 376222.944, 490948.22])| + EPSG_2830|2830|"NAD83(HARN) / New York West"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([251674.02, 220982.544, 451364.67, 404995.23])| + EPSG_2831|2831|"NAD83(HARN) / New York Long Island"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([277950.343, 33682.856, 486555.716, 128169.712])| + EPSG_2832|2832|"NAD83(HARN) / North Dakota North"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([329320.381, 16677.714, 878257.548, 229886.964])| + EPSG_2833|2833|"NAD83(HARN) / North Dakota South"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([324736.864, 29272.088, 906255.477, 247921.648])| + EPSG_2834|2834|"NAD83(HARN) / Ohio North"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([403035.782, 48120.671, 769684.87, 298303.099])| + EPSG_28348|28348|"GDA94 / MGA zone 48"|Projected|[102.14, -34.68, 108.01, -8.87]|Some([185409.281, 6158318.803, 831104.436, 9019519.378])| + EPSG_28349|28349|"GDA94 / MGA zone 49"|Projected|[108.0, -37.84, 114.01, -10.72]|Some([171822.512, 5807666.716, 829272.355, 8814980.011])| + EPSG_2835|2835|"NAD83(HARN) / Ohio South"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([396473.177, 44405.968, 757238.646, 264560.679])| + EPSG_28350|28350|"GDA94 / MGA zone 50"|Projected|[114.0, -38.53, 120.01, -12.06]|Some([173357.972, 5731082.25, 827731.754, 8666811.663])| + EPSG_28351|28351|"GDA94 / MGA zone 51"|Projected|[120.0, -38.07, 126.01, -10.46]|Some([171545.269, 5782139.287, 829550.527, 8843727.573])| + EPSG_28352|28352|"GDA94 / MGA zone 52"|Projected|[125.99, -37.38, 132.0, -9.1]|Some([169104.664, 5858719.403, 829795.052, 8994092.068])| + EPSG_28353|28353|"GDA94 / MGA zone 53"|Projected|[132.0, -40.71, 138.01, -8.88]|Some([170005.496, 5489076.49, 831095.455, 9018414.642])| + EPSG_28354|28354|"GDA94 / MGA zone 54"|Projected|[138.0, -48.19, 144.01, -9.08]|Some([170186.616, 4658200.177, 830913.729, 8996303.223])| + EPSG_28355|28355|"GDA94 / MGA zone 55"|Projected|[144.0, -50.89, 150.01, -9.23]|Some([170325.081, 4358091.03, 830774.8, 8979719.5])| + EPSG_28356|28356|"GDA94 / MGA zone 56"|Projected|[150.0, -58.96, 156.0, -13.87]|Some([175714.327, 3460530.811, 824285.673, 8466650.971])| + EPSG_28357|28357|"GDA94 / MGA zone 57"|Projected|[156.0, -60.56, 162.01, -14.08]|Some([176008.634, 3282444.37, 825072.218, 8443426.056])| + EPSG_28358|28358|"GDA94 / MGA zone 58"|Projected|[162.0, -59.39, 168.0, -25.94]|Some([199529.304, 3412679.943, 800470.696, 7130960.754])| + EPSG_2836|2836|"NAD83(HARN) / Oklahoma North"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([145234.907, 29966.078, 925683.198, 234484.046])| + EPSG_2837|2837|"NAD83(HARN) / Oklahoma South"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([414410.476, 31799.258, 932159.354, 253869.365])| + EPSG_2838|2838|"NAD83(HARN) / Oregon North"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([2205463.73, 31488.524, 2823407.529, 295943.569])| + EPSG_2839|2839|"NAD83(HARN) / Oregon South"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([1160305.824, 34807.165, 1798297.436, 329407.792])| + EPSG_2840|2840|"NAD83(HARN) / Rhode Island"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([70610.827, 5181.689, 135267.115, 104114.241])| + EPSG_28404|28404|"Pulkovo 1942 / Gauss-Kruger zone 4"|Projected|[19.57, 47.95, 24.0, 59.44]|Some([4393311.436, 5313002.348, 4724209.475, 6595660.829])| + EPSG_28405|28405|"Pulkovo 1942 / Gauss-Kruger zone 5"|Projected|[24.0, 45.18, 30.01, 69.47]|Some([5264323.516, 5005069.355, 5736707.502, 7712882.618])| + EPSG_28406|28406|"Pulkovo 1942 / Gauss-Kruger zone 6"|Projected|[30.0, 44.32, 36.0, 70.02]|Some([6260792.863, 4909489.524, 6739442.427, 7774144.124])| + EPSG_28407|28407|"Pulkovo 1942 / Gauss-Kruger zone 7"|Projected|[36.0, 41.43, 42.0, 69.23]|Some([7249337.884, 4588428.31, 7750883.149, 7686092.064])| + EPSG_28408|28408|"Pulkovo 1942 / Gauss-Kruger zone 8"|Projected|[42.0, 38.84, 48.01, 80.91]|Some([8239615.436, 4300832.574, 8761461.845, 8988222.666])| + EPSG_28409|28409|"Pulkovo 1942 / Gauss-Kruger zone 9"|Projected|[48.0, 37.34, 54.0, 81.4]|Some([9234221.968, 4134327.363, 9765968.277, 9042847.212])| + EPSG_2841|2841|"NAD83(HARN) / South Dakota North"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([274446.341, 34077.326, 883989.596, 243154.538])| + EPSG_28410|28410|"Pulkovo 1942 / Gauss-Kruger zone 10"|Projected|[54.0, 37.05, 60.0, 81.91]|Some([10233192.164, 4102135.436, 10766977.869, 9099712.916])| + EPSG_28411|28411|"Pulkovo 1942 / Gauss-Kruger zone 11"|Projected|[60.0, 35.14, 66.0, 81.77]|Some([11226634.717, 3890194.514, 11773511.637, 9084089.076])| + EPSG_28412|28412|"Pulkovo 1942 / Gauss-Kruger zone 12"|Projected|[66.0, 36.67, 72.0, 77.07]|Some([12231843.104, 4059952.286, 12768280.091, 8559969.162])| + EPSG_28413|28413|"Pulkovo 1942 / Gauss-Kruger zone 13"|Projected|[72.0, 36.79, 78.0, 79.71]|Some([13232247.905, 4073263.623, 13767849.342, 8854341.365])| + EPSG_28414|28414|"Pulkovo 1942 / Gauss-Kruger zone 14"|Projected|[78.0, 41.04, 84.01, 81.03]|Some([14247769.313, 4545065.159, 14753144.781, 9001547.239])| + EPSG_28415|28415|"Pulkovo 1942 / Gauss-Kruger zone 15"|Projected|[84.0, 46.82, 90.0, 81.27]|Some([15271096.995, 5187278.682, 15728947.048, 9028298.761])| + EPSG_28416|28416|"Pulkovo 1942 / Gauss-Kruger zone 16"|Projected|[90.0, 49.89, 96.0, 81.35]|Some([16284449.126, 5528648.758, 16715568.137, 9037216.977])| + EPSG_28417|28417|"Pulkovo 1942 / Gauss-Kruger zone 17"|Projected|[96.0, 49.73, 102.0, 81.32]|Some([17283721.763, 5510850.995, 17716265.315, 9033869.281])| + EPSG_28418|28418|"Pulkovo 1942 / Gauss-Kruger zone 18"|Projected|[102.0, 49.64, 108.0, 79.48]|Some([18283306.668, 5500840.387, 18716650.084, 8828675.512])| + EPSG_28419|28419|"Pulkovo 1942 / Gauss-Kruger zone 19"|Projected|[108.0, 49.14, 114.0, 76.81]|Some([19281079.401, 5445232.723, 19718846.794, 8530955.236])| + EPSG_2842|2842|"NAD83(HARN) / South Dakota South"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([293646.34, 16297.068, 920867.782, 280197.334])| + EPSG_28420|28420|"Pulkovo 1942 / Gauss-Kruger zone 20"|Projected|[114.0, 49.51, 120.0, 75.96]|Some([20282699.881, 5486384.198, 20717196.977, 8436186.796])| + EPSG_28421|28421|"Pulkovo 1942 / Gauss-Kruger zone 21"|Projected|[120.0, 51.51, 126.0, 74.0]|Some([21291681.397, 5708863.687, 21708188.301, 8217679.061])| + EPSG_28422|28422|"Pulkovo 1942 / Gauss-Kruger zone 22"|Projected|[126.0, 42.25, 132.0, 73.61]|Some([22252330.493, 4679454.411, 22747503.78, 8174208.545])| + EPSG_28423|28423|"Pulkovo 1942 / Gauss-Kruger zone 23"|Projected|[132.0, 42.63, 138.0, 76.15]|Some([23253810.29, 4721669.871, 23745998.032, 8457387.139])| + EPSG_28424|28424|"Pulkovo 1942 / Gauss-Kruger zone 24"|Projected|[138.0, 45.84, 144.0, 76.27]|Some([24266838.112, 5078359.194, 24732948.118, 8470774.745])| + EPSG_28425|28425|"Pulkovo 1942 / Gauss-Kruger zone 25"|Projected|[144.0, 43.6, 150.0, 76.82]|Some([25257646.756, 4829443.722, 25742115.157, 8532107.385])| + EPSG_28426|28426|"Pulkovo 1942 / Gauss-Kruger zone 26"|Projected|[150.0, 45.77, 156.0, 76.26]|Some([26266523.655, 5070593.451, 26733219.449, 8469679.966])| + EPSG_28427|28427|"Pulkovo 1942 / Gauss-Kruger zone 27"|Projected|[156.0, 50.27, 162.0, 77.2]|Some([27286001.657, 5570960.016, 27713726.722, 8574499.614])| + EPSG_28428|28428|"Pulkovo 1942 / Gauss-Kruger zone 28"|Projected|[162.0, 54.47, 168.0, 70.03]|Some([28305381.84, 6038322.476, 28694334.165, 7775236.972])| + EPSG_28429|28429|"Pulkovo 1942 / Gauss-Kruger zone 29"|Projected|[168.0, 54.45, 174.0, 70.19]|Some([29305280.311, 6036107.29, 29694422.921, 7793081.207])| + EPSG_2843|2843|"NAD83(HARN) / Tennessee"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([206556.918, 71748.648, 997092.977, 269010.649])| + EPSG_28430|28430|"Pulkovo 1942 / Gauss-Kruger zone 30"|Projected|[174.0, 61.65, 180.0, 71.59]|Some([30340893.574, 6838042.968, 30658805.129, 7949129.236])| + EPSG_28431|28431|"Pulkovo 1942 / Gauss-Kruger zone 31"|Projected|[-180.0, 64.35, -174.0, 71.65]|Some([31354931.327, 7139003.599, 31644762.485, 7955830.817])| + EPSG_28432|28432|"Pulkovo 1942 / Gauss-Kruger zone 32"|Projected|[-174.0, 64.2, -168.97, 67.18]|Some([32354140.353, 7122294.961, 32598453.528, 7457709.668])| + EPSG_2844|2844|"NAD83(HARN) / Texas North"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([59142.55, 1033282.073, 339017.91, 1278421.417])| + EPSG_2845|2845|"NAD83(HARN) / Texas North Central"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([166935.941, 2005914.516, 1026436.243, 2332222.332])| + EPSG_2846|2846|"NAD83(HARN) / Texas Central"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([88486.821, 3012578.734, 1360428.083, 3308389.106])| + EPSG_2847|2847|"NAD83(HARN) / Texas South Central"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([8760.165, 3994088.015, 1116404.636, 4329153.165])| + EPSG_2848|2848|"NAD83(HARN) / Texas South"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([129544.257, 5018096.906, 465444.123, 5282925.256])| + EPSG_2849|2849|"NAD83(HARN) / Utah North"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([284874.769, 1024060.805, 708353.397, 1189279.066])| + EPSG_2850|2850|"NAD83(HARN) / Utah Central"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([277518.695, 2017394.565, 714632.974, 2308012.808])| + EPSG_2851|2851|"NAD83(HARN) / Utah South"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([273009.092, 3035885.318, 718983.293, 3215388.75])| + EPSG_2852|2852|"NAD83(HARN) / Vermont"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([423006.929, 24437.331, 581907.719, 281577.882])| + EPSG_2853|2853|"NAD83(HARN) / Virginia North"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([3362553.579, 2011469.29, 3675327.511, 2200926.186])| + EPSG_2854|2854|"NAD83(HARN) / Virginia South"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([3036367.299, 1022941.107, 3785608.301, 1228478.099])| + EPSG_2855|2855|"NAD83(HARN) / Washington North"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([199623.253, 8896.664, 789506.593, 235373.804])| + EPSG_2856|2856|"NAD83(HARN) / Washington South"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([195500.508, 22975.415, 780315.23, 260318.461])| + EPSG_2857|2857|"NAD83(HARN) / West Virginia North"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([403575.07, 28864.395, 754713.008, 240000.181])| + EPSG_2858|2858|"NAD83(HARN) / West Virginia South"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([453513.818, 22197.828, 773117.039, 242634.665])| + EPSG_2859|2859|"NAD83(HARN) / Wisconsin North"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([373645.099, 22598.573, 752750.056, 242220.167])| + EPSG_2860|2860|"NAD83(HARN) / Wisconsin Central"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([368161.39, 16296.727, 900786.43, 225279.769])| + EPSG_28600|28600|"Qatar 1974 / Qatar National Grid"|Projected|[50.69, 24.55, 51.68, 26.2]|Some([146719.131, 311001.198, 247020.002, 493889.954])| + EPSG_2861|2861|"NAD83(HARN) / Wisconsin South"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([482417.129, 53325.921, 850746.657, 263303.842])| + EPSG_2862|2862|"NAD83(HARN) / Wyoming East"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([102113.981, 54410.129, 293960.971, 501633.298])| + EPSG_2863|2863|"NAD83(HARN) / Wyoming East Central"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([290894.638, 154410.047, 512192.508, 601839.845])| + EPSG_2864|2864|"NAD83(HARN) / Wyoming West Central"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([405624.0, 54411.371, 705180.397, 503571.634])| + EPSG_2865|2865|"NAD83(HARN) / Wyoming West"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([717821.085, 154410.443, 887790.45, 563723.085])| + EPSG_2866|2866|"NAD83(HARN) / Puerto Rico and Virgin Is."|Projected|[-67.97, 17.62, -64.51, 18.57]|Some([36907.961, 176388.461, 404129.026, 282602.555])| + EPSG_2867|2867|"NAD83(HARN) / Arizona East (ft)"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([218118.74, 120026.422, 1051780.165, 2190593.112])| + EPSG_2868|2868|"NAD83(HARN) / Arizona Central (ft)"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([252468.27, 120025.811, 1161069.957, 2190284.296])| + EPSG_2869|2869|"NAD83(HARN) / Arizona West (ft)"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([371577.731, 381936.366, 1081104.563, 2185692.083])| + EPSG_2870|2870|"NAD83(HARN) / California zone 1 (ftUS)"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([5871184.868, 1733931.179, 7128174.535, 2624899.567])| + EPSG_2871|2871|"NAD83(HARN) / California zone 2 (ftUS)"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([5968214.525, 1769104.58, 7270334.669, 2557835.943])| + EPSG_2872|2872|"NAD83(HARN) / California zone 3 (ftUS)"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([5823180.081, 1724171.45, 7344106.882, 2456020.71])| + EPSG_2873|2873|"NAD83(HARN) / California zone 4 (ftUS)"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([5668912.3, 1803026.503, 7564126.574, 2475593.063])| + EPSG_2874|2874|"NAD83(HARN) / California zone 5 (ftUS)"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([5509911.3, 1371066.417, 7754827.96, 2503352.13])| + EPSG_2875|2875|"NAD83(HARN) / California zone 6 (ftUS)"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([5976054.992, 1772618.646, 7125712.49, 2341859.623])| + EPSG_2876|2876|"NAD83(HARN) / Colorado North (ftUS)"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([1996491.167, 1082569.831, 3975340.508, 1630519.018])| + EPSG_2877|2877|"NAD83(HARN) / Colorado Central (ftUS)"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([1976278.559, 1111691.372, 3994984.965, 1841441.243])| + EPSG_2878|2878|"NAD83(HARN) / Colorado South (ftUS)"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([1960289.122, 1114094.227, 4010524.727, 1752500.182])| + EPSG_2879|2879|"NAD83(HARN) / Connecticut (ftUS)"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([729403.516, 553436.333, 1267835.912, 944833.516])| + EPSG_2880|2880|"NAD83(HARN) / Delaware (ftUS)"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([546368.036, 160233.239, 784107.026, 674111.485])| + EPSG_2881|2881|"NAD83(HARN) / Florida East (ftUS)"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([213590.65, 27857.3, 998906.98, 2364273.009])| + EPSG_2882|2882|"NAD83(HARN) / Florida West (ftUS)"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([217030.96, 703823.903, 941269.637, 1916924.647])| + EPSG_2883|2883|"NAD83(HARN) / Florida North (ftUS)"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([969968.381, 76373.404, 2753328.348, 744475.186])| + EPSG_2884|2884|"NAD83(HARN) / Georgia East (ftUS)"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([245111.501, 130918.845, 1096662.936, 1705424.723])| + EPSG_2885|2885|"NAD83(HARN) / Georgia West (ftUS)"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([1842578.621, 225475.225, 2666703.119, 1825733.479])| + EPSG_2886|2886|"NAD83(HARN) / Idaho East (ftUS)"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([364384.815, 117815.985, 962453.812, 1125809.244])| + EPSG_2887|2887|"NAD83(HARN) / Idaho Central (ftUS)"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([1287014.672, 117815.268, 2001980.985, 1472976.158])| + EPSG_2888|2888|"NAD83(HARN) / Idaho West (ftUS)"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([2219617.735, 117814.739, 3013411.241, 2680903.242])| + EPSG_2891|2891|"NAD83(HARN) / Kentucky North (ftUS)"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([1145728.306, 76473.672, 2155355.482, 605773.922])| + EPSG_2892|2892|"NAD83(HARN) / Kentucky South (ftUS)"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([517694.42, 1697455.079, 2757266.678, 2331365.177])| + EPSG_2893|2893|"NAD83(HARN) / Maryland (ftUS)"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([594550.404, 110477.239, 1897539.221, 761030.041])| + EPSG_2894|2894|"NAD83(HARN) / Massachusetts Mainland (ftUS)"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([107980.119, 2628253.208, 1105693.395, 3155653.031])| + EPSG_2895|2895|"NAD83(HARN) / Massachusetts Island (ftUS)"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([1527568.57, 69225.253, 1808311.986, 186412.083])| + EPSG_2896|2896|"NAD83(HARN) / Michigan North (ft)"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([25363420.515, 108189.385, 27166155.432, 1309388.147])| + EPSG_2897|2897|"NAD83(HARN) / Michigan Central (ft)"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([18973983.291, 176215.075, 20238614.231, 960517.089])| + EPSG_2898|2898|"NAD83(HARN) / Michigan South (ft)"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([12349528.896, 69244.284, 13734276.336, 1003851.96])| + EPSG_2899|2899|"NAD83(HARN) / Mississippi East (ftUS)"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([624475.929, 185463.038, 1219525.733, 2006466.545])| + EPSG_28991|28991|"Amersfoort / RD Old"|Projected|[3.2, 50.75, 7.22, 53.7]|Some([-154353.639, -156328.118, 129347.25, 174111.024])| + EPSG_28992|28992|"Amersfoort / RD New"|Projected|[3.2, 50.75, 7.22, 53.7]|Some([646.361, 306671.882, 284347.25, 637111.024])| + EPSG_2900|2900|"NAD83(HARN) / Mississippi West (ftUS)"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([1884032.771, 545525.059, 2598421.337, 2007128.786])| + EPSG_2901|2901|"NAD83(HARN) / Montana (ft)"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([251087.665, 36511.189, 3396286.753, 1801592.392])| + EPSG_2902|2902|"NAD83(HARN) / New Mexico East (ftUS)"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([111471.768, 363738.282, 957774.441, 2186263.316])| + EPSG_2903|2903|"NAD83(HARN) / New Mexico Central (ftUS)"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([1180526.555, 283709.235, 2078559.038, 2186653.91])| + EPSG_2904|2904|"NAD83(HARN) / New Mexico West (ftUS)"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([2340087.326, 120029.034, 3195618.109, 2186843.488])| + EPSG_2905|2905|"NAD83(HARN) / New York East (ftUS)"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([113307.16, 745478.646, 843292.001, 2257242.242])| + EPSG_2906|2906|"NAD83(HARN) / New York Central (ftUS)"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([503054.031, 725006.336, 1234324.774, 1610719.287])| + EPSG_2907|2907|"NAD83(HARN) / New York West (ftUS)"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([825700.514, 725006.897, 1480852.255, 1328721.852])| + EPSG_2908|2908|"NAD83(HARN) / New York Long Island (ftUS)"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([911908.749, 110507.838, 1596308.212, 420503.462])| + EPSG_2909|2909|"NAD83(HARN) / North Dakota North (ft)"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([1080447.443, 54716.91, 2881422.403, 754222.324])| + EPSG_2910|2910|"NAD83(HARN) / North Dakota South (ft)"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([1065409.66, 96037.034, 2973279.123, 813391.234])| + EPSG_29101|29101|"SAD69 / Brazil Polyconic"|Projected|[-74.01, -35.71, -25.28, 7.04]|Some([2772555.146, 5669191.152, 8197128.799, 10875962.073])| + EPSG_2911|2911|"NAD83(HARN) / Oklahoma North (ftUS)"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([476491.523, 98313.708, 3037012.294, 769303.073])| + EPSG_2912|2912|"NAD83(HARN) / Oklahoma South (ftUS)"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([1359611.703, 104328.067, 3058259.479, 832903.074])| + EPSG_2913|2913|"NAD83(HARN) / Oregon North (ft)"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([7235773.393, 103308.805, 9263148.062, 970943.469])| + EPSG_2914|2914|"NAD83(HARN) / Oregon South (ft)"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([3806777.638, 114196.734, 5899925.971, 1080734.227])| + EPSG_2915|2915|"NAD83(HARN) / Tennessee (ftUS)"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([677678.821, 235395.357, 3271295.874, 882579.104])| + EPSG_2916|2916|"NAD83(HARN) / Texas North (ftUS)"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([194036.85, 3390026.268, 1112261.259, 4194287.599])| + EPSG_29168|29168|"SAD69 / UTM zone 18N"|Projected|[-78.0, 0.0, -72.0, 12.31]|Some([166066.612, 35.986, 834042.678, 1362692.387])| + EPSG_29169|29169|"SAD69 / UTM zone 19N"|Projected|[-72.0, 0.0, -66.0, 12.52]|Some([166083.155, 38.205, 834039.769, 1385947.559])| + EPSG_2917|2917|"NAD83(HARN) / Texas North Central (ftUS)"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([547688.999, 6581071.207, 3367566.241, 7651632.767])| + EPSG_29170|29170|"SAD69 / UTM zone 20N"|Projected|[-66.0, 0.64, -59.99, 11.23]|Some([166101.003, 70777.338, 835129.668, 1243125.008])| + EPSG_29171|29171|"SAD69 / UTM zone 21N"|Projected|[-60.0, 1.18, -54.0, 8.6]|Some([166147.204, 130463.487, 833961.496, 951962.742])| + EPSG_29172|29172|"SAD69 / UTM zone 22N"|Projected|[-54.0, 1.68, -46.65, 5.81]|Some([166215.417, 185728.646, 984355.335, 644092.505])| + EPSG_2918|2918|"NAD83(HARN) / Texas Central (ftUS)"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([290310.513, 9883768.73, 4463337.801, 10854273.258])| + EPSG_29187|29187|"SAD69 / UTM zone 17S"|Projected|[-81.41, -10.53, -78.0, 0.0]|Some([454432.899, 8834456.453, 834026.134, 10000044.043])| + EPSG_29188|29188|"SAD69 / UTM zone 18S"|Projected|[-78.0, -45.0, -71.99, 0.0]|Some([166066.612, 5012660.078, 835156.97, 10000044.043])| + EPSG_29189|29189|"SAD69 / UTM zone 19S"|Projected|[-72.0, -45.0, -65.99, 2.15]|Some([166100.106, 5012664.469, 835137.053, 10238008.982])| + EPSG_2919|2919|"NAD83(HARN) / Texas South Central (ftUS)"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([28740.64, 13103937.095, 3662737.545, 14203230.008])| + EPSG_29190|29190|"SAD69 / UTM zone 20S"|Projected|[-66.0, -45.0, -59.99, 5.28]|Some([166083.47, 5012668.696, 835147.259, 10584459.406])| + EPSG_29191|29191|"SAD69 / UTM zone 21S"|Projected|[-60.0, -38.91, -53.99, 4.51]|Some([166078.115, 5688936.425, 835144.869, 10499228.111])| + EPSG_29192|29192|"SAD69 / UTM zone 22S"|Projected|[-54.0, -35.71, -47.99, 7.04]|Some([166072.859, 6044081.988, 835141.187, 10779277.386])| + EPSG_29193|29193|"SAD69 / UTM zone 23S"|Projected|[-48.0, -33.5, -42.0, 5.13]|Some([166067.761, 6289297.639, 834021.964, 10567849.212])| + EPSG_29194|29194|"SAD69 / UTM zone 24S"|Projected|[-42.0, -26.35, -36.0, 0.74]|Some([166063.802, 7082121.6, 834014.906, 10081943.115])| + EPSG_29195|29195|"SAD69 / UTM zone 25S"|Projected|[-36.0, -23.8, -29.99, 4.19]|Some([166067.357, 7364711.856, 835113.722, 10463806.566])| + EPSG_2920|2920|"NAD83(HARN) / Texas South (ftUS)"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([425013.115, 16463539.599, 1527044.593, 17332397.278])| + EPSG_2921|2921|"NAD83(HARN) / Utah North (ft)"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([934628.506, 3359779.544, 2323994.084, 3901834.205])| + EPSG_2922|2922|"NAD83(HARN) / Utah Central (ft)"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([910494.408, 6618748.571, 2344596.373, 7572220.499])| + EPSG_29220|29220|"Sapper Hill 1943 / UTM zone 20S"|Projected|[-61.55, -52.33, -59.99, -50.96]|Some([598811.777, 4197869.96, 711382.151, 4353506.797])| + EPSG_29221|29221|"Sapper Hill 1943 / UTM zone 21S"|Projected|[-60.0, -52.51, -57.6, -51.13]|Some([290090.245, 4177882.991, 459565.718, 4335431.111])| + EPSG_2923|2923|"NAD83(HARN) / Utah South (ft)"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([895699.12, 9960253.668, 2358869.071, 10549175.688])| + EPSG_2924|2924|"NAD83(HARN) / Virginia North (ftUS)"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([11031977.866, 6599295.496, 12058137.009, 7220871.996])| + EPSG_2925|2925|"NAD83(HARN) / Virginia South (ftUS)"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([9961815.046, 3356099.28, 12419949.902, 4030431.896])| + EPSG_2926|2926|"NAD83(HARN) / Washington North (ftUS)"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([654930.622, 29188.471, 2590239.546, 772222.222])| + EPSG_2927|2927|"NAD83(HARN) / Washington South (ftUS)"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([641404.583, 75378.506, 2560084.219, 854061.484])| + EPSG_2928|2928|"NAD83(HARN) / Wisconsin North (ftUS)"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([1225867.295, 74142.152, 2469647.475, 794683.997])| + EPSG_2929|2929|"NAD83(HARN) / Wisconsin Central (ftUS)"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([1207876.159, 53466.846, 2955330.147, 739105.377])| + EPSG_2930|2930|"NAD83(HARN) / Wisconsin South (ftUS)"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([1582730.198, 174953.461, 2791157.99, 863856.023])| + EPSG_2931|2931|"Beduaram / TM 13 NE"|Projected|[7.81, 12.8, 14.9, 16.7]|Some([-63899.137, 1414831.975, 706298.099, 1853406.349])| + EPSG_2932|2932|"QND95 / Qatar National Grid"|Projected|[50.69, 24.55, 51.68, 26.2]|Some([146718.728, 311001.514, 247019.245, 493889.937])| + EPSG_2933|2933|"Segara / UTM zone 50S"|Projected|[116.72, -1.24, 117.99, 0.0]|Some([468793.267, 9862875.298, 610119.331, 9999954.548])| + EPSG_29333|29333|"Schwarzeck / UTM zone 33S"|Projected|[8.24, -30.64, 16.46, -17.24]|Some([-219957.703, 6590985.978, 655288.091, 8094144.198])| + EPSG_2935|2935|"Pulkovo 1942 / CS63 zone A1"|Projected|[39.99, 41.37, 43.04, 43.59]|Some([1170985.716, 4568860.315, 1426161.502, 4816621.512])| + EPSG_2936|2936|"Pulkovo 1942 / CS63 zone A2"|Projected|[43.03, 38.87, 46.04, 43.05]|Some([2169635.565, 4291263.044, 2430863.817, 4756566.963])| + EPSG_2937|2937|"Pulkovo 1942 / CS63 zone A3"|Projected|[46.03, 38.38, 49.04, 42.1]|Some([3168740.171, 4236864.813, 3431751.57, 4651027.453])| + EPSG_29371|29371|"Schwarzeck / Lo22/11"|Projected|[11.66, -18.53, 12.0, -17.15]|Some([-106426.494, -536813.543, -69708.379, -383904.999])| + EPSG_29373|29373|"Schwarzeck / Lo22/13"|Projected|[12.0, -21.9, 14.01, -16.95]|Some([-107626.765, -559066.874, 106473.388, -10775.307])| + EPSG_29375|29375|"Schwarzeck / Lo22/15"|Projected|[14.0, -28.3, 16.0, -17.38]|Some([-106337.985, -511479.387, 106206.511, 698240.8])| + EPSG_29377|29377|"Schwarzeck / Lo22/17"|Projected|[15.99, -28.83, 18.01, -17.38]|Some([-107422.356, -511480.186, 107247.679, 756991.28])| + EPSG_29379|29379|"Schwarzeck / Lo22/19"|Projected|[18.0, -28.97, 20.0, -17.38]|Some([-106380.984, -511481.208, 106163.317, 772499.257])| + EPSG_2938|2938|"Pulkovo 1942 / CS63 zone A4"|Projected|[49.03, 37.89, 51.73, 42.59]|Some([4167854.085, 4182471.675, 4405360.717, 4705450.104])| + EPSG_29381|29381|"Schwarzeck / Lo22/21"|Projected|[19.99, -22.01, 22.0, -17.85]|Some([-106127.492, -459465.332, 106927.162, 1401.648])| + EPSG_29383|29383|"Schwarzeck / Lo22/23"|Projected|[21.99, -18.49, 24.0, -17.52]|Some([-106342.298, -495989.726, 107101.498, -388332.865])| + EPSG_29385|29385|"Schwarzeck / Lo22/25"|Projected|[24.0, -18.18, 25.27, -17.47]|Some([-28850.663, -501525.023, 106047.429, -422656.411])| + EPSG_2939|2939|"Pulkovo 1942 / CS63 zone K2"|Projected|[49.26, 41.15, 52.27, 51.77]|Some([2173614.692, 4542569.12, 2426290.074, 5724164.026])| + EPSG_2940|2940|"Pulkovo 1942 / CS63 zone K3"|Projected|[52.26, 41.46, 55.27, 51.79]|Some([3174207.822, 4576995.228, 3425690.964, 5726384.345])| + EPSG_2941|2941|"Pulkovo 1942 / CS63 zone K4"|Projected|[55.26, 41.26, 58.27, 51.14]|Some([4173818.793, 4554779.212, 4426070.873, 5654068.411])| + EPSG_2942|2942|"Porto Santo / UTM zone 28N"|Projected|[-17.31, 32.35, -16.23, 33.15]|Some([282983.856, 3579544.419, 385658.473, 3669940.322])| + EPSG_2943|2943|"Selvagem Grande / UTM zone 28N"|Projected|[-16.11, 29.98, -15.79, 30.21]|Some([393111.985, 3316783.548, 424160.03, 3342528.154])| + EPSG_2945|2945|"NAD83(CSRS) / MTM zone 3"|Projected|[-60.0, 47.5, -57.1, 55.38]|Some([191796.801, 5262308.546, 410269.251, 6139943.609])| + EPSG_2946|2946|"NAD83(CSRS) / MTM zone 4"|Projected|[-63.0, 47.16, -60.0, 58.92]|Some([191069.177, 5224511.007, 418530.426, 6534071.595])| + EPSG_2947|2947|"NAD83(CSRS) / MTM zone 5"|Projected|[-66.0, 47.95, -63.0, 60.52]|Some([192766.258, 5312335.725, 416833.514, 6712275.726])| + EPSG_2948|2948|"NAD83(CSRS) / MTM zone 6"|Projected|[-69.0, 47.31, -66.0, 59.0]|Some([191389.887, 5241185.664, 418210.037, 6542980.843])| + EPSG_2949|2949|"NAD83(CSRS) / MTM zone 7"|Projected|[-72.0, 45.01, -69.0, 61.8]|Some([186562.183, 4985556.073, 423037.88, 6854867.984])| + EPSG_2950|2950|"NAD83(CSRS) / MTM zone 8"|Projected|[-75.0, 44.98, -72.0, 62.53]|Some([186500.542, 4982222.468, 423099.681, 6936201.359])| + EPSG_2951|2951|"NAD83(CSRS) / MTM zone 9"|Projected|[-78.0, 43.63, -75.0, 62.65]|Some([183757.208, 4832227.938, 425843.163, 6949572.011])| + EPSG_2952|2952|"NAD83(CSRS) / MTM zone 10"|Projected|[-81.0, 42.26, -78.0, 62.45]|Some([181042.323, 4680047.613, 428558.198, 6927287.759])| + EPSG_2953|2953|"NAD83(CSRS) / New Brunswick Stereographic"|Projected|[-69.05, 44.56, -63.7, 48.07]|Some([2297385.237, 7284383.089, 2722474.81, 7678288.603])| + EPSG_2954|2954|"NAD83(CSRS) / Prince Edward Isl. Stereographic (NAD83)"|Projected|[-64.49, 45.9, -61.9, 47.09]|Some([284369.754, 649936.039, 485365.596, 783291.366])| + EPSG_2955|2955|"NAD83(CSRS) / UTM zone 11N"|Projected|[-120.0, 48.99, -114.0, 83.5]|Some([280543.396, 5426343.425, 719458.683, 9273261.185])| + EPSG_2956|2956|"NAD83(CSRS) / UTM zone 12N"|Projected|[-114.0, 48.99, -108.0, 84.0]|Some([280543.307, 5426343.354, 719458.587, 9329004.349])| + EPSG_2957|2957|"NAD83(CSRS) / UTM zone 13N"|Projected|[-108.0, 48.99, -102.0, 84.0]|Some([280543.206, 5426343.289, 719458.478, 9329004.245])| + EPSG_2958|2958|"NAD83(CSRS) / UTM zone 17N"|Projected|[-84.0, 41.67, -78.0, 84.0]|Some([250254.512, 4613136.854, 749746.084, 9329003.935])| + EPSG_2959|2959|"NAD83(CSRS) / UTM zone 18N"|Projected|[-78.0, 43.63, -72.0, 84.0]|Some([257982.749, 4830778.119, 742017.543, 9329003.889])| + EPSG_2960|2960|"NAD83(CSRS) / UTM zone 19N"|Projected|[-72.0, 40.8, -66.0, 84.0]|Some([246917.209, 4516554.618, 753082.737, 9329003.856])| + EPSG_2961|2961|"NAD83(CSRS) / UTM zone 20N"|Projected|[-66.0, 40.04, -60.0, 84.0]|Some([244049.864, 4432195.913, 755949.741, 9329003.837])| + EPSG_2962|2962|"NAD83(CSRS) / UTM zone 21N"|Projected|[-60.0, 38.56, -54.0, 84.0]|Some([238596.325, 4267950.251, 761402.924, 9329003.832])| + EPSG_2964|2964|"NAD27 / Alaska Albers"|Projected|[172.42, 51.3, -129.99, 71.4]|Some([-7405489.415, 472448.691, 5408832.34, 8797400.246])| + EPSG_2965|2965|"NAD83 / Indiana East (ftUS)"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([61837.703, 984068.143, 583755.82, 2376913.498])| + EPSG_2966|2966|"NAD83 / Indiana West (ftUS)"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([2658876.887, 918523.342, 3196519.15, 2377200.659])| + EPSG_2967|2967|"NAD83(HARN) / Indiana East (ftUS)"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([61839.114, 984065.508, 583757.129, 2376910.604])| + EPSG_2968|2968|"NAD83(HARN) / Indiana West (ftUS)"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([2658878.421, 918520.743, 3196520.581, 2377197.792])| + EPSG_2969|2969|"Fort Marigot / UTM zone 20N"|Projected|[-63.21, 17.82, -62.73, 18.17]|Some([477511.941, 1970708.999, 528377.11, 2009452.351])| + EPSG_2970|2970|"Guadeloupe 1948 / UTM zone 20N"|Projected|[-61.85, 15.8, -60.97, 16.55]|Some([623128.061, 1747453.118, 717868.742, 1831171.291])| + EPSG_29701|29701|"Tananarive (Paris) / Laborde Grid"|Projected|[43.18, -25.64, 50.56, -11.89]|Some([45119.744, 46899.845, 849613.925, 1575714.152])| + EPSG_29702|29702|"Tananarive (Paris) / Laborde Grid approximation"|Projected|[43.18, -25.64, 50.56, -11.89]|Some([45120.794, 46898.556, 849612.88, 1575713.385])| + EPSG_2971|2971|"CSG67 / UTM zone 22N"|Projected|[-54.0, 3.43, -51.61, 5.81]|Some([166618.619, 379031.742, 432467.538, 642968.737])| + EPSG_2972|2972|"RGFG95 / UTM zone 22N"|Projected|[-54.0, 2.17, -49.45, 8.88]|Some([166259.785, 239851.392, 672374.441, 982919.869])| + EPSG_2973|2973|"Martinique 1938 / UTM zone 20N"|Projected|[-61.29, 14.35, -60.76, 14.93]|Some([683534.837, 1586947.688, 741197.154, 1651628.165])| + EPSG_29738|29738|"Tananarive / UTM zone 38S"|Projected|[42.53, -26.59, 48.0, -13.0]|Some([232105.85, 7055593.165, 825486.832, 8562981.133])| + EPSG_29739|29739|"Tananarive / UTM zone 39S"|Projected|[48.0, -24.21, 51.03, -11.69]|Some([172915.229, 7319372.207, 503266.629, 8707842.843])| + EPSG_2975|2975|"RGR92 / UTM zone 40S"|Projected|[54.0, -24.72, 58.24, -18.28]|Some([182799.922, 7262730.879, 631069.19, 7978835.6])| + EPSG_2976|2976|"Tahiti 52 / UTM zone 6S"|Projected|[-150.0, -17.93, -149.09, -17.41]|Some([181261.952, 8014832.867, 278629.644, 8073713.151])| + EPSG_2977|2977|"Tahaa 54 / UTM zone 5S"|Projected|[-151.91, -16.96, -150.89, -16.17]|Some([616328.444, 8123577.96, 725879.58, 8211867.688])| + EPSG_2978|2978|"IGN72 Nuku Hiva / UTM zone 7S"|Projected|[-140.31, -9.57, -139.44, -8.72]|Some([575879.994, 8941675.3, 671776.122, 9035871.618])| + EPSG_2980|2980|"Combani 1950 / UTM zone 38S"|Projected|[44.98, -13.05, 45.35, -12.61]|Some([497599.532, 8557577.514, 537784.775, 8606260.993])| + EPSG_2981|2981|"IGN56 Lifou / UTM zone 58S"|Projected|[166.98, -21.24, 167.52, -20.62]|Some([705782.778, 7649426.261, 762926.503, 7718872.271])| + EPSG_29849|29849|"Timbalai 1948 / UTM zone 49N"|Projected|[109.31, 0.85, 114.0, 7.37]|Some([311543.579, 93999.033, 833549.497, 815815.893])| + EPSG_29850|29850|"Timbalai 1948 / UTM zone 50N"|Projected|[114.0, 1.43, 119.61, 7.67]|Some([165820.403, 158107.906, 790140.613, 849029.837])| + EPSG_2987|2987|"Saint Pierre et Miquelon 1950 / UTM zone 21N"|Projected|[-56.48, 46.69, -56.07, 47.19]|Some([539152.317, 5170356.148, 570870.794, 5226206.719])| + EPSG_29871|29871|"Timbalai 1948 / RSO Borneo (ch)"|Projected|[109.31, 0.85, 119.61, 7.67]|Some([-2098.145, 4674.781, 54952.671, 42363.353])| + EPSG_29872|29872|"Timbalai 1948 / RSO Borneo (ftSe)"|Projected|[109.31, 0.85, 119.61, 7.67]|Some([-138477.583, 308535.534, 3626876.275, 2795981.29])| + EPSG_29873|29873|"Timbalai 1948 / RSO Borneo (m)"|Projected|[109.31, 0.85, 119.61, 7.67]|Some([-42207.897, 94041.463, 1105469.969, 852213.615])| + EPSG_29874|29874|"Timbalai 1948 / RSO Sarawak LSD (m)"|Projected|[109.54, 0.85, 115.69, 5.03]|Some([1983391.508, 5094041.392, 2668066.409, 5557104.042])| + EPSG_2988|2988|"MOP78 / UTM zone 1S"|Projected|[-176.25, -13.41, -176.07, -13.16]|Some([581052.987, 8517455.814, 600645.028, 8545171.539])| + EPSG_29901|29901|"OSNI 1952 / Irish National Grid"|Projected|[-8.18, 53.96, -5.34, 55.36]|Some([188233.38, 301176.998, 374634.354, 460249.959])| + EPSG_29902|29902|"TM65 / Irish Grid"|Projected|[-10.56, 51.39, -5.93, 55.43]|Some([21861.709, 15178.227, 344136.078, 467800.415])| + EPSG_29903|29903|"TM75 / Irish Grid"|Projected|[-10.56, 51.39, -5.34, 55.43]|Some([21861.709, 15177.485, 385198.216, 468039.711])| + EPSG_2991|2991|"NAD83 / Oregon LCC (m)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([60163.867, 25562.683, 734038.753, 509025.159])| + EPSG_2992|2992|"NAD83 / Oregon GIC Lambert (ft)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([197388.016, 83867.072, 2408263.626, 1670030.051])| + EPSG_2993|2993|"NAD83(HARN) / Oregon LCC (m)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([60165.004, 25562.074, 734039.839, 509024.507])| + EPSG_2994|2994|"NAD83(HARN) / Oregon GIC Lambert (ft)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([197391.744, 83865.072, 2408267.188, 1670027.909])| + EPSG_2995|2995|"IGN53 Mare / UTM zone 58S"|Projected|[167.75, -21.71, 168.0, -21.32]|Some([784760.046, 7596381.752, 811471.977, 7640064.644])| + EPSG_2996|2996|"ST84 Ile des Pins / UTM zone 58S"|Projected|[167.36, -22.73, 167.61, -22.49]|Some([742045.801, 7483671.076, 768200.133, 7510684.737])| + EPSG_2997|2997|"ST71 Belep / UTM zone 58S"|Projected|[163.54, -19.85, 163.75, -19.5]|Some([346446.294, 7804119.126, 368775.606, 7843038.175])| + EPSG_2998|2998|"NEA74 Noumea / UTM zone 58S"|Projected|[166.35, -22.37, 166.54, -22.19]|Some([638653.11, 7525072.308, 658421.566, 7545188.459])| + EPSG_2999|2999|"Grand Comoros / UTM zone 38S"|Projected|[43.16, -11.99, 43.55, -11.31]|Some([298130.937, 8674249.205, 341091.643, 8749716.096])| + EPSG_3000|3000|"Segara / NEIEZ"|Projected|[114.55, -4.24, 119.06, 4.29]|Some([4404838.021, 432092.167, 4905392.313, 1373352.191])| + EPSG_3001|3001|"Batavia / NEIEZ"|Projected|[95.16, -8.91, 115.77, 5.97]|Some([2252848.668, -86253.844, 4540268.934, 1559412.026])| + EPSG_3002|3002|"Makassar / NEIEZ"|Projected|[118.71, -6.54, 120.78, -1.88]|Some([4866305.065, 177275.622, 5096046.823, 692561.029])| + EPSG_3003|3003|"Monte Mario / Italy zone 1"|Projected|[5.93, 36.53, 12.0, 47.04]|Some([1225120.588, 4042801.807, 1768619.28, 5214284.214])| + EPSG_3004|3004|"Monte Mario / Italy zone 2"|Projected|[12.0, 34.76, 18.99, 47.1]|Some([2245391.808, 3846489.345, 2885274.102, 5224106.003])| + EPSG_3005|3005|"NAD83 / BC Albers"|Projected|[-139.04, 48.25, -114.08, 60.01]|Some([34758.749, 359423.26, 1883159.499, 1736633.684])| + EPSG_3006|3006|"SWEREF99 TM"|Projected|[10.03, 54.96, 24.17, 69.07]|Some([181896.329, 6090361.358, 1086312.942, 7689478.306])| + EPSG_3007|3007|"SWEREF99 12 00"|Projected|[10.93, 56.74, 13.11, 60.13]|Some([84522.479, 6290961.103, 217925.159, 6669074.792])| + EPSG_3008|3008|"SWEREF99 13 30"|Projected|[12.12, 55.28, 14.79, 62.28]|Some([62307.125, 6128402.505, 231974.125, 6908900.111])| + EPSG_3009|3009|"SWEREF99 15 00"|Projected|[13.54, 55.95, 16.15, 61.62]|Some([58792.658, 6202996.107, 221842.499, 6835451.012])| + EPSG_3010|3010|"SWEREF99 16 30"|Projected|[15.41, 56.15, 17.63, 62.26]|Some([82257.07, 6225264.671, 220228.789, 6906419.685])| + EPSG_3011|3011|"SWEREF99 18 00"|Projected|[17.08, 58.66, 19.61, 60.7]|Some([96603.437, 6504795.771, 243440.187, 6733142.999])| + EPSG_3012|3012|"SWEREF99 14 15"|Projected|[11.93, 61.55, 15.55, 64.39]|Some([26665.314, 6826782.37, 219117.028, 7145375.083])| + EPSG_3013|3013|"SWEREF99 15 45"|Projected|[13.66, 60.44, 17.01, 65.13]|Some([34943.161, 6703096.285, 219369.435, 7227456.875])| + EPSG_3014|3014|"SWEREF99 17 15"|Projected|[14.31, 62.12, 19.04, 67.19]|Some([-3406.709, 6890305.754, 243415.202, 7458552.415])| + EPSG_3015|3015|"SWEREF99 18 45"|Projected|[17.18, 56.86, 20.22, 66.17]|Some([54234.491, 6304324.794, 239666.351, 7342683.11])| + EPSG_3016|3016|"SWEREF99 20 15"|Projected|[16.08, 63.45, 23.28, 69.07]|Some([-57936.156, 7038547.072, 301128.159, 7670885.776])| + EPSG_30161|30161|"Tokyo / Japan Plane Rectangular CS I"|Projected|[128.17, 26.96, 130.46, 34.74]|Some([-131840.095, -669853.742, 95521.421, 193415.924])| + EPSG_30162|30162|"Tokyo / Japan Plane Rectangular CS II"|Projected|[129.76, 30.18, 132.05, 33.99]|Some([-119197.48, -313022.116, 101342.816, 110110.978])| + EPSG_30163|30163|"Tokyo / Japan Plane Rectangular CS III"|Projected|[130.81, 33.72, 133.49, 36.38]|Some([-125507.473, -253250.642, 122880.531, 42676.341])| + EPSG_30164|30164|"Tokyo / Japan Plane Rectangular CS IV"|Projected|[131.95, 32.69, 134.81, 34.45]|Some([-145107.773, -34752.078, 123086.049, 161529.313])| + EPSG_30165|30165|"Tokyo / Japan Plane Rectangular CS V"|Projected|[133.13, 34.13, 135.47, 35.71]|Some([-110743.045, -207781.913, 105093.132, -31847.254])| + EPSG_30166|30166|"Tokyo / Japan Plane Rectangular CS VI"|Projected|[134.86, 33.4, 136.99, 36.33]|Some([-105782.915, -288749.251, 92355.314, 36872.73])| + EPSG_30167|30167|"Tokyo / Japan Plane Rectangular CS VII"|Projected|[136.22, 34.51, 137.84, 37.58]|Some([-86652.262, -165640.621, 62098.731, 175395.868])| + EPSG_30168|30168|"Tokyo / Japan Plane Rectangular CS VIII"|Projected|[137.32, 34.54, 139.91, 38.58]|Some([-108028.974, -162316.558, 129707.885, 286901.273])| + EPSG_30169|30169|"Tokyo / Japan Plane Rectangular CS IX"|Projected|[138.4, 29.31, 141.11, 37.98]|Some([-138943.98, -742196.649, 124310.075, 220328.704])| + EPSG_3017|3017|"SWEREF99 21 45"|Projected|[19.63, 65.01, 22.91, 66.43]|Some([50039.931, 7212454.723, 204700.755, 7372393.438])| + EPSG_30170|30170|"Tokyo / Japan Plane Rectangular CS X"|Projected|[139.49, 37.73, 142.14, 41.58]|Some([-118110.44, -252283.823, 115479.916, 176006.676])| + EPSG_30171|30171|"Tokyo / Japan Plane Rectangular CS XI"|Projected|[139.34, 41.34, 141.46, 43.42]|Some([-75865.411, -295715.827, 101566.953, -63977.394])| + EPSG_30172|30172|"Tokyo / Japan Plane Rectangular CS XII"|Projected|[140.89, 42.15, 143.61, 45.54]|Some([-112091.478, -205765.656, 112714.44, 171760.902])| + EPSG_30173|30173|"Tokyo / Japan Plane Rectangular CS XIII"|Projected|[142.61, 41.87, 145.87, 44.4]|Some([-135814.868, -236870.804, 134806.901, 45483.249])| + EPSG_30174|30174|"Tokyo / Japan Plane Rectangular CS XIV"|Projected|[141.2, 24.67, 142.33, 27.8]|Some([-80657.752, -147795.099, 33708.059, 199202.763])| + EPSG_30175|30175|"Tokyo / Japan Plane Rectangular CS XV"|Projected|[126.63, 26.02, 128.4, 26.91]|Some([-86888.532, 1767.265, 90276.824, 100683.02])| + EPSG_30176|30176|"Tokyo / Japan Plane Rectangular CS XVI"|Projected|[122.83, 23.98, 125.51, 24.94]|Some([-118899.468, -224182.814, 153828.011, -117011.581])| + EPSG_30177|30177|"Tokyo / Japan Plane Rectangular CS XVII"|Projected|[131.12, 24.4, 131.38, 26.01]|Some([12234.237, -177672.274, 38763.809, 709.153])| + EPSG_30178|30178|"Tokyo / Japan Plane Rectangular CS XVIII"|Projected|[136.02, 20.37, 136.16, 20.48]|Some([2349.506, 40431.556, 16965.449, 52616.298])| + EPSG_30179|30179|"Tokyo / Japan Plane Rectangular CS XIX"|Projected|[153.91, 24.22, 154.05, 24.35]|Some([-8749.17, -197664.041, 5470.055, -183263.705])| + EPSG_3018|3018|"SWEREF99 23 15"|Projected|[21.85, 65.49, 24.17, 68.14]|Some([85170.751, 7265972.318, 192603.657, 7562157.655])| + EPSG_3019|3019|"RT90 7.5 gon V"|Projected|[10.93, 57.29, 12.91, 59.73]|Some([1477337.756, 6351580.0, 1596724.558, 6624455.11])| + EPSG_3020|3020|"RT90 5 gon V"|Projected|[11.81, 55.28, 15.44, 64.39]|Some([1389078.906, 6127771.258, 1619744.804, 7144053.065])| + EPSG_30200|30200|"Trinidad 1903 / Trinidad Grid"|Projected|[-62.09, 9.83, -60.0, 11.51]|Some([16983.973, -13360.021, 1156649.945, 912070.631])| + EPSG_3021|3021|"RT90 2.5 gon V"|Projected|[13.66, 55.95, 17.73, 67.18]|Some([1365989.776, 6202363.25, 1620237.395, 7455406.31])| + EPSG_3022|3022|"RT90 0 gon"|Projected|[16.08, 56.86, 20.22, 68.54]|Some([1379537.855, 6303689.718, 1632057.833, 7607038.879])| + EPSG_3023|3023|"RT90 2.5 gon O"|Projected|[18.4, 63.37, 22.2, 69.07]|Some([1404739.054, 7028992.334, 1594834.787, 7665783.722])| + EPSG_3024|3024|"RT90 5 gon O"|Projected|[21.34, 65.24, 24.17, 68.58]|Some([1443258.572, 7237460.7, 1575557.664, 7610802.611])| + EPSG_3025|3025|"RT38 7.5 gon V"|Projected|[10.93, 57.29, 12.91, 59.73]|Some([1477193.223, 6351548.016, 1596564.633, 6624386.611])| + EPSG_3026|3026|"RT38 5 gon V"|Projected|[11.81, 55.28, 15.44, 64.39]|Some([1388920.626, 6127767.961, 1619556.929, 7143920.561])| + EPSG_3027|3027|"RT38 2.5 gon V"|Projected|[13.66, 55.95, 17.73, 67.18]|Some([1365818.205, 6202352.484, 1620033.191, 7455242.289])| + EPSG_3028|3028|"RT38 0 gon"|Projected|[16.08, 56.86, 20.22, 68.54]|Some([1379349.129, 6303669.115, 1631836.706, 7606852.632])| + EPSG_3029|3029|"RT38 2.5 gon O"|Projected|[18.4, 63.37, 22.2, 69.07]|Some([1404550.456, 7028885.017, 1594621.638, 7665598.739])| + EPSG_3030|3030|"RT38 5 gon O"|Projected|[21.34, 65.24, 24.17, 68.58]|Some([1443053.126, 7237331.234, 1575335.179, 7610624.168])| + EPSG_3031|3031|"WGS 84 / Antarctic Polar Stereographic"|Projected|[-180.0, -90.0, 180.0, -60.0]|Some([-3314874.771, -3333134.028, 3314874.771, 3333134.028])| + EPSG_3032|3032|"WGS 84 / Australian Antarctic Polar Stereographic"|Projected|[45.0, -90.0, 160.0, -60.0]|Some([4591356.691, 6000000.0, 9333134.028, 9331427.854])| + EPSG_3033|3033|"WGS 84 / Australian Antarctic Lambert"|Projected|[45.0, -80.0, 160.0, -60.0]|Some([4622297.438, 1518316.604, 9413886.621, 4846423.819])| + EPSG_30339|30339|"TC(1948) / UTM zone 39N"|Projected|[51.56, 22.76, 54.01, 24.32]|Some([556817.633, 2517088.036, 809115.392, 2692991.361])| + EPSG_3034|3034|"ETRS89-extended / LCC Europe"|Projected|[-35.58, 24.6, 44.83, 84.73]|Some([-587060.159, -228439.691, 7600887.129, 6860717.593])| + EPSG_30340|30340|"TC(1948) / UTM zone 40N"|Projected|[53.99, 22.63, 56.03, 25.34]|Some([190592.367, 2502913.464, 402384.73, 2806034.774])| + EPSG_3035|3035|"ETRS89-extended / LAEA Europe"|Projected|[-35.58, 24.6, 44.83, 84.73]|Some([-146473.113, 196462.701, 7824928.564, 6971477.485])| + EPSG_3036|3036|"Moznet / UTM zone 36S"|Projected|[30.21, -27.58, 36.0, -11.41]|Some([195526.977, 6945729.629, 827409.115, 8738684.599])| + EPSG_3037|3037|"Moznet / UTM zone 37S"|Projected|[36.0, -27.71, 42.0, -10.09]|Some([171162.344, 6931318.281, 828837.656, 8884636.765])| + EPSG_3040|3040|"ETRS89 / UTM zone 28N (N-E)"|Projected|[-16.1, 34.93, -11.99, 72.44]|Some([399535.629, 3865280.383, 774945.079, 8040550.06])| + EPSG_3041|3041|"ETRS89 / UTM zone 29N (N-E)"|Projected|[-12.01, 34.91, -6.0, 74.13]|Some([224988.129, 3863062.492, 774097.919, 8228862.485])| + EPSG_3042|3042|"ETRS89 / UTM zone 30N (N-E)"|Projected|[-6.01, 35.26, 0.0, 80.49]|Some([226161.816, 3901876.707, 772928.143, 8937716.117])| + EPSG_3043|3043|"ETRS89 / UTM zone 31N (N-E)"|Projected|[0.0, 37.0, 6.01, 82.45]|Some([233037.88, 4094872.382, 767852.221, 9156211.355])| + EPSG_3044|3044|"ETRS89 / UTM zone 32N (N-E)"|Projected|[6.0, 36.53, 12.01, 84.01]|Some([231401.819, 4042735.82, 769493.75, 9330126.13])| + EPSG_3045|3045|"ETRS89 / UTM zone 33N (N-E)"|Projected|[12.0, 34.49, 18.01, 84.01]|Some([224511.873, 3816488.39, 776406.725, 9330126.13])| + EPSG_3046|3046|"ETRS89 / UTM zone 34N (N-E)"|Projected|[18.0, 33.59, 24.01, 84.01]|Some([221582.864, 3716697.516, 779345.526, 9330126.13])| + EPSG_3047|3047|"ETRS89 / UTM zone 35N (N-E)"|Projected|[24.0, 33.26, 30.01, 84.01]|Some([220526.088, 3680111.182, 780405.835, 9330126.13])| + EPSG_3048|3048|"ETRS89 / UTM zone 36N (N-E)"|Projected|[30.0, 33.91, 36.01, 84.01]|Some([222616.447, 3752177.04, 778308.488, 9330126.13])| + EPSG_3049|3049|"ETRS89 / UTM zone 37N (N-E)"|Projected|[36.0, 72.99, 38.01, 79.07]|Some([402080.15, 8099631.325, 479045.428, 8779418.626])| + EPSG_30491|30491|"Voirol 1875 / Nord Algerie (ancienne)"|Projected|[-2.22, 34.64, 8.64, 37.14]|Some([49377.283, 149262.911, 1044474.076, 442638.092])| + EPSG_30492|30492|"Voirol 1875 / Sud Algerie (ancienne)"|Projected|[-2.95, 31.99, 9.09, 34.66]|Some([-33384.479, 154848.407, 1103677.724, 468822.059])| + EPSG_30493|30493|"Voirol 1879 / Nord Algerie (ancienne)"|Projected|[-2.22, 34.64, 8.64, 37.14]|Some([49122.747, 149171.216, 1044246.027, 442534.874])| + EPSG_30494|30494|"Voirol 1879 / Sud Algerie (ancienne)"|Projected|[-2.95, 31.99, 9.09, 34.66]|Some([-33639.343, 154772.119, 1103449.627, 468731.293])| + EPSG_3054|3054|"Hjorsey 1955 / UTM zone 26N"|Projected|[-24.66, 64.71, -24.0, 65.85]|Some([606800.576, 7178401.613, 642953.063, 7306670.716])| + EPSG_3055|3055|"Hjorsey 1955 / UTM zone 27N"|Projected|[-24.0, 63.34, -18.0, 66.52]|Some([349834.079, 7023678.482, 650130.5, 7381285.622])| + EPSG_3056|3056|"Hjorsey 1955 / UTM zone 28N"|Projected|[-18.0, 63.45, -13.38, 66.58]|Some([350399.132, 7035939.377, 580759.372, 7387969.797])| + EPSG_3057|3057|"ISN93 / Lambert 1993"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([-161615.711, -62368.597, 1248429.41, 1068116.034])| + EPSG_3058|3058|"Helle 1954 / Jan Mayen Grid"|Projected|[-9.17, 70.75, -7.87, 71.24]|Some([25176.33, 53009.026, 73029.451, 107811.38])| + EPSG_3059|3059|"LKS-92 / Latvia TM"|Projected|[19.06, 55.67, 28.24, 58.09]|Some([189423.143, 169353.349, 766599.759, 449393.558])| + EPSG_3060|3060|"IGN72 Grande Terre / UTM zone 58S"|Projected|[163.92, -22.45, 167.09, -20.03]|Some([386701.846, 7515528.193, 718283.498, 7784865.098])| + EPSG_3061|3061|"Porto Santo 1995 / UTM zone 28N"|Projected|[-17.31, 32.35, -16.23, 33.15]|Some([282606.268, 3579950.558, 385658.338, 3669939.675])| + EPSG_3062|3062|"Azores Oriental 1995 / UTM zone 26N"|Projected|[-25.92, 36.87, -24.72, 37.96]|Some([594842.801, 4080957.927, 703195.163, 4203792.42])| + EPSG_3063|3063|"Azores Central 1995 / UTM zone 26N"|Projected|[-28.9, 38.32, -26.97, 39.14]|Some([333803.778, 4241404.666, 502522.576, 4334116.386])| + EPSG_3064|3064|"IGM95 / UTM zone 32N"|Projected|[5.93, 36.53, 12.0, 47.04]|Some([225132.787, 4042736.378, 768598.181, 5214183.815])| + EPSG_3065|3065|"IGM95 / UTM zone 33N"|Projected|[12.0, 34.79, 18.0, 47.1]|Some([225503.37, 3849755.266, 774496.63, 5220644.505])| + EPSG_3066|3066|"ED50 / Jordan TM"|Projected|[34.88, 29.18, 39.31, 33.38]|Some([293824.672, 228745.755, 724701.596, 696710.58])| + EPSG_3067|3067|"ETRS89 / TM35FIN(E,N)"|Projected|[19.08, 58.84, 31.59, 70.09]|Some([43547.789, 6522236.152, 764796.716, 7795461.188])| + EPSG_3068|3068|"DHDN / Soldner Berlin"|Projected|[13.09, 52.33, 13.76, 52.65]|Some([3499.471, 291.06, 49174.462, 36034.824])| + EPSG_3069|3069|"NAD27 / Wisconsin Transverse Mercator"|Projected|[-92.89, 42.48, -86.25, 47.31]|Some([262464.611, 202858.609, 808246.973, 746217.996])| + EPSG_3070|3070|"NAD83 / Wisconsin Transverse Mercator"|Projected|[-92.89, 42.48, -86.25, 47.31]|Some([282455.202, 223072.39, 828240.811, 766436.435])| + EPSG_3071|3071|"NAD83(HARN) / Wisconsin Transverse Mercator"|Projected|[-92.89, 42.48, -86.25, 47.31]|Some([282455.766, 223071.52, 828241.283, 766435.476])| + EPSG_3072|3072|"NAD83 / Maine CS2000 East"|Projected|[-68.58, 44.18, -66.91, 47.37]|Some([643626.95, 38518.355, 777163.163, 393521.359])| + EPSG_30729|30729|"Nord Sahara 1959 / UTM zone 29N"|Projected|[-8.67, 25.73, -6.0, 29.85]|Some([531982.22, 2845521.433, 801112.574, 3305642.736])| + EPSG_30730|30730|"Nord Sahara 1959 / UTM zone 30N"|Projected|[-6.0, 21.82, 0.0, 37.01]|Some([189938.009, 2412599.464, 810246.592, 4099891.619])| + EPSG_30731|30731|"Nord Sahara 1959 / UTM zone 31N"|Projected|[0.0, 18.97, 6.01, 38.77]|Some([184152.388, 2097203.921, 817044.494, 4295256.85])| + EPSG_30732|30732|"Nord Sahara 1959 / UTM zone 32N"|Projected|[6.0, 19.6, 11.99, 38.8]|Some([185337.783, 2166911.86, 813710.62, 4298554.531])| + EPSG_3074|3074|"NAD83 / Maine CS2000 West"|Projected|[-71.09, 43.07, -69.6, 46.58]|Some([241765.496, 26291.387, 363121.336, 416635.763])| + EPSG_3075|3075|"NAD83(HARN) / Maine CS2000 East"|Projected|[-68.58, 44.18, -66.91, 47.37]|Some([643626.918, 38517.345, 777163.106, 393520.283])| + EPSG_3077|3077|"NAD83(HARN) / Maine CS2000 West"|Projected|[-71.09, 43.07, -69.6, 46.58]|Some([241765.527, 26290.408, 363121.344, 416634.711])| + EPSG_3078|3078|"NAD83 / Michigan Oblique Mercator"|Projected|[-90.42, 41.69, -82.13, 48.32]|Some([131754.146, 126622.954, 821906.052, 872458.276])| + EPSG_3079|3079|"NAD83(HARN) / Michigan Oblique Mercator"|Projected|[-90.42, 41.69, -82.13, 48.32]|Some([131754.636, 126622.075, 821906.42, 872457.281])| + EPSG_30791|30791|"Nord Sahara 1959 / Nord Algerie"|Projected|[-2.22, 34.64, 8.64, 37.14]|Some([49339.673, 149239.906, 1044432.819, 442615.611])| + EPSG_30792|30792|"Nord Sahara 1959 / Sud Algerie"|Projected|[-3.85, 31.49, 9.22, 34.66]|Some([-121794.765, 99385.537, 1119358.166, 469705.596])| + EPSG_3080|3080|"NAD27 / Shackleford"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([805989.158, 1060840.114, 5141592.607, 4998471.126])| + EPSG_3081|3081|"NAD83 / Texas State Mapping System"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([331226.354, 408960.368, 1652725.732, 1609156.673])| + EPSG_3082|3082|"NAD83 / Texas Centric Lambert Conformal"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831139.345, 5877072.129, 2152810.743, 7077309.646])| + EPSG_3083|3083|"NAD83 / Texas Centric Albers Equal Area"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831192.278, 6857764.52, 2152758.974, 8059593.093])| + EPSG_3084|3084|"NAD83(HARN) / Texas Centric Lambert Conformal"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831140.267, 5877071.655, 2152811.403, 7077308.939])| + EPSG_3085|3085|"NAD83(HARN) / Texas Centric Albers Equal Area"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831193.2, 6857764.049, 2152759.633, 8059592.389])| + EPSG_3086|3086|"NAD83 / Florida GDL Albers"|Projected|[-87.63, 24.41, -79.97, 31.01]|Some([32031.289, 45423.238, 808502.508, 784246.475])| + EPSG_3087|3087|"NAD83(HARN) / Florida GDL Albers"|Projected|[-87.63, 24.41, -79.97, 31.01]|Some([32031.749, 45422.768, 808502.774, 784245.824])| + EPSG_3088|3088|"NAD83 / Kentucky Single Zone"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([1157746.949, 1017388.797, 1840462.143, 1319387.434])| + EPSG_3089|3089|"NAD83 / Kentucky Single Zone (ftUS)"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([3798374.782, 3337883.077, 6038249.548, 4328690.274])| + EPSG_3090|3090|"NAD83(HARN) / Kentucky Single Zone"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([1157747.432, 1017388.026, 1840462.49, 1319386.606])| + EPSG_3091|3091|"NAD83(HARN) / Kentucky Single Zone (ftUS)"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([3798376.365, 3337880.548, 6038250.687, 4328687.557])| + EPSG_3092|3092|"Tokyo / UTM zone 51N"|Projected|[123.62, 23.98, 125.51, 24.94]|Some([562748.79, 2651426.704, 755543.164, 2759921.692])| + EPSG_3093|3093|"Tokyo / UTM zone 52N"|Projected|[126.63, 24.4, 132.0, 34.9]|Some([259855.793, 2697782.023, 804480.11, 3865336.08])| + EPSG_3094|3094|"Tokyo / UTM zone 53N"|Projected|[132.0, 20.37, 138.0, 37.58]|Some([187073.005, 2251687.562, 813437.212, 4162711.926])| + EPSG_3095|3095|"Tokyo / UTM zone 54N"|Projected|[138.0, 24.67, 144.0, 45.54]|Some([196680.692, 2727653.935, 803923.332, 5046573.203])| + EPSG_3096|3096|"Tokyo / UTM zone 55N"|Projected|[144.0, 42.84, 145.87, 44.4]|Some([255176.642, 4742894.284, 410358.851, 4919908.562])| + EPSG_3097|3097|"JGD2000 / UTM zone 51N"|Projected|[122.38, 21.1, 126.0, 29.71]|Some([435606.214, 2333215.068, 811684.528, 3290419.882])| + EPSG_3098|3098|"JGD2000 / UTM zone 52N"|Projected|[126.0, 21.12, 132.0, 38.63]|Some([188357.296, 2335428.461, 811642.704, 4279988.956])| + EPSG_3099|3099|"JGD2000 / UTM zone 53N"|Projected|[132.0, 17.09, 138.0, 43.55]|Some([180701.572, 1889511.329, 819298.428, 4826267.586])| + EPSG_3100|3100|"JGD2000 / UTM zone 54N"|Projected|[138.0, 17.63, 144.0, 46.05]|Some([181636.801, 1949250.955, 818363.199, 5103979.181])| + EPSG_3101|3101|"JGD2000 / UTM zone 55N"|Projected|[144.0, 23.03, 147.86, 45.65]|Some([192525.243, 2546841.34, 588116.936, 5059539.363])| + EPSG_3102|3102|"American Samoa 1962 / American Samoa Lambert"|Projected|[-170.88, -14.43, -169.38, -14.11]|Some([188684.759, 250553.782, 720097.496, 367302.663])| + EPSG_31028|31028|"Yoff / UTM zone 28N"|Projected|[-20.22, 10.64, -11.36, 16.7]|Some([-71855.279, 1176094.385, 898200.583, 1853598.424])| + EPSG_3106|3106|"Gulshan 303 / TM 90 NE"|Projected|[88.01, 20.52, 92.67, 26.64]|Some([292781.483, 2268734.499, 778725.025, 2949043.525])| + EPSG_3107|3107|"GDA94 / SA Lambert"|Projected|[128.99, -38.13, 141.01, -25.99]|Some([396749.099, 1305574.976, 1603250.901, 2665724.452])| + EPSG_3108|3108|"ETRS89 / Guernsey Grid"|Projected|[-3.06, 49.21, -2.03, 49.94]|Some([124.653, 17747.319, 75173.839, 99136.705])| + EPSG_3109|3109|"ETRS89 / Jersey Transverse Mercator"|Projected|[-2.56, 48.87, -1.81, 49.44]|Some([8821.176, 30520.22, 63842.643, 93998.326])| + EPSG_3110|3110|"AGD66 / Vicgrid66"|Projected|[140.96, -39.2, 150.04, -33.98]|Some([2126220.389, 4244084.853, 2965961.094, 4834996.785])| + EPSG_3111|3111|"GDA94 / Vicgrid"|Projected|[140.96, -39.2, 150.04, -33.98]|Some([2126335.946, 2244252.056, 2966077.314, 2835165.132])| + EPSG_3112|3112|"GDA94 / Geoscience Australia Lambert"|Projected|[112.85, -43.7, 153.69, -9.86]|Some([-2382330.288, -5065635.719, 2219274.762, -1158972.006])| + EPSG_31121|31121|"Zanderij / UTM zone 21N"|Projected|[-58.08, 1.83, -52.66, 9.35]|Some([380033.706, 202628.542, 983309.513, 1036841.865])| + EPSG_3113|3113|"GDA94 / BCSG02"|Projected|[152.37, -29.36, 153.69, -24.64]|Some([-13782.821, -50925.82, 119857.56, 472261.061])| + EPSG_3114|3114|"MAGNA-SIRGAS / Colombia Far West zone"|Projected|[-79.1, 1.23, -78.58, 2.48]|Some([1108719.737, 627794.027, 1166682.753, 766087.662])| + EPSG_3115|3115|"MAGNA-SIRGAS / Colombia West zone"|Projected|[-78.59, 0.03, -75.58, 10.21]|Some([831610.484, 495084.763, 1166720.907, 1621237.977])| + EPSG_31154|31154|"Zanderij / TM 54 NW"|Projected|[-57.25, 5.34, -52.66, 9.35]|Some([139870.068, 590603.841, 648621.996, 1035547.485])| + EPSG_3116|3116|"MAGNA-SIRGAS / Colombia Bogota zone"|Projected|[-75.59, -2.51, -72.58, 11.82]|Some([831610.839, 214127.061, 1166720.556, 1799385.867])| + EPSG_3117|3117|"MAGNA-SIRGAS / Colombia East Central zone"|Projected|[-72.59, -4.23, -69.58, 12.52]|Some([831611.889, 23866.301, 1166719.516, 1876847.178])| + EPSG_31170|31170|"Zanderij / Suriname Old TM"|Projected|[-58.08, 1.83, -53.95, 6.06]|Some([233519.517, 202628.527, 692959.329, 670779.539])| + EPSG_31171|31171|"Zanderij / Suriname TM"|Projected|[-58.08, 1.83, -53.95, 6.06]|Some([233439.541, 202689.34, 693017.24, 670980.853])| + EPSG_3118|3118|"MAGNA-SIRGAS / Colombia East zone"|Projected|[-69.59, -2.25, -66.87, 6.31]|Some([831610.489, 242886.924, 1134429.163, 1189762.323])| + EPSG_3119|3119|"Douala 1948 / AEF west"|Projected|[8.45, 2.16, 10.4, 4.99]|Some([772268.948, 1238691.268, 989055.112, 1551670.223])| + EPSG_3120|3120|"Pulkovo 1942(58) / Poland zone I"|Projected|[18.0, 49.0, 24.15, 52.34]|Some([4411549.764, 5286313.226, 4861478.115, 5662240.772])| + EPSG_3121|3121|"PRS92 / Philippines zone 1"|Projected|[116.04, 6.21, 118.0, 18.64]|Some([393582.046, 686728.502, 610498.041, 2062087.492])| + EPSG_3122|3122|"PRS92 / Philippines zone 2"|Projected|[118.0, 3.02, 120.07, 20.42]|Some([388649.18, 333980.926, 618766.21, 2259186.714])| + EPSG_3123|3123|"PRS92 / Philippines zone 3"|Projected|[119.7, 3.0, 122.21, 21.62]|Some([355291.333, 331763.492, 634334.621, 2392242.956])| + EPSG_3124|3124|"PRS92 / Philippines zone 4"|Projected|[121.74, 3.44, 124.29, 22.18]|Some([359800.771, 380409.924, 643170.135, 2454239.043])| + EPSG_3125|3125|"PRS92 / Philippines zone 5"|Projected|[123.73, 4.76, 126.65, 21.97]|Some([358925.368, 526364.248, 682889.195, 2431333.906])| + EPSG_31251|31251|"MGI (Ferro) / Austria GK West Zone"|Projected|[9.53, 46.77, 11.84, 47.61]|Some([-61338.468, 181209.678, 115103.428, 275692.205])| + EPSG_31252|31252|"MGI (Ferro) / Austria GK Central Zone"|Projected|[11.83, 46.4, 14.84, 48.79]|Some([-115559.833, 140068.852, 115918.74, 406890.523])| + EPSG_31253|31253|"MGI (Ferro) / Austria GK East Zone"|Projected|[14.83, 46.56, 17.17, 49.02]|Some([-115190.122, 157843.997, 64236.661, 432456.225])| + EPSG_31254|31254|"MGI / Austria GK West"|Projected|[9.53, 46.77, 11.84, 47.61]|Some([-61338.468, 181209.678, 115103.428, 275692.205])| + EPSG_31255|31255|"MGI / Austria GK Central"|Projected|[11.83, 46.4, 14.84, 48.79]|Some([-115559.833, 140068.852, 115918.74, 406890.523])| + EPSG_31256|31256|"MGI / Austria GK East"|Projected|[14.83, 46.56, 17.17, 49.02]|Some([-115190.122, 157843.997, 64236.661, 432456.225])| + EPSG_31257|31257|"MGI / Austria GK M28"|Projected|[9.53, 46.77, 11.84, 47.61]|Some([88661.532, 181209.678, 265103.428, 275692.205])| + EPSG_31258|31258|"MGI / Austria GK M31"|Projected|[11.83, 46.4, 14.84, 48.79]|Some([334440.167, 140068.852, 565918.74, 406890.523])| + EPSG_31259|31259|"MGI / Austria GK M34"|Projected|[14.83, 46.56, 17.17, 49.02]|Some([634809.878, 157843.997, 814236.661, 432456.225])| + EPSG_3126|3126|"ETRS89 / ETRS-GK19FIN"|Projected|[19.24, 60.08, 19.5, 60.34]|Some([513254.328, 6663010.108, 527832.436, 6692058.677])| + EPSG_3127|3127|"ETRS89 / ETRS-GK20FIN"|Projected|[19.5, 59.92, 20.5, 60.48]|Some([472032.843, 6645159.891, 527967.157, 6707657.064])| + EPSG_3128|3128|"ETRS89 / ETRS-GK21FIN"|Projected|[20.5, 59.84, 21.5, 69.33]|Some([471965.566, 6636247.071, 528034.434, 7694317.479])| + EPSG_31281|31281|"MGI (Ferro) / Austria West Zone"|Projected|[9.53, 46.77, 11.84, 47.61]|Some([-61338.468, 5181209.678, 115103.428, 5275692.205])| + EPSG_31282|31282|"MGI (Ferro) / Austria Central Zone"|Projected|[11.83, 46.4, 14.84, 48.79]|Some([-115559.833, 5140068.852, 115918.74, 5406890.523])| + EPSG_31283|31283|"MGI (Ferro) / Austria East Zone"|Projected|[14.83, 46.56, 17.17, 49.02]|Some([-115190.122, 5157843.997, 64236.661, 5432456.225])| + EPSG_31284|31284|"MGI / Austria M28"|Projected|[9.53, 46.77, 11.84, 47.61]|Some([88661.532, 5181209.678, 265103.428, 5275692.205])| + EPSG_31285|31285|"MGI / Austria M31"|Projected|[11.83, 46.4, 14.84, 48.79]|Some([334440.167, 5140068.852, 565918.74, 5406890.523])| + EPSG_31286|31286|"MGI / Austria M34"|Projected|[14.83, 46.56, 17.17, 49.02]|Some([634809.878, 5157843.997, 814236.661, 5432456.225])| + EPSG_31287|31287|"MGI / Austria Lambert"|Projected|[9.53, 46.4, 17.17, 49.02]|Some([107724.113, 277804.371, 694938.749, 575953.615])| + EPSG_31288|31288|"MGI (Ferro) / Austria zone M28"|Projected|[9.53, 46.77, 11.84, 47.61]|Some([88661.532, 5181209.678, 265103.428, 5275692.205])| + EPSG_31289|31289|"MGI (Ferro) / Austria zone M31"|Projected|[11.83, 46.4, 14.84, 48.79]|Some([334440.167, 5140068.852, 565918.74, 5406890.523])| + EPSG_3129|3129|"ETRS89 / ETRS-GK22FIN"|Projected|[21.5, 59.76, 22.5, 69.31]|Some([471898.344, 6627334.361, 528101.656, 7692086.476])| + EPSG_31290|31290|"MGI (Ferro) / Austria zone M34"|Projected|[14.83, 46.56, 17.17, 49.02]|Some([634809.878, 5157843.997, 814236.661, 5432456.225])| + EPSG_3130|3130|"ETRS89 / ETRS-GK23FIN"|Projected|[22.5, 59.75, 23.5, 68.74]|Some([471889.945, 6626220.279, 528110.055, 7628505.093])| + EPSG_31300|31300|"BD72 / Belge Lambert 72"|Projected|[2.5, 49.5, 6.4, 51.51]|Some([15401.082, 20906.574, 297896.919, 246444.315])| + EPSG_3131|3131|"ETRS89 / ETRS-GK24FIN"|Projected|[23.5, 59.86, 24.5, 68.84]|Some([471982.38, 6638475.266, 528017.62, 7639659.414])| + EPSG_3132|3132|"ETRS89 / ETRS-GK25FIN"|Projected|[24.5, 59.94, 25.5, 68.9]|Some([472049.671, 6647388.113, 527950.329, 7646352.07])| + EPSG_3133|3133|"ETRS89 / ETRS-GK26FIN"|Projected|[25.5, 60.18, 26.5, 69.94]|Some([472251.876, 6674127.306, 527748.124, 7762365.51])| + EPSG_3134|3134|"ETRS89 / ETRS-GK27FIN"|Projected|[26.5, 60.36, 27.5, 70.05]|Some([472403.853, 6694182.342, 527596.147, 7774636.968])| + EPSG_3135|3135|"ETRS89 / ETRS-GK28FIN"|Projected|[27.5, 60.42, 28.5, 70.09]|Some([472454.574, 6700867.475, 527545.426, 7779099.354])| + EPSG_3136|3136|"ETRS89 / ETRS-GK29FIN"|Projected|[28.5, 60.94, 29.5, 69.81]|Some([472895.429, 6758807.831, 527104.571, 7747863.074])| + EPSG_3137|3137|"ETRS89 / ETRS-GK30FIN"|Projected|[29.5, 61.43, 30.5, 67.98]|Some([473312.921, 6813409.599, 526687.079, 7543736.596])| + EPSG_31370|31370|"BD72 / Belgian Lambert 72"|Projected|[2.5, 49.5, 6.4, 51.51]|Some([14637.247, 20907.064, 297133.132, 246424.275])| + EPSG_3138|3138|"ETRS89 / ETRS-GK31FIN"|Projected|[30.5, 62.08, 31.59, 64.27]|Some([473869.786, 6885846.648, 530833.566, 7130086.161])| + EPSG_3139|3139|"Vanua Levu 1915 / Vanua Levu Grid"|Projected|[178.42, -17.07, -179.77, -16.1]|Some([767561.475, 1209975.96, 1730131.291, 1744708.3])| + EPSG_3140|3140|"Viti Levu 1912 / Viti Levu Grid"|Projected|[177.19, -18.32, 178.75, -17.25]|Some([117760.149, 526203.651, 942430.635, 1115843.951])| + EPSG_3141|3141|"Fiji 1956 / UTM zone 60S"|Projected|[176.81, -19.22, 180.0, -16.1]|Some([480079.627, 7872290.168, 821340.816, 8220187.301])| + EPSG_3142|3142|"Fiji 1956 / UTM zone 1S"|Projected|[-180.0, -17.04, -179.77, -16.1]|Some([179429.638, 8113763.429, 205488.161, 8218211.358])| + EPSG_31466|31466|"DHDN / 3-degree Gauss-Kruger zone 2"|Projected|[5.86, 49.11, 7.51, 53.81]|Some([2489822.302, 5441421.616, 2610288.01, 5965387.975])| + EPSG_31467|31467|"DHDN / 3-degree Gauss-Kruger zone 3"|Projected|[7.5, 47.27, 10.51, 55.09]|Some([3386569.827, 5236823.484, 3614340.947, 6107852.389])| + EPSG_31468|31468|"DHDN / 3-degree Gauss-Kruger zone 4"|Projected|[10.5, 47.39, 13.51, 54.59]|Some([4386858.109, 5250163.04, 4614113.737, 6052198.107])| + EPSG_31469|31469|"DHDN / 3-degree Gauss-Kruger zone 5"|Projected|[13.5, 48.51, 13.84, 48.98]|Some([5389309.875, 5375341.527, 5415225.792, 5428042.192])| + EPSG_3148|3148|"Indian 1960 / UTM zone 48N"|Projected|[102.14, 8.33, 108.0, 23.4]|Some([185005.461, 920708.441, 830881.492, 2590776.668])| + EPSG_3149|3149|"Indian 1960 / UTM zone 49N"|Projected|[108.0, 10.43, 109.53, 21.56]|Some([172008.769, 1153003.833, 347810.597, 2386921.788])| + EPSG_3152|3152|"ST74"|Projected|[17.75, 59.22, 18.2, 59.43]|Some([82600.978, 66523.909, 108299.493, 89958.307])| + EPSG_31528|31528|"Conakry 1905 / UTM zone 28N"|Projected|[-15.13, 9.01, -12.0, 12.68]|Some([485467.406, 995969.95, 829633.845, 1403635.441])| + EPSG_31529|31529|"Conakry 1905 / UTM zone 29N"|Projected|[-12.0, 7.19, -7.65, 12.51]|Some([168380.713, 794767.897, 648810.1, 1384818.486])| + EPSG_3153|3153|"NAD83(CSRS) / BC Albers"|Projected|[-139.04, 48.25, -114.08, 60.01]|Some([34759.911, 359422.673, 1883160.609, 1736633.073])| + EPSG_3154|3154|"NAD83(CSRS) / UTM zone 7N"|Projected|[-141.01, 52.05, -138.0, 72.53]|Some([499315.509, 5766599.041, 705700.505, 8050560.903])| + EPSG_3155|3155|"NAD83(CSRS) / UTM zone 8N"|Projected|[-138.0, 48.06, -132.0, 79.42]|Some([276485.132, 5322968.538, 723517.334, 8818431.938])| + EPSG_3156|3156|"NAD83(CSRS) / UTM zone 9N"|Projected|[-132.0, 46.52, -126.0, 80.93]|Some([269895.416, 5151825.153, 730106.966, 8986755.03])| + EPSG_3157|3157|"NAD83(CSRS) / UTM zone 10N"|Projected|[-126.0, 48.13, -120.0, 81.8]|Some([276788.455, 5330748.723, 723213.786, 9083740.558])| + EPSG_3158|3158|"NAD83(CSRS) / UTM zone 14N"|Projected|[-102.0, 48.99, -96.0, 84.0]|Some([280543.092, 5426343.229, 719458.357, 9329004.15])| + EPSG_3159|3159|"NAD83(CSRS) / UTM zone 15N"|Projected|[-96.0, 48.03, -90.0, 84.0]|Some([276354.586, 5319633.668, 723646.606, 9329004.067])| + EPSG_3160|3160|"NAD83(CSRS) / UTM zone 16N"|Projected|[-90.0, 46.11, -84.0, 84.0]|Some([268168.291, 5106268.41, 731832.619, 9329003.995])| + EPSG_31600|31600|"Dealul Piscului 1930 / Stereo 33"|Projected|[20.26, 43.62, 29.74, 48.27]|Some([85969.038, 247024.466, 850992.717, 776275.657])| + EPSG_3161|3161|"NAD83 / Ontario MNR Lambert"|Projected|[-95.16, 41.67, -74.35, 56.9]|Some([82306.08, 11672134.053, 1818313.942, 13411520.074])| + EPSG_3162|3162|"NAD83(CSRS) / Ontario MNR Lambert"|Projected|[-95.16, 41.67, -74.35, 56.9]|Some([82306.612, 11672133.164, 1818314.195, 13411518.907])| + EPSG_3163|3163|"RGNC91-93 / Lambert New Caledonia"|Projected|[163.54, -22.73, 168.19, -19.5]|Some([141647.059, 161815.207, 629999.1, 521442.55])| + EPSG_3164|3164|"ST87 Ouvea / UTM zone 58S"|Projected|[166.44, -20.77, 166.71, -20.34]|Some([649899.946, 7702365.175, 678510.763, 7750237.972])| + EPSG_3165|3165|"NEA74 Noumea / Noumea Lambert"|Projected|[166.35, -22.37, 166.54, -22.19]|Some([-9873.809, -11416.121, 9719.959, 8519.45])| + EPSG_3166|3166|"NEA74 Noumea / Noumea Lambert 2"|Projected|[166.35, -22.37, 166.54, -22.19]|Some([-9873.813, -11416.116, 9719.955, 8519.451])| + EPSG_3167|3167|"Kertau (RSO) / RSO Malaya (ch)"|Projected|[99.59, 1.21, 104.6, 6.72]|Some([8757.385, 6650.645, 36489.879, 37000.928])| + EPSG_3168|3168|"Kertau (RSO) / RSO Malaya (m)"|Projected|[99.59, 1.21, 104.6, 6.72]|Some([176170.186, 133789.398, 734057.987, 744338.646])| + EPSG_3169|3169|"RGNC91-93 / UTM zone 57S"|Projected|[156.25, -26.03, 162.01, -15.34]|Some([204728.62, 7117519.258, 823209.236, 8304066.981])| + EPSG_3170|3170|"RGNC91-93 / UTM zone 58S"|Projected|[162.0, -26.45, 168.0, -14.83]|Some([177095.101, 7070989.316, 822904.899, 8360476.621])| + EPSG_3171|3171|"RGNC91-93 / UTM zone 59S"|Projected|[168.0, -25.95, 174.28, -19.75]|Some([185580.248, 7125735.751, 843789.449, 7816181.82])| + EPSG_3172|3172|"IGN53 Mare / UTM zone 59S"|Projected|[168.0, -21.71, 168.19, -21.35]|Some([189059.058, 7596390.818, 209496.362, 7636640.309])| + EPSG_3174|3174|"NAD83 / Great Lakes Albers"|Projected|[-93.17, 40.99, -74.47, 50.74]|Some([267230.569, 490910.639, 1839209.602, 1619073.613])| + EPSG_3175|3175|"NAD83 / Great Lakes and St Lawrence Albers"|Projected|[-93.17, 40.99, -54.75, 52.22]|Some([166190.123, 490965.561, 3351176.389, 2082826.261])| + EPSG_3176|3176|"Indian 1960 / TM 106 NE"|Projected|[106.54, 7.99, 110.0, 11.15]|Some([559385.8, 882912.511, 941578.559, 1235200.857])| + EPSG_3177|3177|"LGD2006 / Libya TM"|Projected|[9.31, 19.5, 26.21, 35.23]|Some([193725.606, 2149487.291, 1966727.934, 3925464.707])| + EPSG_3178|3178|"GR96 / UTM zone 18N"|Projected|[-75.0, 74.52, -72.0, 79.04]|Some([500000.0, 8270052.563, 589341.79, 8776074.625])| + EPSG_3179|3179|"GR96 / UTM zone 19N"|Projected|[-72.0, 73.24, -66.0, 80.9]|Some([403477.123, 8127252.808, 596522.877, 8983411.251])| + EPSG_3180|3180|"GR96 / UTM zone 20N"|Projected|[-66.0, 68.92, -60.0, 82.22]|Some([379622.181, 7645441.822, 620377.819, 9130562.947])| + EPSG_3181|3181|"GR96 / UTM zone 21N"|Projected|[-60.0, 58.91, -54.0, 84.0]|Some([327230.349, 6530030.536, 672769.651, 9329005.182])| + EPSG_3182|3182|"GR96 / UTM zone 22N"|Projected|[-54.0, 56.9, -48.0, 84.0]|Some([317301.513, 6306254.477, 682698.487, 9329005.182])| + EPSG_3183|3183|"GR96 / UTM zone 23N"|Projected|[-48.0, 56.38, -42.0, 84.0]|Some([314769.302, 6248373.867, 685230.698, 9329005.182])| + EPSG_31838|31838|"NGN / UTM zone 38N"|Projected|[46.54, 28.53, 48.0, 30.09]|Some([648405.854, 3156877.314, 793586.817, 3332549.985])| + EPSG_31839|31839|"NGN / UTM zone 39N"|Projected|[48.0, 28.54, 48.48, 30.04]|Some([206443.811, 3159609.483, 257011.26, 3327005.643])| + EPSG_3184|3184|"GR96 / UTM zone 24N"|Projected|[-42.0, 56.56, -36.0, 84.0]|Some([315644.106, 6268408.912, 684355.894, 9329005.182])| + EPSG_3185|3185|"GR96 / UTM zone 25N"|Projected|[-36.0, 60.16, -30.0, 84.0]|Some([333514.017, 6669230.243, 666485.983, 9329005.182])| + EPSG_3186|3186|"GR96 / UTM zone 26N"|Projected|[-30.0, 64.96, -24.0, 84.0]|Some([358360.051, 7203996.673, 641639.949, 9329005.182])| + EPSG_3187|3187|"GR96 / UTM zone 27N"|Projected|[-24.0, 67.7, -18.0, 84.0]|Some([373004.91, 7509417.584, 626995.09, 9329005.182])| + EPSG_3188|3188|"GR96 / UTM zone 28N"|Projected|[-18.0, 68.67, -12.0, 84.0]|Some([378261.584, 7617566.373, 621738.416, 9329005.182])| + EPSG_3189|3189|"GR96 / UTM zone 29N"|Projected|[-12.0, 72.43, -6.0, 84.0]|Some([398957.825, 8036896.456, 601042.175, 9329005.182])| + EPSG_3190|3190|"LGD2006 / Libya TM zone 5"|Projected|[9.31, 25.37, 10.01, 30.49]|Some([229837.373, 2806952.344, 301726.829, 3374716.373])| + EPSG_31901|31901|"KUDAMS / KTM"|Projected|[47.78, 29.17, 48.16, 29.45]|Some([478575.588, 3228106.861, 515541.607, 3259162.441])| + EPSG_3191|3191|"LGD2006 / Libya TM zone 6"|Projected|[10.0, 23.51, 12.0, 33.23]|Some([97935.853, 2600902.155, 302200.327, 3678536.98])| + EPSG_3192|3192|"LGD2006 / Libya TM zone 7"|Projected|[12.0, 22.8, 14.0, 33.06]|Some([97388.258, 2522275.07, 302732.097, 3659681.24])| + EPSG_3193|3193|"LGD2006 / Libya TM zone 8"|Projected|[14.0, 22.61, 16.0, 32.79]|Some([97238.534, 2501234.698, 302865.85, 3629735.802])| + EPSG_3194|3194|"LGD2006 / Libya TM zone 9"|Projected|[16.0, 22.51, 18.01, 31.34]|Some([97156.349, 2490160.783, 303960.89, 3468952.579])| + EPSG_3195|3195|"LGD2006 / Libya TM zone 10"|Projected|[18.0, 21.54, 20.0, 32.17]|Some([96445.154, 2382756.537, 303626.925, 3560977.758])| + EPSG_3196|3196|"LGD2006 / Libya TM zone 11"|Projected|[20.0, 20.54, 22.0, 33.0]|Some([95743.156, 2272043.745, 304312.629, 3653023.471])| + EPSG_31965|31965|"SIRGAS 2000 / UTM zone 11N"|Projected|[-120.0, 15.01, -114.0, 32.72]|Some([177364.064, 1659432.038, 822635.936, 3624228.097])| + EPSG_31966|31966|"SIRGAS 2000 / UTM zone 12N"|Projected|[-114.0, 15.09, -108.0, 32.27]|Some([177484.621, 1668280.432, 822515.379, 3574315.953])| + EPSG_31967|31967|"SIRGAS 2000 / UTM zone 13N"|Projected|[-108.0, 14.05, -102.0, 31.79]|Some([175966.325, 1553256.07, 824033.675, 3521079.182])| + EPSG_31968|31968|"SIRGAS 2000 / UTM zone 14N"|Projected|[-102.01, 12.3, -96.0, 29.81]|Some([172563.051, 1359727.411, 826348.206, 3301533.285])| + EPSG_31969|31969|"SIRGAS 2000 / UTM zone 15N"|Projected|[-96.0, 0.0, -90.0, 26.0]|Some([166021.443, 0.0, 833978.557, 2879132.534])| + EPSG_3197|3197|"LGD2006 / Libya TM zone 12"|Projected|[22.0, 19.5, 24.0, 32.97]|Some([95046.953, 2156916.124, 304992.47, 3649695.838])| + EPSG_31970|31970|"SIRGAS 2000 / UTM zone 16N"|Projected|[-90.0, 0.0, -83.99, 25.77]|Some([166021.443, 0.0, 835092.849, 2853663.033])| + EPSG_31971|31971|"SIRGAS 2000 / UTM zone 17N"|Projected|[-84.0, 0.0, -78.0, 19.54]|Some([166021.443, 0.0, 833978.557, 2163337.523])| + EPSG_31972|31972|"SIRGAS 2000 / UTM zone 18N"|Projected|[-78.0, 0.0, -72.0, 15.04]|Some([166021.443, 0.0, 833978.557, 1664943.162])| + EPSG_31973|31973|"SIRGAS 2000 / UTM zone 19N"|Projected|[-72.0, 0.0, -66.0, 15.64]|Some([166021.443, 0.0, 833978.557, 1731386.661])| + EPSG_31974|31974|"SIRGAS 2000 / UTM zone 20N"|Projected|[-66.0, 0.64, -59.99, 16.75]|Some([166042.177, 70739.259, 835072.045, 1854330.283])| + EPSG_31975|31975|"SIRGAS 2000 / UTM zone 21N"|Projected|[-60.0, 1.18, -54.0, 12.19]|Some([166091.925, 130425.64, 833908.075, 1349369.628])| + EPSG_31976|31976|"SIRGAS 2000 / UTM zone 22N"|Projected|[-54.0, 0.0, -47.99, 9.24]|Some([166021.443, 0.0, 835092.849, 1022782.124])| + EPSG_31977|31977|"SIRGAS 2000 / UTM zone 17S"|Projected|[-84.0, -56.45, -78.0, 0.0]|Some([166021.443, 3739799.276, 833978.557, 10000000.0])| + EPSG_31978|31978|"SIRGAS 2000 / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, 0.0]|Some([166021.443, 3415992.79, 835092.849, 10000000.0])| + EPSG_31979|31979|"SIRGAS 2000 / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, 2.15]|Some([166021.789, 3359262.29, 833978.211, 10237968.738])| + EPSG_3198|3198|"LGD2006 / Libya TM zone 13"|Projected|[24.0, 19.99, 25.21, 32.15]|Some([95358.646, 2211156.96, 221987.784, 3558758.594])| + EPSG_31980|31980|"SIRGAS 2000 / UTM zone 20S"|Projected|[-66.0, -58.39, -60.0, 5.28]|Some([166075.703, 3523957.437, 833924.297, 10584416.969])| + EPSG_31981|31981|"SIRGAS 2000 / UTM zone 21S"|Projected|[-60.0, -44.82, -54.0, 4.51]|Some([166030.501, 5032666.011, 833969.499, 10499186.722])| + EPSG_31982|31982|"SIRGAS 2000 / UTM zone 22S"|Projected|[-54.0, -54.18, -47.99, 7.04]|Some([166064.102, 3992265.895, 835050.048, 10779242.167])| + EPSG_31983|31983|"SIRGAS 2000 / UTM zone 23S"|Projected|[-48.0, -33.5, -42.0, 5.13]|Some([166021.465, 6289251.342, 833978.535, 10567813.561])| + EPSG_31984|31984|"SIRGAS 2000 / UTM zone 24S"|Projected|[-42.0, -26.35, -36.0, 0.74]|Some([166022.788, 7082073.545, 833977.212, 10081905.273])| + EPSG_31985|31985|"SIRGAS 2000 / UTM zone 25S"|Projected|[-36.0, -23.8, -29.99, 4.19]|Some([166032.062, 7364662.724, 835082.195, 10463771.019])| + EPSG_31986|31986|"SIRGAS 1995 / UTM zone 17N"|Projected|[-84.0, 0.9, -78.0, 15.51]|Some([166062.445, 99477.124, 833937.555, 1716990.35])| + EPSG_31987|31987|"SIRGAS 1995 / UTM zone 18N"|Projected|[-78.0, 0.0, -72.0, 15.04]|Some([166021.443, 0.0, 833978.557, 1664943.162])| + EPSG_31988|31988|"SIRGAS 1995 / UTM zone 19N"|Projected|[-72.0, 0.0, -66.0, 15.64]|Some([166021.443, 0.0, 833978.557, 1731386.661])| + EPSG_31989|31989|"SIRGAS 1995 / UTM zone 20N"|Projected|[-66.0, 0.0, -60.0, 16.75]|Some([166021.443, 0.0, 833978.557, 1854314.139])| + EPSG_3199|3199|"LGD2006 / UTM zone 32N"|Projected|[9.31, 23.51, 12.0, 33.92]|Some([528730.745, 2600026.95, 806450.893, 3757360.307])| + EPSG_31990|31990|"SIRGAS 1995 / UTM zone 21N"|Projected|[-60.0, 0.0, -54.0, 10.7]|Some([166021.443, 0.0, 833978.557, 1184404.993])| + EPSG_31991|31991|"SIRGAS 1995 / UTM zone 22N"|Projected|[-54.0, 0.0, -48.0, 9.24]|Some([166021.443, 0.0, 833978.557, 1022772.853])| + EPSG_31992|31992|"SIRGAS 1995 / UTM zone 17S"|Projected|[-84.0, -56.45, -75.21, 1.45]|Some([166033.106, 3728795.069, 1145366.206, 10161095.968])| + EPSG_31993|31993|"SIRGAS 1995 / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, 0.0]|Some([166021.443, 3415992.79, 835092.849, 10000000.0])| + EPSG_31994|31994|"SIRGAS 1995 / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, 0.0]|Some([166021.443, 3359262.29, 833978.557, 10000000.0])| + EPSG_31995|31995|"SIRGAS 1995 / UTM zone 20S"|Projected|[-66.0, -58.39, -60.0, 0.0]|Some([166021.443, 3523957.437, 833978.557, 10000000.0])| + EPSG_31996|31996|"SIRGAS 1995 / UTM zone 21S"|Projected|[-60.0, -44.82, -54.0, 0.0]|Some([166021.443, 5032666.011, 833978.557, 10000000.0])| + EPSG_31997|31997|"SIRGAS 1995 / UTM zone 22S"|Projected|[-54.0, -39.95, -48.0, 0.0]|Some([166021.443, 5573481.771, 833978.557, 10000000.0])| + EPSG_31998|31998|"SIRGAS 1995 / UTM zone 23S"|Projected|[-48.0, -33.5, -42.0, 0.0]|Some([166021.443, 6289251.342, 833978.557, 10000000.0])| + EPSG_31999|31999|"SIRGAS 1995 / UTM zone 24S"|Projected|[-42.0, -26.35, -36.0, 0.0]|Some([166021.443, 7082073.545, 833978.557, 10000000.0])| + EPSG_3200|3200|"FD58 / Iraq zone"|Projected|[47.13, 26.21, 53.61, 33.22]|Some([1698328.077, 470649.39, 2363578.17, 1278300.613])| + EPSG_32000|32000|"SIRGAS 1995 / UTM zone 25S"|Projected|[-36.0, -20.11, -30.0, 0.0]|Some([166021.443, 7773520.44, 833978.557, 10000000.0])| + EPSG_32001|32001|"NAD27 / Montana North"|Projected|[-116.07, 47.41, -104.04, 49.01]|Some([375240.941, 149639.803, 3351130.057, 800747.312])| + EPSG_32002|32002|"NAD27 / Montana Central"|Projected|[-116.06, 46.17, -104.04, 48.26]|Some([339965.486, 122857.107, 3382537.68, 952176.961])| + EPSG_32003|32003|"NAD27 / Montana South"|Projected|[-114.57, 44.35, -104.04, 46.87]|Some([674609.563, 127693.544, 3427603.615, 1093053.195])| + EPSG_32005|32005|"NAD27 / Nebraska North"|Projected|[-104.06, 41.68, -96.07, 43.01]|Some([891411.723, 126340.236, 3073346.11, 636930.472])| + EPSG_32006|32006|"NAD27 / Nebraska South"|Projected|[-104.06, 39.99, -95.3, 42.01]|Some([722872.024, 117816.218, 3176597.943, 886108.766])| + EPSG_32007|32007|"NAD27 / Nevada East"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([72937.476, 87342.414, 965502.287, 2643897.28])| + EPSG_32008|32008|"NAD27 / Nevada Central"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([49667.262, 454954.03, 996206.501, 2280170.214])| + EPSG_32009|32009|"NAD27 / Nevada West"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([86320.077, 800786.074, 965850.951, 2644106.239])| + EPSG_3201|3201|"LGD2006 / UTM zone 33N"|Projected|[12.0, 22.51, 18.0, 35.23]|Some([191407.531, 2489289.818, 808696.817, 3902692.556])| + EPSG_32010|32010|"NAD27 / New Hampshire"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([259683.747, 69252.489, 778571.896, 1025985.135])| + EPSG_32011|32011|"NAD27 / New Jersey"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([1734147.714, 13344.896, 2223865.728, 921786.576])| + EPSG_32012|32012|"NAD27 / New Mexico East"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([70288.151, 363677.282, 916586.532, 2186193.158])| + EPSG_32013|32013|"NAD27 / New Mexico Central"|Projected|[-107.73, 31.78, -104.83, 37.0]|Some([40280.289, 283651.02, 941416.045, 2186586.992])| + EPSG_32014|32014|"NAD27 / New Mexico West"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([117179.076, 119974.617, 972705.849, 2186779.466])| + EPSG_32015|32015|"NAD27 / New York East"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([74952.743, 320582.865, 804983.09, 1833171.008])| + EPSG_32016|32016|"NAD27 / New York Central"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([182750.571, 724999.054, 914017.309, 1610666.872])| + EPSG_32017|32017|"NAD27 / New York West"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([177331.702, 724997.549, 832479.782, 1328669.611])| + EPSG_32019|32019|"NAD27 / North Carolina"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([381696.784, 29071.401, 3099270.096, 1075651.027])| + EPSG_3202|3202|"LGD2006 / UTM zone 34N"|Projected|[17.99, 19.5, 24.01, 35.03]|Some([184069.262, 2156161.443, 815986.504, 3880527.468])| + EPSG_32020|32020|"NAD27 / North Dakota North"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([1112068.378, 54734.973, 2913029.989, 754235.112])| + EPSG_32021|32021|"NAD27 / North Dakota South"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([1097030.681, 96054.797, 3004886.025, 813403.592])| + EPSG_32022|32022|"NAD27 / Ohio North"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([1353753.358, 157865.503, 2556661.004, 978667.591])| + EPSG_32023|32023|"NAD27 / Ohio South"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([1332222.682, 145669.627, 2515827.253, 867956.351])| + EPSG_32024|32024|"NAD27 / Oklahoma North"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([508095.686, 98283.5, 3068602.552, 769269.326])| + EPSG_32025|32025|"NAD27 / Oklahoma South"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([1391211.254, 104283.802, 3089849.569, 832854.673])| + EPSG_32026|32026|"NAD27 / Oregon North"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([1033961.971, 103373.463, 3061326.442, 971003.488])| + EPSG_32027|32027|"NAD27 / Oregon South"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([885806.593, 114254.297, 2978944.791, 1080787.474])| + EPSG_32028|32028|"NAD27 / Pennsylvania North"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([1228013.129, 157884.887, 2846754.732, 875620.503])| + EPSG_3203|3203|"LGD2006 / UTM zone 35N"|Projected|[24.0, 19.99, 26.21, 33.6]|Some([186056.225, 2210577.691, 426707.126, 3721854.834])| + EPSG_32030|32030|"NAD27 / Rhode Island"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([403437.695, 17008.295, 615563.131, 341587.873])| + EPSG_32031|32031|"NAD27 / South Carolina North"|Projected|[-83.36, 33.46, -78.52, 35.21]|Some([1280154.467, 167352.93, 2756325.56, 813275.95])| + EPSG_32033|32033|"NAD27 / South Carolina South"|Projected|[-82.03, 32.05, -78.95, 33.95]|Some([1680785.431, 78762.771, 2635131.907, 776133.927])| + EPSG_32034|32034|"NAD27 / South Dakota North"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([932032.297, 111817.112, 2931831.384, 797761.0])| + EPSG_32035|32035|"NAD27 / South Dakota South"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([995026.86, 53480.348, 3052824.864, 919288.371])| + EPSG_32037|32037|"NAD27 / Texas North"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([1537999.684, 109158.338, 2456219.277, 913415.497])| + EPSG_32038|32038|"NAD27 / Texas North Central"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([268655.153, 19366.937, 3088380.143, 1104425.562])| + EPSG_32039|32039|"NAD27 / Texas Central"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([-6145.595, 41189.985, 4166858.88, 1011688.905])| + EPSG_3204|3204|"WGS 84 / SCAR IMW SP19-20"|Projected|[-72.0, -64.0, -60.0, -60.0]|Some([-334433.763, 3162494.191, 334433.763, 3621814.609])| + EPSG_32040|32040|"NAD27 / Texas South Central"|Projected|[-105.0, 27.78, -93.41, 30.67]|Some([60356.222, -19452.038, 3807413.032, 1079789.505])| + EPSG_32041|32041|"NAD27 / Texas South"|Projected|[-100.2, 25.83, -95.36, 28.21]|Some([1440866.973, 59249.45, 3032969.525, 936989.531])| + EPSG_32042|32042|"NAD27 / Utah North"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([1294425.205, 78965.588, 2683782.203, 621017.009])| + EPSG_32043|32043|"NAD27 / Utah Central"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([1270291.228, 57084.034, 2704384.385, 1010550.273])| + EPSG_32044|32044|"NAD27 / Utah South"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([1255495.953, 117737.714, 2718656.971, 706656.261])| + EPSG_32045|32045|"NAD27 / Vermont"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([247266.815, 80185.612, 768591.116, 923774.274])| + EPSG_32046|32046|"NAD27 / Virginia North"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([1548984.061, 37611.204, 2575137.486, 659184.265])| + EPSG_32047|32047|"NAD27 / Virginia South"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([478827.404, 75239.459, 2936948.411, 749568.192])| + EPSG_32048|32048|"NAD27 / Washington North"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([1014803.054, 29260.019, 2950105.954, 772291.699])| + EPSG_32049|32049|"NAD27 / Washington South"|Projected|[-124.4, 45.54, -116.91, 47.96]|Some([1001274.468, 75447.101, 2919948.235, 981663.11])| + EPSG_3205|3205|"WGS 84 / SCAR IMW SP21-22"|Projected|[-60.0, -64.0, -48.0, -60.0]|Some([-334433.763, 3162494.191, 334433.763, 3621814.609])| + EPSG_32050|32050|"NAD27 / West Virginia North"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([1355495.514, 94685.288, 2507514.072, 787382.652])| + EPSG_32051|32051|"NAD27 / West Virginia South"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([1519348.856, 72803.559, 2567907.785, 796015.979])| + EPSG_32052|32052|"NAD27 / Wisconsin North"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([1257396.112, 74150.553, 2501169.083, 794688.218])| + EPSG_32053|32053|"NAD27 / Wisconsin Central"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([1239405.075, 53473.828, 2986848.937, 739103.736])| + EPSG_32054|32054|"NAD27 / Wisconsin South"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([1614256.954, 174955.546, 2822677.742, 863849.469])| + EPSG_32055|32055|"NAD27 / Wyoming East"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([179011.664, 117808.237, 808428.888, 1585071.033])| + EPSG_32056|32056|"NAD27 / Wyoming East Central"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([142221.357, 117812.153, 868262.054, 1585753.179])| + EPSG_32057|32057|"NAD27 / Wyoming West Central"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([-137525.239, 117819.485, 845268.54, 1591438.128])| + EPSG_32058|32058|"NAD27 / Wyoming West"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([230585.506, 117819.427, 788226.283, 1460704.976])| + EPSG_3206|3206|"WGS 84 / SCAR IMW SP23-24"|Projected|[-48.0, -64.0, -36.0, -60.0]|Some([-334433.763, 3162494.191, 334433.763, 3621814.609])| + EPSG_32064|32064|"NAD27 / BLM 14N (ftUS)"|Projected|[-102.0, 25.83, -95.87, 49.01]|Some([653820.8, 9372219.648, 2670010.677, 17825006.159])| + EPSG_32065|32065|"NAD27 / BLM 15N (ftUS)"|Projected|[-96.01, 25.61, -89.86, 49.38]|Some([648650.982, 9292282.238, 2675152.227, 17959988.363])| + EPSG_32066|32066|"NAD27 / BLM 16N (ftUS)"|Projected|[-90.01, 23.95, -83.91, 48.32]|Some([635291.436, 8689256.434, 2672265.547, 17573009.168])| + EPSG_32067|32067|"NAD27 / BLM 17N (ftUS)"|Projected|[-84.09, 23.81, -77.99, 46.13]|Some([607393.553, 8638411.785, 2646569.342, 16774628.213])| + EPSG_3207|3207|"WGS 84 / SCAR IMW SQ01-02"|Projected|[-180.0, -68.0, -168.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_3208|3208|"WGS 84 / SCAR IMW SQ19-20"|Projected|[-72.0, -68.0, -60.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32081|32081|"NAD27 / MTM zone 1"|Projected|[-54.5, 46.56, -52.54, 49.89]|Some([189716.586, 5157604.084, 339989.59, 5528923.508])| + EPSG_32082|32082|"NAD27 / MTM zone 2"|Projected|[-57.5, 46.81, -54.49, 54.71]|Some([190253.554, 5185389.56, 419967.154, 6065168.377])| + EPSG_32083|32083|"NAD27 / MTM zone 3"|Projected|[-59.48, 47.5, -57.5, 50.54]|Some([230906.388, 5262089.491, 380070.639, 5600610.864])| + EPSG_32084|32084|"NAD27 / MTM zone 4"|Projected|[-63.0, 52.0, -60.0, 58.92]|Some([201716.978, 5762555.724, 407746.535, 6533859.539])| + EPSG_32085|32085|"NAD27 / MTM zone 5"|Projected|[-66.0, 51.58, -63.0, 60.52]|Some([200763.007, 5715826.789, 408692.885, 6712059.81])| + EPSG_32086|32086|"NAD27 / MTM zone 6"|Projected|[-67.81, 52.05, -66.0, 55.34]|Some([283471.224, 5768112.632, 407624.219, 6135274.031])| + EPSG_3209|3209|"WGS 84 / SCAR IMW SQ21-22"|Projected|[-60.0, -68.0, -48.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32098|32098|"NAD27 / Quebec Lambert"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([-893309.91, 110374.64, 897136.009, 2112778.885])| + EPSG_32099|32099|"NAD27 / Louisiana Offshore"|Projected|[-94.05, 28.85, -88.75, 33.03]|Some([1130032.494, 1157176.175, 2827350.693, 2690429.381])| + EPSG_3210|3210|"WGS 84 / SCAR IMW SQ37-38"|Projected|[36.0, -68.0, 48.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32100|32100|"NAD83 / Montana"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([76530.54, 11129.352, 1035187.319, 549126.161])| + EPSG_32104|32104|"NAD83 / Nebraska"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([153376.225, 17396.267, 901213.811, 363144.755])| + EPSG_32107|32107|"NAD83 / Nevada East"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([69754.746, 8026623.097, 341810.023, 8805865.426])| + EPSG_32108|32108|"NAD83 / Nevada Central"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([362659.338, 6138670.888, 651166.06, 6694999.823])| + EPSG_32109|32109|"NAD83 / Nevada West"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([673826.63, 4244079.443, 941909.004, 4805926.331])| + EPSG_3211|3211|"WGS 84 / SCAR IMW SQ39-40"|Projected|[48.0, -68.0, 60.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32110|32110|"NAD83 / New Hampshire"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([226793.802, 21105.444, 384952.066, 312732.672])| + EPSG_32111|32111|"NAD83 / New Jersey"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([54545.12, 4070.111, 203801.357, 281108.052])| + EPSG_32112|32112|"NAD83 / New Mexico East"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([33975.771, 110868.236, 291929.385, 666375.071])| + EPSG_32113|32113|"NAD83 / New Mexico Central"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([359824.279, 86475.321, 633545.173, 666494.112])| + EPSG_32114|32114|"NAD83 / New Mexico West"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([713259.079, 36585.48, 974025.425, 666551.877])| + EPSG_32115|32115|"NAD83 / New York East"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([34535.945, 227223.262, 257035.813, 688009.811])| + EPSG_32116|32116|"NAD83 / New York Central"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([153330.974, 220983.306, 376222.785, 490949.206])| + EPSG_32117|32117|"NAD83 / New York West"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([251673.766, 220983.468, 451364.453, 404996.19])| + EPSG_32118|32118|"NAD83 / New York Long Island"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([277950.229, 33683.766, 486555.644, 128170.646])| + EPSG_32119|32119|"NAD83 / North Carolina"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([116361.135, 8875.962, 944684.034, 327875.984])| + EPSG_3212|3212|"WGS 84 / SCAR IMW SQ41-42"|Projected|[60.0, -68.0, 72.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32120|32120|"NAD83 / North Dakota North"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([329319.593, 16678.577, 878256.827, 229887.853])| + EPSG_32121|32121|"NAD83 / North Dakota South"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([324736.074, 29272.934, 906254.759, 247922.521])| + EPSG_32122|32122|"NAD83 / Ohio North"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([403035.411, 48121.536, 769684.568, 298304.008])| + EPSG_32123|32123|"NAD83 / Ohio South"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([396472.805, 44406.796, 757238.344, 264561.547])| + EPSG_32124|32124|"NAD83 / Oklahoma North"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([145234.111, 29966.761, 925682.539, 234484.772])| + EPSG_32125|32125|"NAD83 / Oklahoma South"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([414409.72, 31799.907, 932158.693, 253870.049])| + EPSG_32126|32126|"NAD83 / Oregon North"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([2205462.606, 31489.15, 2823406.45, 295944.206])| + EPSG_32127|32127|"NAD83 / Oregon South"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([1160304.687, 34807.774, 1798296.348, 329408.434])| + EPSG_32128|32128|"NAD83 / Pennsylvania North"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([364722.698, 48125.726, 858118.947, 266893.215])| + EPSG_32129|32129|"NAD83 / Pennsylvania South"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([361633.135, 41825.709, 859794.669, 209415.985])| + EPSG_3213|3213|"WGS 84 / SCAR IMW SQ43-44"|Projected|[72.0, -68.0, 84.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32130|32130|"NAD83 / Rhode Island"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([70610.778, 5182.622, 135267.079, 104115.195])| + EPSG_32133|32133|"NAD83 / South Carolina"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([386694.594, 24032.757, 843837.456, 377206.795])| + EPSG_32134|32134|"NAD83 / South Dakota North"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([274445.551, 34078.149, 883988.887, 243155.392])| + EPSG_32135|32135|"NAD83 / South Dakota South"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([293645.541, 16297.864, 920867.071, 280198.165])| + EPSG_32136|32136|"NAD83 / Tennessee"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([206556.417, 71749.383, 997092.638, 269011.428])| + EPSG_32137|32137|"NAD83 / Texas North"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([59141.726, 1033282.718, 339017.133, 1278422.106])| + EPSG_32138|32138|"NAD83 / Texas North Central"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([166935.124, 2005915.123, 1026435.587, 2332223.008])| + EPSG_32139|32139|"NAD83 / Texas Central"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([88485.924, 3012579.293, 1360427.427, 3308389.708])| + EPSG_3214|3214|"WGS 84 / SCAR IMW SQ45-46"|Projected|[84.0, -68.0, 96.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32140|32140|"NAD83 / Texas South Central"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([8759.289, 3994088.534, 1116403.98, 4329153.767])| + EPSG_32141|32141|"NAD83 / Texas South"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([129543.463, 5018097.381, 465443.4, 5282925.792])| + EPSG_32142|32142|"NAD83 / Utah North"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([284873.767, 1024061.481, 708352.443, 1189279.766])| + EPSG_32143|32143|"NAD83 / Utah Central"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([277517.684, 2017395.213, 714632.015, 2308013.497])| + EPSG_32144|32144|"NAD83 / Utah South"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([273008.072, 3035885.945, 718982.33, 3215389.407])| + EPSG_32145|32145|"NAD83 / Vermont"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([423006.838, 24438.295, 581907.658, 281578.897])| + EPSG_32146|32146|"NAD83 / Virginia North"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([3362553.326, 2011470.121, 3675327.323, 2200927.06])| + EPSG_32147|32147|"NAD83 / Virginia South"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([3036366.981, 1022941.909, 3785608.14, 1228478.938])| + EPSG_32148|32148|"NAD83 / Washington North"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([199622.139, 8897.31, 789505.514, 235374.47])| + EPSG_32149|32149|"NAD83 / Washington South"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([195499.391, 22976.053, 780314.152, 260319.122])| + EPSG_3215|3215|"WGS 84 / SCAR IMW SQ47-48"|Projected|[96.0, -68.0, 108.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32150|32150|"NAD83 / West Virginia North"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([403574.778, 28865.245, 754712.786, 240001.069])| + EPSG_32151|32151|"NAD83 / West Virginia South"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([453513.498, 22198.636, 773116.784, 242635.52])| + EPSG_32152|32152|"NAD83 / Wisconsin North"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([373644.539, 22599.495, 752749.553, 242221.119])| + EPSG_32153|32153|"NAD83 / Wisconsin Central"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([368160.829, 16297.624, 900785.954, 225280.703])| + EPSG_32154|32154|"NAD83 / Wisconsin South"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([482416.586, 53326.791, 850746.174, 263304.749])| + EPSG_32155|32155|"NAD83 / Wyoming East"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([102113.109, 54410.863, 293960.125, 501634.091])| + EPSG_32156|32156|"NAD83 / Wyoming East Central"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([290893.723, 154410.763, 512191.622, 601840.614])| + EPSG_32157|32157|"NAD83 / Wyoming West Central"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([405623.049, 54412.076, 705179.483, 503572.395])| + EPSG_32158|32158|"NAD83 / Wyoming West"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([717820.124, 154411.136, 887789.509, 563723.822])| + EPSG_32159|32159|"NAD83 / Wyoming Lambert"|Projected|[-111.06, 40.99, -104.05, 45.01]|Some([200521.354, 198890.974, 790230.38, 651249.118])| + EPSG_3216|3216|"WGS 84 / SCAR IMW SQ49-50"|Projected|[108.0, -68.0, 120.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32161|32161|"NAD83 / Puerto Rico & Virgin Is."|Projected|[-68.49, 14.92, -63.88, 21.86]|Some([-21637.44, -122596.613, 475157.774, 647872.53])| + EPSG_32164|32164|"NAD83 / BLM 14N (ftUS)"|Projected|[-102.0, 25.83, -95.87, 49.01]|Some([653707.206, 9372879.038, 2669909.564, 17825713.903])| + EPSG_32165|32165|"NAD83 / BLM 15N (ftUS)"|Projected|[-96.01, 25.61, -89.86, 49.38]|Some([648590.723, 9292949.147, 2675105.445, 17960732.599])| + EPSG_32166|32166|"NAD83 / BLM 16N (ftUS)"|Projected|[-90.01, 23.95, -83.91, 48.32]|Some([635288.654, 8689915.157, 2672276.314, 17573728.479])| + EPSG_32167|32167|"NAD83 / BLM 17N (ftUS)"|Projected|[-84.09, 23.81, -77.99, 46.13]|Some([607444.622, 8639063.196, 2646628.182, 16775340.563])| + EPSG_3217|3217|"WGS 84 / SCAR IMW SQ51-52"|Projected|[120.0, -68.0, 132.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_3218|3218|"WGS 84 / SCAR IMW SQ53-54"|Projected|[132.0, -68.0, 144.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32181|32181|"NAD83 / MTM zone 1"|Projected|[-54.5, 46.56, -52.54, 49.89]|Some([189795.031, 5157817.897, 340068.384, 5529138.193])| + EPSG_32182|32182|"NAD83 / MTM zone 2"|Projected|[-57.5, 46.81, -54.49, 54.71]|Some([190324.464, 5185606.301, 420038.695, 6065387.551])| + EPSG_32183|32183|"NAD83 / MTM zone 3"|Projected|[-60.0, 47.5, -57.1, 55.38]|Some([191797.059, 5262309.639, 410269.545, 6139944.825])| + EPSG_32184|32184|"NAD83 / MTM zone 4"|Projected|[-63.0, 47.16, -60.0, 58.92]|Some([191069.356, 5224512.09, 418530.644, 6534072.854])| + EPSG_32185|32185|"NAD83 / MTM zone 5"|Projected|[-66.0, 47.95, -63.0, 60.52]|Some([192766.354, 5312336.817, 416833.646, 6712276.997])| + EPSG_32186|32186|"NAD83 / MTM zone 6"|Projected|[-69.0, 47.31, -66.0, 59.0]|Some([191389.906, 5241186.736, 418210.094, 6542982.089])| + EPSG_32187|32187|"NAD83 / MTM zone 7"|Projected|[-72.0, 45.01, -69.0, 61.8]|Some([186562.13, 4985557.091, 423037.87, 6854869.25])| + EPSG_32188|32188|"NAD83 / MTM zone 8"|Projected|[-75.0, 44.98, -72.0, 62.53]|Some([186500.409, 4982223.474, 423099.591, 6936202.617])| + EPSG_32189|32189|"NAD83 / MTM zone 9"|Projected|[-78.0, 43.63, -75.0, 62.65]|Some([183757.001, 4832228.905, 425842.999, 6949573.254])| + EPSG_3219|3219|"WGS 84 / SCAR IMW SQ55-56"|Projected|[144.0, -68.0, 156.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32190|32190|"NAD83 / MTM zone 10"|Projected|[-81.0, 42.26, -78.0, 62.45]|Some([181042.039, 4680048.538, 428557.961, 6927288.98])| + EPSG_32191|32191|"NAD83 / MTM zone 11"|Projected|[-83.6, 41.67, -81.0, 46.0]|Some([213203.307, 4614522.598, 429705.359, 5096670.435])| + EPSG_32192|32192|"NAD83 / MTM zone 12"|Projected|[-82.5, 46.0, -79.5, 55.21]|Some([188617.115, 5095576.318, 420982.885, 6121023.073])| + EPSG_32193|32193|"NAD83 / MTM zone 13"|Projected|[-85.5, 46.0, -82.5, 55.59]|Some([188617.115, 5095576.318, 420982.885, 6163319.436])| + EPSG_32194|32194|"NAD83 / MTM zone 14"|Projected|[-88.5, 47.17, -85.5, 56.7]|Some([191090.702, 5225623.719, 418509.298, 6286883.372])| + EPSG_32195|32195|"NAD83 / MTM zone 15"|Projected|[-91.5, 47.97, -88.5, 56.9]|Some([192809.596, 5314560.385, 416790.404, 6309149.351])| + EPSG_32196|32196|"NAD83 / MTM zone 16"|Projected|[-94.5, 48.06, -91.5, 55.2]|Some([193004.354, 5324566.54, 416595.646, 6119910.044])| + EPSG_32197|32197|"NAD83 / MTM zone 17"|Projected|[-95.16, 48.69, -94.5, 53.24]|Some([360875.873, 5394954.536, 415224.583, 5901789.692])| + EPSG_32198|32198|"NAD83 / Quebec Lambert"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([-893275.479, 110383.071, 897177.342, 2112794.117])| + EPSG_32199|32199|"NAD83 / Louisiana Offshore"|Projected|[-94.05, 28.85, -88.75, 33.03]|Some([734819.956, 371218.718, 1252166.937, 838558.25])| + EPSG_3220|3220|"WGS 84 / SCAR IMW SQ57-58"|Projected|[156.0, -68.0, 168.0, -64.0]|Some([-293239.463, 2611639.577, 293239.463, 3069636.822])| + EPSG_32201|32201|"WGS 72 / UTM zone 1N"|Projected|[-180.0, 0.0, -174.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32202|32202|"WGS 72 / UTM zone 2N"|Projected|[-174.0, 0.0, -168.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32203|32203|"WGS 72 / UTM zone 3N"|Projected|[-168.0, 0.0, -162.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32204|32204|"WGS 72 / UTM zone 4N"|Projected|[-162.0, 0.0, -156.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32205|32205|"WGS 72 / UTM zone 5N"|Projected|[-156.0, 0.0, -150.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32206|32206|"WGS 72 / UTM zone 6N"|Projected|[-150.0, 0.0, -144.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32207|32207|"WGS 72 / UTM zone 7N"|Projected|[-144.0, 0.0, -138.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32208|32208|"WGS 72 / UTM zone 8N"|Projected|[-138.0, 0.0, -132.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32209|32209|"WGS 72 / UTM zone 9N"|Projected|[-132.0, 0.0, -126.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_3221|3221|"WGS 84 / SCAR IMW SR13-14"|Projected|[-108.0, -72.0, -96.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32210|32210|"WGS 72 / UTM zone 10N"|Projected|[-126.0, 0.0, -120.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32211|32211|"WGS 72 / UTM zone 11N"|Projected|[-120.0, 0.0, -114.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32212|32212|"WGS 72 / UTM zone 12N"|Projected|[-114.0, 0.0, -108.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32213|32213|"WGS 72 / UTM zone 13N"|Projected|[-108.0, 0.0, -102.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32214|32214|"WGS 72 / UTM zone 14N"|Projected|[-102.0, 0.0, -96.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32215|32215|"WGS 72 / UTM zone 15N"|Projected|[-96.0, 0.0, -90.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32216|32216|"WGS 72 / UTM zone 16N"|Projected|[-90.0, 0.0, -84.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32217|32217|"WGS 72 / UTM zone 17N"|Projected|[-84.0, 0.0, -78.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32218|32218|"WGS 72 / UTM zone 18N"|Projected|[-78.0, 0.0, -72.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32219|32219|"WGS 72 / UTM zone 19N"|Projected|[-72.0, 0.0, -66.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_3222|3222|"WGS 84 / SCAR IMW SR15-16"|Projected|[-96.0, -72.0, -84.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32220|32220|"WGS 72 / UTM zone 20N"|Projected|[-66.0, 0.0, -60.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32221|32221|"WGS 72 / UTM zone 21N"|Projected|[-60.0, 0.0, -54.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32222|32222|"WGS 72 / UTM zone 22N"|Projected|[-54.0, 0.0, -48.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32223|32223|"WGS 72 / UTM zone 23N"|Projected|[-48.0, 0.0, -42.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32224|32224|"WGS 72 / UTM zone 24N"|Projected|[-42.0, 0.0, -36.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32225|32225|"WGS 72 / UTM zone 25N"|Projected|[-36.0, 0.0, -30.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32226|32226|"WGS 72 / UTM zone 26N"|Projected|[-30.0, 0.0, -24.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32227|32227|"WGS 72 / UTM zone 27N"|Projected|[-24.0, 0.0, -18.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32228|32228|"WGS 72 / UTM zone 28N"|Projected|[-18.0, 0.0, -12.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32229|32229|"WGS 72 / UTM zone 29N"|Projected|[-12.0, 0.0, -6.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_3223|3223|"WGS 84 / SCAR IMW SR17-18"|Projected|[-84.0, -72.0, -72.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32230|32230|"WGS 72 / UTM zone 30N"|Projected|[-6.0, 0.0, 0.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32231|32231|"WGS 72 / UTM zone 31N"|Projected|[0.0, 0.0, 6.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32232|32232|"WGS 72 / UTM zone 32N"|Projected|[6.0, 0.0, 12.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32233|32233|"WGS 72 / UTM zone 33N"|Projected|[12.0, 0.0, 18.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32234|32234|"WGS 72 / UTM zone 34N"|Projected|[18.0, 0.0, 24.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32235|32235|"WGS 72 / UTM zone 35N"|Projected|[24.0, 0.0, 30.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32236|32236|"WGS 72 / UTM zone 36N"|Projected|[30.0, 0.0, 36.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32237|32237|"WGS 72 / UTM zone 37N"|Projected|[36.0, 0.0, 42.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32238|32238|"WGS 72 / UTM zone 38N"|Projected|[42.0, 0.0, 48.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32239|32239|"WGS 72 / UTM zone 39N"|Projected|[48.0, 0.0, 54.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_3224|3224|"WGS 84 / SCAR IMW SR19-20"|Projected|[-72.0, -72.0, -60.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32240|32240|"WGS 72 / UTM zone 40N"|Projected|[54.0, 0.0, 60.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32241|32241|"WGS 72 / UTM zone 41N"|Projected|[60.0, 0.0, 66.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32242|32242|"WGS 72 / UTM zone 42N"|Projected|[66.0, 0.0, 72.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32243|32243|"WGS 72 / UTM zone 43N"|Projected|[72.0, 0.0, 78.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32244|32244|"WGS 72 / UTM zone 44N"|Projected|[78.0, 0.0, 84.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32245|32245|"WGS 72 / UTM zone 45N"|Projected|[84.0, 0.0, 90.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32246|32246|"WGS 72 / UTM zone 46N"|Projected|[90.0, 0.0, 96.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32247|32247|"WGS 72 / UTM zone 47N"|Projected|[96.0, 0.0, 102.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32248|32248|"WGS 72 / UTM zone 48N"|Projected|[102.0, 0.0, 108.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32249|32249|"WGS 72 / UTM zone 49N"|Projected|[108.0, 0.0, 114.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_3225|3225|"WGS 84 / SCAR IMW SR27-28"|Projected|[-24.0, -72.0, -12.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32250|32250|"WGS 72 / UTM zone 50N"|Projected|[114.0, 0.0, 120.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32251|32251|"WGS 72 / UTM zone 51N"|Projected|[120.0, 0.0, 126.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32252|32252|"WGS 72 / UTM zone 52N"|Projected|[126.0, 0.0, 132.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32253|32253|"WGS 72 / UTM zone 53N"|Projected|[132.0, 0.0, 138.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32254|32254|"WGS 72 / UTM zone 54N"|Projected|[138.0, 0.0, 144.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32255|32255|"WGS 72 / UTM zone 55N"|Projected|[144.0, 0.0, 150.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32256|32256|"WGS 72 / UTM zone 56N"|Projected|[150.0, 0.0, 156.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32257|32257|"WGS 72 / UTM zone 57N"|Projected|[156.0, 0.0, 162.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32258|32258|"WGS 72 / UTM zone 58N"|Projected|[162.0, 0.0, 168.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_32259|32259|"WGS 72 / UTM zone 59N"|Projected|[168.0, 0.0, 174.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_3226|3226|"WGS 84 / SCAR IMW SR29-30"|Projected|[-12.0, -72.0, 0.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32260|32260|"WGS 72 / UTM zone 60N"|Projected|[174.0, 0.0, 180.0, 84.0]|Some([166004.4, -4.504, 833961.305, 9329002.016])| + EPSG_3227|3227|"WGS 84 / SCAR IMW SR31-32"|Projected|[0.0, -72.0, 12.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_3228|3228|"WGS 84 / SCAR IMW SR33-34"|Projected|[12.0, -72.0, 24.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_3229|3229|"WGS 84 / SCAR IMW SR35-36"|Projected|[24.0, -72.0, 36.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_3230|3230|"WGS 84 / SCAR IMW SR37-38"|Projected|[36.0, -72.0, 48.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32301|32301|"WGS 72 / UTM zone 1S"|Projected|[-180.0, -80.0, -174.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32302|32302|"WGS 72 / UTM zone 2S"|Projected|[-174.0, -80.0, -168.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32303|32303|"WGS 72 / UTM zone 3S"|Projected|[-168.0, -80.0, -162.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32304|32304|"WGS 72 / UTM zone 4S"|Projected|[-162.0, -80.0, -156.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32305|32305|"WGS 72 / UTM zone 5S"|Projected|[-156.0, -80.0, -150.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32306|32306|"WGS 72 / UTM zone 6S"|Projected|[-150.0, -80.0, -144.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32307|32307|"WGS 72 / UTM zone 7S"|Projected|[-144.0, -80.0, -138.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32308|32308|"WGS 72 / UTM zone 8S"|Projected|[-138.0, -80.0, -132.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32309|32309|"WGS 72 / UTM zone 9S"|Projected|[-132.0, -80.0, -126.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_3231|3231|"WGS 84 / SCAR IMW SR39-40"|Projected|[48.0, -72.0, 60.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32310|32310|"WGS 72 / UTM zone 10S"|Projected|[-126.0, -80.0, -120.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32311|32311|"WGS 72 / UTM zone 11S"|Projected|[-120.0, -80.0, -114.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32312|32312|"WGS 72 / UTM zone 12S"|Projected|[-114.0, -80.0, -108.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32313|32313|"WGS 72 / UTM zone 13S"|Projected|[-108.0, -80.0, -102.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32314|32314|"WGS 72 / UTM zone 14S"|Projected|[-102.0, -80.0, -96.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32315|32315|"WGS 72 / UTM zone 15S"|Projected|[-96.0, -80.0, -90.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32316|32316|"WGS 72 / UTM zone 16S"|Projected|[-90.0, -80.0, -84.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32317|32317|"WGS 72 / UTM zone 17S"|Projected|[-84.0, -80.0, -78.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32318|32318|"WGS 72 / UTM zone 18S"|Projected|[-78.0, -80.0, -72.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32319|32319|"WGS 72 / UTM zone 19S"|Projected|[-72.0, -80.0, -66.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_3232|3232|"WGS 84 / SCAR IMW SR41-42"|Projected|[60.0, -72.0, 72.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32320|32320|"WGS 72 / UTM zone 20S"|Projected|[-66.0, -80.0, -60.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32321|32321|"WGS 72 / UTM zone 21S"|Projected|[-60.0, -80.0, -54.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32322|32322|"WGS 72 / UTM zone 22S"|Projected|[-54.0, -80.0, -48.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32323|32323|"WGS 72 / UTM zone 23S"|Projected|[-48.0, -80.0, -42.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32324|32324|"WGS 72 / UTM zone 24S"|Projected|[-42.0, -80.0, -36.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32325|32325|"WGS 72 / UTM zone 25S"|Projected|[-36.0, -80.0, -30.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32326|32326|"WGS 72 / UTM zone 26S"|Projected|[-30.0, -80.0, -24.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32327|32327|"WGS 72 / UTM zone 27S"|Projected|[-24.0, -80.0, -18.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32328|32328|"WGS 72 / UTM zone 28S"|Projected|[-18.0, -80.0, -12.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32329|32329|"WGS 72 / UTM zone 29S"|Projected|[-12.0, -80.0, -6.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_3233|3233|"WGS 84 / SCAR IMW SR43-44"|Projected|[72.0, -72.0, 84.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32330|32330|"WGS 72 / UTM zone 30S"|Projected|[-6.0, -80.0, 0.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32331|32331|"WGS 72 / UTM zone 31S"|Projected|[0.0, -80.0, 6.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32332|32332|"WGS 72 / UTM zone 32S"|Projected|[6.0, -80.0, 12.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32333|32333|"WGS 72 / UTM zone 33S"|Projected|[12.0, -80.0, 18.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32334|32334|"WGS 72 / UTM zone 34S"|Projected|[18.0, -80.0, 24.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32335|32335|"WGS 72 / UTM zone 35S"|Projected|[24.0, -80.0, 30.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32336|32336|"WGS 72 / UTM zone 36S"|Projected|[30.0, -80.0, 36.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32337|32337|"WGS 72 / UTM zone 37S"|Projected|[36.0, -80.0, 42.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32338|32338|"WGS 72 / UTM zone 38S"|Projected|[42.0, -80.0, 48.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32339|32339|"WGS 72 / UTM zone 39S"|Projected|[48.0, -80.0, 54.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_3234|3234|"WGS 84 / SCAR IMW SR45-46"|Projected|[84.0, -72.0, 96.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32340|32340|"WGS 72 / UTM zone 40S"|Projected|[54.0, -80.0, 60.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32341|32341|"WGS 72 / UTM zone 41S"|Projected|[60.0, -80.0, 66.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32342|32342|"WGS 72 / UTM zone 42S"|Projected|[66.0, -80.0, 72.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32343|32343|"WGS 72 / UTM zone 43S"|Projected|[72.0, -80.0, 78.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32344|32344|"WGS 72 / UTM zone 44S"|Projected|[78.0, -80.0, 84.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32345|32345|"WGS 72 / UTM zone 45S"|Projected|[84.0, -80.0, 90.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32346|32346|"WGS 72 / UTM zone 46S"|Projected|[90.0, -80.0, 96.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32347|32347|"WGS 72 / UTM zone 47S"|Projected|[96.0, -80.0, 102.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32348|32348|"WGS 72 / UTM zone 48S"|Projected|[102.0, -80.0, 108.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32349|32349|"WGS 72 / UTM zone 49S"|Projected|[108.0, -80.0, 114.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_3235|3235|"WGS 84 / SCAR IMW SR47-48"|Projected|[96.0, -72.0, 108.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32350|32350|"WGS 72 / UTM zone 50S"|Projected|[114.0, -80.0, 120.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32351|32351|"WGS 72 / UTM zone 51S"|Projected|[120.0, -80.0, 126.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32352|32352|"WGS 72 / UTM zone 52S"|Projected|[126.0, -80.0, 132.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32353|32353|"WGS 72 / UTM zone 53S"|Projected|[132.0, -80.0, 138.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32354|32354|"WGS 72 / UTM zone 54S"|Projected|[138.0, -80.0, 144.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32355|32355|"WGS 72 / UTM zone 55S"|Projected|[144.0, -80.0, 150.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32356|32356|"WGS 72 / UTM zone 56S"|Projected|[150.0, -80.0, 156.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32357|32357|"WGS 72 / UTM zone 57S"|Projected|[156.0, -80.0, 162.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32358|32358|"WGS 72 / UTM zone 58S"|Projected|[162.0, -80.0, 168.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_32359|32359|"WGS 72 / UTM zone 59S"|Projected|[168.0, -80.0, 174.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_3236|3236|"WGS 84 / SCAR IMW SR49-50"|Projected|[108.0, -72.0, 120.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32360|32360|"WGS 72 / UTM zone 60S"|Projected|[174.0, -80.0, 180.0, 0.0]|Some([166004.4, 1116916.775, 833961.305, 9999995.502])| + EPSG_3237|3237|"WGS 84 / SCAR IMW SR51-52"|Projected|[120.0, -72.0, 132.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_3238|3238|"WGS 84 / SCAR IMW SR53-54"|Projected|[132.0, -72.0, 144.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_3239|3239|"WGS 84 / SCAR IMW SR55-56"|Projected|[144.0, -72.0, 156.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_3240|3240|"WGS 84 / SCAR IMW SR57-58"|Projected|[156.0, -72.0, 168.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32401|32401|"WGS 72BE / UTM zone 1N"|Projected|[-180.0, 0.0, -174.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32402|32402|"WGS 72BE / UTM zone 2N"|Projected|[-174.0, 0.0, -168.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32403|32403|"WGS 72BE / UTM zone 3N"|Projected|[-168.0, 0.0, -162.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32404|32404|"WGS 72BE / UTM zone 4N"|Projected|[-162.0, 0.0, -156.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32405|32405|"WGS 72BE / UTM zone 5N"|Projected|[-156.0, 0.0, -150.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32406|32406|"WGS 72BE / UTM zone 6N"|Projected|[-150.0, 0.0, -144.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32407|32407|"WGS 72BE / UTM zone 7N"|Projected|[-144.0, 0.0, -138.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32408|32408|"WGS 72BE / UTM zone 8N"|Projected|[-138.0, 0.0, -132.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32409|32409|"WGS 72BE / UTM zone 9N"|Projected|[-132.0, 0.0, -126.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_3241|3241|"WGS 84 / SCAR IMW SR59-60"|Projected|[168.0, -72.0, 180.0, -68.0]|Some([-250606.576, 2094186.781, 250606.576, 2550585.705])| + EPSG_32410|32410|"WGS 72BE / UTM zone 10N"|Projected|[-126.0, 0.0, -120.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32411|32411|"WGS 72BE / UTM zone 11N"|Projected|[-120.0, 0.0, -114.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32412|32412|"WGS 72BE / UTM zone 12N"|Projected|[-114.0, 0.0, -108.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32413|32413|"WGS 72BE / UTM zone 13N"|Projected|[-108.0, 0.0, -102.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32414|32414|"WGS 72BE / UTM zone 14N"|Projected|[-102.0, 0.0, -96.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32415|32415|"WGS 72BE / UTM zone 15N"|Projected|[-96.0, 0.0, -90.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32416|32416|"WGS 72BE / UTM zone 16N"|Projected|[-90.0, 0.0, -84.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32417|32417|"WGS 72BE / UTM zone 17N"|Projected|[-84.0, 0.0, -78.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32418|32418|"WGS 72BE / UTM zone 18N"|Projected|[-78.0, 0.0, -72.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32419|32419|"WGS 72BE / UTM zone 19N"|Projected|[-72.0, 0.0, -66.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_3242|3242|"WGS 84 / SCAR IMW SS04-06"|Projected|[-162.0, -76.0, -144.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32420|32420|"WGS 72BE / UTM zone 20N"|Projected|[-66.0, 0.0, -60.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32421|32421|"WGS 72BE / UTM zone 21N"|Projected|[-60.0, 0.0, -54.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32422|32422|"WGS 72BE / UTM zone 22N"|Projected|[-54.0, 0.0, -48.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32423|32423|"WGS 72BE / UTM zone 23N"|Projected|[-48.0, 0.0, -42.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32424|32424|"WGS 72BE / UTM zone 24N"|Projected|[-42.0, 0.0, -36.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32425|32425|"WGS 72BE / UTM zone 25N"|Projected|[-36.0, 0.0, -30.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32426|32426|"WGS 72BE / UTM zone 26N"|Projected|[-30.0, 0.0, -24.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32427|32427|"WGS 72BE / UTM zone 27N"|Projected|[-24.0, 0.0, -18.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32428|32428|"WGS 72BE / UTM zone 28N"|Projected|[-18.0, 0.0, -12.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32429|32429|"WGS 72BE / UTM zone 29N"|Projected|[-12.0, 0.0, -6.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_3243|3243|"WGS 84 / SCAR IMW SS07-09"|Projected|[-144.0, -76.0, -126.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32430|32430|"WGS 72BE / UTM zone 30N"|Projected|[-6.0, 0.0, 0.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32431|32431|"WGS 72BE / UTM zone 31N"|Projected|[0.0, 0.0, 6.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32432|32432|"WGS 72BE / UTM zone 32N"|Projected|[6.0, 0.0, 12.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32433|32433|"WGS 72BE / UTM zone 33N"|Projected|[12.0, 0.0, 18.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32434|32434|"WGS 72BE / UTM zone 34N"|Projected|[18.0, 0.0, 24.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32435|32435|"WGS 72BE / UTM zone 35N"|Projected|[24.0, 0.0, 30.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32436|32436|"WGS 72BE / UTM zone 36N"|Projected|[30.0, 0.0, 36.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32437|32437|"WGS 72BE / UTM zone 37N"|Projected|[36.0, 0.0, 42.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32438|32438|"WGS 72BE / UTM zone 38N"|Projected|[42.0, 0.0, 48.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32439|32439|"WGS 72BE / UTM zone 39N"|Projected|[48.0, 0.0, 54.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_3244|3244|"WGS 84 / SCAR IMW SS10-12"|Projected|[-126.0, -76.0, -108.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32440|32440|"WGS 72BE / UTM zone 40N"|Projected|[54.0, 0.0, 60.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32441|32441|"WGS 72BE / UTM zone 41N"|Projected|[60.0, 0.0, 66.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32442|32442|"WGS 72BE / UTM zone 42N"|Projected|[66.0, 0.0, 72.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32443|32443|"WGS 72BE / UTM zone 43N"|Projected|[72.0, 0.0, 78.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32444|32444|"WGS 72BE / UTM zone 44N"|Projected|[78.0, 0.0, 84.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32445|32445|"WGS 72BE / UTM zone 45N"|Projected|[84.0, 0.0, 90.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32446|32446|"WGS 72BE / UTM zone 46N"|Projected|[90.0, 0.0, 96.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32447|32447|"WGS 72BE / UTM zone 47N"|Projected|[96.0, 0.0, 102.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32448|32448|"WGS 72BE / UTM zone 48N"|Projected|[102.0, 0.0, 108.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32449|32449|"WGS 72BE / UTM zone 49N"|Projected|[108.0, 0.0, 114.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_3245|3245|"WGS 84 / SCAR IMW SS13-15"|Projected|[-108.0, -76.0, -90.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32450|32450|"WGS 72BE / UTM zone 50N"|Projected|[114.0, 0.0, 120.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32451|32451|"WGS 72BE / UTM zone 51N"|Projected|[120.0, 0.0, 126.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32452|32452|"WGS 72BE / UTM zone 52N"|Projected|[126.0, 0.0, 132.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32453|32453|"WGS 72BE / UTM zone 53N"|Projected|[132.0, 0.0, 138.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32454|32454|"WGS 72BE / UTM zone 54N"|Projected|[138.0, 0.0, 144.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32455|32455|"WGS 72BE / UTM zone 55N"|Projected|[144.0, 0.0, 150.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32456|32456|"WGS 72BE / UTM zone 56N"|Projected|[150.0, 0.0, 156.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32457|32457|"WGS 72BE / UTM zone 57N"|Projected|[156.0, 0.0, 162.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32458|32458|"WGS 72BE / UTM zone 58N"|Projected|[162.0, 0.0, 168.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_32459|32459|"WGS 72BE / UTM zone 59N"|Projected|[168.0, 0.0, 174.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_3246|3246|"WGS 84 / SCAR IMW SS16-18"|Projected|[-90.0, -76.0, -72.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32460|32460|"WGS 72BE / UTM zone 60N"|Projected|[174.0, 0.0, 180.0, 84.0]|Some([165996.353, -1.902, 833953.257, 9329002.329])| + EPSG_3247|3247|"WGS 84 / SCAR IMW SS19-21"|Projected|[-72.0, -76.0, -54.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_3248|3248|"WGS 84 / SCAR IMW SS25-27"|Projected|[-36.0, -76.0, -18.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_3249|3249|"WGS 84 / SCAR IMW SS28-30"|Projected|[-18.0, -76.0, 0.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_3250|3250|"WGS 84 / SCAR IMW SS31-33"|Projected|[0.0, -76.0, 18.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32501|32501|"WGS 72BE / UTM zone 1S"|Projected|[-180.0, -80.0, -174.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32502|32502|"WGS 72BE / UTM zone 2S"|Projected|[-174.0, -80.0, -168.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32503|32503|"WGS 72BE / UTM zone 3S"|Projected|[-168.0, -80.0, -162.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32504|32504|"WGS 72BE / UTM zone 4S"|Projected|[-162.0, -80.0, -156.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32505|32505|"WGS 72BE / UTM zone 5S"|Projected|[-156.0, -80.0, -150.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32506|32506|"WGS 72BE / UTM zone 6S"|Projected|[-150.0, -80.0, -144.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32507|32507|"WGS 72BE / UTM zone 7S"|Projected|[-144.0, -80.0, -138.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32508|32508|"WGS 72BE / UTM zone 8S"|Projected|[-138.0, -80.0, -132.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32509|32509|"WGS 72BE / UTM zone 9S"|Projected|[-132.0, -80.0, -126.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_3251|3251|"WGS 84 / SCAR IMW SS34-36"|Projected|[18.0, -76.0, 36.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32510|32510|"WGS 72BE / UTM zone 10S"|Projected|[-126.0, -80.0, -120.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32511|32511|"WGS 72BE / UTM zone 11S"|Projected|[-120.0, -80.0, -114.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32512|32512|"WGS 72BE / UTM zone 12S"|Projected|[-114.0, -80.0, -108.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32513|32513|"WGS 72BE / UTM zone 13S"|Projected|[-108.0, -80.0, -102.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32514|32514|"WGS 72BE / UTM zone 14S"|Projected|[-102.0, -80.0, -96.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32515|32515|"WGS 72BE / UTM zone 15S"|Projected|[-96.0, -80.0, -90.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32516|32516|"WGS 72BE / UTM zone 16S"|Projected|[-90.0, -80.0, -84.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32517|32517|"WGS 72BE / UTM zone 17S"|Projected|[-84.0, -80.0, -78.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32518|32518|"WGS 72BE / UTM zone 18S"|Projected|[-78.0, -80.0, -72.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32519|32519|"WGS 72BE / UTM zone 19S"|Projected|[-72.0, -80.0, -66.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_3252|3252|"WGS 84 / SCAR IMW SS37-39"|Projected|[36.0, -76.0, 54.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32520|32520|"WGS 72BE / UTM zone 20S"|Projected|[-66.0, -80.0, -60.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32521|32521|"WGS 72BE / UTM zone 21S"|Projected|[-60.0, -80.0, -54.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32522|32522|"WGS 72BE / UTM zone 22S"|Projected|[-54.0, -80.0, -48.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32523|32523|"WGS 72BE / UTM zone 23S"|Projected|[-48.0, -80.0, -42.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32524|32524|"WGS 72BE / UTM zone 24S"|Projected|[-42.0, -80.0, -36.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32525|32525|"WGS 72BE / UTM zone 25S"|Projected|[-36.0, -80.0, -30.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32526|32526|"WGS 72BE / UTM zone 26S"|Projected|[-30.0, -80.0, -24.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32527|32527|"WGS 72BE / UTM zone 27S"|Projected|[-24.0, -80.0, -18.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32528|32528|"WGS 72BE / UTM zone 28S"|Projected|[-18.0, -80.0, -12.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32529|32529|"WGS 72BE / UTM zone 29S"|Projected|[-12.0, -80.0, -6.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_3253|3253|"WGS 84 / SCAR IMW SS40-42"|Projected|[54.0, -76.0, 72.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32530|32530|"WGS 72BE / UTM zone 30S"|Projected|[-6.0, -80.0, 0.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32531|32531|"WGS 72BE / UTM zone 31S"|Projected|[0.0, -80.0, 6.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32532|32532|"WGS 72BE / UTM zone 32S"|Projected|[6.0, -80.0, 12.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32533|32533|"WGS 72BE / UTM zone 33S"|Projected|[12.0, -80.0, 18.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32534|32534|"WGS 72BE / UTM zone 34S"|Projected|[18.0, -80.0, 24.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32535|32535|"WGS 72BE / UTM zone 35S"|Projected|[24.0, -80.0, 30.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32536|32536|"WGS 72BE / UTM zone 36S"|Projected|[30.0, -80.0, 36.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32537|32537|"WGS 72BE / UTM zone 37S"|Projected|[36.0, -80.0, 42.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32538|32538|"WGS 72BE / UTM zone 38S"|Projected|[42.0, -80.0, 48.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32539|32539|"WGS 72BE / UTM zone 39S"|Projected|[48.0, -80.0, 54.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_3254|3254|"WGS 84 / SCAR IMW SS43-45"|Projected|[72.0, -76.0, 90.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32540|32540|"WGS 72BE / UTM zone 40S"|Projected|[54.0, -80.0, 60.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32541|32541|"WGS 72BE / UTM zone 41S"|Projected|[60.0, -80.0, 66.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32542|32542|"WGS 72BE / UTM zone 42S"|Projected|[66.0, -80.0, 72.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32543|32543|"WGS 72BE / UTM zone 43S"|Projected|[72.0, -80.0, 78.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32544|32544|"WGS 72BE / UTM zone 44S"|Projected|[78.0, -80.0, 84.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32545|32545|"WGS 72BE / UTM zone 45S"|Projected|[84.0, -80.0, 90.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32546|32546|"WGS 72BE / UTM zone 46S"|Projected|[90.0, -80.0, 96.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32547|32547|"WGS 72BE / UTM zone 47S"|Projected|[96.0, -80.0, 102.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32548|32548|"WGS 72BE / UTM zone 48S"|Projected|[102.0, -80.0, 108.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32549|32549|"WGS 72BE / UTM zone 49S"|Projected|[108.0, -80.0, 114.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_3255|3255|"WGS 84 / SCAR IMW SS46-48"|Projected|[90.0, -76.0, 108.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32550|32550|"WGS 72BE / UTM zone 50S"|Projected|[114.0, -80.0, 120.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32551|32551|"WGS 72BE / UTM zone 51S"|Projected|[120.0, -80.0, 126.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32552|32552|"WGS 72BE / UTM zone 52S"|Projected|[126.0, -80.0, 132.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32553|32553|"WGS 72BE / UTM zone 53S"|Projected|[132.0, -80.0, 138.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32554|32554|"WGS 72BE / UTM zone 54S"|Projected|[138.0, -80.0, 144.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32555|32555|"WGS 72BE / UTM zone 55S"|Projected|[144.0, -80.0, 150.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32556|32556|"WGS 72BE / UTM zone 56S"|Projected|[150.0, -80.0, 156.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32557|32557|"WGS 72BE / UTM zone 57S"|Projected|[156.0, -80.0, 162.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32558|32558|"WGS 72BE / UTM zone 58S"|Projected|[162.0, -80.0, 168.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_32559|32559|"WGS 72BE / UTM zone 59S"|Projected|[168.0, -80.0, 174.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_3256|3256|"WGS 84 / SCAR IMW SS49-51"|Projected|[108.0, -76.0, 126.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_32560|32560|"WGS 72BE / UTM zone 60S"|Projected|[174.0, -80.0, 180.0, 0.0]|Some([165996.353, 1116917.157, 833953.257, 9999998.101])| + EPSG_3257|3257|"WGS 84 / SCAR IMW SS52-54"|Projected|[126.0, -76.0, 144.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_3258|3258|"WGS 84 / SCAR IMW SS55-57"|Projected|[144.0, -76.0, 162.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_3259|3259|"WGS 84 / SCAR IMW SS58-60"|Projected|[162.0, -76.0, 180.0, -72.0]|Some([-309460.095, 1592369.796, 309460.095, 2057101.007])| + EPSG_3260|3260|"WGS 84 / SCAR IMW ST01-04"|Projected|[-180.0, -80.0, -156.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32601|32601|"WGS 84 / UTM zone 1N"|Projected|[-180.0, 0.0, -174.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32602|32602|"WGS 84 / UTM zone 2N"|Projected|[-174.0, 0.0, -168.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32603|32603|"WGS 84 / UTM zone 3N"|Projected|[-168.0, 0.0, -162.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32604|32604|"WGS 84 / UTM zone 4N"|Projected|[-162.0, 0.0, -156.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32605|32605|"WGS 84 / UTM zone 5N"|Projected|[-156.0, 0.0, -150.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32606|32606|"WGS 84 / UTM zone 6N"|Projected|[-150.0, 0.0, -144.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32607|32607|"WGS 84 / UTM zone 7N"|Projected|[-144.0, 0.0, -138.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32608|32608|"WGS 84 / UTM zone 8N"|Projected|[-138.0, 0.0, -132.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32609|32609|"WGS 84 / UTM zone 9N"|Projected|[-132.0, 0.0, -126.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_3261|3261|"WGS 84 / SCAR IMW ST05-08"|Projected|[-156.0, -80.0, -132.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32610|32610|"WGS 84 / UTM zone 10N"|Projected|[-126.0, 0.0, -120.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32611|32611|"WGS 84 / UTM zone 11N"|Projected|[-120.0, 0.0, -114.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32612|32612|"WGS 84 / UTM zone 12N"|Projected|[-114.0, 0.0, -108.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32613|32613|"WGS 84 / UTM zone 13N"|Projected|[-108.0, 0.0, -102.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32614|32614|"WGS 84 / UTM zone 14N"|Projected|[-102.0, 0.0, -96.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32615|32615|"WGS 84 / UTM zone 15N"|Projected|[-96.0, 0.0, -90.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32616|32616|"WGS 84 / UTM zone 16N"|Projected|[-90.0, 0.0, -84.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32617|32617|"WGS 84 / UTM zone 17N"|Projected|[-84.0, 0.0, -78.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32618|32618|"WGS 84 / UTM zone 18N"|Projected|[-78.0, 0.0, -72.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32619|32619|"WGS 84 / UTM zone 19N"|Projected|[-72.0, 0.0, -66.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_3262|3262|"WGS 84 / SCAR IMW ST09-12"|Projected|[-132.0, -80.0, -108.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32620|32620|"WGS 84 / UTM zone 20N"|Projected|[-66.0, 0.0, -60.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32621|32621|"WGS 84 / UTM zone 21N"|Projected|[-60.0, 0.0, -54.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32622|32622|"WGS 84 / UTM zone 22N"|Projected|[-54.0, 0.0, -48.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32623|32623|"WGS 84 / UTM zone 23N"|Projected|[-48.0, 0.0, -42.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32624|32624|"WGS 84 / UTM zone 24N"|Projected|[-42.0, 0.0, -36.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32625|32625|"WGS 84 / UTM zone 25N"|Projected|[-36.0, 0.0, -30.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32626|32626|"WGS 84 / UTM zone 26N"|Projected|[-30.0, 0.0, -24.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32627|32627|"WGS 84 / UTM zone 27N"|Projected|[-24.0, 0.0, -18.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32628|32628|"WGS 84 / UTM zone 28N"|Projected|[-18.0, 0.0, -12.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32629|32629|"WGS 84 / UTM zone 29N"|Projected|[-12.01, 0.0, -6.0, 84.01]|Some([164907.151, 0.0, 833978.557, 9330126.13])| + EPSG_3263|3263|"WGS 84 / SCAR IMW ST13-16"|Projected|[-108.0, -80.0, -84.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32630|32630|"WGS 84 / UTM zone 30N"|Projected|[-6.0, 0.0, 0.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32631|32631|"WGS 84 / UTM zone 31N"|Projected|[0.0, 0.0, 6.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32632|32632|"WGS 84 / UTM zone 32N"|Projected|[6.0, 0.0, 12.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32633|32633|"WGS 84 / UTM zone 33N"|Projected|[12.0, 0.0, 18.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32634|32634|"WGS 84 / UTM zone 34N"|Projected|[18.0, 0.0, 24.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32635|32635|"WGS 84 / UTM zone 35N"|Projected|[24.0, 0.0, 30.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32636|32636|"WGS 84 / UTM zone 36N"|Projected|[30.0, 0.0, 36.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32637|32637|"WGS 84 / UTM zone 37N"|Projected|[36.0, 0.0, 42.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32638|32638|"WGS 84 / UTM zone 38N"|Projected|[42.0, 0.0, 48.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32639|32639|"WGS 84 / UTM zone 39N"|Projected|[48.0, 0.0, 54.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_3264|3264|"WGS 84 / SCAR IMW ST17-20"|Projected|[-84.0, -80.0, -60.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32640|32640|"WGS 84 / UTM zone 40N"|Projected|[54.0, 0.0, 60.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32641|32641|"WGS 84 / UTM zone 41N"|Projected|[60.0, 0.0, 66.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32642|32642|"WGS 84 / UTM zone 42N"|Projected|[66.0, 0.0, 72.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32643|32643|"WGS 84 / UTM zone 43N"|Projected|[72.0, 0.0, 78.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32644|32644|"WGS 84 / UTM zone 44N"|Projected|[78.0, 0.0, 84.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32645|32645|"WGS 84 / UTM zone 45N"|Projected|[84.0, 0.0, 90.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32646|32646|"WGS 84 / UTM zone 46N"|Projected|[90.0, 0.0, 96.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32647|32647|"WGS 84 / UTM zone 47N"|Projected|[96.0, 0.0, 102.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32648|32648|"WGS 84 / UTM zone 48N"|Projected|[102.0, 0.0, 108.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32649|32649|"WGS 84 / UTM zone 49N"|Projected|[108.0, 0.0, 114.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_3265|3265|"WGS 84 / SCAR IMW ST21-24"|Projected|[-60.0, -80.0, -36.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32650|32650|"WGS 84 / UTM zone 50N"|Projected|[114.0, 0.0, 120.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32651|32651|"WGS 84 / UTM zone 51N"|Projected|[120.0, 0.0, 126.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32652|32652|"WGS 84 / UTM zone 52N"|Projected|[126.0, 0.0, 132.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32653|32653|"WGS 84 / UTM zone 53N"|Projected|[132.0, 0.0, 138.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32654|32654|"WGS 84 / UTM zone 54N"|Projected|[138.0, 0.0, 144.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32655|32655|"WGS 84 / UTM zone 55N"|Projected|[144.0, 0.0, 150.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32656|32656|"WGS 84 / UTM zone 56N"|Projected|[150.0, 0.0, 156.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32657|32657|"WGS 84 / UTM zone 57N"|Projected|[156.0, 0.0, 162.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32658|32658|"WGS 84 / UTM zone 58N"|Projected|[162.0, 0.0, 168.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32659|32659|"WGS 84 / UTM zone 59N"|Projected|[168.0, 0.0, 174.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_3266|3266|"WGS 84 / SCAR IMW ST25-28"|Projected|[-36.0, -80.0, -12.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32660|32660|"WGS 84 / UTM zone 60N"|Projected|[174.0, 0.0, 180.0, 84.0]|Some([166021.443, 0.0, 833978.557, 9329005.182])| + EPSG_32661|32661|"WGS 84 / UPS North (N,E)"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-1387222.946, -1405880.717, 5387222.946, 5405880.717])| + EPSG_32664|32664|"WGS 84 / BLM 14N (ftUS)"|Projected|[-97.22, 25.97, -95.87, 28.43]|Some([2212363.96, 9427717.154, 2668693.455, 10330775.869])| + EPSG_32665|32665|"WGS 84 / BLM 15N (ftUS)"|Projected|[-96.0, 25.61, -89.86, 29.73]|Some([651887.728, 9292949.986, 2675105.445, 10803777.204])| + EPSG_32666|32666|"WGS 84 / BLM 16N (ftUS)"|Projected|[-90.01, 23.95, -83.91, 30.25]|Some([635288.654, 8689915.158, 2672276.314, 10992529.317])| + EPSG_32667|32667|"WGS 84 / BLM 17N (ftUS)"|Projected|[-84.09, 23.82, -81.17, 29.94]|Some([607523.875, 8642727.6, 1586591.407, 10879747.31])| + EPSG_3267|3267|"WGS 84 / SCAR IMW ST29-32"|Projected|[-12.0, -80.0, 12.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_3268|3268|"WGS 84 / SCAR IMW ST33-36"|Projected|[12.0, -80.0, 36.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_3269|3269|"WGS 84 / SCAR IMW ST37-40"|Projected|[36.0, -80.0, 60.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_3270|3270|"WGS 84 / SCAR IMW ST41-44"|Projected|[60.0, -80.0, 84.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32701|32701|"WGS 84 / UTM zone 1S"|Projected|[-180.0, -80.0, -174.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32702|32702|"WGS 84 / UTM zone 2S"|Projected|[-174.0, -80.0, -168.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32703|32703|"WGS 84 / UTM zone 3S"|Projected|[-168.0, -80.0, -162.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32704|32704|"WGS 84 / UTM zone 4S"|Projected|[-162.0, -80.0, -156.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32705|32705|"WGS 84 / UTM zone 5S"|Projected|[-156.0, -80.0, -150.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32706|32706|"WGS 84 / UTM zone 6S"|Projected|[-150.0, -80.0, -144.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32707|32707|"WGS 84 / UTM zone 7S"|Projected|[-144.0, -80.0, -138.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32708|32708|"WGS 84 / UTM zone 8S"|Projected|[-138.0, -80.0, -132.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32709|32709|"WGS 84 / UTM zone 9S"|Projected|[-132.0, -80.0, -126.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_3271|3271|"WGS 84 / SCAR IMW ST45-48"|Projected|[84.0, -80.0, 108.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32710|32710|"WGS 84 / UTM zone 10S"|Projected|[-126.0, -80.0, -120.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32711|32711|"WGS 84 / UTM zone 11S"|Projected|[-120.0, -80.0, -114.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32712|32712|"WGS 84 / UTM zone 12S"|Projected|[-114.0, -80.0, -108.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32713|32713|"WGS 84 / UTM zone 13S"|Projected|[-108.0, -80.0, -102.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32714|32714|"WGS 84 / UTM zone 14S"|Projected|[-102.0, -80.0, -96.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32715|32715|"WGS 84 / UTM zone 15S"|Projected|[-96.0, -80.0, -90.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32716|32716|"WGS 84 / UTM zone 16S"|Projected|[-90.0, -80.0, -84.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32717|32717|"WGS 84 / UTM zone 17S"|Projected|[-84.0, -80.0, -78.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32718|32718|"WGS 84 / UTM zone 18S"|Projected|[-78.0, -80.0, -72.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32719|32719|"WGS 84 / UTM zone 19S"|Projected|[-72.0, -80.0, -66.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_3272|3272|"WGS 84 / SCAR IMW ST49-52"|Projected|[108.0, -80.0, 132.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32720|32720|"WGS 84 / UTM zone 20S"|Projected|[-66.0, -80.0, -60.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32721|32721|"WGS 84 / UTM zone 21S"|Projected|[-60.0, -80.0, -54.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32722|32722|"WGS 84 / UTM zone 22S"|Projected|[-54.0, -80.0, -48.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32723|32723|"WGS 84 / UTM zone 23S"|Projected|[-48.0, -80.0, -42.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32724|32724|"WGS 84 / UTM zone 24S"|Projected|[-42.0, -80.0, -36.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32725|32725|"WGS 84 / UTM zone 25S"|Projected|[-36.0, -80.0, -30.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32726|32726|"WGS 84 / UTM zone 26S"|Projected|[-30.0, -80.0, -24.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32727|32727|"WGS 84 / UTM zone 27S"|Projected|[-24.0, -80.0, -18.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32728|32728|"WGS 84 / UTM zone 28S"|Projected|[-18.0, -80.0, -12.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32729|32729|"WGS 84 / UTM zone 29S"|Projected|[-12.0, -80.0, -6.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_3273|3273|"WGS 84 / SCAR IMW ST53-56"|Projected|[132.0, -80.0, 156.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32730|32730|"WGS 84 / UTM zone 30S"|Projected|[-6.0, -80.0, 0.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32731|32731|"WGS 84 / UTM zone 31S"|Projected|[0.0, -80.0, 6.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32732|32732|"WGS 84 / UTM zone 32S"|Projected|[6.0, -80.0, 12.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32733|32733|"WGS 84 / UTM zone 33S"|Projected|[12.0, -80.0, 18.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32734|32734|"WGS 84 / UTM zone 34S"|Projected|[18.0, -80.0, 24.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32735|32735|"WGS 84 / UTM zone 35S"|Projected|[24.0, -80.0, 30.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32736|32736|"WGS 84 / UTM zone 36S"|Projected|[30.0, -80.0, 36.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32737|32737|"WGS 84 / UTM zone 37S"|Projected|[36.0, -80.0, 42.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32738|32738|"WGS 84 / UTM zone 38S"|Projected|[42.0, -80.0, 48.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32739|32739|"WGS 84 / UTM zone 39S"|Projected|[48.0, -80.0, 54.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_3274|3274|"WGS 84 / SCAR IMW ST57-60"|Projected|[156.0, -80.0, 180.0, -76.0]|Some([-322031.144, 1112529.574, 322031.144, 1582843.964])| + EPSG_32740|32740|"WGS 84 / UTM zone 40S"|Projected|[54.0, -80.0, 60.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32741|32741|"WGS 84 / UTM zone 41S"|Projected|[60.0, -80.0, 66.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32742|32742|"WGS 84 / UTM zone 42S"|Projected|[66.0, -80.0, 72.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32743|32743|"WGS 84 / UTM zone 43S"|Projected|[72.0, -80.0, 78.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32744|32744|"WGS 84 / UTM zone 44S"|Projected|[78.0, -80.0, 84.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32745|32745|"WGS 84 / UTM zone 45S"|Projected|[84.0, -80.0, 90.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32746|32746|"WGS 84 / UTM zone 46S"|Projected|[90.0, -80.0, 96.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32747|32747|"WGS 84 / UTM zone 47S"|Projected|[96.0, -80.0, 102.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32748|32748|"WGS 84 / UTM zone 48S"|Projected|[102.0, -80.0, 108.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32749|32749|"WGS 84 / UTM zone 49S"|Projected|[108.0, -80.0, 114.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_3275|3275|"WGS 84 / SCAR IMW SU01-05"|Projected|[-180.0, -84.0, -150.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_32750|32750|"WGS 84 / UTM zone 50S"|Projected|[114.0, -80.0, 120.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32751|32751|"WGS 84 / UTM zone 51S"|Projected|[120.0, -80.0, 126.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32752|32752|"WGS 84 / UTM zone 52S"|Projected|[126.0, -80.0, 132.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32753|32753|"WGS 84 / UTM zone 53S"|Projected|[132.0, -80.0, 138.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32754|32754|"WGS 84 / UTM zone 54S"|Projected|[138.0, -80.0, 144.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32755|32755|"WGS 84 / UTM zone 55S"|Projected|[144.0, -80.0, 150.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32756|32756|"WGS 84 / UTM zone 56S"|Projected|[150.0, -80.0, 156.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32757|32757|"WGS 84 / UTM zone 57S"|Projected|[156.0, -80.0, 162.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32758|32758|"WGS 84 / UTM zone 58S"|Projected|[162.0, -80.0, 168.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32759|32759|"WGS 84 / UTM zone 59S"|Projected|[168.0, -80.0, 174.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_3276|3276|"WGS 84 / SCAR IMW SU06-10"|Projected|[-150.0, -84.0, -120.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_32760|32760|"WGS 84 / UTM zone 60S"|Projected|[174.0, -80.0, 180.0, 0.0]|Some([166021.443, 1116915.044, 833978.557, 10000000.0])| + EPSG_32761|32761|"WGS 84 / UPS South (N,E)"|Projected|[-180.0, -90.0, 180.0, -60.0]|Some([-1387222.946, -1405880.717, 5387222.946, 5405880.717])| + EPSG_32766|32766|"WGS 84 / TM 36 SE"|Projected|[32.64, -27.71, 43.03, -10.09]|Some([131661.332, 6915058.182, 1272075.859, 8884634.976])| + EPSG_3277|3277|"WGS 84 / SCAR IMW SU11-15"|Projected|[-120.0, -84.0, -90.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3278|3278|"WGS 84 / SCAR IMW SU16-20"|Projected|[-90.0, -84.0, -60.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3279|3279|"WGS 84 / SCAR IMW SU21-25"|Projected|[-60.0, -84.0, -30.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3280|3280|"WGS 84 / SCAR IMW SU26-30"|Projected|[-30.0, -84.0, 0.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3281|3281|"WGS 84 / SCAR IMW SU31-35"|Projected|[0.0, -84.0, 30.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3282|3282|"WGS 84 / SCAR IMW SU36-40"|Projected|[30.0, -84.0, 60.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3283|3283|"WGS 84 / SCAR IMW SU41-45"|Projected|[60.0, -84.0, 90.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3284|3284|"WGS 84 / SCAR IMW SU46-50"|Projected|[90.0, -84.0, 120.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3285|3285|"WGS 84 / SCAR IMW SU51-55"|Projected|[120.0, -84.0, 150.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3286|3286|"WGS 84 / SCAR IMW SU56-60"|Projected|[150.0, -84.0, 180.0, -80.0]|Some([-287694.156, 643207.34, 287694.156, 1111564.863])| + EPSG_3287|3287|"WGS 84 / SCAR IMW SV01-10"|Projected|[-180.0, -88.0, -120.0, -84.0]|Some([-332948.619, 192078.081, 332948.619, 665897.239])| + EPSG_3288|3288|"WGS 84 / SCAR IMW SV11-20"|Projected|[-120.0, -88.0, -60.0, -84.0]|Some([-332948.619, 192078.081, 332948.619, 665897.239])| + EPSG_3289|3289|"WGS 84 / SCAR IMW SV21-30"|Projected|[-60.0, -88.0, 0.0, -84.0]|Some([-332948.619, 192078.081, 332948.619, 665897.239])| + EPSG_3290|3290|"WGS 84 / SCAR IMW SV31-40"|Projected|[0.0, -88.0, 60.0, -84.0]|Some([-332948.619, 192078.081, 332948.619, 665897.239])| + EPSG_3291|3291|"WGS 84 / SCAR IMW SV41-50"|Projected|[60.0, -88.0, 120.0, -84.0]|Some([-332948.619, 192078.081, 332948.619, 665897.239])| + EPSG_3292|3292|"WGS 84 / SCAR IMW SV51-60"|Projected|[120.0, -88.0, 180.0, -84.0]|Some([-332948.619, 192078.081, 332948.619, 665897.239])| + EPSG_3293|3293|"WGS 84 / SCAR IMW SW01-60"|Projected|[-180.0, -90.0, 180.0, -88.0]|Some([-220577.66, -221792.663, 220577.66, 221792.663])| + EPSG_3294|3294|"WGS 84 / USGS Transantarctic Mountains"|Projected|[149.83, -80.0, 174.01, -68.6]|Some([-497669.809, -247731.835, 491219.487, 1052866.546])| + EPSG_3295|3295|"Guam 1963 / Yap Islands"|Projected|[137.99, 9.39, 138.27, 9.69]|Some([20366.87, 42668.435, 51121.82, 75852.952])| + EPSG_3296|3296|"RGPF / UTM zone 5S"|Projected|[-158.13, -26.7, -150.0, -12.5]|Some([-58078.033, 7036500.849, 826098.421, 8618156.739])| + EPSG_3297|3297|"RGPF / UTM zone 6S"|Projected|[-150.0, -31.2, -144.0, -7.29]|Some([168707.55, 6544354.079, 831291.21, 9194192.971])| + EPSG_3298|3298|"RGPF / UTM zone 7S"|Projected|[-144.0, -31.24, -138.0, -4.52]|Some([167054.503, 6539918.114, 832944.255, 9500394.384])| + EPSG_3299|3299|"RGPF / UTM zone 8S"|Projected|[-138.0, -26.58, -131.97, -5.52]|Some([167562.141, 7056509.693, 835764.107, 9389855.824])| + EPSG_3300|3300|"Estonian Coordinate System of 1992"|Projected|[21.74, 57.52, 28.2, 59.75]|Some([364585.142, 6375277.253, 751542.152, 6631058.746])| + EPSG_3301|3301|"Estonian Coordinate System of 1997"|Projected|[20.37, 57.52, 28.2, 60.0]|Some([282560.67, 6375272.71, 751541.098, 6658861.368])| + EPSG_3302|3302|"IGN63 Hiva Oa / UTM zone 7S"|Projected|[-139.23, -10.08, -138.75, -9.64]|Some([693860.843, 8884882.027, 746731.813, 8933828.486])| + EPSG_3303|3303|"Fatu Iva 72 / UTM zone 7S"|Projected|[-138.75, -10.6, -138.54, -10.36]|Some([746368.51, 8827068.059, 769564.065, 8853794.461])| + EPSG_3304|3304|"Tahiti 79 / UTM zone 6S"|Projected|[-149.7, -17.93, -149.09, -17.44]|Some([213204.267, 8015320.509, 278629.661, 8070392.891])| + EPSG_3305|3305|"Moorea 87 / UTM zone 6S"|Projected|[-150.0, -17.63, -149.73, -17.41]|Some([181262.061, 8048059.278, 210318.119, 8072855.773])| + EPSG_3306|3306|"Maupiti 83 / UTM zone 5S"|Projected|[-152.39, -16.57, -152.14, -16.34]|Some([565056.205, 8167788.381, 591837.068, 8193329.646])| + EPSG_3307|3307|"Nakhl-e Ghanem / UTM zone 39N"|Projected|[51.8, 27.3, 53.01, 28.2]|Some([578513.735, 3019917.197, 698914.402, 3120993.406])| + EPSG_3308|3308|"GDA94 / NSW Lambert"|Projected|[140.99, -37.53, 153.69, -28.15]|Some([8708252.164, 4006187.691, 9958614.108, 5065609.188])| + EPSG_3309|3309|"NAD27 / California Albers"|Projected|[-124.45, 32.53, -114.12, 42.01]|Some([-418517.346, -609244.486, 553056.08, 458493.069])| + EPSG_3310|3310|"NAD83 / California Albers"|Projected|[-124.45, 32.53, -114.12, 42.01]|Some([-418604.909, -609074.348, 552971.381, 458666.86])| + EPSG_3311|3311|"NAD83(HARN) / California Albers"|Projected|[-124.45, 32.53, -114.12, 42.01]|Some([-418603.712, -609074.86, 552972.474, 458666.256])| + EPSG_3312|3312|"CSG67 / UTM zone 21N"|Projected|[-54.45, 4.84, -54.0, 5.69]|Some([782483.724, 535396.175, 832822.366, 629686.065])| + EPSG_3313|3313|"RGFG95 / UTM zone 21N"|Projected|[-54.61, 2.11, -54.0, 5.69]|Some([764721.296, 233423.69, 833753.211, 629799.918])| + EPSG_3316|3316|"Kasai 1953 / Congo TM zone 22"|Projected|[21.5, -7.31, 23.01, -5.31]|Some([444581.372, 9191687.777, 611949.96, 9412946.366])| + EPSG_3317|3317|"Kasai 1953 / Congo TM zone 24"|Projected|[23.0, -6.99, 25.0, -5.01]|Some([389106.489, 9227078.06, 610893.511, 9446115.011])| + EPSG_3318|3318|"IGC 1962 / Congo TM zone 12"|Projected|[12.17, -6.04, 13.01, -4.67]|Some([518818.443, 9332130.899, 612058.483, 9483703.49])| + EPSG_3319|3319|"IGC 1962 / Congo TM zone 14"|Projected|[13.0, -5.91, 15.01, -4.28]|Some([388992.387, 9346506.58, 612117.803, 9526824.122])| + EPSG_3320|3320|"IGC 1962 / Congo TM zone 16"|Projected|[15.0, -5.87, 17.0, -3.29]|Some([388866.252, 9350931.84, 611133.748, 9636276.504])| + EPSG_3321|3321|"IGC 1962 / Congo TM zone 18"|Projected|[17.0, -5.38, 19.0, -3.4]|Some([388878.639, 9405116.282, 611121.361, 9624115.25])| + EPSG_3322|3322|"IGC 1962 / Congo TM zone 20"|Projected|[19.0, -7.29, 21.0, -4.01]|Some([388954.719, 9193901.995, 611045.281, 9556675.03])| + EPSG_3323|3323|"IGC 1962 / Congo TM zone 22"|Projected|[21.0, -7.31, 23.0, -5.31]|Some([389158.567, 9191690.242, 610841.433, 9412946.367])| + EPSG_3324|3324|"IGC 1962 / Congo TM zone 24"|Projected|[23.0, -6.99, 25.0, -5.01]|Some([389106.489, 9227078.06, 610893.511, 9446115.011])| + EPSG_3325|3325|"IGC 1962 / Congo TM zone 26"|Projected|[25.0, -7.26, 27.0, -4.23]|Some([388985.226, 9197219.62, 611014.774, 9532352.085])| + EPSG_3326|3326|"IGC 1962 / Congo TM zone 28"|Projected|[27.0, -7.36, 29.0, -4.24]|Some([388986.652, 9186160.851, 611013.348, 9531246.494])| + EPSG_3327|3327|"IGC 1962 / Congo TM zone 30"|Projected|[29.0, -6.04, 29.64, -4.34]|Some([389001.09, 9332132.942, 460148.978, 9520181.061])| + EPSG_3328|3328|"Pulkovo 1942(58) / GUGiK-80"|Projected|[14.14, 49.0, 24.15, 54.89]|Some([132323.228, 147781.767, 864757.137, 814487.692])| + EPSG_3329|3329|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 5"|Projected|[13.5, 46.54, 16.5, 54.72]|Some([5384939.346, 5156201.335, 5615060.654, 6067237.67])| + EPSG_3330|3330|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 6"|Projected|[16.5, 40.14, 19.5, 54.89]|Some([6372166.553, 4445152.674, 6627963.094, 6086154.621])| + EPSG_3331|3331|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 7"|Projected|[19.5, 39.64, 22.5, 54.51]|Some([7371365.653, 4389640.232, 7628765.829, 6043852.536])| + EPSG_3332|3332|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 8"|Projected|[22.5, 41.24, 25.5, 54.41]|Some([8374252.101, 4567307.504, 8625747.899, 6032716.611])| + EPSG_3333|3333|"Pulkovo 1942(58) / Gauss-Kruger zone 3"|Projected|[12.0, 45.78, 18.0, 54.89]|Some([3266689.693, 5071722.51, 3733310.307, 6089252.821])| + EPSG_3334|3334|"Pulkovo 1942(58) / Gauss-Kruger zone 4"|Projected|[18.0, 39.64, 24.0, 54.89]|Some([4242451.755, 4389654.569, 4757548.245, 6089242.049])| + EPSG_3335|3335|"Pulkovo 1942(58) / Gauss-Kruger zone 5"|Projected|[24.0, 41.24, 29.74, 50.93]|Some([5248492.776, 4567309.318, 5729707.627, 5648706.691])| + EPSG_3336|3336|"IGN 1962 Kerguelen / UTM zone 42S"|Projected|[68.69, -49.78, 70.62, -48.6]|Some([477346.975, 4484396.548, 619637.928, 4616835.782])| + EPSG_3337|3337|"Le Pouce 1934 / Mauritius Grid"|Projected|[57.25, -20.57, 57.85, -19.94]|Some([970808.367, 958796.008, 1033620.762, 1028575.73])| + EPSG_3338|3338|"NAD83 / Alaska Albers"|Projected|[172.42, 51.3, -129.99, 71.4]|Some([-2257390.75, 144002.179, 1648473.206, 2681390.481])| + EPSG_3339|3339|"IGCB 1955 / Congo TM zone 12"|Projected|[12.17, -6.04, 13.01, -4.67]|Some([518956.069, 9332237.629, 612194.622, 9483824.309])| + EPSG_3340|3340|"IGCB 1955 / Congo TM zone 14"|Projected|[13.0, -5.91, 15.01, -4.28]|Some([389128.208, 9346615.136, 612249.892, 9526949.316])| + EPSG_3341|3341|"IGCB 1955 / Congo TM zone 16"|Projected|[14.99, -5.87, 16.28, -4.42]|Some([388034.693, 9351039.289, 531204.248, 9511469.877])| + EPSG_3342|3342|"IGCB 1955 / UTM zone 33S"|Projected|[12.17, -6.04, 16.28, -4.28]|Some([185968.63, 9331727.862, 642181.154, 9527091.261])| + EPSG_3343|3343|"Mauritania 1999 / UTM zone 28N"|Projected|[-18.0, 14.72, -12.0, 23.46]|Some([176932.299, 1627357.129, 823067.701, 2597639.473])| + EPSG_3344|3344|"Mauritania 1999 / UTM zone 29N"|Projected|[-12.0, 14.75, -6.0, 27.3]|Some([176976.582, 1630675.186, 823023.418, 3023231.21])| + EPSG_3345|3345|"Mauritania 1999 / UTM zone 30N"|Projected|[-6.0, 15.49, -4.8, 25.74]|Some([178096.802, 1713333.587, 319446.887, 2850315.074])| + EPSG_3346|3346|"LKS94 / Lithuania TM"|Projected|[19.02, 53.89, 26.82, 56.45]|Some([172763.813, 5972478.598, 685350.953, 6268543.172])| + EPSG_3347|3347|"NAD83 / Statistics Canada Lambert"|Projected|[-141.01, 38.21, -40.73, 86.46]|Some([2093069.002, 200142.422, 10436930.85, 5740847.388])| + EPSG_3348|3348|"NAD83(CSRS) / Statistics Canada Lambert"|Projected|[-141.01, 38.21, -40.73, 86.46]|Some([2093069.71, 200141.606, 10436930.919, 5740846.055])| + EPSG_3350|3350|"Pulkovo 1942 / CS63 zone C0"|Projected|[19.57, 54.17, 23.45, 59.27]|Some([94709.617, 5993916.26, 348087.838, 6564249.722])| + EPSG_3351|3351|"Pulkovo 1942 / CS63 zone C1"|Projected|[23.45, 53.89, 26.45, 59.72]|Some([1151507.269, 5962743.219, 1348746.7, 6612905.422])| + EPSG_3352|3352|"Pulkovo 1942 / CS63 zone C2"|Projected|[26.45, 55.15, 28.24, 59.61]|Some([2154496.464, 6102994.085, 2268614.736, 6600646.29])| + EPSG_3353|3353|"Mhast (onshore) / UTM zone 32S"|Projected|[10.53, -6.04, 13.1, -4.38]|Some([669338.355, 9330654.536, 955308.549, 9515690.478])| + EPSG_3354|3354|"Mhast (offshore) / UTM zone 32S"|Projected|[10.53, -6.04, 12.37, -5.05]|Some([669295.546, 9331321.448, 873728.213, 9441712.618])| + EPSG_3355|3355|"Egypt Gulf of Suez S-650 TL / Red Belt"|Projected|[32.34, 27.19, 34.27, 30.01]|Some([744112.888, 499239.488, 938947.857, 815575.799])| + EPSG_3358|3358|"NAD83(HARN) / North Carolina"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([116361.473, 8875.227, 944684.19, 327875.185])| + EPSG_3360|3360|"NAD83(HARN) / South Carolina"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([386694.929, 24032.074, 843837.688, 377206.03])| + EPSG_3361|3361|"NAD83(HARN) / South Carolina (ft)"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([1268684.15, 78845.386, 2768496.35, 1237552.593])| + EPSG_3362|3362|"NAD83(HARN) / Pennsylvania North"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([364722.953, 48124.829, 858119.107, 266892.268])| + EPSG_3363|3363|"NAD83(HARN) / Pennsylvania North (ftUS)"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([1196595.223, 157889.542, 2815345.771, 875629.051])| + EPSG_3364|3364|"NAD83(HARN) / Pennsylvania South"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([361633.39, 41824.831, 859794.826, 209415.067])| + EPSG_3365|3365|"NAD83(HARN) / Pennsylvania South (ftUS)"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([1186458.881, 137220.299, 2820843.524, 687055.932])| + EPSG_3367|3367|"IGN Astro 1960 / UTM zone 28N"|Projected|[-17.08, 14.72, -12.0, 23.46]|Some([276048.278, 1627213.283, 823074.535, 2597423.1])| + EPSG_3368|3368|"IGN Astro 1960 / UTM zone 29N"|Projected|[-12.0, 14.75, -6.0, 27.3]|Some([176969.745, 1630530.519, 823030.255, 3022988.435])| + EPSG_3369|3369|"IGN Astro 1960 / UTM zone 30N"|Projected|[-6.0, 15.49, -4.8, 25.74]|Some([178089.874, 1713182.296, 319441.849, 2850082.589])| + EPSG_3370|3370|"NAD27 / UTM zone 59N"|Projected|[167.65, 49.01, 174.01, 56.28]|Some([255033.105, 5428349.274, 720107.948, 6242074.993])| + EPSG_3371|3371|"NAD27 / UTM zone 60N"|Projected|[174.0, 47.92, 180.0, 56.67]|Some([275871.01, 5307189.758, 724128.99, 6284462.386])| + EPSG_3372|3372|"NAD83 / UTM zone 59N"|Projected|[167.65, 49.01, 174.01, 56.28]|Some([255040.976, 5428568.284, 720100.876, 6242288.35])| + EPSG_3373|3373|"NAD83 / UTM zone 60N"|Projected|[174.0, 47.92, 180.0, 56.67]|Some([275878.052, 5307408.619, 724121.948, 6284675.176])| + EPSG_3374|3374|"FD54 / UTM zone 29N"|Projected|[-7.49, 61.33, -6.33, 62.41]|Some([578320.422, 6800561.997, 643182.078, 6922788.568])| + EPSG_3375|3375|"GDM2000 / Peninsula RSO"|Projected|[98.02, 1.13, 105.82, 7.81]|Some([697.305, 124949.282, 869632.64, 866319.569])| + EPSG_3376|3376|"GDM2000 / East Malaysia BRSO"|Projected|[109.31, 0.85, 119.61, 7.67]|Some([-41827.295, 93993.305, 1105843.152, 852159.239])| + EPSG_3377|3377|"GDM2000 / Johor Grid"|Projected|[102.44, 1.21, 104.6, 2.95]|Some([-124762.759, -92050.876, 115634.076, 100419.65])| + EPSG_3378|3378|"GDM2000 / Sembilan and Melaka Grid"|Projected|[101.7, 2.03, 102.71, 3.28]|Some([-26909.427, -76374.671, 85453.167, 61876.193])| + EPSG_3379|3379|"GDM2000 / Pahang Grid"|Projected|[101.33, 2.45, 103.67, 4.78]|Some([-122846.183, -139408.876, 137404.873, 118376.522])| + EPSG_3380|3380|"GDM2000 / Selangor Grid"|Projected|[100.76, 2.54, 101.97, 3.87]|Some([-104799.786, -70108.468, 29765.341, 76985.881])| + EPSG_3381|3381|"GDM2000 / Terengganu Grid"|Projected|[102.38, 3.89, 103.72, 5.9]|Some([-57071.027, -116750.52, 91755.704, 105567.557])| + EPSG_3382|3382|"GDM2000 / Pinang Grid"|Projected|[100.12, 5.12, 100.56, 5.59]|Some([-24901.945, -33280.625, 23884.497, 18698.573])| + EPSG_3383|3383|"GDM2000 / Kedah and Perlis Grid"|Projected|[99.59, 5.08, 101.12, 6.72]|Some([-116026.958, -97831.109, 53627.253, 83653.646])| + EPSG_3384|3384|"GDM2000 / Perak Grid"|Projected|[100.07, 3.66, 102.0, 5.92]|Some([-82812.375, 861.904, 131598.924, 250917.422])| + EPSG_3385|3385|"GDM2000 / Kelantan Grid"|Projected|[101.33, 4.54, 102.67, 6.29]|Some([-93887.43, -149675.992, 54815.73, 43945.002])| + EPSG_3386|3386|"KKJ / Finland zone 0"|Projected|[19.24, 60.08, 19.5, 60.34]|Some([568673.137, 6663791.813, 583688.008, 6693054.883])| + EPSG_3387|3387|"KKJ / Finland zone 5"|Projected|[31.5, 62.83, 31.59, 63.0]|Some([5423705.808, 6970339.301, 5428707.245, 6989387.437])| + EPSG_3388|3388|"Pulkovo 1942 / Caspian Sea Mercator"|Projected|[46.95, 37.35, 53.93, 46.97]|Some([-335456.583, 3321016.193, 242856.629, 4395625.362])| + EPSG_3389|3389|"Pulkovo 1942 / 3-degree Gauss-Kruger zone 60"|Projected|[178.5, 62.24, -178.5, 71.65]|Some([60421873.935, 6903802.069, 60577821.352, 7953858.471])| + EPSG_3390|3390|"Pulkovo 1995 / 3-degree Gauss-Kruger zone 60"|Projected|[178.5, 62.24, -178.5, 71.65]|Some([60421893.548, 6903812.886, 60577840.917, 7953868.801])| + EPSG_3391|3391|"Karbala 1979 / UTM zone 37N"|Projected|[38.79, 31.14, 42.0, 36.75]|Some([480287.002, 3444855.292, 786346.721, 4071076.952])| + EPSG_3392|3392|"Karbala 1979 / UTM zone 38N"|Projected|[42.0, 29.06, 48.0, 37.39]|Some([208186.005, 3214354.931, 792389.227, 4142083.196])| + EPSG_3393|3393|"Karbala 1979 / UTM zone 39N"|Projected|[48.0, 29.87, 48.61, 31.0]|Some([210481.229, 3306486.392, 272072.365, 3433173.269])| + EPSG_3394|3394|"Nahrwan 1934 / Iraq zone"|Projected|[38.79, 29.06, 51.06, 37.39]|Some([895192.083, 784919.538, 2090167.874, 1724605.456])| + EPSG_3395|3395|"WGS 84 / World Mercator"|Projected|[-180.0, -80.0, 180.0, 84.0]|Some([-20037508.343, -15496570.74, 20037508.343, 18764656.231])| + EPSG_3396|3396|"PD/83 / 3-degree Gauss-Kruger zone 3"|Projected|[9.92, 50.35, 10.5, 51.56]|Some([3563873.92, 5579743.389, 3606833.282, 5715015.141])| + EPSG_3397|3397|"PD/83 / 3-degree Gauss-Kruger zone 4"|Projected|[10.5, 50.2, 12.56, 51.64]|Some([4393011.517, 5562651.382, 4540089.0, 5723911.584])| + EPSG_3398|3398|"RD/83 / 3-degree Gauss-Kruger zone 4"|Projected|[11.89, 50.2, 13.51, 51.66]|Some([4492252.698, 5562650.634, 4607915.595, 5726153.056])| + EPSG_3399|3399|"RD/83 / 3-degree Gauss-Kruger zone 5"|Projected|[13.5, 50.62, 15.04, 51.58]|Some([5393985.823, 5609367.741, 5502969.004, 5717233.673])| + EPSG_3400|3400|"NAD83 / Alberta 10-TM (Forest)"|Projected|[-120.0, 48.99, -109.98, 60.0]|Some([134424.954, 5424172.771, 867036.823, 6659371.69])| + EPSG_3401|3401|"NAD83 / Alberta 10-TM (Resource)"|Projected|[-120.0, 48.99, -109.98, 60.0]|Some([-365575.046, 5424172.771, 367036.823, 6659371.69])| + EPSG_3402|3402|"NAD83(CSRS) / Alberta 10-TM (Forest)"|Projected|[-120.0, 48.99, -109.98, 60.0]|Some([134425.99, 5424172.043, 867037.805, 6659370.893])| + EPSG_3403|3403|"NAD83(CSRS) / Alberta 10-TM (Resource)"|Projected|[-120.0, 48.99, -109.98, 60.0]|Some([-365574.01, 5424172.043, 367037.805, 6659370.893])| + EPSG_3404|3404|"NAD83(HARN) / North Carolina (ftUS)"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([381762.598, 29118.142, 3099351.379, 1075703.836])| + EPSG_3405|3405|"VN-2000 / UTM zone 48N"|Projected|[102.14, 8.33, 108.0, 23.4]|Some([184767.295, 920892.647, 830276.008, 2591097.764])| + EPSG_3406|3406|"VN-2000 / UTM zone 49N"|Projected|[108.0, 10.43, 109.53, 21.56]|Some([171319.709, 1153444.51, 347596.207, 2387232.977])| + EPSG_3407|3407|"Hong Kong 1963 Grid System"|Projected|[113.76, 22.13, 114.51, 22.58]|Some([-9642.965, -3600.154, 244224.615, 160078.49])| + EPSG_3408|3408|"NSIDC EASE-Grid North"|Projected|[-180.0, 0.0, 180.0, 90.0]|Some([-8960917.806, -9010277.047, 8960917.806, 9010277.047])| + EPSG_3409|3409|"NSIDC EASE-Grid South"|Projected|[-180.0, -90.0, 180.0, 0.0]|Some([-8960917.806, -9010277.047, 8960917.806, 9010277.047])| + EPSG_3410|3410|"NSIDC EASE-Grid Global"|Projected|[-180.0, -86.0, 180.0, 86.0]|Some([-17334193.944, -7338939.46, 17334193.944, 7338939.46])| + EPSG_3411|3411|"NSIDC Sea Ice Polar Stereographic North"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-3318676.147, -3318676.147, 3318676.147, 3318676.147])| + EPSG_3412|3412|"NSIDC Sea Ice Polar Stereographic South"|Projected|[-180.0, -90.0, 180.0, -60.0]|Some([-3305025.515, -3323230.519, 3305025.515, 3323230.519])| + EPSG_3413|3413|"WGS 84 / NSIDC Sea Ice Polar Stereographic North"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-3318605.995, -3318605.995, 3318605.995, 3318605.995])| + EPSG_3414|3414|"SVY21 / Singapore TM"|Projected|[103.59, 1.13, 104.07, 1.47]|Some([919.05, 12575.202, 54342.241, 50172.126])| + EPSG_3415|3415|"WGS 72BE / South China Sea Lambert"|Projected|[110.13, 18.31, 116.76, 22.89]|Some([90994.696, 202514.855, 791666.782, 713833.486])| + EPSG_3416|3416|"ETRS89 / Austria Lambert"|Projected|[9.53, 46.4, 17.17, 49.02]|Some([107674.1, 277747.471, 694885.841, 575905.478])| + EPSG_3417|3417|"NAD83 / Iowa North (ftUS)"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([4063169.315, 3408398.93, 5833784.33, 4030976.178])| + EPSG_3418|3418|"NAD83 / Iowa South (ftUS)"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([904757.43, 131163.729, 2576621.138, 760907.307])| + EPSG_3419|3419|"NAD83 / Kansas North (ftUS)"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([151043.784, 67993.624, 2290657.656, 636186.931])| + EPSG_3420|3420|"NAD83 / Kansas South (ftUS)"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([275664.564, 1430077.432, 2451151.942, 2141532.339])| + EPSG_3421|3421|"NAD83 / Nevada East (ftUS)"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([228853.697, 26334012.61, 1121421.717, 28890576.82])| + EPSG_3422|3422|"NAD83 / Nevada Central (ftUS)"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([1189824.846, 20139956.073, 2136367.315, 21965178.587])| + EPSG_3423|3423|"NAD83 / Nevada West (ftUS)"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([2210712.869, 13924117.307, 3090246.456, 15767443.304])| + EPSG_3424|3424|"NAD83 / New Jersey (ftUS)"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([178953.448, 13353.356, 668638.286, 922268.668])| + EPSG_3425|3425|"NAD83(HARN) / Iowa North (ftUS)"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([4063171.439, 3408396.19, 5833786.171, 4030973.336])| + EPSG_3426|3426|"NAD83(HARN) / Iowa South (ftUS)"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([904759.543, 131161.079, 2576622.971, 760904.551])| + EPSG_3427|3427|"NAD83(HARN) / Kansas North (ftUS)"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([151046.304, 67991.181, 2290659.829, 636184.381])| + EPSG_3428|3428|"NAD83(HARN) / Kansas South (ftUS)"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([275667.117, 1430075.094, 2451154.128, 2141529.894])| + EPSG_3429|3429|"NAD83(HARN) / Nevada East (ftUS)"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([228857.264, 26334010.735, 1121425.176, 28890574.68])| + EPSG_3430|3430|"NAD83(HARN) / Nevada Central (ftUS)"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([1189828.453, 20139954.18, 2136370.815, 21965176.518])| + EPSG_3431|3431|"NAD83(HARN) / Nevada West (ftUS)"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([2210716.551, 13924115.422, 3090250.047, 15767441.259])| + EPSG_3432|3432|"NAD83(HARN) / New Jersey (ftUS)"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([178953.899, 13350.496, 668638.635, 922265.633])| + EPSG_3433|3433|"NAD83 / Arkansas North (ftUS)"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([524546.693, 122545.038, 2021958.47, 798918.07])| + EPSG_3434|3434|"NAD83 / Arkansas South (ftUS)"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([552056.037, 1437273.536, 1802862.824, 2206833.911])| + EPSG_3435|3435|"NAD83 / Illinois East (ftUS)"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([710931.478, 143207.513, 1367484.084, 2127542.465])| + EPSG_3436|3436|"NAD83 / Illinois West (ftUS)"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([1901223.844, 110435.231, 2660780.051, 2131284.449])| + EPSG_3437|3437|"NAD83 / New Hampshire (ftUS)"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([744072.667, 69243.444, 1262963.571, 1026023.774])| + EPSG_3438|3438|"NAD83 / Rhode Island (ftUS)"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([231662.195, 17003.318, 443788.743, 341584.603])| + EPSG_3439|3439|"PSD93 / UTM zone 39N"|Projected|[51.99, 16.59, 54.0, 19.67]|Some([603513.273, 1834223.319, 819868.789, 2177492.287])| + EPSG_3440|3440|"PSD93 / UTM zone 40N"|Projected|[54.0, 16.89, 59.91, 26.58]|Some([180075.722, 1867156.436, 809740.894, 2943169.221])| + EPSG_3441|3441|"NAD83(HARN) / Arkansas North (ftUS)"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([524548.732, 122542.74, 2021960.219, 798915.635])| + EPSG_3442|3442|"NAD83(HARN) / Arkansas South (ftUS)"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([552058.083, 1437271.358, 1802864.62, 2206831.573])| + EPSG_3443|3443|"NAD83(HARN) / Illinois East (ftUS)"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([710933.113, 143204.985, 1367485.591, 2127539.571])| + EPSG_3444|3444|"NAD83(HARN) / Illinois West (ftUS)"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([1901225.652, 110432.74, 2660781.715, 2131281.597])| + EPSG_3445|3445|"NAD83(HARN) / New Hampshire (ftUS)"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([744072.888, 69240.275, 1262963.694, 1026020.418])| + EPSG_3446|3446|"NAD83(HARN) / Rhode Island (ftUS)"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([231662.355, 17000.256, 443788.861, 341581.474])| + EPSG_3447|3447|"ETRS89 / Belgian Lambert 2005"|Projected|[2.5, 49.5, 6.4, 51.51]|Some([15655.596, 21908.261, 298149.152, 247440.853])| + EPSG_3448|3448|"JAD2001 / Jamaica Metric Grid"|Projected|[-78.43, 17.64, -76.17, 18.58]|Some([598250.078, 610155.535, 838079.205, 714779.748])| + EPSG_3449|3449|"JAD2001 / UTM zone 17N"|Projected|[-80.6, 14.16, -77.99, 19.36]|Some([542008.861, 1565458.491, 824958.581, 2143416.831])| + EPSG_3450|3450|"JAD2001 / UTM zone 18N"|Projected|[-78.0, 14.08, -74.51, 19.2]|Some([176008.634, 1556574.078, 552897.697, 2125675.23])| + EPSG_3451|3451|"NAD83 / Louisiana North (ftUS)"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([2794382.087, 127326.029, 3795528.039, 924186.327])| + EPSG_3452|3452|"NAD83 / Louisiana South (ftUS)"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([2446298.884, 127290.341, 4107898.796, 943947.993])| + EPSG_3453|3453|"NAD83 / Louisiana Offshore (ftUS)"|Projected|[-94.05, 28.85, -88.75, 33.03]|Some([2410821.807, 1217906.745, 4108151.025, 2751169.858])| + EPSG_3455|3455|"NAD83 / South Dakota South (ftUS)"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([963402.081, 53470.575, 3021211.384, 919283.478])| + EPSG_3456|3456|"NAD83(HARN) / Louisiana North (ftUS)"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([2794384.135, 127324.021, 3795529.879, 924184.159])| + EPSG_3457|3457|"NAD83(HARN) / Louisiana South (ftUS)"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([2446300.924, 127288.476, 4107900.474, 943945.94])| + EPSG_3458|3458|"NAD83(HARN) / South Dakota North (ftUS)"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([900412.705, 111802.026, 2900222.534, 797749.512])| + EPSG_3459|3459|"NAD83(HARN) / South Dakota South (ftUS)"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([963404.701, 53467.965, 3021213.716, 919280.753])| + EPSG_3460|3460|"Fiji 1986 / Fiji Map Grid"|Projected|[176.81, -20.81, -178.15, -12.42]|Some([1789039.749, 3575221.509, 2337149.402, 4506678.996])| + EPSG_3461|3461|"Dabola 1981 / UTM zone 28N"|Projected|[-15.13, 9.01, -12.0, 12.68]|Some([485697.234, 995838.566, 829864.775, 1403505.465])| + EPSG_3462|3462|"Dabola 1981 / UTM zone 29N"|Projected|[-12.0, 7.19, -7.65, 12.51]|Some([168609.676, 794632.926, 649038.483, 1384683.207])| + EPSG_3463|3463|"NAD83 / Maine CS2000 Central"|Projected|[-70.03, 43.75, -68.33, 47.47]|Some([427110.623, 27775.677, 564029.867, 441619.015])| + EPSG_3464|3464|"NAD83(HARN) / Maine CS2000 Central"|Projected|[-70.03, 43.75, -68.33, 47.47]|Some([427110.625, 27774.68, 564029.845, 441617.948])| + EPSG_3465|3465|"NAD83(NSRS2007) / Alabama East"|Projected|[-86.79, 30.99, -84.89, 35.0]|Some([108626.368, 54321.743, 290100.077, 499449.41])| + EPSG_3466|3466|"NAD83(NSRS2007) / Alabama West"|Projected|[-88.48, 30.14, -86.3, 35.02]|Some([505580.446, 15519.0, 715617.187, 557319.387])| + EPSG_3467|3467|"NAD83(NSRS2007) / Alaska Albers"|Projected|[172.42, 51.3, -129.99, 71.4]|Some([-2257390.059, 144002.179, 1648473.206, 2681390.481])| + EPSG_3468|3468|"NAD83(NSRS2007) / Alaska zone 1"|Projected|[-141.0, 54.61, -129.99, 60.35]|Some([345065.001, 308998.437, 1056160.285, 970307.741])| + EPSG_3469|3469|"NAD83(NSRS2007) / Alaska zone 2"|Projected|[-144.01, 59.72, -140.98, 70.16]|Some([386918.31, 636898.364, 557389.032, 1801991.227])| + EPSG_3470|3470|"NAD83(NSRS2007) / Alaska zone 3"|Projected|[-148.0, 59.72, -144.0, 70.38]|Some([387480.793, 636898.322, 612519.207, 1826509.149])| + EPSG_3471|3471|"NAD83(NSRS2007) / Alaska zone 4"|Projected|[-152.01, 59.11, -147.99, 70.63]|Some([384866.006, 568949.725, 615133.994, 1854397.462])| + EPSG_3472|3472|"NAD83(NSRS2007) / Alaska zone 5"|Projected|[-156.0, 55.72, -151.86, 71.28]|Some([374335.663, 191454.933, 634459.395, 1927037.849])| + EPSG_3473|3473|"NAD83(NSRS2007) / Alaska zone 6"|Projected|[-160.0, 54.89, -155.99, 71.4]|Some([371683.471, 99058.912, 628958.023, 1940259.62])| + EPSG_3474|3474|"NAD83(NSRS2007) / Alaska zone 7"|Projected|[-164.01, 54.32, -160.0, 70.74]|Some([369227.276, 35615.004, 630122.198, 1866663.097])| + EPSG_3475|3475|"NAD83(NSRS2007) / Alaska zone 8"|Projected|[-168.26, 54.34, -164.0, 69.05]|Some([353035.906, 37841.059, 630059.06, 1678580.299])| + EPSG_3476|3476|"NAD83(NSRS2007) / Alaska zone 9"|Projected|[-173.16, 56.49, -168.0, 65.82]|Some([305398.633, 277180.772, 623180.037, 1320354.895])| + EPSG_3477|3477|"NAD83(NSRS2007) / Alaska zone 10"|Projected|[172.42, 51.3, -164.84, 54.34]|Some([195688.4, 33405.959, 1775378.814, 432207.768])| + EPSG_3478|3478|"NAD83(NSRS2007) / Arizona Central"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([76951.283, 36584.405, 353893.118, 667599.273])| + EPSG_3479|3479|"NAD83(NSRS2007) / Arizona Central (ft)"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([252464.84, 120027.575, 1161066.659, 2190286.329])| + EPSG_3480|3480|"NAD83(NSRS2007) / Arizona East"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([66481.577, 36584.599, 320581.618, 667693.422])| + EPSG_3481|3481|"NAD83(NSRS2007) / Arizona East (ft)"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([218115.411, 120028.214, 1051776.963, 2190595.217])| + EPSG_3482|3482|"NAD83(NSRS2007) / Arizona West"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([113255.823, 116414.744, 329519.631, 666199.554])| + EPSG_3483|3483|"NAD83(NSRS2007) / Arizona West (ft)"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([371574.223, 381938.138, 1081101.152, 2185694.073])| + EPSG_3484|3484|"NAD83(NSRS2007) / Arkansas North"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([159882.152, 37351.802, 616294.174, 243510.715])| + EPSG_3485|3485|"NAD83(NSRS2007) / Arkansas North (ftUS)"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([524546.693, 122545.038, 2021958.47, 798918.07])| + EPSG_3486|3486|"NAD83(NSRS2007) / Arkansas South"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([168267.017, 438081.85, 549513.688, 672644.321])| + EPSG_3487|3487|"NAD83(NSRS2007) / Arkansas South (ftUS)"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([552056.037, 1437273.536, 1802862.824, 2206833.911])| + EPSG_3488|3488|"NAD83(NSRS2007) / California Albers"|Projected|[-124.45, 32.53, -114.12, 42.01]|Some([-418604.909, -609074.348, 552971.381, 458666.86])| + EPSG_3489|3489|"NAD83(NSRS2007) / California zone 1"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([1789539.567, 528503.854, 2172670.813, 800071.591])| + EPSG_3490|3490|"NAD83(NSRS2007) / California zone 1 (ftUS)"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([5871181.062, 1733933.063, 7128170.827, 2624901.545])| + EPSG_3491|3491|"NAD83(NSRS2007) / California zone 2"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([1819114.258, 539224.713, 2216001.305, 779630.525])| + EPSG_3492|3492|"NAD83(NSRS2007) / California zone 2 (ftUS)"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([5968210.696, 1769106.414, 7270330.95, 2557837.816])| + EPSG_3493|3493|"NAD83(NSRS2007) / California zone 3"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([1774907.68, 525529.067, 2238487.139, 748597.178])| + EPSG_3494|3494|"NAD83(NSRS2007) / California zone 3 (ftUS)"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([5823176.279, 1724173.279, 7344103.224, 2456022.577])| + EPSG_3495|3495|"NAD83(NSRS2007) / California zone 4"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([1727886.776, 549564.135, 2305549.302, 754562.842])| + EPSG_3496|3496|"NAD83(NSRS2007) / California zone 4 (ftUS)"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([5668908.533, 1803028.335, 7564123.003, 2475594.923])| + EPSG_3497|3497|"NAD83(NSRS2007) / California zone 5"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([1679423.165, 417902.407, 2363675.21, 763023.807])| + EPSG_3498|3498|"NAD83(NSRS2007) / California zone 5 (ftUS)"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([5509907.499, 1371068.148, 7754824.418, 2503353.939])| + EPSG_3499|3499|"NAD83(NSRS2007) / California zone 6"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([1821504.087, 540295.778, 2171920.436, 713800.804])| + EPSG_3500|3500|"NAD83(NSRS2007) / California zone 6 (ftUS)"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([5976051.325, 1772620.398, 7125708.965, 2341861.472])| + EPSG_3501|3501|"NAD83(NSRS2007) / Colorado Central"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([602369.994, 338844.895, 1217673.025, 561273.139])| + EPSG_3502|3502|"NAD83(NSRS2007) / Colorado Central (ftUS)"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([1976275.555, 1111693.626, 3994982.248, 1841443.624])| + EPSG_3503|3503|"NAD83(NSRS2007) / Colorado North"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([608530.815, 329968.654, 1211685.384, 496983.93])| + EPSG_3504|3504|"NAD83(NSRS2007) / Colorado North (ftUS)"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([1996488.183, 1082572.159, 3975337.797, 1630521.443])| + EPSG_3505|3505|"NAD83(NSRS2007) / Colorado South"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([597496.398, 339577.268, 1222409.552, 534163.828])| + EPSG_3506|3506|"NAD83(NSRS2007) / Colorado South (ftUS)"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([1960286.099, 1114096.419, 4010522.005, 1752502.494])| + EPSG_3507|3507|"NAD83(NSRS2007) / Connecticut"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([222322.543, 168688.657, 386437.098, 287986.778])| + EPSG_3508|3508|"NAD83(NSRS2007) / Connecticut (ftUS)"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([729403.211, 553439.369, 1267835.713, 944836.621])| + EPSG_3509|3509|"NAD83(NSRS2007) / Delaware"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([166533.162, 48840.047, 238996.166, 205470.483])| + EPSG_3510|3510|"NAD83(NSRS2007) / Delaware (ftUS)"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([546367.548, 160236.055, 784106.588, 674114.41])| + EPSG_3511|3511|"NAD83(NSRS2007) / Florida East"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([65102.243, 8491.398, 304467.201, 720632.506])| + EPSG_3512|3512|"NAD83(NSRS2007) / Florida East (ftUS)"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([213589.61, 27858.86, 998906.144, 2364275.146])| + EPSG_3513|3513|"NAD83(NSRS2007) / Florida GDL Albers"|Projected|[-87.63, 24.41, -79.97, 31.01]|Some([32031.289, 45423.238, 808502.508, 784246.475])| + EPSG_3514|3514|"NAD83(NSRS2007) / Florida North"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([295646.502, 23279.259, 839215.833, 226917.137])| + EPSG_3515|3515|"NAD83(NSRS2007) / Florida North (ftUS)"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([969966.9, 76375.368, 2753327.28, 744477.307])| + EPSG_3516|3516|"NAD83(NSRS2007) / Florida West"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([66150.824, 214526.481, 286899.27, 584280.418])| + EPSG_3517|3517|"NAD83(NSRS2007) / Florida West (ftUS)"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([217029.83, 703825.629, 941268.688, 1916926.67])| + EPSG_3518|3518|"NAD83(NSRS2007) / Georgia East"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([74709.789, 39904.779, 334263.246, 519815.24])| + EPSG_3519|3519|"NAD83(NSRS2007) / Georgia East (ftUS)"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([245110.367, 130920.93, 1096662.0, 1705427.168])| + EPSG_3520|3520|"NAD83(NSRS2007) / Georgia West"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([561618.683, 68725.622, 812812.39, 556485.421])| + EPSG_3521|3521|"NAD83(NSRS2007) / Georgia West (ftUS)"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([1842577.296, 225477.311, 2666701.982, 1825735.918])| + EPSG_3522|3522|"NAD83(NSRS2007) / Idaho Central"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([392281.83, 35910.836, 610204.021, 448964.739])| + EPSG_3523|3523|"NAD83(NSRS2007) / Idaho Central (ftUS)"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([1287011.305, 117817.469, 2001977.692, 1472978.482])| + EPSG_3524|3524|"NAD83(NSRS2007) / Idaho East"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([111063.719, 35911.072, 293355.534, 343148.061])| + EPSG_3525|3525|"NAD83(NSRS2007) / Idaho East (ftUS)"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([364381.553, 117818.241, 962450.615, 1125811.597])| + EPSG_3526|3526|"NAD83(NSRS2007) / Idaho West"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([676539.784, 35910.659, 918488.551, 817141.665])| + EPSG_3527|3527|"NAD83(NSRS2007) / Idaho West (ftUS)"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([2219614.274, 117816.887, 3013407.856, 2680905.614])| + EPSG_3528|3528|"NAD83(NSRS2007) / Illinois East"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([216692.348, 43649.737, 416809.982, 648476.24])| + EPSG_3529|3529|"NAD83(NSRS2007) / Illinois East (ftUS)"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([710931.478, 143207.513, 1367484.084, 2127542.465])| + EPSG_3530|3530|"NAD83(NSRS2007) / Illinois West"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([579494.187, 33660.726, 811007.382, 649616.799])| + EPSG_3531|3531|"NAD83(NSRS2007) / Illinois West (ftUS)"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([1901223.844, 110435.231, 2660780.051, 2131284.449])| + EPSG_3532|3532|"NAD83(NSRS2007) / Indiana East"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([18848.17, 299944.57, 177929.13, 724484.683])| + EPSG_3533|3533|"NAD83(NSRS2007) / Indiana East (ftUS)"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([61837.703, 984068.143, 583755.82, 2376913.498])| + EPSG_3534|3534|"NAD83(NSRS2007) / Indiana West"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([810427.296, 279966.475, 974300.985, 724572.21])| + EPSG_3535|3535|"NAD83(NSRS2007) / Indiana West (ftUS)"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([2658876.887, 918523.342, 3196519.15, 2377200.659])| + EPSG_3536|3536|"NAD83(NSRS2007) / Iowa North"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([1238456.484, 1038882.072, 1778141.02, 1228643.996])| + EPSG_3537|3537|"NAD83(NSRS2007) / Iowa North (ftUS)"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([4063169.315, 3408398.93, 5833784.33, 4030976.178])| + EPSG_3538|3538|"NAD83(NSRS2007) / Iowa South"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([275770.616, 39978.785, 785355.693, 231925.011])| + EPSG_3539|3539|"NAD83(NSRS2007) / Iowa South (ftUS)"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([904757.43, 131163.729, 2576621.138, 760907.307])| + EPSG_3540|3540|"NAD83(NSRS2007) / Kansas North"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([46038.237, 20724.498, 698193.85, 193910.164])| + EPSG_3541|3541|"NAD83(NSRS2007) / Kansas North (ftUS)"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([151043.784, 67993.624, 2290657.656, 636186.931])| + EPSG_3542|3542|"NAD83(NSRS2007) / Kansas South"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([84022.727, 435888.473, 747112.606, 652740.363])| + EPSG_3543|3543|"NAD83(NSRS2007) / Kansas South (ftUS)"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([275664.564, 1430077.432, 2451151.942, 2141532.339])| + EPSG_3544|3544|"NAD83(NSRS2007) / Kentucky North"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([349218.279, 23310.027, 656953.319, 184641.099])| + EPSG_3545|3545|"NAD83(NSRS2007) / Kentucky North (ftUS)"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([1145726.971, 76476.313, 2155354.347, 605776.672])| + EPSG_3546|3546|"NAD83(NSRS2007) / Kentucky Single Zone"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([1157746.949, 1017388.797, 1840462.143, 1319387.434])| + EPSG_3547|3547|"NAD83(NSRS2007) / Kentucky Single Zone (ftUS)"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([3798374.782, 3337883.077, 6038249.548, 4328690.274])| + EPSG_3548|3548|"NAD83(NSRS2007) / Kentucky South"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([157793.092, 517386.113, 840416.218, 710602.335])| + EPSG_3549|3549|"NAD83(NSRS2007) / Kentucky South (ftUS)"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([517692.837, 1697457.607, 2757265.541, 2331367.827])| + EPSG_3550|3550|"NAD83(NSRS2007) / Louisiana North"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([851729.364, 38809.051, 1156879.26, 281692.556])| + EPSG_3551|3551|"NAD83(NSRS2007) / Louisiana North (ftUS)"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([2794382.087, 127326.029, 3795528.039, 924186.327])| + EPSG_3552|3552|"NAD83(NSRS2007) / Louisiana South"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([745633.391, 38798.174, 1252090.057, 287715.924])| + EPSG_3553|3553|"NAD83(NSRS2007) / Louisiana South (ftUS)"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([2446298.884, 127290.341, 4107898.796, 943947.993])| + EPSG_3554|3554|"NAD83(NSRS2007) / Maine CS2000 Central"|Projected|[-70.03, 43.75, -68.33, 47.47]|Some([427110.623, 27775.677, 564029.867, 441619.015])| + EPSG_3555|3555|"NAD83(NSRS2007) / Maine CS2000 East"|Projected|[-68.58, 44.18, -66.91, 47.37]|Some([643626.95, 38518.355, 777163.163, 393521.359])| + EPSG_3556|3556|"NAD83(NSRS2007) / Maine CS2000 West"|Projected|[-71.09, 43.07, -69.6, 46.58]|Some([241765.496, 26291.387, 363121.336, 416635.763])| + EPSG_3557|3557|"NAD83(NSRS2007) / Maine East"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([177049.162, 23701.068, 427772.489, 423896.787])| + EPSG_3558|3558|"NAD83(NSRS2007) / Maine West"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([824761.643, 22958.179, 973880.256, 416752.605])| + EPSG_3559|3559|"NAD83(NSRS2007) / Maryland"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([181219.097, 33674.371, 578370.965, 231963.298])| + EPSG_3560|3560|"NAD83 / Utah North (ftUS)"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([934623.35, 3359775.041, 2323986.308, 3901828.698])| + EPSG_3561|3561|"Old Hawaiian / Hawaii zone 1"|Projected|[-156.1, 18.87, -154.74, 20.33]|Some([291640.858, 14419.591, 761807.24, 545257.219])| + EPSG_3562|3562|"Old Hawaiian / Hawaii zone 2"|Projected|[-157.36, 20.45, -155.93, 21.26]|Some([261706.425, 43503.758, 751253.298, 338302.366])| + EPSG_3563|3563|"Old Hawaiian / Hawaii zone 3"|Projected|[-158.33, 21.2, -157.61, 21.75]|Some([386652.623, 13251.408, 631922.437, 213212.449])| + EPSG_3564|3564|"Old Hawaiian / Hawaii zone 4"|Projected|[-159.85, 21.81, -159.23, 22.29]|Some([380324.875, -7342.539, 590650.385, 167172.653])| + EPSG_3565|3565|"Old Hawaiian / Hawaii zone 5"|Projected|[-160.3, 21.73, -159.99, 22.07]|Some([453795.987, 24141.231, 559017.463, 147692.267])| + EPSG_3566|3566|"NAD83 / Utah Central (ftUS)"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([910489.268, 6618737.46, 2344588.537, 7572207.614])| + EPSG_3567|3567|"NAD83 / Utah South (ftUS)"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([895693.984, 9960235.804, 2358861.194, 10549156.745])| + EPSG_3568|3568|"NAD83(HARN) / Utah North (ftUS)"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([934626.637, 3359772.825, 2323989.436, 3901826.401])| + EPSG_3569|3569|"NAD83(HARN) / Utah Central (ftUS)"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([910492.587, 6618735.334, 2344591.683, 7572205.354])| + EPSG_3570|3570|"NAD83(HARN) / Utah South (ftUS)"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([895697.328, 9960233.747, 2358864.353, 10549154.589])| + EPSG_3571|3571|"WGS 84 / North Pole LAEA Bering Sea"|Projected|[-180.0, 45.0, 180.0, 90.0]|Some([-4862550.515, -4889334.803, 4862550.515, 4889334.803])| + EPSG_3572|3572|"WGS 84 / North Pole LAEA Alaska"|Projected|[-180.0, 45.0, 180.0, 90.0]|Some([-4889334.803, -4862550.515, 4889334.803, 4862550.515])| + EPSG_3573|3573|"WGS 84 / North Pole LAEA Canada"|Projected|[-180.0, 45.0, 180.0, 90.0]|Some([-4886356.352, -4877424.629, 4886356.352, 4877424.629])| + EPSG_3574|3574|"WGS 84 / North Pole LAEA Atlantic"|Projected|[-180.0, 45.0, 180.0, 90.0]|Some([-4886356.352, -4877424.629, 4886356.352, 4877424.629])| + EPSG_3575|3575|"WGS 84 / North Pole LAEA Europe"|Projected|[-180.0, 45.0, 180.0, 90.0]|Some([-4877424.629, -4886356.352, 4877424.629, 4886356.352])| + EPSG_3576|3576|"WGS 84 / North Pole LAEA Russia"|Projected|[-180.0, 45.0, 180.0, 90.0]|Some([-4889334.803, -4862550.515, 4889334.803, 4862550.515])| + EPSG_3577|3577|"GDA94 / Australian Albers"|Projected|[112.85, -43.7, 153.69, -9.86]|Some([-2153255.276, -4928351.64, 2436270.293, -1033174.082])| + EPSG_3578|3578|"NAD83 / Yukon Albers"|Projected|[-141.01, 59.99, -123.91, 69.7]|Some([25598.64, 610029.427, 978833.874, 1715492.359])| + EPSG_3579|3579|"NAD83(CSRS) / Yukon Albers"|Projected|[-141.01, 59.99, -123.91, 69.7]|Some([25599.812, 610028.919, 978835.062, 1715491.868])| + EPSG_3580|3580|"NAD83 / NWT Lambert"|Projected|[-136.46, 59.98, -102.0, 78.81]|Some([-1335122.527, 8119752.583, 557575.849, 10335355.211])| + EPSG_3581|3581|"NAD83(CSRS) / NWT Lambert"|Projected|[-136.46, 59.98, -102.0, 78.81]|Some([-1335121.574, 8119751.74, 557576.787, 10335354.395])| + EPSG_3582|3582|"NAD83(NSRS2007) / Maryland (ftUS)"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([594549.654, 110480.0, 1897538.739, 761032.921])| + EPSG_3583|3583|"NAD83(NSRS2007) / Massachusetts Island"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([465603.809, 21100.837, 551174.59, 56819.463])| + EPSG_3584|3584|"NAD83(NSRS2007) / Massachusetts Island (ftUS)"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([1527568.495, 69228.329, 1808311.967, 186415.188])| + EPSG_3585|3585|"NAD83(NSRS2007) / Massachusetts Mainland"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([32912.329, 801094.12, 337016.003, 961845.933])| + EPSG_3586|3586|"NAD83(NSRS2007) / Massachusetts Mainland (ftUS)"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([107979.867, 2628256.293, 1105693.337, 3155656.2])| + EPSG_3587|3587|"NAD83(NSRS2007) / Michigan Central"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([5783269.684, 53711.284, 6168729.258, 292766.575])| + EPSG_3588|3588|"NAD83(NSRS2007) / Michigan Central (ft)"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([18973981.902, 176218.125, 20238613.052, 960520.258])| + EPSG_3589|3589|"NAD83(NSRS2007) / Michigan North"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([7730770.077, 32977.062, 8280243.766, 399102.506])| + EPSG_3590|3590|"NAD83(NSRS2007) / Michigan North (ft)"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([25363418.889, 108192.459, 27166154.088, 1309391.423])| + EPSG_3591|3591|"NAD83(NSRS2007) / Michigan Oblique Mercator"|Projected|[-90.42, 41.69, -82.13, 48.32]|Some([131754.146, 126622.954, 821906.052, 872458.276])| + EPSG_3592|3592|"NAD83(NSRS2007) / Michigan South"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([3764135.981, 21106.546, 4186207.076, 305975.012])| + EPSG_3593|3593|"NAD83(NSRS2007) / Michigan South (ft)"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([12349527.498, 69247.199, 13734275.183, 1003855.027])| + EPSG_3594|3594|"NAD83(NSRS2007) / Minnesota Central"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([595233.186, 131122.17, 953783.384, 378908.518])| + EPSG_3595|3595|"NAD83(NSRS2007) / Minnesota North"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([484662.883, 115564.992, 1076333.058, 428200.772])| + EPSG_3596|3596|"NAD83(NSRS2007) / Minnesota South"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([569492.513, 154447.097, 1025656.603, 391676.367])| + EPSG_3597|3597|"NAD83(NSRS2007) / Mississippi East"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([190340.121, 56529.852, 371711.704, 611572.95])| + EPSG_3598|3598|"NAD83(NSRS2007) / Mississippi East (ftUS)"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([624474.212, 185465.023, 1219524.147, 2006468.92])| + EPSG_3599|3599|"NAD83(NSRS2007) / Mississippi West"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([574253.773, 166276.994, 791999.89, 611774.795])| + EPSG_3600|3600|"NAD83(NSRS2007) / Mississippi West (ftUS)"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([1884030.919, 545527.105, 2598419.639, 2007131.139])| + EPSG_3601|3601|"NAD83(NSRS2007) / Missouri Central"|Projected|[-93.79, 36.48, -91.41, 40.61]|Some([384402.583, 71750.784, 597674.639, 530978.613])| + EPSG_3602|3602|"NAD83(NSRS2007) / Missouri East"|Projected|[-91.97, 35.98, -89.1, 40.61]|Some([117430.214, 16273.215, 376256.537, 531217.473])| + EPSG_3603|3603|"NAD83(NSRS2007) / Missouri West"|Projected|[-95.77, 36.48, -93.48, 40.59]|Some([736193.986, 34767.346, 941402.47, 491753.126])| + EPSG_3604|3604|"NAD83(NSRS2007) / Montana"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([76530.54, 11129.352, 1035187.319, 549126.161])| + EPSG_3605|3605|"NAD83(NSRS2007) / Montana (ft)"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([251084.45, 36513.623, 3396283.855, 1801595.017])| + EPSG_3606|3606|"NAD83(NSRS2007) / Nebraska"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([153376.225, 17396.267, 901213.811, 363144.755])| + EPSG_3607|3607|"NAD83(NSRS2007) / Nevada Central"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([362659.338, 6138670.888, 651166.06, 6694999.823])| + EPSG_3608|3608|"NAD83(NSRS2007) / Nevada Central (ftUS)"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([1189824.846, 20139956.073, 2136367.315, 21965178.587])| + EPSG_3609|3609|"NAD83(NSRS2007) / Nevada East"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([69754.746, 8026623.097, 341810.023, 8805865.426])| + EPSG_3610|3610|"NAD83(NSRS2007) / Nevada East (ftUS)"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([228853.697, 26334012.61, 1121421.717, 28890576.82])| + EPSG_3611|3611|"NAD83(NSRS2007) / Nevada West"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([673826.63, 4244079.443, 941909.004, 4805926.331])| + EPSG_3612|3612|"NAD83(NSRS2007) / Nevada West (ftUS)"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([2210712.869, 13924117.307, 3090246.456, 15767443.304])| + EPSG_3613|3613|"NAD83(NSRS2007) / New Hampshire"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([226793.802, 21105.444, 384952.066, 312732.672])| + EPSG_3614|3614|"NAD83(NSRS2007) / New Hampshire (ftUS)"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([744072.667, 69243.444, 1262963.571, 1026023.774])| + EPSG_3615|3615|"NAD83(NSRS2007) / New Jersey"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([54545.12, 4070.111, 203801.357, 281108.052])| + EPSG_3616|3616|"NAD83(NSRS2007) / New Jersey (ftUS)"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([178953.448, 13353.356, 668638.286, 922268.668])| + EPSG_3617|3617|"NAD83(NSRS2007) / New Mexico Central"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([359824.279, 86475.321, 633545.173, 666494.112])| + EPSG_3618|3618|"NAD83(NSRS2007) / New Mexico Central (ftUS)"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([1180523.49, 283711.114, 2078556.121, 2186656.1])| + EPSG_3619|3619|"NAD83(NSRS2007) / New Mexico East"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([33975.771, 110868.236, 291929.385, 666375.071])| + EPSG_3620|3620|"NAD83(NSRS2007) / New Mexico East (ftUS)"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([111468.841, 363740.204, 957771.658, 2186265.545])| + EPSG_3621|3621|"NAD83(NSRS2007) / New Mexico West"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([713259.079, 36585.48, 974025.425, 666551.877])| + EPSG_3622|3622|"NAD83(NSRS2007) / New Mexico West (ftUS)"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([2340084.161, 120030.862, 3195615.082, 2186845.616])| + EPSG_3623|3623|"NAD83(NSRS2007) / New York Central"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([153330.974, 220983.306, 376222.785, 490949.206])| + EPSG_3624|3624|"NAD83(NSRS2007) / New York Central (ftUS)"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([503053.371, 725009.395, 1234324.252, 1610722.52])| + EPSG_3625|3625|"NAD83(NSRS2007) / New York East"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([34535.945, 227223.262, 257035.813, 688009.811])| + EPSG_3626|3626|"NAD83(NSRS2007) / New York East (ftUS)"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([113306.679, 745481.654, 843291.664, 2257245.523])| + EPSG_3627|3627|"NAD83(NSRS2007) / New York Long Island"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([277950.229, 33683.766, 486555.644, 128170.646])| + EPSG_3628|3628|"NAD83(NSRS2007) / New York Long Island (ftUS)"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([911908.377, 110510.822, 1596307.976, 420506.527])| + EPSG_3629|3629|"NAD83(NSRS2007) / New York West"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([251673.766, 220983.468, 451364.453, 404996.19])| + EPSG_3630|3630|"NAD83(NSRS2007) / New York West (ftUS)"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([825699.68, 725009.927, 1480851.543, 1328725.001])| + EPSG_3631|3631|"NAD83(NSRS2007) / North Carolina"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([116361.135, 8875.962, 944684.034, 327875.984])| + EPSG_3632|3632|"NAD83(NSRS2007) / North Carolina (ftUS)"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([381761.488, 29120.552, 3099350.865, 1075706.457])| + EPSG_3633|3633|"NAD83(NSRS2007) / North Dakota North"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([329319.593, 16678.577, 878256.827, 229887.853])| + EPSG_3634|3634|"NAD83(NSRS2007) / North Dakota North (ft)"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([1080444.859, 54719.74, 2881420.035, 754225.239])| + EPSG_3635|3635|"NAD83(NSRS2007) / North Dakota South"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([324736.074, 29272.934, 906254.759, 247922.521])| + EPSG_3636|3636|"NAD83(NSRS2007) / North Dakota South (ft)"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([1065407.067, 96039.809, 2973276.77, 813394.098])| + EPSG_3637|3637|"NAD83(NSRS2007) / Ohio North"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([403035.411, 48121.536, 769684.568, 298304.008])| + EPSG_3638|3638|"NAD83(NSRS2007) / Ohio South"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([396472.805, 44406.796, 757238.344, 264561.547])| + EPSG_3639|3639|"NAD83(NSRS2007) / Oklahoma North"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([145234.111, 29966.761, 925682.539, 234484.772])| + EPSG_3640|3640|"NAD83(NSRS2007) / Oklahoma North (ftUS)"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([476488.914, 98315.947, 3037010.129, 769305.456])| + EPSG_3641|3641|"NAD83(NSRS2007) / Oklahoma South"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([414409.72, 31799.907, 932158.693, 253870.049])| + EPSG_3642|3642|"NAD83(NSRS2007) / Oklahoma South (ftUS)"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([1359609.223, 104330.196, 3058257.314, 832905.32])| + EPSG_3643|3643|"NAD83(NSRS2007) / Oregon LCC (m)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([60163.867, 25562.683, 734038.753, 509025.159])| + EPSG_3644|3644|"NAD83(NSRS2007) / Oregon GIC Lambert (ft)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([197388.016, 83867.072, 2408263.626, 1670030.051])| + EPSG_3645|3645|"NAD83(NSRS2007) / Oregon North"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([2205462.606, 31489.15, 2823406.45, 295944.206])| + EPSG_3646|3646|"NAD83(NSRS2007) / Oregon North (ft)"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([7235769.707, 103310.859, 9263144.52, 970945.557])| + EPSG_3647|3647|"NAD83(NSRS2007) / Oregon South"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([1160304.687, 34807.774, 1798296.348, 329408.434])| + EPSG_3648|3648|"NAD83(NSRS2007) / Oregon South (ft)"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([3806773.909, 114198.733, 5899922.402, 1080736.333])| + EPSG_3649|3649|"NAD83(NSRS2007) / Pennsylvania North"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([364722.698, 48125.726, 858118.947, 266893.215])| + EPSG_3650|3650|"NAD83(NSRS2007) / Pennsylvania North (ftUS)"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([1196594.385, 157892.487, 2815345.245, 875632.155])| + EPSG_3651|3651|"NAD83(NSRS2007) / Pennsylvania South"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([361633.135, 41825.709, 859794.669, 209415.985])| + EPSG_3652|3652|"NAD83(NSRS2007) / Pennsylvania South (ftUS)"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([1186458.044, 137223.18, 2820843.01, 687058.943])| + EPSG_3653|3653|"NAD83(NSRS2007) / Rhode Island"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([70610.778, 5182.622, 135267.079, 104115.195])| + EPSG_3654|3654|"NAD83(NSRS2007) / Rhode Island (ftUS)"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([231662.195, 17003.318, 443788.743, 341584.603])| + EPSG_3655|3655|"NAD83(NSRS2007) / South Carolina"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([386694.594, 24032.757, 843837.456, 377206.795])| + EPSG_3656|3656|"NAD83(NSRS2007) / South Carolina (ft)"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([1268683.052, 78847.627, 2768495.59, 1237555.103])| + EPSG_3657|3657|"NAD83(NSRS2007) / South Dakota North"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([274445.551, 34078.149, 883988.887, 243155.392])| + EPSG_3658|3658|"NAD83(NSRS2007) / South Dakota North (ftUS)"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([900410.112, 111804.729, 2900220.208, 797752.314])| + EPSG_3659|3659|"NAD83(NSRS2007) / South Dakota South"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([293645.541, 16297.864, 920867.071, 280198.165])| + EPSG_3660|3660|"NAD83(NSRS2007) / South Dakota South (ftUS)"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([963402.081, 53470.575, 3021211.384, 919283.478])| + EPSG_3661|3661|"NAD83(NSRS2007) / Tennessee"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([206556.417, 71749.383, 997092.638, 269011.428])| + EPSG_3662|3662|"NAD83(NSRS2007) / Tennessee (ftUS)"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([677677.177, 235397.768, 3271294.763, 882581.658])| + EPSG_3663|3663|"NAD83(NSRS2007) / Texas Central"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([88485.924, 3012579.293, 1360427.427, 3308389.708])| + EPSG_3664|3664|"NAD83(NSRS2007) / Texas Central (ftUS)"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([290307.569, 9883770.564, 4463335.649, 10854275.232])| + EPSG_3665|3665|"NAD83(NSRS2007) / Texas Centric Albers Equal Area"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831192.278, 6857764.52, 2152758.974, 8059593.093])| + EPSG_3666|3666|"NAD83(NSRS2007) / Texas Centric Lambert Conformal"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831139.345, 5877072.129, 2152810.743, 7077309.646])| + EPSG_3667|3667|"NAD83(NSRS2007) / Texas North"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([59141.726, 1033282.718, 339017.133, 1278422.106])| + EPSG_3668|3668|"NAD83(NSRS2007) / Texas North (ftUS)"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([194034.147, 3390028.382, 1112258.712, 4194289.858])| + EPSG_3669|3669|"NAD83(NSRS2007) / Texas North Central"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([166935.124, 2005915.123, 1026435.587, 2332223.008])| + EPSG_3670|3670|"NAD83(NSRS2007) / Texas North Central (ftUS)"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([547686.318, 6581073.199, 3367564.088, 7651634.987])| + EPSG_3671|3671|"NAD83(NSRS2007) / Texas South"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([129543.463, 5018097.381, 465443.4, 5282925.792])| + EPSG_3672|3672|"NAD83(NSRS2007) / Texas South (ftUS)"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([425010.511, 16463541.159, 1527042.22, 17332399.035])| + EPSG_3673|3673|"NAD83(NSRS2007) / Texas South Central"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([8759.289, 3994088.534, 1116403.98, 4329153.767])| + EPSG_3674|3674|"NAD83(NSRS2007) / Texas South Central (ftUS)"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([28737.767, 13103938.798, 3662735.393, 14203231.984])| + EPSG_3675|3675|"NAD83(NSRS2007) / Utah Central"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([277517.684, 2017395.213, 714632.015, 2308013.497])| + EPSG_3676|3676|"NAD83(NSRS2007) / Utah Central (ft)"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([910491.089, 6618750.698, 2344593.227, 7572222.758])| + EPSG_3677|3677|"NAD83(NSRS2007) / Utah Central (ftUS)"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([910489.268, 6618737.46, 2344588.537, 7572207.614])| + EPSG_3678|3678|"NAD83(NSRS2007) / Utah North"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([284873.767, 1024061.481, 708352.443, 1189279.766])| + EPSG_3679|3679|"NAD83(NSRS2007) / Utah North (ft)"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([934625.219, 3359781.761, 2323990.957, 3901836.502])| + EPSG_3680|3680|"NAD83(NSRS2007) / Utah North (ftUS)"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([934623.35, 3359775.041, 2323986.308, 3901828.698])| + EPSG_3681|3681|"NAD83(NSRS2007) / Utah South"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([273008.072, 3035885.945, 718982.33, 3215389.407])| + EPSG_3682|3682|"NAD83(NSRS2007) / Utah South (ft)"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([895695.776, 9960255.725, 2358865.912, 10549177.844])| + EPSG_3683|3683|"NAD83(NSRS2007) / Utah South (ftUS)"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([895693.984, 9960235.804, 2358861.194, 10549156.745])| + EPSG_3684|3684|"NAD83(NSRS2007) / Vermont"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([423006.838, 24438.295, 581907.658, 281578.897])| + EPSG_3685|3685|"NAD83(NSRS2007) / Virginia North"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([3362553.326, 2011470.121, 3675327.323, 2200927.06])| + EPSG_3686|3686|"NAD83(NSRS2007) / Virginia North (ftUS)"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([11031977.038, 6599298.223, 12058136.392, 7220874.863])| + EPSG_3687|3687|"NAD83(NSRS2007) / Virginia South"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([3036366.981, 1022941.909, 3785608.14, 1228478.938])| + EPSG_3688|3688|"NAD83(NSRS2007) / Virginia South (ftUS)"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([9961814.003, 3356101.914, 12419949.373, 4030434.647])| + EPSG_3689|3689|"NAD83(NSRS2007) / Washington North"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([199622.139, 8897.31, 789505.514, 235374.47])| + EPSG_3690|3690|"NAD83(NSRS2007) / Washington North (ftUS)"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([654926.968, 29190.59, 2590236.007, 772224.407])| + EPSG_3691|3691|"NAD83(NSRS2007) / Washington South"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([195499.391, 22976.053, 780314.152, 260319.122])| + EPSG_3692|3692|"NAD83(NSRS2007) / Washington South (ftUS)"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([641400.92, 75380.599, 2560080.681, 854063.654])| + EPSG_3693|3693|"NAD83(NSRS2007) / West Virginia North"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([403574.778, 28865.245, 754712.786, 240001.069])| + EPSG_3694|3694|"NAD83(NSRS2007) / West Virginia South"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([453513.498, 22198.636, 773116.784, 242635.52])| + EPSG_3695|3695|"NAD83(NSRS2007) / Wisconsin Central"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([368160.829, 16297.624, 900785.954, 225280.703])| + EPSG_3696|3696|"NAD83(NSRS2007) / Wisconsin Central (ftUS)"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([1207874.319, 53469.789, 2955328.583, 739108.439])| + EPSG_3697|3697|"NAD83(NSRS2007) / Wisconsin North"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([373644.539, 22599.495, 752749.553, 242221.119])| + EPSG_3698|3698|"NAD83(NSRS2007) / Wisconsin North (ftUS)"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([1225865.459, 74145.175, 2469645.826, 794687.121])| + EPSG_3699|3699|"NAD83(NSRS2007) / Wisconsin South"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([482416.586, 53326.791, 850746.174, 263304.749])| + EPSG_3700|3700|"NAD83(NSRS2007) / Wisconsin South (ftUS)"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([1582728.415, 174956.315, 2791156.407, 863858.997])| + EPSG_3701|3701|"NAD83(NSRS2007) / Wisconsin Transverse Mercator"|Projected|[-92.89, 42.48, -86.25, 47.31]|Some([282455.202, 223072.39, 828240.811, 766436.435])| + EPSG_3702|3702|"NAD83(NSRS2007) / Wyoming East"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([102113.109, 54410.863, 293960.125, 501634.091])| + EPSG_3703|3703|"NAD83(NSRS2007) / Wyoming East Central"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([290893.723, 154410.763, 512191.622, 601840.614])| + EPSG_3704|3704|"NAD83(NSRS2007) / Wyoming West Central"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([405623.049, 54412.076, 705179.483, 503572.395])| + EPSG_3705|3705|"NAD83(NSRS2007) / Wyoming West"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([717820.124, 154411.136, 887789.509, 563723.822])| + EPSG_3706|3706|"NAD83(NSRS2007) / UTM zone 59N"|Projected|[167.65, 49.01, 174.01, 56.28]|Some([255040.976, 5428568.284, 720100.876, 6242288.35])| + EPSG_3707|3707|"NAD83(NSRS2007) / UTM zone 60N"|Projected|[174.0, 47.92, 180.0, 56.67]|Some([275878.052, 5307408.619, 724121.948, 6284675.176])| + EPSG_3708|3708|"NAD83(NSRS2007) / UTM zone 1N"|Projected|[-180.0, 47.88, -173.99, 63.21]|Some([275705.201, 5302962.875, 725042.38, 7012536.576])| + EPSG_3709|3709|"NAD83(NSRS2007) / UTM zone 2N"|Projected|[-174.0, 48.66, -167.99, 73.05]|Some([279095.534, 5389660.727, 721640.728, 8108518.215])| + EPSG_3710|3710|"NAD83(NSRS2007) / UTM zone 3N"|Projected|[-168.0, 49.52, -161.99, 74.29]|Some([282881.311, 5485264.341, 717842.313, 8246692.024])| + EPSG_3711|3711|"NAD83(NSRS2007) / UTM zone 4N"|Projected|[-162.0, 50.98, -155.99, 74.71]|Some([289420.598, 5647600.828, 711281.2, 8293495.466])| + EPSG_3712|3712|"NAD83(NSRS2007) / UTM zone 5N"|Projected|[-156.0, 52.15, -149.99, 74.71]|Some([294760.555, 5777721.865, 705923.421, 8293495.466])| + EPSG_3713|3713|"NAD83(NSRS2007) / UTM zone 6N"|Projected|[-150.0, 54.05, -143.99, 74.13]|Some([303614.943, 5989084.721, 697039.487, 8228862.485])| + EPSG_3714|3714|"NAD83(NSRS2007) / UTM zone 7N"|Projected|[-144.0, 53.47, -137.99, 73.59]|Some([300888.495, 5924556.24, 699775.033, 8168689.221])| + EPSG_3715|3715|"NAD83(NSRS2007) / UTM zone 8N"|Projected|[-138.0, 53.6, -131.99, 73.04]|Some([301497.817, 5939018.973, 699163.677, 8107403.958])| + EPSG_3716|3716|"NAD83(NSRS2007) / UTM zone 9N"|Projected|[-132.0, 35.38, -126.0, 56.84]|Some([227475.274, 3915184.943, 772524.726, 6303587.687])| + EPSG_3717|3717|"NAD83(NSRS2007) / UTM zone 10N"|Projected|[-126.0, 30.54, -119.99, 49.09]|Some([212172.222, 3378624.203, 788787.633, 5441824.853])| + EPSG_3718|3718|"NAD83(NSRS2007) / UTM zone 11N"|Projected|[-120.0, 30.88, -114.0, 49.01]|Some([213181.345, 3416303.043, 786818.655, 5432904.306])| + EPSG_3719|3719|"NAD83(NSRS2007) / UTM zone 12N"|Projected|[-114.0, 31.33, -108.0, 49.01]|Some([214532.476, 3466175.159, 785467.524, 5432904.306])| + EPSG_3720|3720|"NAD83(NSRS2007) / UTM zone 13N"|Projected|[-108.0, 28.98, -102.0, 49.01]|Some([207672.606, 3205769.791, 792327.394, 5432904.306])| + EPSG_3721|3721|"NAD83(NSRS2007) / UTM zone 14N"|Projected|[-102.0, 25.83, -96.0, 49.01]|Some([199250.354, 2856857.634, 800749.646, 5432904.306])| + EPSG_3722|3722|"NAD83(NSRS2007) / UTM zone 15N"|Projected|[-96.01, 25.61, -90.0, 49.38]|Some([197690.847, 2832494.974, 801304.224, 5474057.564])| + EPSG_3723|3723|"NAD83(NSRS2007) / UTM zone 16N"|Projected|[-90.0, 23.97, -84.0, 48.32]|Some([194701.986, 2650905.127, 805298.014, 5356218.118])| + EPSG_3724|3724|"NAD83(NSRS2007) / UTM zone 17N"|Projected|[-84.0, 23.81, -78.0, 46.13]|Some([194325.668, 2633191.154, 805674.332, 5112867.417])| + EPSG_3725|3725|"NAD83(NSRS2007) / UTM zone 18N"|Projected|[-78.0, 28.28, -72.0, 45.03]|Some([205723.769, 3128220.038, 794276.231, 4990662.135])| + EPSG_3726|3726|"NAD83(NSRS2007) / UTM zone 19N"|Projected|[-72.0, 33.61, -65.99, 47.47]|Some([221647.209, 3718914.933, 779280.966, 5261788.015])| + EPSG_3727|3727|"Reunion 1947 / TM Reunion"|Projected|[55.16, -21.42, 55.91, -20.81]|Some([121749.224, 17746.236, 199825.782, 85331.543])| + EPSG_3728|3728|"NAD83(NSRS2007) / Ohio North (ftUS)"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([1322292.01, 157878.74, 2525206.786, 978685.731])| + EPSG_3729|3729|"NAD83(NSRS2007) / Ohio South (ftUS)"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([1300761.196, 145691.296, 2484372.802, 867982.343])| + EPSG_3730|3730|"NAD83(NSRS2007) / Wyoming East (ftUS)"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([335016.091, 178512.974, 964434.176, 1645777.845])| + EPSG_3731|3731|"NAD83(NSRS2007) / Wyoming East Central (ftUS)"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([954373.824, 506595.978, 1680415.346, 1974538.747])| + EPSG_3732|3732|"NAD83(NSRS2007) / Wyoming West Central (ftUS)"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([1330781.622, 178516.951, 2313576.353, 1652137.098])| + EPSG_3733|3733|"NAD83(NSRS2007) / Wyoming West (ftUS)"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([2355048.189, 506597.202, 2912689.414, 1849483.907])| + EPSG_3734|3734|"NAD83 / Ohio North (ftUS)"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([1322292.01, 157878.74, 2525206.786, 978685.731])| + EPSG_3735|3735|"NAD83 / Ohio South (ftUS)"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([1300761.196, 145691.296, 2484372.802, 867982.343])| + EPSG_3736|3736|"NAD83 / Wyoming East (ftUS)"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([335016.091, 178512.974, 964434.176, 1645777.845])| + EPSG_3737|3737|"NAD83 / Wyoming East Central (ftUS)"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([954373.824, 506595.978, 1680415.346, 1974538.747])| + EPSG_3738|3738|"NAD83 / Wyoming West Central (ftUS)"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([1330781.622, 178516.951, 2313576.353, 1652137.098])| + EPSG_3739|3739|"NAD83 / Wyoming West (ftUS)"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([2355048.189, 506597.202, 2912689.414, 1849483.907])| + EPSG_3740|3740|"NAD83(HARN) / UTM zone 10N"|Projected|[-124.79, 33.85, -119.99, 49.05]|Some([334398.292, 3745524.372, 778504.759, 5437378.428])| + EPSG_3741|3741|"NAD83(HARN) / UTM zone 11N"|Projected|[-120.0, 32.26, -114.0, 49.01]|Some([217381.686, 3569254.798, 782620.532, 5432903.605])| + EPSG_3742|3742|"NAD83(HARN) / UTM zone 12N"|Projected|[-114.0, 31.33, -108.0, 49.01]|Some([214533.526, 3466174.618, 785468.489, 5432903.533])| + EPSG_3743|3743|"NAD83(HARN) / UTM zone 13N"|Projected|[-108.0, 28.98, -102.0, 49.01]|Some([207673.554, 3205769.267, 792328.238, 5432903.466])| + EPSG_3744|3744|"NAD83(HARN) / UTM zone 14N"|Projected|[-102.0, 25.83, -95.99, 49.01]|Some([199251.187, 2856857.17, 801753.427, 5432932.375])| + EPSG_3745|3745|"NAD83(HARN) / UTM zone 15N"|Projected|[-96.0, 28.42, -89.99, 49.38]|Some([206110.708, 3143728.806, 794870.642, 5474056.6])| + EPSG_3746|3746|"NAD83(HARN) / UTM zone 16N"|Projected|[-90.0, 28.85, -83.99, 48.32]|Some([207307.89, 3191366.516, 793669.135, 5356246.182])| + EPSG_3747|3747|"NAD83(HARN) / UTM zone 17N"|Projected|[-84.01, 24.41, -78.0, 46.13]|Some([194734.682, 2699619.996, 804251.094, 5112895.662])| + EPSG_3748|3748|"NAD83(HARN) / UTM zone 18N"|Projected|[-78.0, 33.84, -72.0, 45.03]|Some([222389.791, 3744414.978, 777610.445, 4990661.14])| + EPSG_3749|3749|"NAD83(HARN) / UTM zone 19N"|Projected|[-72.0, 40.96, -66.91, 47.47]|Some([247526.536, 4534317.165, 675883.92, 5261757.805])| + EPSG_3750|3750|"NAD83(HARN) / UTM zone 4N"|Projected|[-160.3, 19.51, -155.99, 22.29]|Some([363588.225, 2157259.407, 815938.21, 2468018.556])| + EPSG_3751|3751|"NAD83(HARN) / UTM zone 5N"|Projected|[-156.0, 18.87, -154.74, 20.86]|Some([183890.923, 2087343.709, 318974.866, 2309565.702])| + EPSG_3753|3753|"NAD83(HARN) / Ohio North (ftUS)"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([1322293.228, 157875.902, 2525207.778, 978682.749])| + EPSG_3754|3754|"NAD83(HARN) / Ohio South (ftUS)"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([1300762.415, 145688.579, 2484373.79, 867979.494])| + EPSG_3755|3755|"NAD83(HARN) / Wyoming East (ftUS)"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([335018.951, 178510.566, 964436.952, 1645775.247])| + EPSG_3756|3756|"NAD83(HARN) / Wyoming East Central (ftUS)"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([954376.825, 506593.628, 1680418.253, 1974536.226])| + EPSG_3757|3757|"NAD83(HARN) / Wyoming West Central (ftUS)"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([1330784.741, 178514.64, 2313579.352, 1652134.602])| + EPSG_3758|3758|"NAD83(HARN) / Wyoming West (ftUS)"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([2355051.343, 506594.928, 2912692.502, 1849481.487])| + EPSG_3759|3759|"NAD83 / Hawaii zone 3 (ftUS)"|Projected|[-158.33, 21.2, -157.61, 21.75]|Some([1528003.649, 12109.831, 1773272.587, 212070.183])| + EPSG_3760|3760|"NAD83(HARN) / Hawaii zone 3 (ftUS)"|Projected|[-158.33, 21.2, -157.61, 21.75]|Some([1528001.809, 12108.316, 1773270.806, 212068.718])| + EPSG_3761|3761|"NAD83(CSRS) / UTM zone 22N"|Projected|[-54.0, 39.5, -47.99, 57.65]|Some([242039.743, 4372263.591, 758819.199, 6393730.595])| + EPSG_3762|3762|"WGS 84 / South Georgia Lambert"|Projected|[-38.08, -54.95, -35.74, -53.93]|Some([-70938.438, 4844.766, 82760.342, 119104.853])| + EPSG_3763|3763|"ETRS89 / Portugal TM06"|Projected|[-9.56, 36.95, -6.19, 42.16]|Some([-127096.807, -301732.634, 173081.794, 278541.669])| + EPSG_3764|3764|"NZGD2000 / Chatham Island Circuit 2000"|Projected|[-177.25, -44.64, -175.54, -43.3]|Some([339142.538, 728435.795, 477897.638, 877773.749])| + EPSG_3765|3765|"HTRS96 / Croatia TM"|Projected|[13.43, 42.34, 19.43, 46.54]|Some([247020.527, 4688936.352, 741441.983, 5160175.806])| + EPSG_3766|3766|"HTRS96 / Croatia LCC"|Projected|[13.0, 41.62, 19.43, 46.54]|Some([-291881.447, 5092855.373, 244368.813, 5645296.654])| + EPSG_3767|3767|"HTRS96 / UTM zone 33N"|Projected|[13.0, 41.63, 18.0, 46.54]|Some([333404.676, 4608696.877, 749900.53, 5158421.053])| + EPSG_3768|3768|"HTRS96 / UTM zone 34N"|Projected|[18.0, 41.62, 19.43, 45.92]|Some([250060.814, 4608777.056, 378256.65, 5089535.991])| + EPSG_3769|3769|"Bermuda 1957 / UTM zone 20N"|Projected|[-64.89, 32.21, -64.61, 32.43]|Some([321841.131, 3564505.015, 348600.144, 3589327.253])| + EPSG_3770|3770|"BDA2000 / Bermuda 2000 National Grid"|Projected|[-68.83, 28.91, -60.7, 35.73]|Some([151924.968, -242558.17, 945145.386, 521416.333])| + EPSG_3771|3771|"NAD27 / Alberta 3TM ref merid 111 W"|Projected|[-112.5, 48.99, -109.98, 60.0]|Some([-109702.01, 5427764.715, 74706.262, 6654132.153])| + EPSG_3772|3772|"NAD27 / Alberta 3TM ref merid 114 W"|Projected|[-115.5, 48.99, -112.5, 60.0]|Some([-109694.521, 5427767.341, 109838.465, 6654134.745])| + EPSG_3773|3773|"NAD27 / Alberta 3TM ref merid 117 W"|Projected|[-118.5, 50.77, -115.5, 60.0]|Some([-105734.341, 5625720.916, 105871.567, 6654137.685])| + EPSG_3775|3775|"NAD83 / Alberta 3TM ref merid 111 W"|Projected|[-112.5, 48.99, -109.98, 60.0]|Some([-109766.957, 5427972.754, 74642.162, 6654356.188])| + EPSG_3776|3776|"NAD83 / Alberta 3TM ref merid 114 W"|Projected|[-115.5, 48.99, -112.5, 60.0]|Some([-109766.957, 5427972.684, 109766.957, 6654356.188])| + EPSG_3777|3777|"NAD83 / Alberta 3TM ref merid 117 W"|Projected|[-118.5, 50.77, -115.5, 60.0]|Some([-105803.257, 5625936.476, 105803.257, 6654356.188])| + EPSG_3779|3779|"NAD83(CSRS) / Alberta 3TM ref merid 111 W"|Projected|[-112.5, 48.99, -109.98, 60.0]|Some([-109766.0, 5427971.978, 74643.103, 6654355.334])| + EPSG_3780|3780|"NAD83(CSRS) / Alberta 3TM ref merid 114 W"|Projected|[-115.5, 48.99, -112.5, 60.0]|Some([-109765.953, 5427971.943, 109767.944, 6654355.378])| + EPSG_3781|3781|"NAD83(CSRS) / Alberta 3TM ref merid 117 W"|Projected|[-118.5, 50.77, -115.5, 60.0]|Some([-105802.214, 5625935.757, 105804.286, 6654355.424])| + EPSG_3783|3783|"Pitcairn 2006 / Pitcairn TM 2006"|Projected|[-130.16, -25.14, -130.01, -25.0]|Some([9452.025, 7581.492, 24594.543, 23093.804])| + EPSG_3784|3784|"Pitcairn 1967 / UTM zone 9S"|Projected|[-130.16, -25.14, -130.01, -25.0]|Some([382899.729, 7219005.477, 398153.748, 7234630.829])| + EPSG_3788|3788|"NZGD2000 / Auckland Islands TM 2000"|Projected|[165.55, -51.13, 166.93, -47.8]|Some([3466289.035, 4333040.471, 3569669.102, 4703810.032])| + EPSG_3789|3789|"NZGD2000 / Campbell Island TM 2000"|Projected|[168.65, -52.83, 169.6, -52.26]|Some([3476102.238, 4144129.288, 3540967.468, 4207726.295])| + EPSG_3790|3790|"NZGD2000 / Antipodes Islands TM 2000"|Projected|[178.4, -49.92, 179.37, -47.54]|Some([3454828.279, 4467878.593, 3527855.922, 4732717.794])| + EPSG_3791|3791|"NZGD2000 / Raoul Island TM 2000"|Projected|[-179.07, -31.56, -177.62, -29.03]|Some([3395767.258, 6506439.548, 3537016.233, 6787405.578])| + EPSG_3793|3793|"NZGD2000 / Chatham Islands TM 2000"|Projected|[-177.25, -44.64, -175.54, -43.3]|Some([3439142.538, 5054613.426, 3577897.638, 5203951.379])| + EPSG_3794|3794|"Slovenia 1996 / Slovene National Grid"|Projected|[13.38, 45.42, 16.61, 46.88]|Some([373217.654, 31118.298, 625999.741, 194661.546])| + EPSG_3795|3795|"NAD27 / Cuba Norte"|Projected|[-85.01, 21.38, -76.91, 23.25]|Some([84145.308, 172838.63, 924116.727, 385588.699])| + EPSG_3796|3796|"NAD27 / Cuba Sur"|Projected|[-78.69, 19.77, -74.07, 21.5]|Some([305387.61, 124265.159, 789575.833, 318237.714])| + EPSG_3797|3797|"NAD27 / MTQ Lambert"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([24720.47, 110138.527, 1813285.75, 2148060.703])| + EPSG_3798|3798|"NAD83 / MTQ Lambert"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([24751.162, 110147.605, 1813323.428, 2148077.32])| + EPSG_3799|3799|"NAD83(CSRS) / MTQ Lambert"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([24751.311, 110146.585, 1813323.276, 2148075.949])| + EPSG_3800|3800|"NAD27 / Alberta 3TM ref merid 120 W"|Projected|[-120.0, 52.88, -118.5, 60.0]|Some([76.028, 5860466.817, 101047.384, 6654140.966])| + EPSG_3801|3801|"NAD83 / Alberta 3TM ref merid 120 W"|Projected|[-120.0, 52.88, -118.5, 60.0]|Some([0.0, 5860680.032, 100971.61, 6654356.188])| + EPSG_3802|3802|"NAD83(CSRS) / Alberta 3TM ref merid 120 W"|Projected|[-120.0, 52.88, -118.5, 60.0]|Some([1.064, 5860679.341, 100972.677, 6654355.471])| + EPSG_3812|3812|"ETRS89 / Belgian Lambert 2008"|Projected|[2.5, 49.5, 6.4, 51.51]|Some([514655.596, 520908.261, 797149.152, 746440.853])| + EPSG_3814|3814|"NAD83 / Mississippi TM"|Projected|[-91.65, 30.01, -88.09, 35.01]|Some([316708.059, 1023969.858, 660135.778, 1580007.849])| + EPSG_3815|3815|"NAD83(HARN) / Mississippi TM"|Projected|[-91.65, 30.01, -88.09, 35.01]|Some([316708.623, 1023969.256, 660136.266, 1580007.129])| + EPSG_3816|3816|"NAD83(NSRS2007) / Mississippi TM"|Projected|[-91.65, 30.01, -88.09, 35.01]|Some([316708.059, 1023969.858, 660135.778, 1580007.849])| + EPSG_3819|3819|"HD1909"|Geographic2d|[16.11, 45.74, 22.9, 48.58]|Some([16.111, 45.739, 22.902, 48.579])| + EPSG_3821|3821|"TWD67"|Geographic2d|[119.25, 21.87, 122.06, 25.34]|Some([119.25, 21.87, 122.06, 25.34])| + EPSG_3824|3824|"TWD97"|Geographic2d|[114.32, 17.36, 123.61, 26.96]|Some([114.32, 17.36, 123.61, 26.96])| + EPSG_3825|3825|"TWD97 / TM2 zone 119"|Projected|[118.0, 18.63, 120.0, 24.65]|Some([144483.717, 2060506.795, 355516.283, 2727380.298])| + EPSG_3826|3826|"TWD97 / TM2 zone 121"|Projected|[119.99, 20.41, 122.06, 26.72]|Some([144589.171, 2257530.49, 360629.63, 2956748.936])| + EPSG_3827|3827|"TWD67 / TM2 zone 119"|Projected|[119.25, 23.13, 119.78, 23.82]|Some([275470.8, 2558709.309, 329884.811, 2635316.456])| + EPSG_3828|3828|"TWD67 / TM2 zone 121"|Projected|[119.99, 21.87, 122.06, 25.34]|Some([145616.569, 2419172.283, 359551.347, 2803869.612])| + EPSG_3829|3829|"Hu Tzu Shan 1950 / UTM zone 51N"|Projected|[119.25, 21.87, 122.06, 25.34]|Some([111532.318, 2418974.887, 404571.284, 2808132.768])| + EPSG_3832|3832|"WGS 84 / PDC Mercator"|Projected|[98.69, -60.0, -68.0, 66.67]|Some([-5711803.073, -8362698.549, 15807367.693, 10023392.492])| + EPSG_3833|3833|"Pulkovo 1942(58) / Gauss-Kruger zone 2"|Projected|[9.92, 50.2, 12.0, 54.23]|Some([2559996.847, 5563596.228, 2714179.627, 6015778.908])| + EPSG_3834|3834|"Pulkovo 1942(83) / Gauss-Kruger zone 2"|Projected|[9.92, 50.2, 12.0, 54.23]|Some([2560123.302, 5563649.506, 2714304.768, 6015827.306])| + EPSG_3835|3835|"Pulkovo 1942(83) / Gauss-Kruger zone 3"|Projected|[12.0, 45.78, 18.01, 54.74]|Some([3266689.693, 5071722.522, 3734210.084, 6072561.63])| + EPSG_3836|3836|"Pulkovo 1942(83) / Gauss-Kruger zone 4"|Projected|[18.0, 45.74, 22.9, 50.06]|Some([4266648.123, 5067317.021, 4647991.425, 5551964.626])| + EPSG_3837|3837|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 3"|Projected|[9.92, 50.35, 10.5, 51.56]|Some([3563801.795, 5580281.274, 3606760.391, 5715554.778])| + EPSG_3838|3838|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 4"|Projected|[10.5, 48.97, 13.5, 54.74]|Some([4390176.295, 5426386.82, 4609823.705, 6069425.908])| + EPSG_3839|3839|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 9"|Projected|[25.5, 41.28, 28.5, 48.27]|Some([9374328.821, 4571749.938, 9625671.179, 5349655.607])| + EPSG_3840|3840|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 10"|Projected|[28.5, 43.34, 29.74, 45.44]|Some([10378362.247, 4800610.113, 10479772.754, 5035052.432])| + EPSG_3841|3841|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 6"|Projected|[16.5, 45.74, 19.5, 50.45]|Some([6383385.22, 5067319.897, 6616862.243, 5592112.997])| + EPSG_3844|3844|"Pulkovo 1942(58) / Stereo70"|Projected|[20.26, 43.44, 31.41, 48.27]|Some([116396.249, 215576.028, 1018918.067, 771888.508])| + EPSG_3845|3845|"SWEREF99 / RT90 7.5 gon V emulation"|Projected|[10.93, 57.29, 12.91, 59.73]|Some([1477337.691, 6351580.006, 1596724.496, 6624455.107])| + EPSG_3846|3846|"SWEREF99 / RT90 5 gon V emulation"|Projected|[11.81, 55.28, 15.44, 64.39]|Some([1389078.818, 6127771.282, 1619744.777, 7144053.053])| + EPSG_3847|3847|"SWEREF99 / RT90 2.5 gon V emulation"|Projected|[13.66, 55.95, 17.73, 67.18]|Some([1365989.726, 6202363.301, 1620237.391, 7455406.396])| + EPSG_3848|3848|"SWEREF99 / RT90 0 gon emulation"|Projected|[16.08, 56.86, 20.22, 68.54]|Some([1379537.768, 6303689.753, 1632057.774, 7607039.137])| + EPSG_3849|3849|"SWEREF99 / RT90 2.5 gon O emulation"|Projected|[18.4, 63.37, 22.2, 69.07]|Some([1404738.967, 7028992.33, 1594834.716, 7665784.069])| + EPSG_3850|3850|"SWEREF99 / RT90 5 gon O emulation"|Projected|[21.34, 65.24, 24.17, 68.58]|Some([1443258.507, 7237460.723, 1575557.643, 7610802.873])| + EPSG_3851|3851|"NZGD2000 / NZCS2000"|Projected|[160.6, -55.95, -171.2, -25.88]|Some([1722483.9, 5228058.614, 4624385.495, 8692658.92])| + EPSG_3852|3852|"RSRGD2000 / DGLC2000"|Projected|[145.0, -81.0, 169.0, -76.0]|Some([177968.856, 1003119.345, 822031.144, 1582843.964])| + EPSG_3854|3854|"County ST74"|Projected|[17.25, 58.69, 19.61, 60.27]|Some([53334.444, 7485.353, 190186.908, 184512.021])| + EPSG_3857|3857|"WGS 84 / Pseudo-Mercator"|Projected|[-180.0, -85.06, 180.0, 85.06]|Some([-20037508.343, -20048966.104, 20037508.343, 20048966.104])| + EPSG_3873|3873|"ETRS89 / GK19FIN"|Projected|[19.24, 60.08, 19.5, 60.34]|Some([19513254.328, 6663010.108, 19527832.436, 6692058.677])| + EPSG_3874|3874|"ETRS89 / GK20FIN"|Projected|[19.5, 59.92, 20.5, 60.48]|Some([20472032.843, 6645159.891, 20527967.157, 6707657.064])| + EPSG_3875|3875|"ETRS89 / GK21FIN"|Projected|[20.5, 59.84, 21.5, 69.33]|Some([21471965.566, 6636247.071, 21528034.434, 7694317.479])| + EPSG_3876|3876|"ETRS89 / GK22FIN"|Projected|[21.5, 59.76, 22.5, 69.31]|Some([22471898.344, 6627334.361, 22528101.656, 7692086.476])| + EPSG_3877|3877|"ETRS89 / GK23FIN"|Projected|[22.5, 59.75, 23.5, 68.74]|Some([23471889.945, 6626220.279, 23528110.055, 7628505.093])| + EPSG_3878|3878|"ETRS89 / GK24FIN"|Projected|[23.5, 59.86, 24.5, 68.84]|Some([24471982.38, 6638475.266, 24528017.62, 7639659.414])| + EPSG_3879|3879|"ETRS89 / GK25FIN"|Projected|[24.5, 59.94, 25.5, 68.9]|Some([25472049.671, 6647388.113, 25527950.329, 7646352.07])| + EPSG_3880|3880|"ETRS89 / GK26FIN"|Projected|[25.5, 60.18, 26.5, 69.94]|Some([26472251.876, 6674127.306, 26527748.124, 7762365.51])| + EPSG_3881|3881|"ETRS89 / GK27FIN"|Projected|[26.5, 60.36, 27.5, 70.05]|Some([27472403.853, 6694182.342, 27527596.147, 7774636.968])| + EPSG_3882|3882|"ETRS89 / GK28FIN"|Projected|[27.5, 60.42, 28.5, 70.09]|Some([28472454.574, 6700867.475, 28527545.426, 7779099.354])| + EPSG_3883|3883|"ETRS89 / GK29FIN"|Projected|[28.5, 60.94, 29.5, 69.81]|Some([29472895.429, 6758807.831, 29527104.571, 7747863.074])| + EPSG_3884|3884|"ETRS89 / GK30FIN"|Projected|[29.5, 61.43, 30.5, 67.98]|Some([30473312.921, 6813409.599, 30526687.079, 7543736.596])| + EPSG_3885|3885|"ETRS89 / GK31FIN"|Projected|[30.5, 62.08, 31.59, 64.27]|Some([31473869.786, 6885846.648, 31530833.566, 7130086.161])| + EPSG_3889|3889|"IGRS"|Geographic2d|[38.79, 29.06, 48.75, 37.39]|Some([38.79, 29.06, 48.75, 37.39])| + EPSG_3890|3890|"IGRS / UTM zone 37N"|Projected|[38.79, 31.14, 42.0, 36.75]|Some([479981.455, 3445117.625, 786040.155, 4071337.24])| + EPSG_3891|3891|"IGRS / UTM zone 38N"|Projected|[42.0, 29.06, 48.0, 37.39]|Some([207898.112, 3214633.132, 792101.888, 4142362.18])| + EPSG_3892|3892|"IGRS / UTM zone 39N"|Projected|[48.0, 29.6, 48.75, 31.0]|Some([209435.153, 3276577.466, 285175.247, 3433466.669])| + EPSG_3893|3893|"ED50 / Iraq National Grid"|Projected|[38.79, 29.06, 48.61, 37.39]|Some([48421.038, 3874.164, 1005377.938, 955187.981])| + EPSG_3906|3906|"MGI 1901"|Geographic2d|[13.38, 40.85, 23.04, 46.88]|Some([13.384, 40.849, 23.046, 46.88])| + EPSG_3912|3912|"MGI 1901 / Slovene National Grid"|Projected|[13.38, 45.42, 16.61, 46.88]|Some([373591.224, 30633.629, 626370.811, 194172.126])| + EPSG_3920|3920|"Puerto Rico / UTM zone 20N"|Projected|[-64.88, 18.28, -64.25, 18.78]|Some([301224.732, 2021713.204, 368215.792, 2077628.756])| + EPSG_3942|3942|"RGF93 v1 / CC42"|Projected|[-1.06, 41.31, 9.63, 43.07]|Some([1360148.436, 1123378.371, 2254632.683, 1339754.14])| + EPSG_3943|3943|"RGF93 v1 / CC43"|Projected|[-1.79, 42.33, 7.65, 44.01]|Some([1305422.558, 2125579.297, 2083056.865, 2323158.503])| + EPSG_3944|3944|"RGF93 v1 / CC44"|Projected|[-1.79, 42.92, 7.71, 45.0]|Some([1309096.616, 3080014.115, 2084381.885, 3322082.757])| + EPSG_3945|3945|"RGF93 v1 / CC45"|Projected|[-1.46, 44.0, 7.71, 46.0]|Some([1342437.381, 4088889.588, 2077583.387, 4321739.72])| + EPSG_3946|3946|"RGF93 v1 / CC46"|Projected|[-2.21, 45.0, 7.16, 47.0]|Some([1289473.773, 5088867.232, 2027875.373, 5324113.396])| + EPSG_3947|3947|"RGF93 v1 / CC47"|Projected|[-4.77, 46.0, 7.63, 48.0]|Some([1099056.839, 6088846.216, 2058469.98, 6339910.234])| + EPSG_3948|3948|"RGF93 v1 / CC48"|Projected|[-4.87, 47.0, 8.23, 49.0]|Some([1102438.827, 7088823.329, 2097494.1, 7340564.439])| + EPSG_3949|3949|"RGF93 v1 / CC49"|Projected|[-4.87, 48.0, 8.23, 50.0]|Some([1113711.165, 8088803.879, 2090007.684, 8340438.398])| + EPSG_3950|3950|"RGF93 v1 / CC50"|Projected|[-2.03, 49.0, 8.08, 51.14]|Some([1332198.993, 9088784.764, 2071451.484, 9338881.939])| + EPSG_3968|3968|"NAD83 / Virginia Lambert"|Projected|[-83.68, 36.54, -75.31, 39.46]|Some([-374247.52, 59942.043, 375142.238, 392175.157])| + EPSG_3969|3969|"NAD83(HARN) / Virginia Lambert"|Projected|[-83.68, 36.54, -75.31, 39.46]|Some([-374247.195, 59941.244, 375142.409, 392174.282])| + EPSG_3970|3970|"NAD83(NSRS2007) / Virginia Lambert"|Projected|[-83.68, 36.54, -75.31, 39.46]|Some([-374247.52, 59942.043, 375142.238, 392175.157])| + EPSG_3976|3976|"WGS 84 / NSIDC Sea Ice Polar Stereographic South"|Projected|[-180.0, -90.0, 180.0, -60.0]|Some([-3304955.651, -3323160.271, 3304955.651, 3323160.271])| + EPSG_3978|3978|"NAD83 / Canada Atlas Lambert"|Projected|[-141.01, 38.21, -40.73, 86.46]|Some([-3894481.379, -1229117.152, 4432820.002, 4329768.401])| + EPSG_3979|3979|"NAD83(CSRS) / Canada Atlas Lambert"|Projected|[-141.01, 38.21, -40.73, 86.46]|Some([-3894480.609, -1229117.949, 4432820.135, 4329767.105])| + EPSG_3986|3986|"Katanga 1955 / Katanga Gauss zone A"|Projected|[28.5, -13.46, 30.78, -4.99]|Some([33628.717, 6182.926, 286505.917, 943443.035])| + EPSG_3987|3987|"Katanga 1955 / Katanga Gauss zone B"|Projected|[26.5, -13.44, 29.5, -4.99]|Some([33628.717, 8396.166, 366371.283, 943443.069])| + EPSG_3988|3988|"Katanga 1955 / Katanga Gauss zone C"|Projected|[24.5, -12.08, 27.5, -4.99]|Some([33628.717, 158888.997, 366371.283, 943443.069])| + EPSG_3989|3989|"Katanga 1955 / Katanga Gauss zone D"|Projected|[21.74, -11.72, 25.51, -6.32]|Some([-50129.787, 198170.247, 367098.539, 796374.114])| + EPSG_3991|3991|"Puerto Rico State Plane CS of 1927"|Projected|[-67.97, 17.87, -65.19, 18.57]|Some([-34439.809, 14023.312, 932192.037, 270445.647])| + EPSG_3992|3992|"Puerto Rico / St. Croix"|Projected|[-65.09, 17.62, -64.51, 18.44]|Some([965444.074, 24956.789, 1169581.238, 324504.869])| + EPSG_3993|3993|"Guam 1963 / Guam SPCS"|Projected|[144.58, 13.18, 145.01, 13.7]|Some([31446.001, 17484.484, 78061.011, 75030.088])| + EPSG_3994|3994|"WGS 84 / Mercator 41"|Projected|[155.0, -60.0, -169.99, -25.0]|Some([4627435.181, -6320521.1, 7573008.012, -2159841.867])| + EPSG_3995|3995|"WGS 84 / Arctic Polar Stereographic"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-3314874.771, -3333134.028, 3314874.771, 3333134.028])| + EPSG_3996|3996|"WGS 84 / IBCAO Polar Stereographic"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-3349625.309, -3368075.982, 3349625.309, 3368075.982])| + EPSG_3997|3997|"WGS 84 / Dubai Local TM"|Projected|[54.84, 24.85, 55.55, 25.34]|Some([450137.261, 2749438.408, 521899.033, 2803809.325])| + EPSG_4023|4023|"MOLDREF99"|Geographic2d|[26.63, 45.44, 30.13, 48.47]|Some([26.63, 45.44, 30.13, 48.47])| + EPSG_4026|4026|"MOLDREF99 / Moldova TM"|Projected|[26.63, 45.44, 30.13, 48.47]|Some([61521.985, 33542.416, 335348.582, 371880.474])| + EPSG_4037|4037|"WGS 84 / TMzn35N"|Projected|[26.63, 45.44, 30.0, 48.47]|Some([471062.4, 5031831.244, 734627.695, 5372888.457])| + EPSG_4038|4038|"WGS 84 / TMzn36N"|Projected|[30.0, 46.37, 30.13, 46.47]|Some([269261.222, 5139161.633, 279663.012, 5150643.434])| + EPSG_4046|4046|"RGRDC 2005"|Geographic2d|[11.79, -13.46, 29.81, -3.41]|Some([11.79, -13.46, 29.81, -3.41])| + EPSG_4048|4048|"RGRDC 2005 / Congo TM zone 12"|Projected|[11.79, -6.04, 13.0, -4.67]|Some([476702.277, 9332071.717, 610946.889, 9483658.298])| + EPSG_4049|4049|"RGRDC 2005 / Congo TM zone 14"|Projected|[13.0, -5.91, 15.01, -4.28]|Some([388994.373, 9346446.656, 612115.797, 9526780.595])| + EPSG_4050|4050|"RGRDC 2005 / Congo TM zone 16"|Projected|[15.0, -7.31, 17.0, -3.41]|Some([388881.762, 9191616.368, 611118.238, 9622974.958])| + EPSG_4051|4051|"RGRDC 2005 / Congo TM zone 18"|Projected|[17.0, -8.11, 19.0, -3.43]|Some([388884.065, 9103136.713, 611115.935, 9620763.615])| + EPSG_4056|4056|"RGRDC 2005 / Congo TM zone 20"|Projected|[19.0, -8.0, 21.0, -3.77]|Some([388925.273, 9115302.858, 611074.727, 9583170.631])| + EPSG_4057|4057|"RGRDC 2005 / Congo TM zone 22"|Projected|[21.0, -11.24, 23.01, -4.18]|Some([388980.135, 8756921.056, 612130.178, 9537837.521])| + EPSG_4058|4058|"RGRDC 2005 / Congo TM zone 24"|Projected|[23.0, -11.47, 25.0, -4.58]|Some([389039.102, 8731481.749, 610960.898, 9493609.643])| + EPSG_4059|4059|"RGRDC 2005 / Congo TM zone 26"|Projected|[25.0, -11.99, 27.0, -4.99]|Some([389105.121, 8673956.912, 610894.879, 9448275.554])| + EPSG_4060|4060|"RGRDC 2005 / Congo TM zone 28"|Projected|[27.0, -13.39, 29.0, -6.43]|Some([389381.68, 8519071.843, 610618.32, 9289048.61])| + EPSG_4061|4061|"RGRDC 2005 / UTM zone 33S"|Projected|[12.0, -8.11, 18.01, -3.41]|Some([166609.904, 9102311.228, 834502.418, 9623088.076])| + EPSG_4062|4062|"RGRDC 2005 / UTM zone 34S"|Projected|[18.0, -11.24, 24.0, -3.57]|Some([166666.404, 8755810.562, 833333.596, 9605402.612])| + EPSG_4063|4063|"RGRDC 2005 / UTM zone 35S"|Projected|[24.0, -13.46, 29.81, -4.79]|Some([167182.251, 8510012.812, 811721.934, 9470547.981])| + EPSG_4071|4071|"Chua / UTM zone 23S"|Projected|[-48.1, -15.94, -47.1, -15.37]|Some([167097.208, 8235271.811, 275138.453, 8299692.276])| + EPSG_4075|4075|"SREF98"|Geographic2d|[18.81, 42.23, 23.01, 46.19]|Some([18.81, 42.23, 23.01, 46.19])| + EPSG_4081|4081|"REGCAN95"|Geographic2d|[-21.93, 24.6, -11.75, 32.76]|Some([-21.93, 24.6, -11.75, 32.76])| + EPSG_4082|4082|"REGCAN95 / UTM zone 27N"|Projected|[-21.93, 24.6, -18.0, 31.19]|Some([405849.706, 2720657.565, 803793.182, 3454537.317])| + EPSG_4083|4083|"REGCAN95 / UTM zone 28N"|Projected|[-18.0, 25.25, -11.75, 32.76]|Some([197797.858, 2792632.961, 827402.291, 3629357.101])| + EPSG_4087|4087|"WGS 84 / World Equidistant Cylindrical"|Projected|[-180.0, -90.0, 180.0, 90.0]|Some([-20037508.343, -10018754.171, 20037508.343, 10018754.171])| + EPSG_4093|4093|"ETRS89 / DKTM1"|Projected|[8.0, 54.8, 10.0, 57.64]|Some([135690.329, 1074844.476, 264309.671, 1391500.297])| + EPSG_4094|4094|"ETRS89 / DKTM2"|Projected|[9.0, 54.67, 11.29, 57.8]|Some([335484.098, 1060373.369, 483224.586, 1409610.019])| + EPSG_4095|4095|"ETRS89 / DKTM3"|Projected|[10.79, 54.51, 12.87, 56.79]|Some([537821.427, 1042563.197, 672541.27, 1296962.51])| + EPSG_4096|4096|"ETRS89 / DKTM4"|Projected|[14.59, 54.94, 15.25, 55.38]|Some([773723.345, 1090550.964, 816022.38, 1139611.105])| + EPSG_4120|4120|"Greek"|Geographic2d|[19.57, 34.88, 28.3, 41.75]|Some([19.568, 34.879, 28.298, 41.749])| + EPSG_4121|4121|"GGRS87"|Geographic2d|[19.57, 34.88, 28.3, 41.75]|Some([19.568, 34.877, 28.298, 41.748])| + EPSG_4122|4122|"ATS77"|Geographic2d|[-69.05, 43.41, -59.73, 48.07]|Some([-69.05, 43.41, -59.73, 48.07])| + EPSG_4123|4123|"KKJ"|Geographic2d|[19.24, 59.75, 31.59, 70.09]|Some([19.243, 59.75, 31.594, 70.09])| + EPSG_4124|4124|"RT90"|Geographic2d|[10.03, 54.96, 24.17, 69.07]|Some([10.032, 54.96, 24.175, 69.072])| + EPSG_4127|4127|"Tete"|Geographic2d|[30.21, -26.87, 40.9, -10.42]|Some([30.21, -26.869, 40.901, -10.418])| + EPSG_4128|4128|"Madzansua"|Geographic2d|[30.21, -17.76, 35.37, -14.01]|Some([30.21, -17.76, 35.37, -14.01])| + EPSG_4129|4129|"Observatario"|Geographic2d|[31.29, -26.87, 35.65, -19.84]|Some([31.29, -26.868, 35.65, -19.838])| + EPSG_4130|4130|"Moznet"|Geographic2d|[30.21, -27.71, 43.03, -10.09]|Some([30.21, -27.71, 43.03, -10.09])| + EPSG_4131|4131|"Indian 1960"|Geographic2d|[102.14, 7.99, 110.0, 23.4]|Some([102.14, 7.988, 110.004, 23.4])| + EPSG_4132|4132|"FD58"|Geographic2d|[47.13, 26.21, 53.61, 33.22]|Some([47.13, 26.208, 53.61, 33.22])| + EPSG_4133|4133|"EST92"|Geographic2d|[21.74, 57.52, 28.2, 59.75]|Some([21.74, 57.52, 28.2, 59.75])| + EPSG_4134|4134|"PSD93"|Geographic2d|[51.99, 16.59, 59.91, 26.58]|Some([51.987, 16.589, 59.907, 26.58])| + EPSG_4135|4135|"Old Hawaiian"|Geographic2d|[-160.3, 18.87, -154.74, 22.29]|Some([-160.303, 18.873, -154.743, 22.293])| + EPSG_4136|4136|"St. Lawrence Island"|Geographic2d|[-171.97, 62.89, -168.59, 63.84]|Some([-171.97, 62.89, -168.59, 63.84])| + EPSG_4137|4137|"St. Paul Island"|Geographic2d|[-170.51, 57.06, -170.04, 57.28]|Some([-170.51, 57.06, -170.04, 57.28])| + EPSG_4138|4138|"St. George Island"|Geographic2d|[-169.88, 56.49, -169.38, 56.67]|Some([-169.88, 56.49, -169.38, 56.67])| + EPSG_4139|4139|"Puerto Rico"|Geographic2d|[-67.97, 17.62, -64.25, 18.78]|Some([-67.97, 17.622, -64.25, 18.782])| + EPSG_4141|4141|"Israel 1993"|Geographic2d|[34.17, 29.45, 35.69, 33.28]|Some([34.169, 29.45, 35.689, 33.28])| + EPSG_4142|4142|"Locodjo 1965"|Geographic2d|[-8.61, 1.02, -2.48, 10.74]|Some([-8.61, 1.02, -2.48, 10.74])| + EPSG_4143|4143|"Abidjan 1987"|Geographic2d|[-8.61, 1.02, -2.48, 10.74]|Some([-8.61, 1.016, -2.48, 10.737])| + EPSG_4144|4144|"Kalianpur 1937"|Geographic2d|[60.86, 8.02, 101.17, 37.07]|Some([60.86, 8.02, 101.17, 37.07])| + EPSG_4145|4145|"Kalianpur 1962"|Geographic2d|[60.86, 21.05, 77.83, 37.07]|Some([60.859, 21.049, 77.831, 37.071])| + EPSG_4146|4146|"Kalianpur 1975"|Geographic2d|[68.13, 8.02, 97.42, 35.51]|Some([68.13, 8.018, 97.424, 35.511])| + EPSG_4147|4147|"Hanoi 1972"|Geographic2d|[102.14, 8.33, 109.53, 23.4]|Some([102.14, 8.33, 109.53, 23.4])| + EPSG_4148|4148|"Hartebeesthoek94"|Geographic2d|[13.33, -50.32, 42.85, -22.13]|Some([13.33, -50.32, 42.85, -22.13])| + EPSG_4149|4149|"CH1903"|Geographic2d|[5.96, 45.82, 10.49, 47.81]|Some([5.961, 45.821, 10.491, 47.811])| + EPSG_4150|4150|"CH1903+"|Geographic2d|[5.96, 45.82, 10.49, 47.81]|Some([5.961, 45.821, 10.491, 47.811])| + EPSG_4151|4151|"CHTRS95"|Geographic2d|[5.96, 45.82, 10.49, 47.81]|Some([5.96, 45.82, 10.49, 47.81])| + EPSG_4152|4152|"NAD83(HARN)"|Geographic2d|[144.58, -14.59, -64.51, 71.4]|Some([-175.177, -14.59, 179.792, 71.4])| + EPSG_4153|4153|"Rassadiran"|Geographic2d|[52.5, 27.39, 52.71, 27.61]|Some([52.5, 27.391, 52.71, 27.611])| + EPSG_4154|4154|"ED50(ED77)"|Geographic2d|[44.03, 23.34, 63.34, 39.78]|Some([44.03, 23.341, 63.34, 39.781])| + EPSG_4155|4155|"Dabola 1981"|Geographic2d|[-15.13, 7.19, -7.65, 12.68]|Some([-15.13, 7.19, -7.65, 12.68])| + EPSG_4156|4156|"S-JTSK"|Geographic2d|[12.09, 47.73, 22.56, 51.06]|Some([12.091, 47.73, 22.562, 51.061])| + EPSG_4157|4157|"Mount Dillon"|Geographic2d|[-60.9, 11.08, -60.44, 11.41]|Some([-60.9, 11.08, -60.44, 11.41])| + EPSG_4158|4158|"Naparima 1955"|Geographic2d|[-61.98, 9.99, -60.85, 10.9]|Some([-61.982, 9.988, -60.852, 10.898])| + EPSG_4159|4159|"ELD79"|Geographic2d|[9.31, 19.5, 26.21, 35.23]|Some([9.31, 19.5, 26.21, 35.23])| + EPSG_4160|4160|"Chos Malal 1914"|Geographic2d|[-72.14, -43.41, -65.86, -31.91]|Some([-72.14, -43.41, -65.86, -31.91])| + EPSG_4161|4161|"Pampa del Castillo"|Geographic2d|[-73.59, -50.34, -65.47, -42.49]|Some([-73.59, -50.34, -65.467, -42.49])| + EPSG_4162|4162|"Korean 1985"|Geographic2d|[124.53, 33.14, 131.01, 38.64]|Some([124.532, 33.137, 131.012, 38.637])| + EPSG_4163|4163|"Yemen NGN96"|Geographic2d|[41.08, 8.95, 57.96, 19.0]|Some([41.08, 8.95, 57.96, 19.0])| + EPSG_4164|4164|"South Yemen"|Geographic2d|[43.37, 12.54, 53.14, 19.0]|Some([43.37, 12.539, 53.14, 18.999])| + EPSG_4165|4165|"Bissau"|Geographic2d|[-16.77, 10.87, -13.64, 12.69]|Some([-16.772, 10.87, -13.642, 12.69])| + EPSG_4166|4166|"Korean 1995"|Geographic2d|[124.53, 33.14, 131.01, 38.64]|Some([124.53, 33.14, 131.01, 38.64])| + EPSG_4167|4167|"NZGD2000"|Geographic2d|[160.6, -55.95, -171.2, -25.88]|Some([-179.66, -55.95, 179.4, -25.88])| + EPSG_4168|4168|"Accra"|Geographic2d|[-3.79, 1.4, 2.1, 11.16]|Some([-3.79, 1.397, 2.1, 11.157])| + EPSG_4169|4169|"American Samoa 1962"|Geographic2d|[-170.88, -14.43, -169.38, -14.11]|Some([-170.879, -14.435, -169.379, -14.115])| + EPSG_4170|4170|"SIRGAS 1995"|Geographic2d|[-113.21, -59.87, -26.0, 16.75]|Some([-113.21, -59.87, -26.0, 16.75])| + EPSG_4171|4171|"RGF93 v1"|Geographic2d|[-9.86, 41.15, 10.38, 51.56]|Some([-9.86, 41.15, 10.38, 51.56])| + EPSG_4173|4173|"IRENET95"|Geographic2d|[-10.56, 51.39, -5.34, 55.43]|Some([-10.56, 51.39, -5.34, 55.43])| + EPSG_4174|4174|"Sierra Leone 1924"|Geographic2d|[-13.34, 8.32, -13.13, 8.55]|Some([-13.34, 8.32, -13.13, 8.55])| + EPSG_4175|4175|"Sierra Leone 1968"|Geographic2d|[-13.35, 6.88, -10.26, 10.0]|Some([-13.35, 6.88, -10.26, 10.0])| + EPSG_4176|4176|"Australian Antarctic"|Geographic2d|[45.0, -90.0, 160.0, -60.0]|Some([45.0, -90.0, 160.0, -60.0])| + EPSG_4178|4178|"Pulkovo 1942(83)"|Geographic2d|[9.92, 41.24, 28.68, 54.74]|Some([9.92, 41.24, 28.68, 54.74])| + EPSG_4179|4179|"Pulkovo 1942(58)"|Geographic2d|[9.92, 39.63, 31.41, 54.89]|Some([9.92, 39.63, 31.412, 54.89])| + EPSG_4180|4180|"EST97"|Geographic2d|[20.37, 57.52, 28.2, 60.0]|Some([20.37, 57.52, 28.2, 60.0])| + EPSG_4181|4181|"LUREF"|Geographic2d|[5.73, 49.44, 6.53, 50.19]|Some([5.729, 49.439, 6.529, 50.189])| + EPSG_4182|4182|"Azores Occidental 1939"|Geographic2d|[-31.34, 39.3, -31.02, 39.77]|Some([-31.336, 39.299, -31.016, 39.769])| + EPSG_4183|4183|"Azores Central 1948"|Geographic2d|[-28.9, 38.32, -26.97, 39.14]|Some([-28.901, 38.32, -26.971, 39.14])| + EPSG_4184|4184|"Azores Oriental 1940"|Geographic2d|[-25.92, 36.87, -24.72, 37.96]|Some([-25.92, 36.869, -24.72, 37.959])| + EPSG_4188|4188|"OSNI 1952"|Geographic2d|[-8.18, 53.96, -5.34, 55.36]|Some([-8.179, 53.96, -5.339, 55.36])| + EPSG_4189|4189|"REGVEN"|Geographic2d|[-73.38, 0.64, -58.95, 16.75]|Some([-73.38, 0.64, -58.95, 16.75])| + EPSG_4190|4190|"POSGAR 98"|Geographic2d|[-73.59, -58.41, -52.63, -21.78]|Some([-73.59, -58.41, -52.63, -21.78])| + EPSG_4191|4191|"Albanian 1987"|Geographic2d|[19.22, 39.64, 21.06, 42.67]|Some([19.222, 39.64, 21.062, 42.67])| + EPSG_4192|4192|"Douala 1948"|Geographic2d|[8.45, 2.16, 10.4, 4.99]|Some([8.451, 2.161, 10.401, 4.991])| + EPSG_4193|4193|"Manoca 1962"|Geographic2d|[8.45, 2.16, 10.4, 4.99]|Some([8.451, 2.161, 10.401, 4.991])| + EPSG_4194|4194|"Qornoq 1927"|Geographic2d|[-73.29, 59.74, -42.52, 79.0]|Some([-73.299, 59.741, -42.524, 79.001])| + EPSG_4195|4195|"Scoresbysund 1952"|Geographic2d|[-29.69, 68.66, -19.89, 74.58]|Some([-29.702, 68.66, -19.899, 74.581])| + EPSG_4196|4196|"Ammassalik 1958"|Geographic2d|[-38.86, 65.52, -36.81, 65.91]|Some([-38.867, 65.518, -36.817, 65.908])| + EPSG_4197|4197|"Garoua"|Geographic2d|[12.9, 8.92, 14.19, 9.87]|Some([12.901, 8.921, 14.191, 9.871])| + EPSG_4198|4198|"Kousseri"|Geographic2d|[14.17, 11.7, 15.09, 12.77]|Some([14.171, 11.699, 15.091, 12.769])| + EPSG_4199|4199|"Egypt 1930"|Geographic2d|[24.7, 21.97, 36.95, 31.68]|Some([24.7, 21.97, 36.95, 31.68])| + EPSG_4200|4200|"Pulkovo 1995"|Geographic2d|[18.92, 39.87, -168.97, 85.19]|Some([-174.721, 39.87, 179.554, 85.19])| + EPSG_4201|4201|"Adindan"|Geographic2d|[21.82, 3.4, 47.99, 22.24]|Some([21.82, 3.398, 47.989, 22.24])| + EPSG_4202|4202|"AGD66"|Geographic2d|[109.23, -47.2, 163.2, -1.3]|Some([109.228, -47.202, 163.2, -1.3])| + EPSG_4203|4203|"AGD84"|Geographic2d|[109.23, -38.53, 153.61, -9.37]|Some([109.228, -38.532, 153.609, -9.371])| + EPSG_4204|4204|"Ain el Abd"|Geographic2d|[34.51, 16.37, 55.67, 32.16]|Some([34.511, 16.37, 55.67, 32.159])| + EPSG_4205|4205|"Afgooye"|Geographic2d|[40.98, -1.71, 51.47, 12.03]|Some([40.981, -1.71, 51.471, 12.029])| + EPSG_4206|4206|"Agadez"|Geographic2d|[0.16, 11.69, 16.0, 23.53]|Some([0.16, 11.69, 16.0, 23.53])| + EPSG_4207|4207|"Lisbon"|Geographic2d|[-9.56, 36.95, -6.19, 42.16]|Some([-9.559, 36.948, -6.189, 42.158])| + EPSG_4208|4208|"Aratu"|Geographic2d|[-53.38, -35.71, -26.0, 4.26]|Some([-53.381, -35.71, -26.0, 4.261])| + EPSG_4209|4209|"Arc 1950"|Geographic2d|[19.99, -26.88, 35.93, -8.19]|Some([19.99, -26.88, 35.93, -8.188])| + EPSG_4210|4210|"Arc 1960"|Geographic2d|[28.85, -11.75, 41.91, 4.63]|Some([28.85, -11.75, 41.909, 4.633])| + EPSG_4211|4211|"Batavia"|Geographic2d|[95.16, -8.91, 117.01, 5.97]|Some([95.157, -8.91, 117.01, 5.971])| + EPSG_4212|4212|"Barbados 1938"|Geographic2d|[-59.71, 13.0, -59.37, 13.39]|Some([-59.712, 12.997, -59.372, 13.387])| + EPSG_4213|4213|"Beduaram"|Geographic2d|[7.81, 12.8, 14.9, 16.7]|Some([7.811, 12.799, 14.901, 16.7])| + EPSG_4214|4214|"Beijing 1954"|Geographic2d|[73.62, 16.7, 134.77, 53.56]|Some([73.62, 16.7, 134.77, 53.56])| + EPSG_4215|4215|"BD50"|Geographic2d|[2.5, 49.5, 6.4, 51.51]|Some([2.5, 49.5, 6.4, 51.51])| + EPSG_4216|4216|"Bermuda 1957"|Geographic2d|[-64.89, 32.21, -64.61, 32.43]|Some([-64.89, 32.209, -64.61, 32.429])| + EPSG_4217|4217|"NAD83 / BLM 59N (ftUS)"|Projected|[167.65, 49.01, 174.01, 56.28]|Some([836746.939, 17810227.779, 2362530.962, 20479907.695])| + EPSG_4218|4218|"Bogota 1975"|Geographic2d|[-79.1, -4.23, -66.87, 13.68]|Some([-79.103, -4.227, -66.874, 13.683])| + EPSG_4219|4219|"Bukit Rimpah"|Geographic2d|[105.07, -3.3, 108.35, -1.44]|Some([105.068, -3.3, 108.349, -1.44])| + EPSG_4220|4220|"Camacupa 1948"|Geographic2d|[8.2, -18.02, 24.09, -5.82]|Some([8.2, -18.02, 24.09, -5.82])| + EPSG_4221|4221|"Campo Inchauspe"|Geographic2d|[-73.59, -54.93, -53.65, -21.78]|Some([-73.589, -54.93, -53.649, -21.781])| + EPSG_4222|4222|"Cape"|Geographic2d|[16.45, -34.88, 32.95, -17.78]|Some([16.45, -34.88, 32.95, -17.78])| + EPSG_4223|4223|"Carthage"|Geographic2d|[7.49, 30.23, 13.67, 38.41]|Some([7.49, 30.228, 13.669, 38.409])| + EPSG_4224|4224|"Chua"|Geographic2d|[-62.57, -31.91, -47.1, -15.37]|Some([-62.57, -31.91, -47.1, -15.369])| + EPSG_4225|4225|"Corrego Alegre 1970-72"|Geographic2d|[-58.16, -33.78, -34.74, -2.68]|Some([-58.159, -33.779, -34.74, -2.68])| + EPSG_4227|4227|"Deir ez Zor"|Geographic2d|[35.04, 32.31, 42.38, 37.3]|Some([35.04, 32.31, 42.379, 37.301])| + EPSG_4229|4229|"Egypt 1907"|Geographic2d|[24.7, 21.89, 37.91, 33.82]|Some([24.698, 21.89, 37.908, 33.82])| + EPSG_4230|4230|"ED50"|Geographic2d|[-16.1, 25.71, 48.61, 84.73]|Some([-16.1, 25.71, 48.61, 84.73])| + EPSG_4231|4231|"ED87"|Geographic2d|[-10.56, 34.88, 38.01, 84.73]|Some([-10.559, 34.881, 38.012, 84.73])| + EPSG_4232|4232|"Fahud"|Geographic2d|[51.99, 16.59, 59.91, 26.42]|Some([51.987, 16.589, 59.907, 26.42])| + EPSG_4236|4236|"Hu Tzu Shan 1950"|Geographic2d|[119.25, 21.87, 122.06, 25.34]|Some([119.242, 21.872, 122.052, 25.342])| + EPSG_4237|4237|"HD72"|Geographic2d|[16.11, 45.74, 22.9, 48.58]|Some([16.111, 45.74, 22.901, 48.58])| + EPSG_4238|4238|"ID74"|Geographic2d|[95.16, -10.98, 141.01, 5.97]|Some([95.16, -10.98, 141.01, 5.97])| + EPSG_4239|4239|"Indian 1954"|Geographic2d|[92.2, 5.63, 105.64, 28.55]|Some([92.2, 5.628, 105.644, 28.55])| + EPSG_4240|4240|"Indian 1975"|Geographic2d|[97.34, 5.63, 105.64, 20.46]|Some([97.344, 5.627, 105.646, 20.459])| + EPSG_4241|4241|"Jamaica 1875"|Geographic2d|[-78.43, 17.64, -76.17, 18.58]|Some([-78.43, 17.64, -76.17, 18.58])| + EPSG_4242|4242|"JAD69"|Geographic2d|[-78.43, 17.64, -76.17, 18.58]|Some([-78.431, 17.637, -76.171, 18.577])| + EPSG_4243|4243|"Kalianpur 1880"|Geographic2d|[60.86, 8.02, 101.17, 37.07]|Some([60.86, 8.02, 101.17, 37.07])| + EPSG_4244|4244|"Kandawala"|Geographic2d|[79.64, 5.86, 81.95, 9.88]|Some([79.638, 5.86, 81.948, 9.88])| + EPSG_4245|4245|"Kertau 1968"|Geographic2d|[99.59, 1.13, 105.82, 7.81]|Some([99.591, 1.13, 105.822, 7.811])| + EPSG_4246|4246|"KOC"|Geographic2d|[46.54, 28.53, 48.48, 30.09]|Some([46.539, 28.527, 48.479, 30.087])| + EPSG_4247|4247|"La Canoa"|Geographic2d|[-73.38, 0.64, -59.8, 12.25]|Some([-73.378, 0.643, -59.798, 12.253])| + EPSG_4248|4248|"PSAD56"|Geographic2d|[-81.41, -43.5, -47.99, 12.68]|Some([-81.41, -43.5, -47.99, 12.68])| + EPSG_4249|4249|"Lake"|Geographic2d|[-72.4, 8.72, -70.78, 11.04]|Some([-72.4, 8.72, -70.78, 11.04])| + EPSG_4250|4250|"Leigon"|Geographic2d|[-3.79, 1.4, 2.1, 11.16]|Some([-3.79, 1.397, 2.1, 11.158])| + EPSG_4251|4251|"Liberia 1964"|Geographic2d|[-11.52, 4.29, -7.36, 8.52]|Some([-11.52, 4.29, -7.36, 8.52])| + EPSG_4252|4252|"Lome"|Geographic2d|[-0.15, 2.91, 2.42, 11.14]|Some([-0.15, 2.91, 2.42, 11.14])| + EPSG_4253|4253|"Luzon 1911"|Geographic2d|[116.89, 4.99, 126.65, 19.45]|Some([116.889, 4.99, 126.65, 19.452])| + EPSG_4254|4254|"Hito XVIII 1963"|Geographic2d|[-74.83, -55.96, -63.73, -51.65]|Some([-74.831, -55.96, -63.73, -51.65])| + EPSG_4255|4255|"Herat North"|Geographic2d|[60.5, 29.4, 74.92, 38.48]|Some([60.498, 29.398, 74.917, 38.478])| + EPSG_4256|4256|"Mahe 1971"|Geographic2d|[55.3, -4.86, 55.59, -4.5]|Some([55.301, -4.859, 55.591, -4.499])| + EPSG_4257|4257|"Makassar"|Geographic2d|[118.71, -6.54, 120.78, -1.88]|Some([118.708, -6.542, 120.778, -1.881])| + EPSG_4258|4258|"ETRS89"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_4259|4259|"Malongo 1987"|Geographic2d|[10.53, -6.04, 12.37, -5.05]|Some([10.53, -6.039, 12.37, -5.049])| + EPSG_4261|4261|"Merchich"|Geographic2d|[-17.16, 20.71, -1.01, 35.97]|Some([-17.16, 20.71, -1.01, 35.973])| + EPSG_4262|4262|"Massawa"|Geographic2d|[36.44, 12.36, 43.31, 18.1]|Some([36.44, 12.361, 43.311, 18.101])| + EPSG_4263|4263|"Minna"|Geographic2d|[2.66, 1.92, 14.65, 13.9]|Some([2.661, 1.919, 14.651, 13.9])| + EPSG_4265|4265|"Monte Mario"|Geographic2d|[5.93, 34.76, 18.99, 47.1]|Some([5.93, 34.76, 18.99, 47.1])| + EPSG_4266|4266|"M'poraloko"|Geographic2d|[7.03, -6.37, 14.52, 2.32]|Some([7.031, -6.371, 14.521, 2.32])| + EPSG_4267|4267|"NAD27"|Geographic2d|[167.65, 7.15, -47.74, 83.17]|Some([-177.889, 7.149, 177.291, 83.17])| + EPSG_4269|4269|"NAD83"|Geographic2d|[167.65, 14.92, -40.73, 86.45]|Some([-177.188, 14.92, 177.758, 86.45])| + EPSG_4270|4270|"Nahrwan 1967"|Geographic2d|[50.55, 22.63, 57.13, 27.05]|Some([50.549, 22.628, 57.13, 27.05])| + EPSG_4271|4271|"Naparima 1972"|Geographic2d|[-60.9, 11.08, -60.44, 11.41]|Some([-60.902, 11.078, -60.442, 11.408])| + EPSG_4272|4272|"NZGD49"|Geographic2d|[165.87, -47.65, 179.27, -33.89]|Some([165.87, -47.652, 179.27, -33.892])| + EPSG_4273|4273|"NGO 1948"|Geographic2d|[4.39, 57.9, 31.32, 71.24]|Some([4.394, 57.898, 31.329, 71.241])| + EPSG_4274|4274|"Datum 73"|Geographic2d|[-9.56, 36.95, -6.19, 42.16]|Some([-9.561, 36.949, -6.191, 42.159])| + EPSG_4275|4275|"NTF"|Geographic2d|[-4.87, 41.31, 9.63, 51.14]|Some([-4.869, 41.31, 9.63, 51.14])| + EPSG_4276|4276|"NSWC 9Z-2"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_4277|4277|"OSGB36"|Geographic2d|[-9.01, 49.75, 2.01, 61.01]|Some([-9.01, 49.75, 2.01, 61.01])| + EPSG_4278|4278|"OSGB70"|Geographic2d|[-8.82, 49.79, 1.92, 60.94]|Some([-8.82, 49.79, 1.92, 60.94])| + EPSG_4279|4279|"OS(SN)80"|Geographic2d|[-10.56, 49.81, 1.84, 60.9]|Some([-10.56, 49.81, 1.84, 60.9])| + EPSG_4281|4281|"Palestine 1923"|Geographic2d|[34.17, 29.18, 39.31, 33.38]|Some([34.169, 29.18, 39.31, 33.38])| + EPSG_4282|4282|"Pointe Noire"|Geographic2d|[8.84, -6.91, 18.65, 3.72]|Some([8.839, -6.908, 18.649, 3.723])| + EPSG_4283|4283|"GDA94"|Geographic2d|[93.41, -60.55, 173.34, -8.47]|Some([93.41, -60.55, 173.34, -8.47])| + EPSG_4284|4284|"Pulkovo 1942"|Geographic2d|[19.57, 35.14, -168.97, 81.91]|Some([-174.695, 35.14, 179.598, 81.91])| + EPSG_4285|4285|"Qatar 1974"|Geographic2d|[50.55, 24.55, 53.04, 27.05]|Some([50.55, 24.549, 53.041, 27.049])| + EPSG_4286|4286|"Qatar 1948"|Geographic2d|[50.69, 24.55, 51.68, 26.2]|Some([50.69, 24.55, 51.68, 26.2])| + EPSG_4288|4288|"Loma Quintana"|Geographic2d|[-73.38, 7.75, -59.8, 12.25]|Some([-73.38, 7.75, -59.8, 12.25])| + EPSG_4289|4289|"Amersfoort"|Geographic2d|[3.2, 50.75, 7.22, 53.7]|Some([3.2, 50.751, 7.221, 53.701])| + EPSG_4292|4292|"Sapper Hill 1943"|Geographic2d|[-61.55, -52.51, -57.6, -50.96]|Some([-61.55, -52.51, -57.596, -50.96])| + EPSG_4293|4293|"Schwarzeck"|Geographic2d|[8.24, -30.64, 25.27, -16.95]|Some([8.24, -30.64, 25.272, -16.949])| + EPSG_4295|4295|"Serindung"|Geographic2d|[108.79, 0.06, 109.78, 2.13]|Some([108.79, 0.06, 109.78, 2.13])| + EPSG_4297|4297|"Tananarive"|Geographic2d|[42.53, -26.59, 51.03, -11.69]|Some([42.53, -26.589, 51.03, -11.689])| + EPSG_4298|4298|"Timbalai 1948"|Geographic2d|[109.31, 0.85, 119.61, 7.67]|Some([109.306, 0.85, 119.608, 7.671])| + EPSG_4299|4299|"TM65"|Geographic2d|[-10.56, 51.39, -5.34, 55.43]|Some([-10.56, 51.39, -5.339, 55.43])| + EPSG_4300|4300|"TM75"|Geographic2d|[-10.56, 51.39, -5.34, 55.43]|Some([-10.56, 51.39, -5.339, 55.43])| + EPSG_4301|4301|"Tokyo"|Geographic2d|[122.83, 20.37, 154.05, 45.54]|Some([122.831, 20.365, 154.055, 45.538])| + EPSG_4302|4302|"Trinidad 1903"|Geographic2d|[-62.09, 9.83, -60.0, 11.51]|Some([-62.091, 9.826, -60.001, 11.506])| + EPSG_4303|4303|"TC(1948)"|Geographic2d|[51.56, 22.63, 56.03, 25.34]|Some([51.56, 22.63, 56.03, 25.34])| + EPSG_4304|4304|"Voirol 1875"|Geographic2d|[-2.95, 31.99, 9.09, 37.14]|Some([-2.947, 31.991, 9.093, 37.141])| + EPSG_4306|4306|"Bern 1938"|Geographic2d|[5.96, 45.82, 10.49, 47.81]|Some([5.96, 45.82, 10.49, 47.81])| + EPSG_4307|4307|"Nord Sahara 1959"|Geographic2d|[-8.67, 18.97, 11.99, 38.8]|Some([-8.669, 18.969, 11.99, 38.8])| + EPSG_4308|4308|"RT38"|Geographic2d|[10.93, 55.28, 24.17, 69.07]|Some([10.93, 55.28, 24.17, 69.07])| + EPSG_4309|4309|"Yacare"|Geographic2d|[-58.49, -35.0, -53.09, -30.09]|Some([-58.49, -35.0, -53.09, -30.09])| + EPSG_4310|4310|"Yoff"|Geographic2d|[-20.22, 10.64, -11.36, 16.7]|Some([-20.222, 10.64, -11.362, 16.701])| + EPSG_4311|4311|"Zanderij"|Geographic2d|[-58.08, 1.83, -52.66, 9.35]|Some([-58.079, 1.833, -52.659, 9.353])| + EPSG_4312|4312|"MGI"|Geographic2d|[9.53, 46.4, 17.17, 49.02]|Some([9.53, 46.4, 17.171, 49.021])| + EPSG_4313|4313|"BD72"|Geographic2d|[2.5, 49.5, 6.4, 51.51]|Some([2.499, 49.501, 6.399, 51.511])| + EPSG_4314|4314|"DHDN"|Geographic2d|[5.86, 47.27, 13.84, 55.09]|Some([5.861, 47.271, 13.842, 55.092])| + EPSG_4315|4315|"Conakry 1905"|Geographic2d|[-15.13, 7.19, -7.65, 12.68]|Some([-15.132, 7.191, -7.652, 12.681])| + EPSG_4316|4316|"Dealul Piscului 1930"|Geographic2d|[20.26, 43.62, 29.74, 48.27]|Some([20.262, 43.623, 29.742, 48.273])| + EPSG_4318|4318|"NGN"|Geographic2d|[46.54, 28.53, 48.48, 30.09]|Some([46.54, 28.53, 48.48, 30.09])| + EPSG_4319|4319|"KUDAMS"|Geographic2d|[47.78, 29.17, 48.16, 29.45]|Some([47.78, 29.17, 48.16, 29.45])| + EPSG_4322|4322|"WGS 72"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-168.0, -90.0, 180.0, 90.0])| + EPSG_4324|4324|"WGS 72BE"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-168.0, -90.0, 180.0, 90.0])| + EPSG_4326|4326|"WGS 84"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_4390|4390|"Kertau 1968 / Johor Grid"|Projected|[102.44, 1.21, 104.6, 2.95]|Some([-124768.299, -92051.457, 115628.424, 100413.314])| + EPSG_4391|4391|"Kertau 1968 / Sembilan and Melaka Grid"|Projected|[101.7, 2.03, 102.71, 3.28]|Some([-26906.513, -76375.651, 85456.099, 61878.067])| + EPSG_4392|4392|"Kertau 1968 / Pahang Grid"|Projected|[101.33, 2.45, 103.67, 4.78]|Some([-122854.838, -139409.878, 137396.219, 118361.646])| + EPSG_4393|4393|"Kertau 1968 / Selangor Grid"|Projected|[100.76, 2.54, 101.97, 3.87]|Some([-104813.384, -70110.438, 29751.812, 76994.709])| + EPSG_4394|4394|"Kertau 1968 / Terengganu Grid"|Projected|[102.38, 3.89, 103.72, 5.9]|Some([-57042.272, -116753.725, 91784.405, 105584.523])| + EPSG_4395|4395|"Kertau 1968 / Pinang Grid"|Projected|[100.12, 5.12, 100.56, 5.59]|Some([-24902.025, -33281.773, 23884.454, 18697.525])| + EPSG_4396|4396|"Kertau 1968 / Kedah and Perlis Grid"|Projected|[99.59, 5.08, 101.12, 6.72]|Some([-116016.424, -97839.333, 53637.913, 83645.824])| + EPSG_4397|4397|"Kertau 1968 / Perak Revised Grid"|Projected|[100.07, 3.66, 102.0, 5.92]|Some([-82812.425, 860.689, 131598.999, 250916.048])| + EPSG_4398|4398|"Kertau 1968 / Kelantan Grid"|Projected|[101.33, 4.54, 102.67, 6.29]|Some([-93857.346, -149678.175, 54845.838, 43920.213])| + EPSG_4399|4399|"NAD27 / BLM 59N (ftUS)"|Projected|[167.65, 49.01, 174.01, 56.28]|Some([836721.116, 17809509.243, 2362554.163, 20479207.706])| + EPSG_4400|4400|"NAD27 / BLM 60N (ftUS)"|Projected|[174.0, 47.92, 180.0, 56.67]|Some([905086.807, 17412005.064, 2375746.533, 20618273.678])| + EPSG_4401|4401|"NAD27 / BLM 1N (ftUS)"|Projected|[-180.0, 47.88, -173.99, 63.21]|Some([904519.712, 17397419.353, 2378766.393, 23006313.217])| + EPSG_4402|4402|"NAD27 / BLM 2N (ftUS)"|Projected|[-174.0, 48.66, -167.99, 73.05]|Some([915642.813, 17681860.096, 2367606.158, 26602162.886])| + EPSG_4403|4403|"NAD27 / BLM 3N (ftUS)"|Projected|[-168.0, 49.52, -161.99, 74.29]|Some([928063.318, 17995519.616, 2355144.192, 27055505.961])| + EPSG_4404|4404|"NAD27 / BLM 4N (ftUS)"|Projected|[-162.0, 50.98, -155.99, 74.71]|Some([949517.677, 18528119.757, 2333618.222, 27209066.429])| + EPSG_4405|4405|"NAD27 / BLM 5N (ftUS)"|Projected|[-156.0, 52.15, -149.99, 74.71]|Some([967037.269, 18955027.217, 2316040.16, 27209066.429])| + EPSG_4406|4406|"NAD27 / BLM 6N (ftUS)"|Projected|[-150.0, 54.05, -143.99, 74.13]|Some([996087.265, 19648478.829, 2286893.227, 26997007.88])| + EPSG_4407|4407|"NAD27 / BLM 7N (ftUS)"|Projected|[-144.0, 53.47, -137.99, 73.59]|Some([987142.162, 19436827.985, 2296273.568, 26799543.751])| + EPSG_4408|4408|"NAD27 / BLM 8N (ftUS)"|Projected|[-138.0, 53.6, -131.99, 73.04]|Some([989555.826, 19484295.951, 2294198.772, 26598401.9])| + EPSG_4409|4409|"NAD27 / BLM 9N (ftUS)"|Projected|[-132.0, 35.38, -126.0, 56.84]|Some([746287.565, 12844417.386, 2534545.775, 20680354.998])| + EPSG_4410|4410|"NAD27 / BLM 10N (ftUS)"|Projected|[-126.0, 30.54, -119.99, 49.09]|Some([696082.338, 11084077.862, 2588184.692, 17853034.835])| + EPSG_4411|4411|"NAD27 / BLM 11N (ftUS)"|Projected|[-120.0, 30.88, -114.0, 49.01]|Some([699675.77, 11207680.357, 2581677.785, 17823789.806])| + EPSG_4412|4412|"NAD27 / BLM 12N (ftUS)"|Projected|[-114.0, 31.33, -108.0, 49.01]|Some([704060.777, 11371289.051, 2577195.288, 17823771.133])| + EPSG_4413|4413|"NAD27 / BLM 13N (ftUS)"|Projected|[-108.0, 28.98, -102.0, 49.01]|Some([681504.616, 10516936.828, 2599649.328, 17823756.413])| + EPSG_4414|4414|"NAD83(HARN) / Guam Map Grid"|Projected|[144.58, 13.18, 145.01, 13.7]|Some([81570.949, 164597.308, 128185.658, 222142.253])| + EPSG_4415|4415|"Katanga 1955 / Katanga Lambert"|Projected|[21.74, -13.46, 30.78, -4.99]|Some([26868.399, 3262.991, 1030881.652, 943383.021])| + EPSG_4417|4417|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 7"|Projected|[19.5, 46.1, 22.5, 49.59]|Some([7384137.279, 5107329.446, 7616108.718, 5496455.837])| + EPSG_4418|4418|"NAD27 / BLM 18N (ftUS)"|Projected|[-78.0, 28.28, -72.0, 45.03]|Some([674840.732, 10262499.563, 2605771.676, 16372786.028])| + EPSG_4419|4419|"NAD27 / BLM 19N (ftUS)"|Projected|[-72.0, 33.61, -65.99, 47.47]|Some([727028.672, 12200467.649, 2556711.406, 17262308.154])| + EPSG_4420|4420|"NAD83 / BLM 60N (ftUS)"|Projected|[174.0, 47.92, 180.0, 56.67]|Some([905109.913, 17412723.11, 2375723.427, 20618971.808])| + EPSG_4421|4421|"NAD83 / BLM 1N (ftUS)"|Projected|[-180.0, 47.88, -173.99, 63.21]|Some([904542.816, 17398137.365, 2378743.212, 23006963.749])| + EPSG_4422|4422|"NAD83 / BLM 2N (ftUS)"|Projected|[-174.0, 48.66, -167.99, 73.05]|Some([915665.935, 17682578.568, 2367582.959, 26602696.843])| + EPSG_4423|4423|"NAD83 / BLM 3N (ftUS)"|Projected|[-168.0, 49.52, -161.99, 74.29]|Some([928086.439, 17996238.094, 2355120.993, 27056022.081])| + EPSG_4424|4424|"NAD83 / BLM 4N (ftUS)"|Projected|[-162.0, 50.98, -155.99, 74.71]|Some([949540.749, 18528837.051, 2333595.073, 27209576.375])| + EPSG_4425|4425|"NAD83 / BLM 5N (ftUS)"|Projected|[-156.0, 52.15, -149.99, 74.71]|Some([967060.258, 18955742.485, 2316017.095, 27209576.375])| + EPSG_4426|4426|"NAD83 / BLM 6N (ftUS)"|Projected|[-150.0, 54.05, -143.99, 74.13]|Some([996110.029, 19649188.788, 2286870.387, 26997526.335])| + EPSG_4427|4427|"NAD83 / BLM 7N (ftUS)"|Projected|[-144.0, 53.47, -137.99, 73.59]|Some([987165.007, 19437481.596, 2295845.256, 26800107.886])| + EPSG_4428|4428|"NAD83 / BLM 8N (ftUS)"|Projected|[-138.0, 53.6, -131.99, 73.04]|Some([989164.092, 19484931.415, 2293839.5, 26599041.154])| + EPSG_4429|4429|"NAD83 / BLM 9N (ftUS)"|Projected|[-132.0, 35.38, -126.0, 56.84]|Some([746308.465, 12845069.269, 2534524.875, 20681020.603])| + EPSG_4430|4430|"NAD83 / BLM 10N (ftUS)"|Projected|[-126.0, 30.54, -119.99, 49.09]|Some([696101.702, 11084702.906, 2587880.763, 17853720.37])| + EPSG_4431|4431|"NAD83 / BLM 11N (ftUS)"|Projected|[-120.0, 30.88, -114.0, 49.01]|Some([699412.464, 11208320.901, 2581420.876, 17824453.545])| + EPSG_4432|4432|"NAD83 / BLM 12N (ftUS)"|Projected|[-114.0, 31.33, -108.0, 49.01]|Some([703845.3, 11371943.002, 2576988.04, 17824453.545])| + EPSG_4433|4433|"NAD83 / BLM 13N (ftUS)"|Projected|[-108.0, 28.98, -102.0, 49.01]|Some([681339.21, 10517596.388, 2599494.13, 17824453.545])| + EPSG_4434|4434|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 8"|Projected|[22.5, 47.76, 22.9, 49.1]|Some([8387666.282, 5292453.334, 8419671.647, 5441957.452])| + EPSG_4437|4437|"NAD83(NSRS2007) / Puerto Rico and Virgin Is."|Projected|[-67.97, 17.62, -64.51, 18.57]|Some([36907.961, 176388.461, 404129.026, 282602.555])| + EPSG_4438|4438|"NAD83 / BLM 18N (ftUS)"|Projected|[-78.0, 28.28, -72.0, 45.03]|Some([674945.403, 10263168.576, 2605887.937, 16373530.689])| + EPSG_4439|4439|"NAD83 / BLM 19N (ftUS)"|Projected|[-72.0, 33.61, -65.99, 47.47]|Some([727187.555, 12201140.075, 2556690.973, 17263049.514])| + EPSG_4455|4455|"NAD27 / Pennsylvania South"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([1217876.851, 137211.918, 2852252.465, 687044.567])| + EPSG_4456|4456|"NAD27 / New York Long Island"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([1927539.312, 89069.753, 2611935.205, 399063.779])| + EPSG_4457|4457|"NAD83 / South Dakota North (ftUS)"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([900410.112, 111804.729, 2900220.208, 797752.314])| + EPSG_4462|4462|"WGS 84 / Australian Centre for Remote Sensing Lambert"|Projected|[112.85, -43.7, 153.69, -9.86]|Some([-2158889.502, -2012491.045, 2442558.21, 1901974.751])| + EPSG_4463|4463|"RGSPM06"|Geographic2d|[-57.1, 43.41, -55.9, 47.37]|Some([-57.1, 43.41, -55.9, 47.37])| + EPSG_4467|4467|"RGSPM06 / UTM zone 21N"|Projected|[-57.1, 43.41, -55.9, 47.37]|Some([491903.569, 4806346.43, 589061.055, 5246868.778])| + EPSG_4470|4470|"RGM04"|Geographic2d|[43.68, -14.49, 46.7, -11.33]|Some([43.68, -14.49, 46.7, -11.33])| + EPSG_4471|4471|"RGM04 / UTM zone 38S"|Projected|[43.68, -14.49, 46.7, -11.33]|Some([355949.066, 8397400.8, 685530.186, 8747531.889])| + EPSG_4475|4475|"Cadastre 1997"|Geographic2d|[44.98, -13.05, 45.35, -12.61]|Some([44.978, -13.047, 45.348, -12.607])| + EPSG_4483|4483|"Mexico ITRF92"|Geographic2d|[-122.19, 12.1, -84.64, 32.72]|Some([-122.19, 12.1, -84.64, 32.72])| + EPSG_4484|4484|"Mexico ITRF92 / UTM zone 11N"|Projected|[-122.19, 15.01, -114.0, 32.72]|Some([-58603.971, 1659432.041, 822635.936, 3632180.201])| + EPSG_4485|4485|"Mexico ITRF92 / UTM zone 12N"|Projected|[-114.0, 15.09, -108.0, 32.27]|Some([177484.621, 1668280.432, 822515.379, 3574315.953])| + EPSG_4486|4486|"Mexico ITRF92 / UTM zone 13N"|Projected|[-108.0, 14.05, -102.0, 31.79]|Some([175966.325, 1553256.07, 824033.675, 3521079.182])| + EPSG_4487|4487|"Mexico ITRF92 / UTM zone 14N"|Projected|[-102.0, 12.3, -96.0, 29.81]|Some([173651.794, 1359727.406, 826348.206, 3301508.05])| + EPSG_4488|4488|"Mexico ITRF92 / UTM zone 15N"|Projected|[-96.0, 12.1, -90.0, 26.0]|Some([173406.546, 1337611.479, 826593.454, 2879132.534])| + EPSG_4489|4489|"Mexico ITRF92 / UTM zone 16N"|Projected|[-90.0, 17.81, -84.64, 25.77]|Some([181954.812, 1969165.279, 750159.873, 2853640.128])| + EPSG_4490|4490|"China Geodetic Coordinate System 2000"|Geographic2d|[73.62, 16.7, 134.77, 53.56]|Some([73.62, 16.7, 134.77, 53.56])| + EPSG_4491|4491|"CGCS2000 / Gauss-Kruger zone 13"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([13374665.405, 3921191.384, 13773407.593, 4552715.218])| + EPSG_4492|4492|"CGCS2000 / Gauss-Kruger zone 14"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([14206116.832, 3227003.299, 14791935.978, 5237243.202])| + EPSG_4493|4493|"CGCS2000 / Gauss-Kruger zone 15"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([15203003.725, 3023088.229, 15796996.275, 5453980.627])| + EPSG_4494|4494|"CGCS2000 / Gauss-Kruger zone 16"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([16204050.896, 3066304.793, 16796936.119, 5311696.644])| + EPSG_4495|4495|"CGCS2000 / Gauss-Kruger zone 17"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([17188253.523, 2337470.201, 17812786.344, 4787117.846])| + EPSG_4496|4496|"CGCS2000 / Gauss-Kruger zone 18"|Projected|[102.0, 17.75, 108.0, 42.47]|Some([18181721.149, 1963312.193, 18818278.851, 4708206.873])| + EPSG_4497|4497|"CGCS2000 / Gauss-Kruger zone 19"|Projected|[108.0, 16.7, 114.0, 45.11]|Some([19179915.673, 1847106.867, 19820084.327, 5001549.833])| + EPSG_4498|4498|"CGCS2000 / Gauss-Kruger zone 20"|Projected|[114.0, 19.02, 120.0, 51.52]|Some([20184047.248, 2103881.963, 20815952.752, 5714206.25])| + EPSG_4499|4499|"CGCS2000 / Gauss-Kruger zone 21"|Projected|[120.0, 24.64, 126.0, 53.56]|Some([21196182.076, 2726176.891, 21803817.924, 5941131.595])| + EPSG_4500|4500|"CGCS2000 / Gauss-Kruger zone 22"|Projected|[126.0, 29.7, 132.0, 52.79]|Some([22209606.469, 3286858.422, 22790393.531, 5855471.683])| + EPSG_4501|4501|"CGCS2000 / Gauss-Kruger zone 23"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([23263541.739, 4987192.755, 23482969.275, 5367255.828])| + EPSG_4502|4502|"CGCS2000 / Gauss-Kruger CM 75E"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([374665.405, 3921191.384, 773407.593, 4552715.218])| + EPSG_4503|4503|"CGCS2000 / Gauss-Kruger CM 81E"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([206116.832, 3227003.299, 791935.978, 5237243.202])| + EPSG_4504|4504|"CGCS2000 / Gauss-Kruger CM 87E"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([203003.725, 3023088.229, 796996.275, 5453980.627])| + EPSG_4505|4505|"CGCS2000 / Gauss-Kruger CM 93E"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([204050.896, 3066304.793, 796936.119, 5311696.644])| + EPSG_4506|4506|"CGCS2000 / Gauss-Kruger CM 99E"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([188253.523, 2337470.201, 812786.344, 4787117.846])| + EPSG_4507|4507|"CGCS2000 / Gauss-Kruger CM 105E"|Projected|[102.0, 17.75, 108.0, 42.47]|Some([181721.149, 1963312.193, 818278.851, 4708206.873])| + EPSG_4508|4508|"CGCS2000 / Gauss-Kruger CM 111E"|Projected|[108.0, 16.7, 114.0, 45.11]|Some([179915.673, 1847106.867, 820084.327, 5001549.833])| + EPSG_4509|4509|"CGCS2000 / Gauss-Kruger CM 117E"|Projected|[114.0, 19.02, 120.0, 51.52]|Some([184047.248, 2103881.963, 815952.752, 5714206.25])| + EPSG_4510|4510|"CGCS2000 / Gauss-Kruger CM 123E"|Projected|[120.0, 24.64, 126.0, 53.56]|Some([196182.076, 2726176.891, 803817.924, 5941131.595])| + EPSG_4511|4511|"CGCS2000 / Gauss-Kruger CM 129E"|Projected|[126.0, 29.7, 132.0, 52.79]|Some([209606.469, 3286858.422, 790393.531, 5855471.683])| + EPSG_4512|4512|"CGCS2000 / Gauss-Kruger CM 135E"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([263541.739, 4987192.755, 482969.275, 5367255.828])| + EPSG_4513|4513|"CGCS2000 / 3-degree Gauss-Kruger zone 25"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([25375272.497, 3964461.393, 25635574.13, 4502787.599])| + EPSG_4514|4514|"CGCS2000 / 3-degree Gauss-Kruger zone 26"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([26356780.75, 3434300.415, 26643219.25, 4633842.661])| + EPSG_4515|4515|"CGCS2000 / 3-degree Gauss-Kruger zone 27"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([27355189.724, 3314570.807, 27645775.79, 5083856.921])| + EPSG_4516|4516|"CGCS2000 / 3-degree Gauss-Kruger zone 28"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([28352803.505, 3127255.351, 28647196.495, 5233908.467])| + EPSG_4517|4517|"CGCS2000 / 3-degree Gauss-Kruger zone 29"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([29352176.077, 3076278.179, 29647823.923, 5450729.139])| + EPSG_4518|4518|"CGCS2000 / 3-degree Gauss-Kruger zone 30"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([30350541.597, 3023088.229, 30649458.403, 5366231.031])| + EPSG_4519|4519|"CGCS2000 / 3-degree Gauss-Kruger zone 31"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([31352054.432, 3066304.783, 31647945.568, 5000486.506])| + EPSG_4520|4520|"CGCS2000 / 3-degree Gauss-Kruger zone 32"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([32352762.297, 3123930.658, 32648219.414, 4930490.218])| + EPSG_4521|4521|"CGCS2000 / 3-degree Gauss-Kruger zone 33"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([33344482.324, 2370686.399, 33655517.676, 4737149.551])| + EPSG_4522|4522|"CGCS2000 / 3-degree Gauss-Kruger zone 34"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([34344166.571, 2337470.193, 34655833.429, 4729373.218])| + EPSG_4523|4523|"CGCS2000 / 3-degree Gauss-Kruger zone 35"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([35345643.071, 2489167.32, 35655386.144, 4676066.763])| + EPSG_4524|4524|"CGCS2000 / 3-degree Gauss-Kruger zone 36"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([36341298.828, 2012011.422, 36659759.378, 4704948.483])| + EPSG_4525|4525|"CGCS2000 / 3-degree Gauss-Kruger zone 37"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([37341226.58, 2003156.847, 37658773.42, 4998263.826])| + EPSG_4526|4526|"CGCS2000 / 3-degree Gauss-Kruger zone 38"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([38344577.879, 2380651.492, 38655422.121, 5036050.383])| + EPSG_4527|4527|"CGCS2000 / 3-degree Gauss-Kruger zone 39"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([39345754.302, 2500241.088, 39654245.698, 5528578.958])| + EPSG_4528|4528|"CGCS2000 / 3-degree Gauss-Kruger zone 40"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([40347872.247, 2702916.029, 40652127.753, 5912395.2])| + EPSG_4529|4529|"CGCS2000 / 3-degree Gauss-Kruger zone 41"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([41352748.569, 3122822.417, 41647251.431, 5937990.422])| + EPSG_4530|4530|"CGCS2000 / 3-degree Gauss-Kruger zone 42"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([42372262.467, 4450625.957, 42627737.533, 5897928.745])| + EPSG_4531|4531|"CGCS2000 / 3-degree Gauss-Kruger zone 43"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([43374503.765, 4581664.561, 43625496.235, 5569731.708])| + EPSG_4532|4532|"CGCS2000 / 3-degree Gauss-Kruger zone 44"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([44376543.131, 4698289.372, 44623456.869, 5417367.634])| + EPSG_4533|4533|"CGCS2000 / 3-degree Gauss-Kruger zone 45"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([45383491.843, 5079439.176, 45482969.275, 5363992.608])| + EPSG_4534|4534|"CGCS2000 / 3-degree Gauss-Kruger CM 75E"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([375272.497, 3964461.393, 635574.13, 4502787.599])| + EPSG_4535|4535|"CGCS2000 / 3-degree Gauss-Kruger CM 78E"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([356780.75, 3434300.415, 643219.25, 4633842.661])| + EPSG_4536|4536|"CGCS2000 / 3-degree Gauss-Kruger CM 81E"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([355189.724, 3314570.807, 645775.79, 5083856.921])| + EPSG_4537|4537|"CGCS2000 / 3-degree Gauss-Kruger CM 84E"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([352803.505, 3127255.351, 647196.495, 5233908.467])| + EPSG_4538|4538|"CGCS2000 / 3-degree Gauss-Kruger CM 87E"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([352176.077, 3076278.179, 647823.923, 5450729.139])| + EPSG_4539|4539|"CGCS2000 / 3-degree Gauss-Kruger CM 90E"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([350541.597, 3023088.229, 649458.403, 5366231.031])| + EPSG_4540|4540|"CGCS2000 / 3-degree Gauss-Kruger CM 93E"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([352054.432, 3066304.783, 647945.568, 5000486.506])| + EPSG_4541|4541|"CGCS2000 / 3-degree Gauss-Kruger CM 96E"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([352762.297, 3123930.658, 648219.414, 4930490.218])| + EPSG_4542|4542|"CGCS2000 / 3-degree Gauss-Kruger CM 99E"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([344482.324, 2370686.399, 655517.676, 4737149.551])| + EPSG_4543|4543|"CGCS2000 / 3-degree Gauss-Kruger CM 102E"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([344166.571, 2337470.193, 655833.429, 4729373.218])| + EPSG_4544|4544|"CGCS2000 / 3-degree Gauss-Kruger CM 105E"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([345643.071, 2489167.32, 655386.144, 4676066.763])| + EPSG_4545|4545|"CGCS2000 / 3-degree Gauss-Kruger CM 108E"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([341298.828, 2012011.422, 659759.378, 4704948.483])| + EPSG_4546|4546|"CGCS2000 / 3-degree Gauss-Kruger CM 111E"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([341226.58, 2003156.847, 658773.42, 4998263.826])| + EPSG_4547|4547|"CGCS2000 / 3-degree Gauss-Kruger CM 114E"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([344577.879, 2380651.492, 655422.121, 5036050.383])| + EPSG_4548|4548|"CGCS2000 / 3-degree Gauss-Kruger CM 117E"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([345754.302, 2500241.088, 654245.698, 5528578.958])| + EPSG_4549|4549|"CGCS2000 / 3-degree Gauss-Kruger CM 120E"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([347872.247, 2702916.029, 652127.753, 5912395.2])| + EPSG_4550|4550|"CGCS2000 / 3-degree Gauss-Kruger CM 123E"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([352748.569, 3122822.417, 647251.431, 5937990.422])| + EPSG_4551|4551|"CGCS2000 / 3-degree Gauss-Kruger CM 126E"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([372262.467, 4450625.957, 627737.533, 5897928.745])| + EPSG_4552|4552|"CGCS2000 / 3-degree Gauss-Kruger CM 129E"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([374503.765, 4581664.561, 625496.235, 5569731.708])| + EPSG_4553|4553|"CGCS2000 / 3-degree Gauss-Kruger CM 132E"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([376543.131, 4698289.372, 623456.869, 5417367.634])| + EPSG_4554|4554|"CGCS2000 / 3-degree Gauss-Kruger CM 135E"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([383491.843, 5079439.176, 482969.275, 5363992.608])| + EPSG_4555|4555|"New Beijing"|Geographic2d|[73.62, 18.11, 134.77, 53.56]|Some([73.62, 18.11, 134.77, 53.56])| + EPSG_4558|4558|"RRAF 1991"|Geographic2d|[-63.66, 14.08, -57.52, 18.53]|Some([-63.66, 14.08, -57.52, 18.53])| + EPSG_4559|4559|"RRAF 1991 / UTM zone 20N"|Projected|[-63.66, 14.08, -60.0, 18.31]|Some([428749.413, 1556574.512, 823991.366, 2027093.391])| + EPSG_4568|4568|"New Beijing / Gauss-Kruger zone 13"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([13374663.303, 3921260.898, 13773412.178, 4552795.683])| + EPSG_4569|4569|"New Beijing / Gauss-Kruger zone 14"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([14206111.89, 3227060.674, 14791940.888, 5237335.439])| + EPSG_4570|4570|"New Beijing / Gauss-Kruger zone 15"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([15202998.726, 3023142.02, 15797001.274, 5454076.57])| + EPSG_4571|4571|"New Beijing / Gauss-Kruger zone 16"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([16204045.915, 3066359.345, 16796941.116, 5311790.155])| + EPSG_4572|4572|"New Beijing / Gauss-Kruger zone 17"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([17188248.264, 2337511.888, 17812791.62, 4787202.354])| + EPSG_4573|4573|"New Beijing / Gauss-Kruger zone 18"|Projected|[102.0, 21.53, 108.0, 42.47]|Some([18189093.408, 2381801.202, 18810906.592, 4708290.022])| + EPSG_4574|4574|"New Beijing / Gauss-Kruger zone 19"|Projected|[108.0, 18.11, 114.0, 45.11]|Some([19182359.367, 2003192.605, 19817640.633, 5001638.028])| + EPSG_4575|4575|"New Beijing / Gauss-Kruger zone 20"|Projected|[114.0, 22.14, 120.0, 51.52]|Some([20190411.347, 2449346.515, 20809588.653, 5714306.627])| + EPSG_4576|4576|"New Beijing / Gauss-Kruger zone 21"|Projected|[120.0, 26.34, 126.0, 53.56]|Some([21200428.116, 2914555.258, 21799571.884, 5941235.827])| + EPSG_4577|4577|"New Beijing / Gauss-Kruger zone 22"|Projected|[126.0, 40.89, 132.0, 52.79]|Some([22247154.322, 4528437.525, 22752845.678, 5855574.461])| + EPSG_4578|4578|"New Beijing / Gauss-Kruger zone 23"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([23263537.792, 4987280.706, 23482968.991, 5367350.289])| + EPSG_4579|4579|"New Beijing / Gauss-Kruger CM 75E"|Projected|[73.62, 35.42, 78.01, 41.07]|Some([374663.303, 3921260.898, 773412.178, 4552795.683])| + EPSG_4580|4580|"New Beijing / Gauss-Kruger CM 81E"|Projected|[77.98, 29.16, 84.0, 47.23]|Some([206111.89, 3227060.674, 791940.888, 5237335.439])| + EPSG_4581|4581|"New Beijing / Gauss-Kruger CM 87E"|Projected|[84.0, 27.32, 90.0, 49.18]|Some([202998.726, 3023142.02, 797001.274, 5454076.57])| + EPSG_4582|4582|"New Beijing / Gauss-Kruger CM 93E"|Projected|[90.0, 27.71, 96.01, 47.9]|Some([204045.915, 3066359.345, 796941.116, 5311790.155])| + EPSG_4583|4583|"New Beijing / Gauss-Kruger CM 99E"|Projected|[96.0, 21.13, 102.01, 43.18]|Some([188248.264, 2337511.888, 812791.62, 4787202.354])| + EPSG_4584|4584|"New Beijing / Gauss-Kruger CM 105E"|Projected|[102.0, 21.53, 108.0, 42.47]|Some([189093.408, 2381801.202, 810906.592, 4708290.022])| + EPSG_4585|4585|"New Beijing / Gauss-Kruger CM 111E"|Projected|[108.0, 18.11, 114.0, 45.11]|Some([182359.367, 2003192.605, 817640.633, 5001638.028])| + EPSG_4586|4586|"New Beijing / Gauss-Kruger CM 117E"|Projected|[114.0, 22.14, 120.0, 51.52]|Some([190411.347, 2449346.515, 809588.653, 5714306.627])| + EPSG_4587|4587|"New Beijing / Gauss-Kruger CM 123E"|Projected|[120.0, 26.34, 126.0, 53.56]|Some([200428.116, 2914555.258, 799571.884, 5941235.827])| + EPSG_4588|4588|"New Beijing / Gauss-Kruger CM 129E"|Projected|[126.0, 40.89, 132.0, 52.79]|Some([247154.322, 4528437.525, 752845.678, 5855574.461])| + EPSG_4589|4589|"New Beijing / Gauss-Kruger CM 135E"|Projected|[132.0, 45.02, 134.77, 48.4]|Some([263537.792, 4987280.706, 482968.991, 5367350.289])| + EPSG_4600|4600|"Anguilla 1957"|Geographic2d|[-63.22, 18.11, -62.92, 18.33]|Some([-63.221, 18.115, -62.921, 18.335])| + EPSG_4601|4601|"Antigua 1943"|Geographic2d|[-61.95, 16.94, -61.61, 17.22]|Some([-61.948, 16.941, -61.608, 17.221])| + EPSG_4602|4602|"Dominica 1945"|Geographic2d|[-61.55, 15.14, -61.2, 15.69]|Some([-61.559, 15.136, -61.209, 15.686])| + EPSG_4603|4603|"Grenada 1953"|Geographic2d|[-61.84, 11.94, -61.35, 12.57]|Some([-61.842, 11.94, -61.35, 12.57])| + EPSG_4604|4604|"Montserrat 1958"|Geographic2d|[-62.29, 16.62, -62.08, 16.87]|Some([-62.293, 16.618, -62.083, 16.868])| + EPSG_4605|4605|"St. Kitts 1955"|Geographic2d|[-62.92, 17.06, -62.5, 17.46]|Some([-62.921, 17.059, -62.501, 17.459])| + EPSG_4606|4606|"St. Lucia 1955"|Geographic2d|[-61.13, 13.66, -60.82, 14.16]|Some([-61.129, 13.658, -60.819, 14.158])| + EPSG_4607|4607|"St. Vincent 1945"|Geographic2d|[-61.52, 12.54, -61.07, 13.44]|Some([-61.523, 12.539, -61.073, 13.439])| + EPSG_4608|4608|"NAD27(76)"|Geographic2d|[-95.16, 41.67, -74.35, 56.9]|Some([-95.16, 41.67, -74.35, 56.9])| + EPSG_4609|4609|"NAD27(CGQ77)"|Geographic2d|[-79.85, 44.99, -57.1, 62.62]|Some([-79.85, 44.99, -57.1, 62.62])| + EPSG_4610|4610|"Xian 1980"|Geographic2d|[73.62, 18.11, 134.77, 53.56]|Some([73.62, 18.11, 134.77, 53.56])| + EPSG_4611|4611|"Hong Kong 1980"|Geographic2d|[113.76, 22.13, 114.51, 22.58]|Some([113.758, 22.132, 114.508, 22.582])| + EPSG_4612|4612|"JGD2000"|Geographic2d|[122.38, 17.09, 157.65, 46.05]|Some([122.38, 17.09, 157.65, 46.05])| + EPSG_4613|4613|"Segara"|Geographic2d|[114.55, -4.24, 119.06, 4.29]|Some([114.549, -4.241, 119.06, 4.29])| + EPSG_4614|4614|"QND95"|Geographic2d|[50.69, 24.55, 51.68, 26.2]|Some([50.691, 24.549, 51.681, 26.199])| + EPSG_4615|4615|"Porto Santo"|Geographic2d|[-17.31, 32.35, -16.23, 33.15]|Some([-17.306, 32.346, -16.226, 33.146])| + EPSG_4616|4616|"Selvagem Grande"|Geographic2d|[-16.11, 29.98, -15.79, 30.21]|Some([-16.108, 29.979, -15.788, 30.209])| + EPSG_4617|4617|"NAD83(CSRS)"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_4618|4618|"SAD69"|Geographic2d|[-91.72, -55.96, -25.28, 12.52]|Some([-91.72, -55.96, -25.28, 12.52])| + EPSG_4619|4619|"SWEREF99"|Geographic2d|[10.03, 54.96, 24.17, 69.07]|Some([10.03, 54.96, 24.17, 69.07])| + EPSG_4620|4620|"Point 58"|Geographic2d|[-17.19, 10.26, 30.42, 15.7]|Some([-17.19, 10.26, 30.42, 15.7])| + EPSG_4621|4621|"Fort Marigot"|Geographic2d|[-63.21, 17.82, -62.73, 18.17]|Some([-63.212, 17.824, -62.732, 18.174])| + EPSG_4622|4622|"Guadeloupe 1948"|Geographic2d|[-61.85, 15.8, -60.97, 16.55]|Some([-61.846, 15.803, -60.966, 16.553])| + EPSG_4623|4623|"CSG67"|Geographic2d|[-54.45, 3.43, -51.61, 5.81]|Some([-54.45, 3.429, -51.61, 5.809])| + EPSG_4624|4624|"RGFG95"|Geographic2d|[-54.61, 2.11, -49.45, 8.88]|Some([-54.61, 2.11, -49.45, 8.88])| + EPSG_4625|4625|"Martinique 1938"|Geographic2d|[-61.29, 14.35, -60.76, 14.93]|Some([-61.294, 14.348, -60.764, 14.928])| + EPSG_4626|4626|"Reunion 1947"|Geographic2d|[55.16, -21.42, 55.91, -20.81]|Some([55.166, -21.408, 55.916, -20.798])| + EPSG_4627|4627|"RGR92"|Geographic2d|[51.83, -24.72, 58.24, -18.28]|Some([51.83, -24.72, 58.24, -18.28])| + EPSG_4628|4628|"Tahiti 52"|Geographic2d|[-150.0, -17.93, -149.09, -17.41]|Some([-150.0, -17.931, -149.09, -17.411])| + EPSG_4629|4629|"Tahaa 54"|Geographic2d|[-151.91, -16.96, -150.89, -16.17]|Some([-151.907, -16.961, -150.887, -16.171])| + EPSG_4630|4630|"IGN72 Nuku Hiva"|Geographic2d|[-140.31, -9.57, -139.44, -8.72]|Some([-140.309, -9.572, -139.439, -8.72])| + EPSG_4632|4632|"Combani 1950"|Geographic2d|[44.98, -13.05, 45.35, -12.61]|Some([44.978, -13.047, 45.348, -12.607])| + EPSG_4633|4633|"IGN56 Lifou"|Geographic2d|[166.98, -21.24, 167.52, -20.62]|Some([166.983, -21.238, 167.523, -20.618])| + EPSG_4636|4636|"Petrels 1972"|Geographic2d|[139.44, -66.78, 141.5, -66.1]|Some([139.449, -66.78, 141.509, -66.1])| + EPSG_4637|4637|"Perroud 1950"|Geographic2d|[136.0, -67.13, 142.0, -65.61]|Some([136.007, -67.13, 142.007, -65.61])| + EPSG_4638|4638|"Saint Pierre et Miquelon 1950"|Geographic2d|[-56.48, 46.69, -56.07, 47.19]|Some([-56.483, 46.688, -56.073, 47.188])| + EPSG_4639|4639|"MOP78"|Geographic2d|[-176.25, -13.41, -176.07, -13.16]|Some([-176.251, -13.409, -176.071, -13.159])| + EPSG_4641|4641|"IGN53 Mare"|Geographic2d|[167.75, -21.71, 168.19, -21.32]|Some([167.752, -21.709, 168.192, -21.319])| + EPSG_4642|4642|"ST84 Ile des Pins"|Geographic2d|[167.36, -22.73, 167.61, -22.49]|Some([167.357, -22.733, 167.607, -22.493])| + EPSG_4643|4643|"ST71 Belep"|Geographic2d|[163.54, -19.85, 163.75, -19.5]|Some([163.537, -19.853, 163.747, -19.503])| + EPSG_4644|4644|"NEA74 Noumea"|Geographic2d|[166.35, -22.37, 166.54, -22.19]|Some([166.347, -22.373, 166.537, -22.193])| + EPSG_4646|4646|"Grand Comoros"|Geographic2d|[43.16, -11.99, 43.55, -11.31]|Some([43.151, -11.987, 43.541, -11.307])| + EPSG_4647|4647|"ETRS89 / UTM zone 32N (zE-N)"|Projected|[6.0, 47.27, 12.01, 55.47]|Some([32273082.89, 5235168.953, 32727673.446, 6151213.241])| + EPSG_4652|4652|"New Beijing / 3-degree Gauss-Kruger zone 25"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([25375270.405, 3964531.66, 25635576.403, 4502867.203])| + EPSG_4653|4653|"New Beijing / 3-degree Gauss-Kruger zone 26"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([26356778.343, 3434361.425, 26643221.657, 4633924.528])| + EPSG_4654|4654|"New Beijing / 3-degree Gauss-Kruger zone 27"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([27355187.289, 3314629.719, 27645778.241, 5083946.531])| + EPSG_4655|4655|"New Beijing / 3-degree Gauss-Kruger zone 28"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([28352801.029, 3127310.974, 28647198.971, 5234000.648])| + EPSG_4656|4656|"New Beijing / 3-degree Gauss-Kruger zone 29"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([29352173.589, 3076332.906, 29647826.411, 5450825.027])| + EPSG_4657|4657|"Reykjavik 1900"|Geographic2d|[-24.66, 63.34, -13.38, 66.59]|Some([-24.664, 63.338, -13.384, 66.589])| + EPSG_4658|4658|"Hjorsey 1955"|Geographic2d|[-24.66, 63.34, -13.38, 66.59]|Some([-24.66, 63.34, -13.381, 66.59])| + EPSG_4659|4659|"ISN93"|Geographic2d|[-30.87, 59.96, -5.55, 69.59]|Some([-30.87, 59.96, -5.55, 69.59])| + EPSG_4660|4660|"Helle 1954"|Geographic2d|[-9.17, 70.75, -7.87, 71.24]|Some([-9.175, 70.751, -7.874, 71.241])| + EPSG_4661|4661|"LKS-92"|Geographic2d|[19.06, 55.67, 28.24, 58.09]|Some([19.06, 55.67, 28.24, 58.09])| + EPSG_4662|4662|"IGN72 Grande Terre"|Geographic2d|[163.92, -22.45, 167.09, -20.03]|Some([163.917, -22.453, 167.087, -20.033])| + EPSG_4663|4663|"Porto Santo 1995"|Geographic2d|[-17.31, 32.35, -16.23, 33.15]|Some([-17.31, 32.35, -16.226, 33.146])| + EPSG_4664|4664|"Azores Oriental 1995"|Geographic2d|[-25.92, 36.87, -24.72, 37.96]|Some([-25.92, 36.869, -24.72, 37.959])| + EPSG_4665|4665|"Azores Central 1995"|Geographic2d|[-28.9, 38.32, -26.97, 39.14]|Some([-28.901, 38.32, -26.971, 39.14])| + EPSG_4666|4666|"Lisbon 1890"|Geographic2d|[-9.56, 36.95, -6.19, 42.16]|Some([-9.559, 36.948, -6.188, 42.159])| + EPSG_4667|4667|"IKBD-92"|Geographic2d|[46.36, 29.06, 48.61, 30.32]|Some([46.36, 29.06, 48.61, 30.32])| + EPSG_4668|4668|"ED79"|Geographic2d|[-10.56, 34.88, 38.01, 84.73]|Some([-10.559, 34.881, 38.012, 84.73])| + EPSG_4669|4669|"LKS94"|Geographic2d|[19.02, 53.89, 26.82, 56.45]|Some([19.02, 53.89, 26.82, 56.45])| + EPSG_4670|4670|"IGM95"|Geographic2d|[5.93, 34.76, 18.99, 47.1]|Some([5.93, 34.76, 18.99, 47.1])| + EPSG_4671|4671|"Voirol 1879"|Geographic2d|[-2.95, 31.99, 9.09, 37.14]|Some([-2.95, 31.99, 9.09, 37.14])| + EPSG_4672|4672|"Chatham Islands 1971"|Geographic2d|[-177.25, -44.64, -175.54, -43.3]|Some([-177.251, -44.64, -175.541, -43.3])| + EPSG_4673|4673|"Chatham Islands 1979"|Geographic2d|[-177.25, -44.64, -175.54, -43.3]|Some([-177.251, -44.64, -175.541, -43.3])| + EPSG_4674|4674|"SIRGAS 2000"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_4675|4675|"Guam 1963"|Geographic2d|[144.58, 13.18, 146.12, 20.61]|Some([144.578, 13.179, 146.12, 20.61])| + EPSG_4676|4676|"Vientiane 1982"|Geographic2d|[100.09, 13.92, 107.64, 22.5]|Some([100.09, 13.92, 107.64, 22.5])| + EPSG_4677|4677|"Lao 1993"|Geographic2d|[100.09, 13.92, 107.64, 22.5]|Some([100.09, 13.92, 107.64, 22.5])| + EPSG_4678|4678|"Lao 1997"|Geographic2d|[100.09, 13.92, 107.64, 22.5]|Some([100.09, 13.92, 107.64, 22.5])| + EPSG_4679|4679|"Jouik 1961"|Geographic2d|[-17.08, 19.37, -15.88, 21.34]|Some([-17.082, 19.369, -15.882, 21.339])| + EPSG_4680|4680|"Nouakchott 1965"|Geographic2d|[-16.57, 16.81, -15.59, 19.41]|Some([-16.57, 16.81, -15.59, 19.41])| + EPSG_4682|4682|"Gulshan 303"|Geographic2d|[88.01, 18.56, 92.67, 26.64]|Some([88.012, 18.559, 92.673, 26.64])| + EPSG_4683|4683|"PRS92"|Geographic2d|[116.04, 3.0, 129.95, 22.18]|Some([116.038, 3.0, 129.949, 22.182])| + EPSG_4684|4684|"Gan 1970"|Geographic2d|[72.81, -0.69, 73.69, 7.08]|Some([72.81, -0.69, 73.69, 7.079])| + EPSG_4686|4686|"MAGNA-SIRGAS"|Geographic2d|[-84.77, -4.23, -66.87, 15.51]|Some([-84.77, -4.23, -66.87, 15.51])| + EPSG_4687|4687|"RGPF"|Geographic2d|[-158.13, -31.24, -131.97, -4.52]|Some([-158.13, -31.24, -131.97, -4.52])| + EPSG_4688|4688|"Fatu Iva 72"|Geographic2d|[-138.75, -10.6, -138.54, -10.36]|Some([-138.748, -10.601, -138.538, -10.361])| + EPSG_4689|4689|"IGN63 Hiva Oa"|Geographic2d|[-139.23, -10.08, -138.75, -9.64]|Some([-139.232, -10.08, -138.75, -9.64])| + EPSG_4690|4690|"Tahiti 79"|Geographic2d|[-149.7, -17.93, -149.09, -17.44]|Some([-149.7, -17.931, -149.09, -17.441])| + EPSG_4691|4691|"Moorea 87"|Geographic2d|[-150.0, -17.63, -149.73, -17.41]|Some([-150.0, -17.631, -149.73, -17.411])| + EPSG_4692|4692|"Maupiti 83"|Geographic2d|[-152.39, -16.57, -152.14, -16.34]|Some([-152.39, -16.57, -152.14, -16.34])| + EPSG_4693|4693|"Nakhl-e Ghanem"|Geographic2d|[51.8, 27.3, 53.01, 28.2]|Some([51.8, 27.3, 53.01, 28.2])| + EPSG_4694|4694|"POSGAR 94"|Geographic2d|[-73.59, -58.41, -52.63, -21.78]|Some([-73.59, -58.41, -52.63, -21.78])| + EPSG_4695|4695|"Katanga 1955"|Geographic2d|[21.74, -13.46, 30.78, -4.99]|Some([21.74, -13.46, 30.78, -4.99])| + EPSG_4696|4696|"Kasai 1953"|Geographic2d|[21.5, -7.31, 26.26, -5.01]|Some([21.5, -7.31, 26.26, -5.01])| + EPSG_4697|4697|"IGC 1962 6th Parallel South"|Geographic2d|[12.17, -7.36, 29.64, -3.29]|Some([12.17, -7.36, 29.64, -3.29])| + EPSG_4698|4698|"IGN 1962 Kerguelen"|Geographic2d|[68.69, -49.78, 70.62, -48.6]|Some([68.693, -49.781, 70.623, -48.601])| + EPSG_4699|4699|"Le Pouce 1934"|Geographic2d|[57.25, -20.57, 57.85, -19.94]|Some([57.243, -20.567, 57.843, -19.937])| + EPSG_4700|4700|"IGN Astro 1960"|Geographic2d|[-17.08, 14.72, -4.8, 27.3]|Some([-17.08, 14.72, -4.8, 27.3])| + EPSG_4701|4701|"IGCB 1955"|Geographic2d|[12.17, -6.04, 16.28, -4.28]|Some([12.171, -6.039, 16.281, -4.279])| + EPSG_4702|4702|"Mauritania 1999"|Geographic2d|[-20.04, 14.72, -4.8, 27.3]|Some([-20.04, 14.72, -4.8, 27.3])| + EPSG_4703|4703|"Mhast 1951"|Geographic2d|[10.53, -6.04, 13.1, -4.38]|Some([10.53, -6.04, 13.1, -4.38])| + EPSG_4704|4704|"Mhast (onshore)"|Geographic2d|[10.53, -6.04, 13.1, -4.38]|Some([10.53, -6.04, 13.1, -4.38])| + EPSG_4705|4705|"Mhast (offshore)"|Geographic2d|[10.53, -6.04, 12.37, -5.05]|Some([10.53, -6.039, 12.37, -5.049])| + EPSG_4706|4706|"Egypt Gulf of Suez S-650 TL"|Geographic2d|[32.34, 27.19, 34.27, 30.01]|Some([32.338, 27.19, 34.268, 30.01])| + EPSG_4707|4707|"Tern Island 1961"|Geographic2d|[-166.36, 23.69, -166.03, 23.93]|Some([-166.361, 23.693, -166.031, 23.933])| + EPSG_4708|4708|"Cocos Islands 1965"|Geographic2d|[96.76, -12.27, 96.99, -11.76]|Some([96.755, -12.274, 96.986, -11.764])| + EPSG_4709|4709|"Iwo Jima 1945"|Geographic2d|[141.2, 24.67, 141.42, 24.89]|Some([141.201, 24.673, 141.421, 24.893])| + EPSG_4710|4710|"Astro DOS 71"|Geographic2d|[-5.85, -16.08, -5.59, -15.85]|Some([-5.855, -16.075, -5.595, -15.845])| + EPSG_4711|4711|"Marcus Island 1952"|Geographic2d|[153.91, 24.22, 154.05, 24.35]|Some([153.908, 24.22, 154.048, 24.35])| + EPSG_4712|4712|"Ascension Island 1958"|Geographic2d|[-14.46, -8.03, -14.24, -7.83]|Some([-14.46, -8.03, -14.24, -7.83])| + EPSG_4713|4713|"Ayabelle Lighthouse"|Geographic2d|[41.75, 10.94, 44.15, 12.72]|Some([41.75, 10.94, 44.15, 12.72])| + EPSG_4714|4714|"Bellevue"|Geographic2d|[168.09, -20.31, 169.95, -17.37]|Some([168.083, -20.315, 169.943, -17.374])| + EPSG_4715|4715|"Camp Area Astro"|Geographic2d|[165.73, -77.94, 167.43, -77.17]|Some([165.724, -77.941, 167.424, -77.171])| + EPSG_4716|4716|"Phoenix Islands 1966"|Geographic2d|[-174.6, -4.76, -170.66, -2.68]|Some([-174.603, -4.757, -170.663, -2.677])| + EPSG_4717|4717|"Cape Canaveral"|Geographic2d|[-82.33, 20.86, -72.68, 30.83]|Some([-82.33, 20.859, -72.68, 30.83])| + EPSG_4718|4718|"Solomon 1968"|Geographic2d|[155.62, -10.9, 162.44, -6.55]|Some([155.62, -10.9, 162.44, -6.55])| + EPSG_4719|4719|"Easter Island 1967"|Geographic2d|[-109.51, -27.25, -109.16, -27.01]|Some([-109.512, -27.251, -109.162, -27.011])| + EPSG_4720|4720|"Fiji 1986"|Geographic2d|[176.81, -20.81, -178.15, -12.42]|Some([-179.998, -20.81, 179.834, -12.42])| + EPSG_4721|4721|"Fiji 1956"|Geographic2d|[176.81, -19.22, -179.77, -16.1]|Some([-179.994, -19.218, 179.892, -16.098])| + EPSG_4722|4722|"South Georgia 1968"|Geographic2d|[-38.08, -54.95, -35.74, -53.93]|Some([-38.074, -54.944, -35.734, -53.924])| + EPSG_4723|4723|"GCGD59"|Geographic2d|[-81.46, 19.21, -81.04, 19.41]|Some([-81.461, 19.21, -81.041, 19.41])| + EPSG_4724|4724|"Diego Garcia 1969"|Geographic2d|[72.3, -7.49, 72.55, -7.18]|Some([72.303, -7.488, 72.553, -7.178])| + EPSG_4725|4725|"Johnston Island 1961"|Geographic2d|[-169.59, 16.67, -169.47, 16.79]|Some([-169.591, 16.672, -169.471, 16.792])| + EPSG_4726|4726|"SIGD61"|Geographic2d|[-80.14, 19.63, -79.69, 19.78]|Some([-80.141, 19.63, -79.691, 19.78])| + EPSG_4727|4727|"Midway 1961"|Geographic2d|[-177.45, 28.13, -177.31, 28.28]|Some([-177.451, 28.127, -177.311, 28.277])| + EPSG_4728|4728|"PN84"|Geographic2d|[-18.22, 27.58, -16.08, 28.9]|Some([-18.218, 27.579, -16.078, 28.899])| + EPSG_4729|4729|"Pitcairn 1967"|Geographic2d|[-130.16, -25.14, -130.01, -25.0]|Some([-130.16, -25.14, -130.01, -25.0])| + EPSG_4730|4730|"Santo 1965"|Geographic2d|[166.47, -17.32, 168.71, -14.57]|Some([166.471, -17.321, 168.711, -14.571])| + EPSG_4732|4732|"Marshall Islands 1960"|Geographic2d|[162.27, 8.66, 167.82, 19.38]|Some([162.271, 8.66, 167.821, 19.381])| + EPSG_4733|4733|"Wake Island 1952"|Geographic2d|[166.55, 19.22, 166.72, 19.38]|Some([166.55, 19.218, 166.72, 19.378])| + EPSG_4734|4734|"Tristan 1968"|Geographic2d|[-12.76, -40.42, -9.8, -37.0]|Some([-12.763, -40.413, -9.804, -36.993])| + EPSG_4735|4735|"Kusaie 1951"|Geographic2d|[162.85, 5.21, 163.1, 5.43]|Some([162.867, 5.22, 163.117, 5.44])| + EPSG_4736|4736|"Deception Island"|Geographic2d|[-60.89, -63.08, -60.35, -62.82]|Some([-60.895, -63.083, -60.355, -62.823])| + EPSG_4737|4737|"KGD2002"|Geographic2d|[122.71, 28.6, 134.28, 40.27]|Some([122.71, 28.6, 134.28, 40.27])| + EPSG_4738|4738|"Hong Kong 1963"|Geographic2d|[113.76, 22.13, 114.51, 22.58]|Some([113.76, 22.13, 114.51, 22.58])| + EPSG_4739|4739|"Hong Kong 1963(67)"|Geographic2d|[113.76, 22.13, 114.51, 22.58]|Some([113.758, 22.132, 114.508, 22.582])| + EPSG_4740|4740|"PZ-90"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-168.0, -90.0, 180.0, 90.0])| + EPSG_4741|4741|"FD54"|Geographic2d|[-7.49, 61.33, -6.33, 62.41]|Some([-7.484, 61.329, -6.324, 62.409])| + EPSG_4742|4742|"GDM2000"|Geographic2d|[98.02, 0.85, 119.61, 7.81]|Some([98.02, 0.85, 119.61, 7.81])| + EPSG_4743|4743|"Karbala 1979"|Geographic2d|[38.79, 29.06, 48.61, 37.39]|Some([38.793, 29.06, 48.613, 37.39])| + EPSG_4744|4744|"Nahrwan 1934"|Geographic2d|[38.79, 29.06, 51.06, 37.39]|Some([38.79, 29.059, 51.06, 37.39])| + EPSG_4745|4745|"RD/83"|Geographic2d|[11.89, 50.2, 15.04, 51.66]|Some([11.891, 50.201, 15.042, 51.661])| + EPSG_4746|4746|"PD/83"|Geographic2d|[9.87, 50.2, 12.66, 51.65]|Some([9.871, 50.201, 12.662, 51.651])| + EPSG_4747|4747|"GR96"|Geographic2d|[-75.0, 56.38, 7.99, 87.02]|Some([-75.0, 56.38, 7.99, 87.02])| + EPSG_4748|4748|"Vanua Levu 1915"|Geographic2d|[178.42, -17.07, -179.77, -16.1]|Some([-179.947, -17.071, 179.992, -16.101])| + EPSG_4749|4749|"RGNC91-93"|Geographic2d|[156.25, -26.45, 174.28, -14.83]|Some([156.25, -26.45, 174.28, -14.83])| + EPSG_4750|4750|"ST87 Ouvea"|Geographic2d|[166.44, -20.77, 166.71, -20.34]|Some([166.44, -20.77, 166.71, -20.34])| + EPSG_4751|4751|"Kertau (RSO)"|Geographic2d|[99.59, 1.13, 104.6, 6.72]|Some([99.591, 1.13, 104.602, 6.72])| + EPSG_4752|4752|"Viti Levu 1912"|Geographic2d|[177.19, -18.32, 178.75, -17.25]|Some([177.194, -18.321, 178.754, -17.251])| + EPSG_4753|4753|"fk89"|Geographic2d|[-7.49, 61.33, -6.33, 62.41]|Some([-7.49, 61.33, -6.33, 62.41])| + EPSG_4754|4754|"LGD2006"|Geographic2d|[9.31, 19.5, 26.21, 35.23]|Some([9.311, 19.5, 26.21, 35.23])| + EPSG_4755|4755|"DGN95"|Geographic2d|[92.01, -13.95, 141.46, 7.79]|Some([92.01, -13.95, 141.46, 7.79])| + EPSG_4756|4756|"VN-2000"|Geographic2d|[102.14, 8.33, 109.53, 23.4]|Some([102.138, 8.331, 109.528, 23.401])| + EPSG_4757|4757|"SVY21"|Geographic2d|[103.59, 1.13, 104.07, 1.47]|Some([103.59, 1.13, 104.07, 1.47])| + EPSG_4758|4758|"JAD2001"|Geographic2d|[-80.6, 14.08, -74.51, 19.36]|Some([-80.6, 14.08, -74.51, 19.36])| + EPSG_4759|4759|"NAD83(NSRS2007)"|Geographic2d|[167.65, 14.92, -63.88, 74.71]|Some([-179.503, 14.92, 176.215, 74.71])| + EPSG_4760|4760|"WGS 66"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_4761|4761|"HTRS96"|Geographic2d|[13.0, 41.62, 19.43, 46.54]|Some([13.0, 41.62, 19.43, 46.54])| + EPSG_4762|4762|"BDA2000"|Geographic2d|[-68.83, 28.91, -60.7, 35.73]|Some([-68.83, 28.91, -60.7, 35.73])| + EPSG_4763|4763|"Pitcairn 2006"|Geographic2d|[-130.16, -25.14, -130.01, -25.0]|Some([-130.16, -25.14, -130.01, -25.0])| + EPSG_4764|4764|"RSRGD2000"|Geographic2d|[144.99, -90.0, -144.99, -59.99]|Some([-177.666, -90.0, 180.0, -59.99])| + EPSG_4765|4765|"Slovenia 1996"|Geographic2d|[13.38, 45.42, 16.61, 46.88]|Some([13.38, 45.42, 16.61, 46.88])| + EPSG_4766|4766|"New Beijing / 3-degree Gauss-Kruger zone 30"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([30350539.081, 3023142.02, 30649460.919, 5366325.476])| + EPSG_4767|4767|"New Beijing / 3-degree Gauss-Kruger zone 31"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([31352051.942, 3066359.335, 31647948.058, 5000574.685])| + EPSG_4768|4768|"New Beijing / 3-degree Gauss-Kruger zone 32"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([32352759.819, 3123986.223, 32648221.908, 4930577.194])| + EPSG_4769|4769|"New Beijing / 3-degree Gauss-Kruger zone 33"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([33344479.7, 2370728.674, 33655520.3, 4737233.2])| + EPSG_4770|4770|"New Beijing / 3-degree Gauss-Kruger zone 34"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([34344163.942, 2337511.88, 34655836.058, 4729456.733])| + EPSG_4771|4771|"New Beijing / 3-degree Gauss-Kruger zone 35"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([35345640.468, 2489211.692, 35655388.764, 4676149.359])| + EPSG_4772|4772|"New Beijing / 3-degree Gauss-Kruger zone 36"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([36341296.148, 2012047.338, 36659762.075, 4705031.577])| + EPSG_4773|4773|"New Beijing / 3-degree Gauss-Kruger zone 37"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([37341223.899, 2003192.605, 37658776.101, 4998351.967])| + EPSG_4774|4774|"New Beijing / 3-degree Gauss-Kruger zone 38"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([38344575.257, 2380693.944, 38655424.743, 5036139.173])| + EPSG_4775|4775|"New Beijing / 3-degree Gauss-Kruger zone 39"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([39345751.701, 2500285.656, 39654248.299, 5528676.175])| + EPSG_4776|4776|"New Beijing / 3-degree Gauss-Kruger zone 40"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([40347869.683, 2702964.178, 40652130.317, 5912498.946])| + EPSG_4777|4777|"New Beijing / 3-degree Gauss-Kruger zone 41"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([41352746.092, 3122877.962, 41647253.908, 5938094.602])| + EPSG_4778|4778|"New Beijing / 3-degree Gauss-Kruger zone 42"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([42372260.329, 4450704.66, 42627739.671, 5898032.245])| + EPSG_4779|4779|"New Beijing / 3-degree Gauss-Kruger zone 43"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([43374501.666, 4581745.528, 43625498.334, 5569829.626])| + EPSG_4780|4780|"New Beijing / 3-degree Gauss-Kruger zone 44"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([44376541.067, 4698372.352, 44623458.933, 5417462.952])| + EPSG_4781|4781|"New Beijing / 3-degree Gauss-Kruger zone 45"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([45383489.899, 5079528.711, 45482968.991, 5364087.015])| + EPSG_4782|4782|"New Beijing / 3-degree Gauss-Kruger CM 75E"|Projected|[73.62, 35.81, 76.5, 40.65]|Some([375270.405, 3964531.66, 635576.403, 4502867.203])| + EPSG_4783|4783|"New Beijing / 3-degree Gauss-Kruger CM 78E"|Projected|[76.5, 31.03, 79.5, 41.83]|Some([356778.343, 3434361.425, 643221.657, 4633924.528])| + EPSG_4784|4784|"New Beijing / 3-degree Gauss-Kruger CM 81E"|Projected|[79.5, 29.95, 82.51, 45.88]|Some([355187.289, 3314629.719, 645778.241, 5083946.531])| + EPSG_4785|4785|"New Beijing / 3-degree Gauss-Kruger CM 84E"|Projected|[82.5, 28.26, 85.5, 47.23]|Some([352801.029, 3127310.974, 647198.971, 5234000.648])| + EPSG_4786|4786|"New Beijing / 3-degree Gauss-Kruger CM 87E"|Projected|[85.5, 27.8, 88.5, 49.18]|Some([352173.589, 3076332.906, 647826.411, 5450825.027])| + EPSG_4787|4787|"New Beijing / 3-degree Gauss-Kruger CM 90E"|Projected|[88.49, 27.32, 91.51, 48.42]|Some([350539.081, 3023142.02, 649460.919, 5366325.476])| + EPSG_4788|4788|"New Beijing / 3-degree Gauss-Kruger CM 93E"|Projected|[91.5, 27.71, 94.5, 45.13]|Some([352051.942, 3066359.335, 647948.058, 5000574.685])| + EPSG_4789|4789|"New Beijing / 3-degree Gauss-Kruger CM 96E"|Projected|[94.5, 28.23, 97.51, 44.5]|Some([352759.819, 3123986.223, 648221.908, 4930577.194])| + EPSG_4790|4790|"New Beijing / 3-degree Gauss-Kruger CM 99E"|Projected|[97.5, 21.43, 100.5, 42.76]|Some([344479.7, 2370728.674, 655520.3, 4737233.2])| + EPSG_4791|4791|"New Beijing / 3-degree Gauss-Kruger CM 102E"|Projected|[100.5, 21.13, 103.5, 42.69]|Some([344163.942, 2337511.88, 655836.058, 4729456.733])| + EPSG_4792|4792|"New Beijing / 3-degree Gauss-Kruger CM 105E"|Projected|[103.5, 22.5, 106.51, 42.21]|Some([345640.468, 2489211.692, 655388.764, 4676149.359])| + EPSG_4793|4793|"New Beijing / 3-degree Gauss-Kruger CM 108E"|Projected|[106.5, 18.19, 109.51, 42.47]|Some([341296.148, 2012047.338, 659762.075, 4705031.577])| + EPSG_4794|4794|"New Beijing / 3-degree Gauss-Kruger CM 111E"|Projected|[109.5, 18.11, 112.5, 45.11]|Some([341223.899, 2003192.605, 658776.101, 4998351.967])| + EPSG_4795|4795|"New Beijing / 3-degree Gauss-Kruger CM 114E"|Projected|[112.5, 21.52, 115.5, 45.45]|Some([344575.257, 2380693.944, 655424.743, 5036139.173])| + EPSG_4796|4796|"New Beijing / 3-degree Gauss-Kruger CM 117E"|Projected|[115.5, 22.6, 118.5, 49.88]|Some([345751.701, 2500285.656, 654248.299, 5528676.175])| + EPSG_4797|4797|"New Beijing / 3-degree Gauss-Kruger CM 120E"|Projected|[118.5, 24.43, 121.5, 53.33]|Some([347869.683, 2702964.178, 652130.317, 5912498.946])| + EPSG_4798|4798|"New Beijing / 3-degree Gauss-Kruger CM 123E"|Projected|[121.5, 28.22, 124.5, 53.56]|Some([352746.092, 3122877.962, 647253.908, 5938094.602])| + EPSG_4799|4799|"New Beijing / 3-degree Gauss-Kruger CM 126E"|Projected|[124.5, 40.19, 127.5, 53.2]|Some([372260.329, 4450704.66, 627739.671, 5898032.245])| + EPSG_4800|4800|"New Beijing / 3-degree Gauss-Kruger CM 129E"|Projected|[127.5, 41.37, 130.5, 50.25]|Some([374501.666, 4581745.528, 625498.334, 5569829.626])| + EPSG_4801|4801|"CH1903 (Bern)"|Geographic2d|[5.96, 45.82, 10.49, 47.81]|Some([-1.479, 45.821, 3.052, 47.811])| + EPSG_4802|4802|"Bogota 1975 (Bogota)"|Geographic2d|[-79.1, -4.23, -66.87, 12.52]|Some([-5.022, -4.227, 7.207, 12.523])| + EPSG_4803|4803|"Lisbon (Lisbon)"|Geographic2d|[-9.56, 36.95, -6.19, 42.16]|Some([-0.427, 36.948, 2.943, 42.158])| + EPSG_4804|4804|"Makassar (Jakarta)"|Geographic2d|[118.71, -6.54, 120.78, -1.88]|Some([11.9, -6.542, 13.97, -1.881])| + EPSG_4805|4805|"MGI (Ferro)"|Geographic2d|[9.53, 40.85, 23.04, 49.02]|Some([27.197, 40.849, 40.709, 49.021])| + EPSG_4806|4806|"Monte Mario (Rome)"|Geographic2d|[5.93, 34.76, 18.99, 47.1]|Some([-6.522, 34.76, 6.538, 47.1])| + EPSG_4807|4807|"NTF (Paris)"|Geographic2d|[-4.87, 41.31, 9.63, 51.14]|Some([-8.007, 45.9, 8.104, 56.822])| + EPSG_4809|4809|"BD50 (Brussels)"|Geographic2d|[2.5, 49.5, 6.4, 51.51]|Some([-1.868, 49.5, 2.032, 51.51])| + EPSG_4810|4810|"Tananarive (Paris)"|Geographic2d|[43.18, -25.64, 50.56, -11.89]|Some([45.381, -28.487, 53.581, -13.21])| + EPSG_4811|4811|"Voirol 1875 (Paris)"|Geographic2d|[-2.95, 31.99, 9.09, 37.14]|Some([-5.872, 35.545, 7.506, 41.268])| + EPSG_4812|4812|"New Beijing / 3-degree Gauss-Kruger CM 132E"|Projected|[130.5, 42.42, 133.5, 48.88]|Some([376541.067, 4698372.352, 623458.933, 5417462.952])| + EPSG_4813|4813|"Batavia (Jakarta)"|Geographic2d|[95.16, -8.91, 115.77, 5.97]|Some([-11.651, -8.91, 8.962, 5.971])| + EPSG_4814|4814|"RT38 (Stockholm)"|Geographic2d|[10.93, 55.28, 24.17, 69.07]|Some([-7.128, 55.28, 6.112, 69.07])| + EPSG_4815|4815|"Greek (Athens)"|Geographic2d|[19.57, 34.88, 28.3, 41.75]|Some([-4.148, 34.879, 4.582, 41.749])| + EPSG_4816|4816|"Carthage (Paris)"|Geographic2d|[7.49, 30.23, 11.59, 37.4]|Some([5.725, 33.587, 10.28, 41.554])| + EPSG_4817|4817|"NGO 1948 (Oslo)"|Geographic2d|[4.39, 57.9, 31.32, 71.24]|Some([-6.329, 57.898, 20.606, 71.241])| + EPSG_4818|4818|"S-JTSK (Ferro)"|Geographic2d|[12.09, 47.73, 22.56, 51.06]|Some([29.757, 47.73, 40.229, 51.061])| + EPSG_4820|4820|"Segara (Jakarta)"|Geographic2d|[114.55, -4.24, 119.06, 4.29]|Some([7.742, -4.241, 12.252, 4.29])| + EPSG_4821|4821|"Voirol 1879 (Paris)"|Geographic2d|[-2.95, 31.99, 9.09, 37.14]|Some([-5.875, 35.544, 7.503, 41.267])| + EPSG_4822|4822|"New Beijing / 3-degree Gauss-Kruger CM 135E"|Projected|[133.5, 45.85, 134.77, 48.4]|Some([383489.899, 5079528.711, 482968.991, 5364087.015])| + EPSG_4823|4823|"Sao Tome"|Geographic2d|[6.41, -0.04, 6.82, 0.46]|Some([6.41, -0.04, 6.82, 0.46])| + EPSG_4824|4824|"Principe"|Geographic2d|[7.27, 1.48, 7.52, 1.76]|Some([7.27, 1.48, 7.52, 1.76])| + EPSG_4826|4826|"WGS 84 / Cape Verde National"|Projected|[-28.85, 11.47, -19.53, 20.54]|Some([-369014.405, -354640.726, 650623.653, 655850.982])| + EPSG_4839|4839|"ETRS89 / LCC Germany (N-E)"|Projected|[5.86, 47.27, 15.04, 55.09]|Some([-351349.461, -414758.804, 343787.005, 464431.832])| + EPSG_4901|4901|"ATF (Paris)"|Geographic2d|[-4.87, 42.33, 8.23, 51.14]|Some([-8.008, 47.033, 6.548, 56.822])| + EPSG_4903|4903|"Madrid 1870 (Madrid)"|Geographic2d|[-9.37, 35.95, 3.39, 43.82]|Some([-5.683, 35.95, 7.077, 43.82])| + EPSG_4904|4904|"Lisbon 1890 (Lisbon)"|Geographic2d|[-9.56, 36.95, -6.19, 42.16]|Some([-0.427, 36.948, 2.944, 42.159])| + EPSG_5013|5013|"PTRA08"|Geographic2d|[-35.58, 29.24, -12.48, 43.07]|Some([-35.58, 29.24, -12.48, 43.07])| + EPSG_5014|5014|"PTRA08 / UTM zone 25N"|Projected|[-35.58, 35.25, -30.0, 43.07]|Some([265262.327, 3900767.951, 772961.707, 4772957.105])| + EPSG_5015|5015|"PTRA08 / UTM zone 26N"|Projected|[-30.0, 33.52, -24.0, 42.96]|Some([221357.926, 3708936.604, 778642.074, 4760740.58])| + EPSG_5016|5016|"PTRA08 / UTM zone 28N"|Projected|[-18.0, 29.24, -12.48, 36.46]|Some([208407.584, 3234577.334, 744920.282, 4039155.897])| + EPSG_5018|5018|"Lisbon / Portuguese Grid New"|Projected|[-9.56, 36.95, -6.19, 42.16]|Some([-127100.53, -301736.381, 173081.618, 278544.383])| + EPSG_5041|5041|"WGS 84 / UPS North (E,N)"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-1387222.946, -1405880.717, 5387222.946, 5405880.717])| + EPSG_5042|5042|"WGS 84 / UPS South (E,N)"|Projected|[-180.0, -90.0, 180.0, -60.0]|Some([-1387222.946, -1405880.717, 5387222.946, 5405880.717])| + EPSG_5048|5048|"ETRS89 / TM35FIN(N,E)"|Projected|[19.08, 58.84, 31.59, 70.09]|Some([43547.789, 6522236.152, 764796.716, 7795461.188])| + EPSG_5069|5069|"NAD27 / Conus Albers"|Projected|[-124.79, 24.41, -66.91, 49.38]|Some([-2916274.402, 153575.462, 2945757.847, 3255196.845])| + EPSG_5070|5070|"NAD83 / Conus Albers"|Projected|[-124.79, 24.41, -66.91, 49.38]|Some([-2916311.1, 153628.723, 2945749.913, 3255274.794])| + EPSG_5071|5071|"NAD83(HARN) / Conus Albers"|Projected|[-124.79, 24.41, -66.91, 49.38]|Some([-2916309.972, 153628.283, 2945749.918, 3255273.736])| + EPSG_5072|5072|"NAD83(NSRS2007) / Conus Albers"|Projected|[-124.79, 24.41, -66.91, 49.38]|Some([-2916311.1, 153628.723, 2945749.913, 3255274.794])| + EPSG_5105|5105|"ETRS89 / NTM zone 5"|Projected|[4.39, 58.32, 6.01, 62.64]|Some([34950.936, 1035641.999, 129888.068, 1517466.577])| + EPSG_5106|5106|"ETRS89 / NTM zone 6"|Projected|[6.0, 57.93, 7.01, 63.03]|Some([70376.057, 992203.619, 130216.415, 1560548.157])| + EPSG_5107|5107|"ETRS89 / NTM zone 7"|Projected|[6.99, 57.9, 8.01, 63.59]|Some([69758.386, 988862.323, 130241.614, 1622967.956])| + EPSG_5108|5108|"ETRS89 / NTM zone 8"|Projected|[8.0, 58.0, 9.01, 64.1]|Some([70433.733, 1000000.011, 130157.585, 1679818.796])| + EPSG_5109|5109|"ETRS89 / NTM zone 9"|Projected|[9.0, 58.44, 10.01, 64.25]|Some([70797.291, 1049007.879, 129786.756, 1696540.393])| + EPSG_5110|5110|"ETRS89 / NTM zone 10"|Projected|[10.0, 58.9, 11.01, 65.27]|Some([71179.239, 1100246.998, 129397.169, 1810256.273])| + EPSG_5111|5111|"ETRS89 / NTM zone 11"|Projected|[11.0, 58.88, 12.01, 67.58]|Some([71162.594, 1098019.134, 129414.148, 2067845.528])| + EPSG_5112|5112|"ETRS89 / NTM zone 12"|Projected|[12.0, 59.88, 13.01, 68.16]|Some([71999.198, 1209420.806, 128560.811, 2132533.371])| + EPSG_5113|5113|"ETRS89 / NTM zone 13"|Projected|[13.0, 64.01, 14.01, 68.4]|Some([75543.064, 1669686.216, 124946.067, 2159302.086])| + EPSG_5114|5114|"ETRS89 / NTM zone 14"|Projected|[14.0, 64.03, 15.01, 69.07]|Some([75560.555, 1671915.769, 124928.226, 2234035.463])| + EPSG_5115|5115|"ETRS89 / NTM zone 15"|Projected|[15.0, 66.14, 16.01, 69.38]|Some([77422.351, 1907167.702, 123029.194, 2268615.579])| + EPSG_5116|5116|"ETRS89 / NTM zone 16"|Projected|[15.99, 66.88, 17.01, 69.49]|Some([77644.464, 1989688.658, 122355.536, 2280886.241])| + EPSG_5117|5117|"ETRS89 / NTM zone 17"|Projected|[17.0, 67.94, 18.01, 69.96]|Some([79035.29, 2107907.869, 121383.996, 2333317.169])| + EPSG_5118|5118|"ETRS89 / NTM zone 18"|Projected|[18.0, 68.04, 19.01, 70.29]|Some([79125.53, 2119061.426, 121291.952, 2370132.035])| + EPSG_5119|5119|"ETRS89 / NTM zone 19"|Projected|[19.0, 68.33, 20.01, 70.35]|Some([79387.588, 2151407.513, 121024.653, 2376825.793])| + EPSG_5120|5120|"ETRS89 / NTM zone 20"|Projected|[20.0, 68.37, 21.01, 70.37]|Some([79423.776, 2155869.132, 120987.741, 2379057.055])| + EPSG_5121|5121|"ETRS89 / NTM zone 21"|Projected|[21.0, 69.03, 22.01, 70.73]|Some([80022.34, 2229488.926, 120377.206, 2419220.62])| + EPSG_5122|5122|"ETRS89 / NTM zone 22"|Projected|[22.0, 68.69, 23.01, 70.93]|Some([79713.647, 2191562.853, 120692.072, 2441534.393])| + EPSG_5123|5123|"ETRS89 / NTM zone 23"|Projected|[23.0, 68.62, 24.01, 71.15]|Some([79650.182, 2183754.735, 120756.806, 2466080.101])| + EPSG_5124|5124|"ETRS89 / NTM zone 24"|Projected|[24.0, 68.58, 25.01, 71.17]|Some([79613.931, 2179292.982, 120793.783, 2468311.558])| + EPSG_5125|5125|"ETRS89 / NTM zone 25"|Projected|[25.0, 68.59, 26.01, 71.24]|Some([79622.993, 2180408.418, 120784.54, 2476121.694])| + EPSG_5126|5126|"ETRS89 / NTM zone 26"|Projected|[26.0, 69.71, 27.01, 71.2]|Some([80641.86, 2305345.61, 119745.296, 2471658.752])| + EPSG_5127|5127|"ETRS89 / NTM zone 27"|Projected|[27.0, 69.9, 28.01, 71.19]|Some([80815.46, 2326541.924, 119568.223, 2470543.02])| + EPSG_5128|5128|"ETRS89 / NTM zone 28"|Projected|[28.0, 69.03, 29.01, 71.15]|Some([80022.34, 2229488.926, 120377.206, 2466080.101])| + EPSG_5129|5129|"ETRS89 / NTM zone 29"|Projected|[29.0, 69.02, 30.01, 70.94]|Some([80013.25, 2228373.432, 120386.477, 2442650.095])| + EPSG_5130|5130|"ETRS89 / NTM zone 30"|Projected|[30.0, 69.46, 31.32, 70.77]|Some([80413.768, 2277456.501, 132120.901, 2423808.265])| + EPSG_5132|5132|"Tokyo 1892"|Geographic2d|[122.83, 20.37, 154.05, 45.54]|Some([122.83, 20.37, 154.05, 45.54])| + EPSG_5167|5167|"Korean 1985 / East Sea Belt"|Projected|[130.71, 37.39, 131.01, 37.62]|Some([174536.471, 431986.883, 201103.163, 457553.264])| + EPSG_5168|5168|"Korean 1985 / Central Belt Jeju"|Projected|[126.09, 33.14, 127.01, 33.61]|Some([115283.262, 10468.652, 201125.967, 62967.042])| + EPSG_5169|5169|"Tokyo 1892 / Korea West Belt"|Projected|[124.27, 33.99, 126.0, 40.9]|Some([132558.55, 54747.4, 292306.326, 822416.623])| + EPSG_5170|5170|"Tokyo 1892 / Korea Central Belt"|Projected|[126.0, 33.14, 128.0, 41.8]|Some([106615.6, -39531.408, 293230.859, 922362.309])| + EPSG_5171|5171|"Tokyo 1892 / Korea East Belt"|Projected|[128.0, 34.49, 130.0, 43.01]|Some([108088.081, 110208.789, 291794.15, 1056757.677])| + EPSG_5172|5172|"Tokyo 1892 / Korea East Sea Belt"|Projected|[130.0, 37.39, 131.01, 42.98]|Some([111404.926, 431986.913, 200869.875, 1053425.228])| + EPSG_5173|5173|"Korean 1985 / Modified West Belt"|Projected|[124.53, 33.99, 126.0, 38.04]|Some([156482.074, 54747.405, 292306.326, 504606.97])| + EPSG_5174|5174|"Korean 1985 / Modified Central Belt"|Projected|[126.0, 33.96, 128.0, 38.33]|Some([107494.148, 51418.71, 292354.717, 536800.079])| + EPSG_5175|5175|"Korean 1985 / Modified Central Belt Jeju"|Projected|[126.09, 33.14, 127.01, 33.61]|Some([115013.592, 10468.625, 200856.31, 62969.399])| + EPSG_5176|5176|"Korean 1985 / Modified East Belt"|Projected|[128.0, 34.49, 129.65, 38.64]|Some([108088.081, 110208.847, 259644.967, 571211.709])| + EPSG_5177|5177|"Korean 1985 / Modified East Sea Belt"|Projected|[130.71, 37.39, 131.01, 37.62]|Some([174280.542, 431986.88, 200847.236, 457554.049])| + EPSG_5178|5178|"Korean 1985 / Unified CS"|Projected|[124.53, 33.14, 131.01, 38.64]|Some([723139.402, 1460685.532, 1327651.725, 2076560.411])| + EPSG_5179|5179|"KGD2002 / Unified CS"|Projected|[122.71, 28.6, 134.28, 40.27]|Some([531371.844, 957866.059, 1663741.905, 2274021.308])| + EPSG_5180|5180|"KGD2002 / West Belt"|Projected|[124.53, 33.99, 126.0, 38.04]|Some([156573.879, 55054.38, 292397.384, 504911.966])| + EPSG_5181|5181|"KGD2002 / Central Belt"|Projected|[126.0, 33.96, 128.0, 38.33]|Some([107570.155, 51726.542, 292429.845, 537103.128])| + EPSG_5182|5182|"KGD2002 / Central Belt Jeju"|Projected|[126.09, 33.14, 127.01, 33.61]|Some([115090.69, 10776.867, 200933.053, 63276.322])| + EPSG_5183|5183|"KGD2002 / East Belt"|Projected|[128.0, 34.49, 129.65, 38.64]|Some([108147.367, 110517.611, 259703.578, 571516.082])| + EPSG_5184|5184|"KGD2002 / East Sea Belt"|Projected|[130.71, 37.39, 131.01, 37.62]|Some([174319.022, 432295.663, 200885.55, 457862.254])| + EPSG_5185|5185|"KGD2002 / West Belt 2010"|Projected|[124.53, 33.99, 126.0, 38.04]|Some([156573.879, 155054.38, 292397.384, 604911.966])| + EPSG_5186|5186|"KGD2002 / Central Belt 2010"|Projected|[126.0, 33.14, 128.0, 38.33]|Some([106692.735, 60776.823, 293307.265, 637103.128])| + EPSG_5187|5187|"KGD2002 / East Belt 2010"|Projected|[128.0, 34.49, 129.65, 38.64]|Some([108147.367, 210517.611, 259703.578, 671516.082])| + EPSG_5188|5188|"KGD2002 / East Sea Belt 2010"|Projected|[130.71, 37.39, 131.01, 37.62]|Some([174319.022, 532295.663, 200885.55, 557862.254])| + EPSG_5221|5221|"S-JTSK (Ferro) / Krovak East North"|Projected|[12.09, 47.73, 22.56, 51.06]|Some([-951555.938, -1353306.148, -159523.535, -911041.249])| + EPSG_5223|5223|"WGS 84 / Gabon TM"|Projected|[8.65, -3.98, 14.52, 2.32]|Some([127014.872, 59327.657, 780503.963, 756872.535])| + EPSG_5224|5224|"S-JTSK/05 (Ferro) / Modified Krovak"|Projected|[12.09, 48.58, 18.86, 51.06]|Some([5911041.232, 5418596.799, 6244159.024, 5935719.085])| + EPSG_5225|5225|"S-JTSK/05 (Ferro) / Modified Krovak East North"|Projected|[12.09, 48.58, 18.86, 51.06]|Some([-5935719.085, -6244159.024, -5418596.799, -5911041.232])| + EPSG_5228|5228|"S-JTSK/05"|Geographic2d|[12.09, 48.58, 18.86, 51.06]|Some([12.091, 48.58, 18.862, 51.061])| + EPSG_5229|5229|"S-JTSK/05 (Ferro)"|Geographic2d|[12.09, 48.58, 18.86, 51.06]|Some([29.757, 48.58, 36.528, 51.061])| + EPSG_5233|5233|"SLD99"|Geographic2d|[79.64, 5.86, 81.95, 9.88]|Some([79.638, 5.86, 81.948, 9.88])| + EPSG_5234|5234|"Kandawala / Sri Lanka Grid"|Projected|[79.64, 5.86, 81.95, 9.88]|Some([74453.857, 73850.9, 330260.939, 518632.649])| + EPSG_5235|5235|"SLD99 / Sri Lanka Grid 1999"|Projected|[79.64, 5.86, 81.95, 9.88]|Some([374450.893, 373852.134, 630257.81, 818632.2])| + EPSG_5243|5243|"ETRS89 / LCC Germany (E-N)"|Projected|[5.86, 47.27, 15.04, 55.09]|Some([-351349.461, -414758.804, 343787.005, 464431.832])| + EPSG_5246|5246|"GDBD2009"|Geographic2d|[112.37, 4.01, 115.37, 6.31]|Some([112.37, 4.01, 115.37, 6.31])| + EPSG_5247|5247|"GDBD2009 / Brunei BRSO"|Projected|[112.37, 4.01, 115.37, 6.31]|Some([298423.897, 443468.672, 631598.883, 698468.737])| + EPSG_5252|5252|"TUREF"|Geographic2d|[25.62, 34.42, 44.83, 43.45]|Some([25.62, 34.42, 44.83, 43.45])| + EPSG_5253|5253|"TUREF / TM27"|Projected|[25.62, 36.5, 28.5, 42.11]|Some([376360.753, 4041025.095, 634391.181, 4664944.163])| + EPSG_5254|5254|"TUREF / TM30"|Projected|[28.5, 36.06, 31.5, 41.46]|Some([364852.207, 3992200.244, 635147.793, 4592746.422])| + EPSG_5255|5255|"TUREF / TM33"|Projected|[31.5, 35.97, 34.5, 42.07]|Some([364698.428, 3982213.909, 635301.572, 4660501.004])| + EPSG_5256|5256|"TUREF / TM36"|Projected|[34.5, 35.81, 37.5, 42.15]|Some([364425.87, 3964460.795, 635574.13, 4669387.35])| + EPSG_5257|5257|"TUREF / TM39"|Projected|[37.5, 36.66, 40.5, 41.19]|Some([365885.923, 4058779.658, 634114.077, 4562758.834])| + EPSG_5258|5258|"TUREF / TM42"|Projected|[40.5, 37.02, 43.5, 41.6]|Some([366513.237, 4098730.531, 633486.763, 4608296.059])| + EPSG_5259|5259|"TUREF / TM45"|Projected|[43.5, 36.97, 44.83, 41.02]|Some([366425.794, 4093195.16, 485701.34, 4543878.435])| + EPSG_5264|5264|"DRUKREF 03"|Geographic2d|[88.74, 26.7, 92.13, 28.33]|Some([88.74, 26.7, 92.13, 28.33])| + EPSG_5266|5266|"DRUKREF 03 / Bhutan National Grid"|Projected|[88.74, 26.7, 92.13, 28.33]|Some([124602.612, 2954390.057, 462000.361, 3136856.222])| + EPSG_5269|5269|"TUREF / 3-degree Gauss-Kruger zone 9"|Projected|[25.62, 36.5, 28.5, 42.11]|Some([9376360.753, 4041025.095, 9634391.181, 4664944.163])| + EPSG_5270|5270|"TUREF / 3-degree Gauss-Kruger zone 10"|Projected|[28.5, 36.06, 31.5, 41.46]|Some([10364852.207, 3992200.244, 10635147.793, 4592746.422])| + EPSG_5271|5271|"TUREF / 3-degree Gauss-Kruger zone 11"|Projected|[31.5, 35.97, 34.5, 42.07]|Some([11364698.428, 3982213.909, 11635301.572, 4660501.004])| + EPSG_5272|5272|"TUREF / 3-degree Gauss-Kruger zone 12"|Projected|[34.5, 35.81, 37.5, 42.15]|Some([12364425.87, 3964460.795, 12635574.13, 4669387.35])| + EPSG_5273|5273|"TUREF / 3-degree Gauss-Kruger zone 13"|Projected|[37.5, 36.66, 40.5, 41.19]|Some([13365885.923, 4058779.658, 13634114.077, 4562758.834])| + EPSG_5274|5274|"TUREF / 3-degree Gauss-Kruger zone 14"|Projected|[40.5, 37.02, 43.5, 41.6]|Some([14366513.237, 4098730.531, 14633486.763, 4608296.059])| + EPSG_5275|5275|"TUREF / 3-degree Gauss-Kruger zone 15"|Projected|[43.5, 36.97, 44.83, 41.02]|Some([15366425.794, 4093195.16, 15485701.34, 4543878.435])| + EPSG_5292|5292|"DRUKREF 03 / Bumthang TM"|Projected|[90.46, 27.33, 91.02, 28.09]|Some([222949.912, 524196.333, 278369.611, 608448.75])| + EPSG_5293|5293|"DRUKREF 03 / Chhukha TM"|Projected|[89.26, 26.71, 89.83, 27.32]|Some([221142.539, 455497.943, 277862.371, 523121.566])| + EPSG_5294|5294|"DRUKREF 03 / Dagana TM"|Projected|[89.63, 26.7, 90.08, 27.29]|Some([228106.245, 454389.954, 272888.929, 519784.933])| + EPSG_5295|5295|"DRUKREF 03 / Gasa TM"|Projected|[89.44, 27.72, 90.47, 28.33]|Some([191487.869, 567412.969, 293062.097, 635156.013])| + EPSG_5296|5296|"DRUKREF 03 / Ha TM"|Projected|[88.9, 27.02, 89.39, 27.62]|Some([125947.524, 490073.626, 174983.121, 556955.565])| + EPSG_5297|5297|"DRUKREF 03 / Lhuentse TM"|Projected|[90.77, 27.39, 91.49, 28.09]|Some([214062.491, 530844.872, 285278.1, 608468.874])| + EPSG_5298|5298|"DRUKREF 03 / Mongar TM"|Projected|[90.95, 26.93, 91.5, 27.61]|Some([221860.321, 479874.114, 276484.396, 555255.434])| + EPSG_5299|5299|"DRUKREF 03 / Paro TM"|Projected|[89.12, 27.18, 89.56, 27.79]|Some([227207.8, 507575.192, 270810.264, 575191.229])| + EPSG_5300|5300|"DRUKREF 03 / Pemagatshel TM"|Projected|[91.0, 26.78, 91.56, 27.18]|Some([215193.311, 463253.909, 270883.963, 507623.574])| + EPSG_5301|5301|"DRUKREF 03 / Punakha TM"|Projected|[89.63, 27.46, 90.08, 27.87]|Some([228253.445, 538601.595, 272735.037, 584056.607])| + EPSG_5302|5302|"DRUKREF 03 / Samdrup Jongkhar TM"|Projected|[91.39, 26.79, 92.13, 27.25]|Some([232432.498, 464361.908, 306017.616, 515457.248])| + EPSG_5303|5303|"DRUKREF 03 / Samtse TM"|Projected|[88.74, 26.8, 89.38, 27.28]|Some([217519.468, 465469.922, 281154.788, 518698.157])| + EPSG_5304|5304|"DRUKREF 03 / Sarpang TM"|Projected|[90.01, 26.73, 90.78, 27.23]|Some([224463.967, 457713.916, 301072.374, 513219.746])| + EPSG_5305|5305|"DRUKREF 03 / Thimphu TM"|Projected|[89.22, 27.14, 89.77, 28.01]|Some([217286.44, 503142.942, 271809.001, 599593.838])| + EPSG_5306|5306|"DRUKREF 03 / Trashigang TM"|Projected|[91.37, 27.01, 92.13, 27.49]|Some([212286.315, 488738.331, 287713.685, 541983.398])| + EPSG_5307|5307|"DRUKREF 03 / Trongsa TM"|Projected|[90.26, 27.13, 90.76, 27.79]|Some([226206.239, 502034.903, 275776.582, 575197.124])| + EPSG_5308|5308|"DRUKREF 03 / Tsirang TM"|Projected|[90.0, 26.81, 90.35, 27.2]|Some([233429.793, 466577.914, 268227.231, 509804.598])| + EPSG_5309|5309|"DRUKREF 03 / Wangdue Phodrang TM"|Projected|[89.71, 27.11, 90.54, 28.08]|Some([209675.494, 499818.804, 291977.167, 607379.711])| + EPSG_5310|5310|"DRUKREF 03 / Yangtse TM"|Projected|[91.34, 27.37, 91.77, 28.0]|Some([227576.291, 528628.68, 270115.38, 598462.452])| + EPSG_5311|5311|"DRUKREF 03 / Zhemgang TM"|Projected|[90.53, 26.77, 91.19, 27.39]|Some([216516.357, 462145.918, 282157.55, 530889.876])| + EPSG_5316|5316|"ETRS89 / Faroe TM"|Projected|[-13.91, 59.94, -0.48, 65.7]|Some([-185807.007, 647384.225, 564080.66, 1306811.412])| + EPSG_5320|5320|"NAD83 / Teranet Ontario Lambert"|Projected|[-95.16, 41.67, -74.35, 56.9]|Some([69130.65, 5257742.251, 1805664.107, 7000661.14])| + EPSG_5321|5321|"NAD83(CSRS) / Teranet Ontario Lambert"|Projected|[-95.16, 41.67, -74.35, 56.9]|Some([69131.169, 5257741.357, 1805664.349, 7000660.018])| + EPSG_5324|5324|"ISN2004"|Geographic2d|[-30.87, 59.96, -5.55, 69.59]|Some([-30.87, 59.96, -5.55, 69.59])| + EPSG_5325|5325|"ISN2004 / Lambert 2004"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([1038384.289, -262368.597, 2448429.41, 868116.034])| + EPSG_5329|5329|"Segara (Jakarta) / NEIEZ"|Projected|[114.55, -4.24, 119.06, 4.29]|Some([4404843.237, 432093.744, 4905392.313, 1373352.191])| + EPSG_5330|5330|"Batavia (Jakarta) / NEIEZ"|Projected|[95.16, -8.91, 115.77, 5.97]|Some([2252848.668, -86253.844, 4540268.934, 1559412.026])| + EPSG_5331|5331|"Makassar (Jakarta) / NEIEZ"|Projected|[118.71, -6.54, 120.78, -1.88]|Some([4866305.065, 177275.622, 5096046.823, 692561.029])| + EPSG_5337|5337|"Aratu / UTM zone 25S"|Projected|[-36.0, -20.11, -30.0, 0.0]|Some([165865.274, 7773492.367, 833818.947, 10000147.594])| + EPSG_5340|5340|"POSGAR 2007"|Geographic2d|[-73.59, -58.41, -52.63, -21.78]|Some([-73.59, -58.41, -52.63, -21.78])| + EPSG_5343|5343|"POSGAR 2007 / Argentina 1"|Projected|[-73.59, -52.0, -70.5, -36.16]|Some([1356924.506, 4237428.776, 1634976.778, 5998669.059])| + EPSG_5344|5344|"POSGAR 2007 / Argentina 2"|Projected|[-70.5, -54.9, -67.49, -24.08]|Some([2347455.359, 3914823.925, 2653562.2, 7337816.917])| + EPSG_5345|5345|"POSGAR 2007 / Argentina 3"|Projected|[-67.5, -55.11, -64.49, -21.78]|Some([3344856.272, 3891448.747, 3656178.57, 7592526.12])| + EPSG_5346|5346|"POSGAR 2007 / Argentina 4"|Projected|[-64.5, -54.91, -61.5, -21.99]|Some([4345083.263, 3913724.641, 4654917.05, 7569273.101])| + EPSG_5347|5347|"POSGAR 2007 / Argentina 5"|Projected|[-61.51, -39.06, -58.5, -23.37]|Some([5345603.932, 5675716.049, 5653373.665, 7416452.458])| + EPSG_5348|5348|"POSGAR 2007 / Argentina 6"|Projected|[-58.5, -38.59, -55.49, -24.84]|Some([6348368.174, 5727895.677, 6652643.044, 7253635.596])| + EPSG_5349|5349|"POSGAR 2007 / Argentina 7"|Projected|[-55.5, -28.11, -53.65, -25.49]|Some([7349170.077, 6890425.099, 7535191.314, 7181631.479])| + EPSG_5354|5354|"MARGEN"|Geographic2d|[-69.66, -22.91, -57.52, -9.67]|Some([-69.66, -22.91, -57.52, -9.67])| + EPSG_5355|5355|"MARGEN / UTM zone 20S"|Projected|[-66.0, -22.87, -60.0, -9.67]|Some([170744.22, 7467737.002, 829255.78, 8931073.114])| + EPSG_5356|5356|"MARGEN / UTM zone 19S"|Projected|[-69.66, -22.91, -66.0, -9.77]|Some([427615.159, 7463304.81, 829157.824, 8920016.539])| + EPSG_5357|5357|"MARGEN / UTM zone 21S"|Projected|[-60.0, -20.17, -57.52, -16.27]|Some([179335.496, 7766873.739, 445663.342, 8201127.714])| + EPSG_5360|5360|"SIRGAS-Chile 2002"|Geographic2d|[-113.21, -59.87, -65.72, -17.5]|Some([-113.21, -59.87, -65.72, -17.5])| + EPSG_5361|5361|"SIRGAS-Chile 2002 / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, -17.5]|Some([181409.073, 3359262.29, 818590.927, 8065131.102])| + EPSG_5362|5362|"SIRGAS-Chile 2002 / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, -18.35]|Some([182927.612, 3415992.79, 818130.08, 7971091.108])| + EPSG_5365|5365|"CR05"|Geographic2d|[-90.45, 2.15, -81.43, 11.77]|Some([-90.45, 2.15, -81.43, 11.77])| + EPSG_5367|5367|"CR05 / CRTM05"|Projected|[-86.5, 2.21, -81.43, 11.77]|Some([221845.369, 244345.953, 785946.641, 1302793.471])| + EPSG_5371|5371|"MACARIO SOLIS"|Geographic2d|[-84.32, 5.0, -77.04, 12.51]|Some([-84.32, 5.0, -77.04, 12.51])| + EPSG_5373|5373|"Peru96"|Geographic2d|[-84.68, -21.05, -68.67, -0.03]|Some([-84.68, -21.05, -68.67, -0.03])| + EPSG_5381|5381|"SIRGAS-ROU98"|Geographic2d|[-58.49, -37.77, -50.01, -30.09]|Some([-58.49, -37.77, -50.01, -30.09])| + EPSG_5382|5382|"SIRGAS-ROU98 / UTM zone 21S"|Projected|[-58.49, -36.63, -54.0, -30.09]|Some([356414.69, 5941979.316, 789147.789, 6671241.831])| + EPSG_5383|5383|"SIRGAS-ROU98 / UTM zone 22S"|Projected|[-54.0, -37.77, -50.01, -31.9]|Some([216269.186, 5815464.117, 593614.213, 6470646.947])| + EPSG_5387|5387|"Peru96 / UTM zone 18S"|Projected|[-78.0, -21.05, -72.0, -0.03]|Some([166021.489, 7669384.978, 833978.511, 9996684.099])| + EPSG_5389|5389|"Peru96 / UTM zone 19S"|Projected|[-72.0, -20.44, -68.67, -2.14]|Some([166253.241, 7736963.178, 536695.503, 9763464.597])| + EPSG_5393|5393|"SIRGAS_ES2007.8"|Geographic2d|[-91.43, 9.97, -87.65, 14.44]|Some([-91.43, 9.97, -87.65, 14.44])| + EPSG_5396|5396|"SIRGAS 2000 / UTM zone 26S"|Projected|[-30.0, -23.86, -25.28, 4.26]|Some([166030.658, 7358035.458, 691416.608, 10471514.856])| + EPSG_5451|5451|"Ocotepeque 1935"|Geographic2d|[-92.29, 7.98, -82.53, 17.83]|Some([-92.292, 7.981, -82.532, 17.832])| + EPSG_5456|5456|"Ocotepeque 1935 / Costa Rica Norte"|Projected|[-85.97, 9.53, -82.53, 11.22]|Some([320075.64, 168377.839, 697797.887, 355870.505])| + EPSG_5457|5457|"Ocotepeque 1935 / Costa Rica Sur"|Projected|[-85.74, 7.98, -82.53, 9.94]|Some([271177.188, 215325.313, 625115.898, 432750.82])| + EPSG_5459|5459|"Ocotepeque 1935 / Guatemala Sur"|Projected|[-92.29, 13.69, -88.19, 15.86]|Some([288108.296, 192308.875, 731677.48, 433503.99])| + EPSG_5460|5460|"Ocotepeque 1935 / El Salvador Lambert"|Projected|[-90.11, 13.1, -87.69, 14.44]|Some([379420.678, 220393.475, 641854.688, 369029.236])| + EPSG_5461|5461|"Ocotepeque 1935 / Nicaragua Norte"|Projected|[-87.74, 12.8, -83.08, 15.03]|Some([256576.286, 242075.778, 762543.394, 490106.335])| + EPSG_5462|5462|"Ocotepeque 1935 / Nicaragua Sur"|Projected|[-87.63, 10.7, -83.42, 12.8]|Some([266757.632, 174744.553, 727350.592, 407920.891])| + EPSG_5463|5463|"SAD69 / UTM zone 17N"|Projected|[-80.18, 0.0, -78.0, 2.7]|Some([591204.251, 43.987, 834026.134, 298881.525])| + EPSG_5464|5464|"Sibun Gorge 1922"|Geographic2d|[-89.22, 15.88, -87.72, 18.49]|Some([-89.22, 15.88, -87.72, 18.49])| + EPSG_5467|5467|"Panama-Colon 1911"|Geographic2d|[-83.04, 7.15, -77.19, 9.68]|Some([-83.04, 7.15, -77.19, 9.68])| + EPSG_5469|5469|"Panama-Colon 1911 / Panama Lambert"|Projected|[-83.04, 7.15, -77.19, 9.68]|Some([164154.904, 154791.077, 810436.217, 435876.521])| + EPSG_5472|5472|"Panama-Colon 1911 / Panama Polyconic"|Projected|[-83.04, 7.15, -77.19, 9.68]|Some([753563.673, 959938.133, 1460252.334, 1268485.227])| + EPSG_5479|5479|"RSRGD2000 / MSLC2000"|Projected|[153.0, -81.0, 173.0, -76.0]|Some([6731065.064, 4650065.224, 7268934.936, 5223258.238])| + EPSG_5480|5480|"RSRGD2000 / BCLC2000"|Projected|[157.0, -76.0, 173.0, -73.0]|Some([4739553.43, 2818045.335, 5260446.57, 3167409.606])| + EPSG_5481|5481|"RSRGD2000 / PCLC2000"|Projected|[160.0, -73.0, 172.0, -69.5]|Some([2765671.949, 822893.84, 3234328.051, 1223157.368])| + EPSG_5482|5482|"RSRGD2000 / RSPS2000"|Projected|[150.0, -90.0, -150.0, -76.0]|Some([4219099.649, 1000000.0, 5780900.351, 2561800.702])| + EPSG_5489|5489|"RGAF09"|Geographic2d|[-63.66, 14.08, -57.52, 18.53]|Some([-63.66, 14.08, -57.52, 18.53])| + EPSG_5490|5490|"RGAF09 / UTM zone 20N"|Projected|[-63.66, 14.08, -60.0, 18.31]|Some([428749.413, 1556574.512, 823991.366, 2027093.391])| + EPSG_5513|5513|"S-JTSK / Krovak"|Projected|[12.09, 47.73, 22.56, 51.06]|Some([911041.249, 159365.31, 1353306.148, 951499.373])| + EPSG_5514|5514|"S-JTSK / Krovak East North"|Projected|[12.09, 47.73, 22.56, 51.06]|Some([-951499.373, -1353306.148, -159365.31, -911041.249])| + EPSG_5515|5515|"S-JTSK/05 / Modified Krovak"|Projected|[12.09, 48.58, 18.86, 51.06]|Some([5911041.232, 5418596.799, 6244159.024, 5935719.085])| + EPSG_5516|5516|"S-JTSK/05 / Modified Krovak East North"|Projected|[12.09, 48.58, 18.86, 51.06]|Some([-5935719.085, -6244159.024, -5418596.799, -5911041.232])| + EPSG_5518|5518|"CI1971 / Chatham Islands Map Grid"|Projected|[-177.25, -44.64, -175.54, -43.3]|Some([289093.877, 578382.885, 427849.088, 727720.948])| + EPSG_5519|5519|"CI1979 / Chatham Islands Map Grid"|Projected|[-177.25, -44.64, -175.54, -43.3]|Some([289093.93, 578383.2, 427849.15, 727721.128])| + EPSG_5520|5520|"DHDN / 3-degree Gauss-Kruger zone 1"|Projected|[3.34, 55.24, 4.51, 55.92]|Some([1521254.683, 6123366.717, 1596037.814, 6200042.731])| + EPSG_5523|5523|"WGS 84 / Gabon TM 2011"|Projected|[7.03, -6.37, 14.52, 2.32]|Some([1002092.159, 4793749.635, 1836207.151, 5757217.918])| + EPSG_5524|5524|"Corrego Alegre 1961"|Geographic2d|[-58.16, -27.5, -38.82, -14.99]|Some([-58.16, -27.5, -38.82, -14.99])| + EPSG_5527|5527|"SAD69(96)"|Geographic2d|[-74.01, -35.71, -25.28, 7.04]|Some([-74.009, -35.71, -25.28, 7.04])| + EPSG_5530|5530|"SAD69(96) / Brazil Polyconic"|Projected|[-74.01, -35.71, -25.28, 7.04]|Some([2772555.146, 5669191.152, 8197128.799, 10875962.073])| + EPSG_5531|5531|"SAD69(96) / UTM zone 21S"|Projected|[-60.0, -31.91, -53.99, 4.51]|Some([166082.729, 6465626.455, 835140.24, 10499228.111])| + EPSG_5533|5533|"SAD69(96) / UTM zone 23S"|Projected|[-48.0, -33.5, -42.0, 5.13]|Some([166067.761, 6289297.639, 834021.964, 10567849.212])| + EPSG_5534|5534|"SAD69(96) / UTM zone 24S"|Projected|[-42.0, -26.35, -36.0, 0.74]|Some([166063.802, 7082121.6, 834014.906, 10081943.115])| + EPSG_5535|5535|"SAD69(96) / UTM zone 25S"|Projected|[-36.0, -23.8, -29.99, 4.19]|Some([166067.357, 7364711.856, 835113.722, 10463806.566])| + EPSG_5536|5536|"Corrego Alegre 1961 / UTM zone 21S"|Projected|[-58.16, -27.5, -54.0, -17.99]|Some([377180.605, 6954555.353, 817736.982, 8010895.741])| + EPSG_5537|5537|"Corrego Alegre 1961 / UTM zone 22S"|Projected|[-54.0, -27.5, -47.99, -14.99]|Some([177321.015, 6954531.391, 823755.443, 8342760.445])| + EPSG_5538|5538|"Corrego Alegre 1961 / UTM zone 23S"|Projected|[-48.0, -25.29, -42.0, -15.0]|Some([177336.031, 7199521.564, 822663.969, 8341654.392])| + EPSG_5539|5539|"Corrego Alegre 1961 / UTM zone 24S"|Projected|[-42.0, -22.96, -38.82, -14.99]|Some([177321.015, 7457731.223, 519353.051, 8342760.202])| + EPSG_5546|5546|"PNG94"|Geographic2d|[139.2, -14.75, 162.81, 2.58]|Some([139.2, -14.75, 162.81, 2.58])| + EPSG_5550|5550|"PNG94 / PNGMG94 zone 54"|Projected|[139.2, -11.15, 144.0, 2.31]|Some([299672.024, 8765774.743, 833978.332, 10255678.174])| + EPSG_5551|5551|"PNG94 / PNGMG94 zone 55"|Projected|[144.0, -13.88, 150.01, 2.58]|Some([166022.794, 8463493.527, 835091.494, 10285565.545])| + EPSG_5552|5552|"PNG94 / PNGMG94 zone 56"|Projected|[150.0, -14.75, 156.0, 1.98]|Some([166024.624, 8367170.28, 833975.376, 10219152.491])| + EPSG_5559|5559|"Ocotepeque 1935 / Guatemala Norte"|Projected|[-91.86, 15.85, -88.34, 17.83]|Some([336261.521, 185437.718, 713316.862, 405614.539])| + EPSG_5561|5561|"UCS-2000"|Geographic2d|[22.15, 43.18, 40.18, 52.38]|Some([22.151, 43.18, 40.182, 52.38])| + EPSG_5562|5562|"UCS-2000 / Gauss-Kruger zone 4"|Projected|[22.15, 47.95, 24.0, 51.66]|Some([4579697.459, 5313634.479, 4724204.802, 5729906.409])| + EPSG_5563|5563|"UCS-2000 / Gauss-Kruger zone 5"|Projected|[24.0, 45.1, 30.0, 51.96]|Some([5263989.086, 4996172.935, 5736248.331, 5763265.034])| + EPSG_5564|5564|"UCS-2000 / Gauss-Kruger zone 6"|Projected|[30.0, 43.18, 36.0, 52.38]|Some([6256198.776, 4782822.726, 6744030.327, 5809973.346])| + EPSG_5565|5565|"UCS-2000 / Gauss-Kruger zone 7"|Projected|[36.0, 43.43, 40.18, 50.44]|Some([7257192.157, 4810591.514, 7595654.412, 5594192.295])| + EPSG_5566|5566|"UCS-2000 / Gauss-Kruger CM 21E"|Projected|[22.15, 47.95, 24.0, 51.66]|Some([579697.459, 5313634.479, 724204.802, 5729906.409])| + EPSG_5567|5567|"UCS-2000 / Gauss-Kruger CM 27E"|Projected|[24.0, 45.1, 30.0, 51.96]|Some([263989.086, 4996172.935, 736248.331, 5763265.034])| + EPSG_5568|5568|"UCS-2000 / Gauss-Kruger CM 33E"|Projected|[30.0, 43.18, 36.0, 52.38]|Some([256198.776, 4782822.726, 744030.327, 5809973.346])| + EPSG_5569|5569|"UCS-2000 / Gauss-Kruger CM 39E"|Projected|[36.0, 43.43, 40.18, 50.44]|Some([257192.157, 4810591.514, 595654.412, 5594192.295])| + EPSG_5588|5588|"NAD27 / New Brunswick Stereographic (NAD27)"|Projected|[-69.05, 44.56, -63.7, 48.07]|Some([335113.02, 292582.014, 1729758.661, 1584918.226])| + EPSG_5589|5589|"Sibun Gorge 1922 / Colony Grid"|Projected|[-89.22, 15.88, -87.72, 18.49]|Some([10592.006, 16621.98, 537683.414, 965054.371])| + EPSG_5593|5593|"FEH2010"|Geographic2d|[10.66, 54.33, 12.01, 54.83]|Some([10.66, 54.33, 12.01, 54.83])| + EPSG_5596|5596|"FEH2010 / Fehmarnbelt TM"|Projected|[10.66, 54.33, 12.01, 54.83]|Some([956195.922, 6022647.694, 1044020.927, 6078515.489])| + EPSG_5623|5623|"NAD27 / Michigan East"|Projected|[-84.87, 41.69, -82.13, 46.04]|Some([171313.832, 69223.918, 919664.799, 1658605.681])| + EPSG_5624|5624|"NAD27 / Michigan Old Central"|Projected|[-87.61, 41.75, -84.6, 46.11]|Some([-7532.267, 91085.627, 813772.319, 1685825.657])| + EPSG_5625|5625|"NAD27 / Michigan West"|Projected|[-90.42, 45.09, -83.44, 48.32]|Some([68732.433, 1308409.304, 1871332.811, 2531082.596])| + EPSG_5627|5627|"ED50 / TM 6 NE"|Projected|[3.04, 41.15, 10.38, 43.74]|Some([251697.361, 4555605.478, 867668.106, 4852525.646])| + EPSG_5629|5629|"Moznet / UTM zone 38S"|Projected|[35.99, -18.98, 40.9, -10.42]|Some([-490006.441, 7876909.691, 68131.307, 8845240.269])| + EPSG_5631|5631|"Pulkovo 1942(58) / Gauss-Kruger zone 2 (E-N)"|Projected|[9.92, 50.2, 12.0, 54.23]|Some([2559996.847, 5563596.228, 2714179.627, 6015778.908])| + EPSG_5632|5632|"PTRA08 / LCC Europe"|Projected|[-35.58, 29.24, -12.48, 43.07]|Some([-276612.521, 647735.188, 2243856.751, 2919205.43])| + EPSG_5633|5633|"PTRA08 / LAEA Europe"|Projected|[-35.58, 29.24, -12.48, 43.07]|Some([82549.713, 1014194.756, 2515870.1, 3325979.329])| + EPSG_5634|5634|"REGCAN95 / LCC Europe"|Projected|[-21.93, 24.6, -11.75, 32.76]|Some([679090.188, 112597.492, 1970585.316, 1349067.854])| + EPSG_5635|5635|"REGCAN95 / LAEA Europe"|Projected|[-21.93, 24.6, -11.75, 32.76]|Some([1090443.14, 501635.159, 2284416.103, 1698896.68])| + EPSG_5636|5636|"TUREF / LAEA Europe"|Projected|[25.62, 34.42, 44.83, 43.45]|Some([5578265.878, 1410183.158, 7437720.993, 2908779.05])| + EPSG_5637|5637|"TUREF / LCC Europe"|Projected|[25.62, 34.42, 44.83, 43.45]|Some([5221432.54, 1042961.876, 7092386.033, 2514728.164])| + EPSG_5638|5638|"ISN2004 / LAEA Europe"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([2180027.087, 4190621.841, 3714401.462, 5627446.31])| + EPSG_5639|5639|"ISN2004 / LCC Europe"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([1877057.884, 3749340.562, 3380297.711, 5177062.581])| + EPSG_5641|5641|"SIRGAS 2000 / Brazil Mercator"|Projected|[-51.64, -5.74, -32.43, 7.04]|Some([4038781.584, 9364609.783, 6175935.03, 10779963.088])| + EPSG_5643|5643|"ED50 / SPBA LCC"|Projected|[-1.67, 50.5, 22.0, 56.0]|Some([-10227.878, 278879.816, 1663508.268, 954077.347])| + EPSG_5644|5644|"RGR92 / UTM zone 39S"|Projected|[51.83, -24.37, 54.0, -18.52]|Some([584179.028, 7301518.571, 816760.321, 7952080.557])| + EPSG_5646|5646|"NAD83 / Vermont (ftUS)"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([1387814.935, 80177.973, 1909142.042, 923813.43])| + EPSG_5649|5649|"ETRS89 / UTM zone 31N (zE-N)"|Projected|[3.34, 50.97, 6.01, 55.92]|Some([31521248.793, 5646543.808, 31711326.613, 6201269.317])| + EPSG_5650|5650|"ETRS89 / UTM zone 33N (zE-N)"|Projected|[11.57, 47.46, 15.04, 55.03]|Some([33241491.904, 5256285.079, 33503014.825, 6103509.128])| + EPSG_5651|5651|"ETRS89 / UTM zone 31N (N-zE)"|Projected|[3.34, 50.97, 6.01, 55.92]|Some([31521248.793, 5646543.808, 31711326.613, 6201269.317])| + EPSG_5652|5652|"ETRS89 / UTM zone 32N (N-zE)"|Projected|[6.0, 47.27, 12.01, 55.47]|Some([32273082.89, 5235168.953, 32727673.446, 6151213.241])| + EPSG_5653|5653|"ETRS89 / UTM zone 33N (N-zE)"|Projected|[11.57, 47.46, 15.04, 55.03]|Some([33241491.904, 5256285.079, 33503014.825, 6103509.128])| + EPSG_5654|5654|"NAD83(HARN) / Vermont (ftUS)"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([1387815.233, 80174.81, 1909142.241, 923810.103])| + EPSG_5655|5655|"NAD83(NSRS2007) / Vermont (ftUS)"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([1387814.935, 80177.973, 1909142.042, 923813.43])| + EPSG_5659|5659|"Monte Mario / TM Emilia-Romagna"|Projected|[9.19, 43.73, 12.76, 45.14]|Some([515019.24, 842099.148, 802913.921, 1005581.767])| + EPSG_5663|5663|"Pulkovo 1942(58) / Gauss-Kruger zone 3 (E-N)"|Projected|[12.0, 45.78, 18.0, 54.89]|Some([3266689.693, 5071722.51, 3733310.307, 6089252.821])| + EPSG_5664|5664|"Pulkovo 1942(83) / Gauss-Kruger zone 2 (E-N)"|Projected|[9.92, 50.2, 12.0, 54.23]|Some([2560123.302, 5563649.506, 2714304.768, 6015827.306])| + EPSG_5665|5665|"Pulkovo 1942(83) / Gauss-Kruger zone 3 (E-N)"|Projected|[12.0, 45.78, 18.01, 54.74]|Some([3266689.693, 5071722.522, 3734210.084, 6072561.63])| + EPSG_5666|5666|"PD/83 / 3-degree Gauss-Kruger zone 3 (E-N)"|Projected|[9.92, 50.35, 10.5, 51.56]|Some([3563873.92, 5579743.389, 3606833.282, 5715015.141])| + EPSG_5667|5667|"PD/83 / 3-degree Gauss-Kruger zone 4 (E-N)"|Projected|[10.5, 50.2, 12.56, 51.64]|Some([4393011.517, 5562651.382, 4540089.0, 5723911.584])| + EPSG_5668|5668|"RD/83 / 3-degree Gauss-Kruger zone 4 (E-N)"|Projected|[11.89, 50.2, 13.51, 51.66]|Some([4492252.698, 5562650.634, 4607915.595, 5726153.056])| + EPSG_5669|5669|"RD/83 / 3-degree Gauss-Kruger zone 5 (E-N)"|Projected|[13.5, 50.62, 15.04, 51.58]|Some([5393985.823, 5609367.741, 5502969.004, 5717233.673])| + EPSG_5670|5670|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 3 (E-N)"|Projected|[9.92, 50.35, 10.5, 51.56]|Some([3563801.795, 5580281.274, 3606760.391, 5715554.778])| + EPSG_5671|5671|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 4 (E-N)"|Projected|[10.5, 48.97, 13.5, 54.74]|Some([4390176.295, 5426386.82, 4609823.705, 6069425.908])| + EPSG_5672|5672|"Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 5 (E-N)"|Projected|[13.5, 46.54, 16.5, 54.72]|Some([5384939.346, 5156201.335, 5615060.654, 6067237.67])| + EPSG_5673|5673|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 3 (E-N)"|Projected|[9.92, 50.35, 10.5, 51.56]|Some([3563928.4, 5580334.374, 3606886.635, 5715606.438])| + EPSG_5674|5674|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 4 (E-N)"|Projected|[10.5, 48.97, 13.5, 54.74]|Some([4390176.295, 5426386.82, 4609946.512, 6069468.31])| + EPSG_5675|5675|"Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 5 (E-N)"|Projected|[13.5, 46.54, 16.5, 54.72]|Some([5384939.346, 5156201.335, 5615183.77, 6067236.771])| + EPSG_5676|5676|"DHDN / 3-degree Gauss-Kruger zone 2 (E-N)"|Projected|[5.86, 49.11, 7.51, 53.81]|Some([2489822.302, 5441421.616, 2610288.01, 5965387.975])| + EPSG_5677|5677|"DHDN / 3-degree Gauss-Kruger zone 3 (E-N)"|Projected|[7.5, 47.27, 10.51, 55.09]|Some([3386569.827, 5236823.484, 3614340.947, 6107852.389])| + EPSG_5678|5678|"DHDN / 3-degree Gauss-Kruger zone 4 (E-N)"|Projected|[10.5, 47.39, 13.51, 54.59]|Some([4386858.109, 5250163.04, 4614113.737, 6052198.107])| + EPSG_5679|5679|"DHDN / 3-degree Gauss-Kruger zone 5 (E-N)"|Projected|[13.5, 48.51, 13.84, 48.98]|Some([5389309.875, 5375341.527, 5415225.792, 5428042.192])| + EPSG_5680|5680|"DHDN / 3-degree Gauss-Kruger zone 1 (E-N)"|Projected|[3.34, 55.24, 4.51, 55.92]|Some([1521254.683, 6123366.717, 1596037.814, 6200042.731])| + EPSG_5681|5681|"DB_REF"|Geographic2d|[5.86, 47.27, 15.04, 55.09]|Some([5.861, 47.271, 15.042, 55.092])| + EPSG_5682|5682|"DB_REF / 3-degree Gauss-Kruger zone 2 (E-N)"|Projected|[5.86, 49.11, 7.51, 53.81]|Some([2489822.682, 5441421.669, 2610288.215, 5965387.257])| + EPSG_5683|5683|"DB_REF / 3-degree Gauss-Kruger zone 3 (E-N)"|Projected|[7.5, 47.27, 10.51, 55.09]|Some([3386569.924, 5236823.696, 3614340.742, 6107851.486])| + EPSG_5684|5684|"DB_REF / 3-degree Gauss-Kruger zone 4 (E-N)"|Projected|[10.5, 47.39, 13.51, 54.74]|Some([4386857.919, 5250163.094, 4614113.292, 6068891.589])| + EPSG_5685|5685|"DB_REF / 3-degree Gauss-Kruger zone 5 (E-N)"|Projected|[13.5, 48.51, 15.04, 54.72]|Some([5389309.503, 5374692.14, 5503093.751, 6066646.658])| + EPSG_5700|5700|"NZGD2000 / UTM zone 1S"|Projected|[-180.0, -52.97, -174.0, -25.88]|Some([199377.012, 4126854.846, 800622.988, 7137605.292])| + EPSG_5825|5825|"AGD66 / ACT Standard Grid"|Projected|[148.76, -35.93, 149.4, -35.12]|Some([177163.686, 531818.761, 235508.018, 621766.946])| + EPSG_5836|5836|"Yemen NGN96 / UTM zone 37N"|Projected|[41.08, 14.73, 42.0, 16.36]|Some([722180.971, 1629497.004, 823052.95, 1811122.342])| + EPSG_5837|5837|"Yemen NGN96 / UTM zone 40N"|Projected|[54.0, 8.95, 57.96, 14.95]|Some([170068.433, 989324.518, 605536.607, 1654976.859])| + EPSG_5839|5839|"Peru96 / UTM zone 17S"|Projected|[-84.0, -17.33, -78.0, -3.11]|Some([166510.94, 8081449.712, 833489.06, 9656248.169])| + EPSG_5842|5842|"WGS 84 / TM 12 SE"|Projected|[10.41, -8.01, 12.84, -5.05]|Some([323732.598, 9114257.757, 593113.86, 9441808.698])| + EPSG_5844|5844|"RGRDC 2005 / Congo TM zone 30"|Projected|[29.0, -13.46, 29.81, -12.15]|Some([391163.747, 8511327.167, 479428.542, 8656449.25])| + EPSG_5858|5858|"SAD69(96) / UTM zone 22S"|Projected|[-54.01, -35.71, -47.99, 7.04]|Some([164958.572, 6044081.988, 835141.187, 10779277.386])| + EPSG_5875|5875|"SAD69(96) / UTM zone 18S"|Projected|[-74.01, -10.01, -71.99, -4.59]|Some([608565.516, 8892011.403, 834087.62, 9492618.215])| + EPSG_5876|5876|"SAD69(96) / UTM zone 19S"|Projected|[-72.0, -11.14, -65.99, 2.15]|Some([166083.367, 8766909.216, 835153.848, 10238008.982])| + EPSG_5877|5877|"SAD69(96) / UTM zone 20S"|Projected|[-66.0, -16.28, -59.99, 5.28]|Some([166083.402, 8197761.369, 835147.327, 10584459.406])| + EPSG_5879|5879|"Cadastre 1997 / UTM zone 38S"|Projected|[44.98, -13.05, 45.35, -12.61]|Some([497598.622, 8557572.053, 537783.866, 8606255.533])| + EPSG_5880|5880|"SIRGAS 2000 / Brazil Polyconic"|Projected|[-74.01, -35.71, -25.28, 7.04]|Some([2772499.524, 5669143.943, 8197091.986, 10875923.354])| + EPSG_5886|5886|"TGD2005"|Geographic2d|[-179.08, -25.68, -171.28, -14.14]|Some([-179.08, -25.68, -171.28, -14.14])| + EPSG_5887|5887|"TGD2005 / Tonga Map Grid"|Projected|[-179.08, -25.68, -171.28, -14.14]|Some([1275472.102, 2147296.991, 2118242.412, 3436790.299])| + EPSG_5896|5896|"VN-2000 / TM-3 zone 481"|Projected|[102.14, 9.2, 103.51, 22.82]|Some([514174.358, 1017382.044, 665749.566, 2525246.918])| + EPSG_5897|5897|"VN-2000 / TM-3 zone 482"|Projected|[103.5, 8.33, 106.51, 23.4]|Some([334573.466, 921168.346, 666136.827, 2589492.136])| + EPSG_5898|5898|"VN-2000 / TM-3 zone 491"|Projected|[106.5, 8.57, 109.53, 22.95]|Some([334676.255, 947710.84, 668237.334, 2539674.69])| + EPSG_5899|5899|"VN-2000 / TM-3 107-45"|Projected|[106.43, 8.57, 108.76, 21.67]|Some([354495.249, 947710.678, 610985.121, 2397712.935])| + EPSG_5921|5921|"WGS 84 / EPSG Arctic Regional zone A1"|Projected|[-156.0, 75.0, -66.0, 87.01]|Some([-1176897.641, -704776.607, 1176897.641, 732749.7])| + EPSG_5922|5922|"WGS 84 / EPSG Arctic Regional zone A2"|Projected|[-84.0, 75.0, 6.01, 87.01]|Some([-1176897.641, -704776.601, 1177104.365, 732790.719])| + EPSG_5923|5923|"WGS 84 / EPSG Arctic Regional zone A3"|Projected|[-12.0, 75.0, 78.01, 87.01]|Some([-1176897.641, -704776.601, 1177104.365, 732790.719])| + EPSG_5924|5924|"WGS 84 / EPSG Arctic Regional zone A4"|Projected|[60.0, 75.0, 150.01, 87.01]|Some([-1176897.641, -704776.601, 1177104.365, 732790.719])| + EPSG_5925|5925|"WGS 84 / EPSG Arctic Regional zone A5"|Projected|[132.0, 75.0, -138.0, 87.01]|Some([-1176897.641, -704776.607, 1176897.641, 732749.7])| + EPSG_5926|5926|"WGS 84 / EPSG Arctic Regional zone B1"|Projected|[-156.0, 67.0, -66.0, 79.01]|Some([-1788158.297, -686289.14, 1788158.297, 997837.651])| + EPSG_5927|5927|"WGS 84 / EPSG Arctic Regional zone B2"|Projected|[-84.0, 67.0, 6.01, 79.01]|Some([-1788158.297, -686289.131, 1788477.815, 997983.573])| + EPSG_5928|5928|"WGS 84 / EPSG Arctic Regional zone B3"|Projected|[-12.0, 67.0, 78.01, 79.01]|Some([-1788158.297, -686289.131, 1788477.815, 997983.573])| + EPSG_5929|5929|"WGS 84 / EPSG Arctic Regional zone B4"|Projected|[60.0, 67.0, 150.01, 79.01]|Some([-1788158.297, -686289.131, 1788477.815, 997983.573])| + EPSG_5930|5930|"WGS 84 / EPSG Arctic Regional zone B5"|Projected|[132.0, 67.0, -138.0, 79.01]|Some([-1788158.297, -686289.14, 1788158.297, 997837.651])| + EPSG_5931|5931|"WGS 84 / EPSG Arctic Regional zone C1"|Projected|[-156.0, 59.0, -66.0, 71.0]|Some([-2379893.805, -679520.842, 2379893.805, 1217856.199])| + EPSG_5932|5932|"WGS 84 / EPSG Arctic Regional zone C2"|Projected|[-84.0, 59.0, 6.0, 71.0]|Some([-2379893.805, -679520.842, 2379893.805, 1217856.199])| + EPSG_5933|5933|"WGS 84 / EPSG Arctic Regional zone C3"|Projected|[-12.0, 59.0, 78.01, 71.0]|Some([-2379893.805, -679520.83, 2380329.991, 1218094.624])| + EPSG_5934|5934|"WGS 84 / EPSG Arctic Regional zone C4"|Projected|[60.0, 59.0, 150.01, 71.0]|Some([-2379893.805, -679520.83, 2380329.991, 1218094.624])| + EPSG_5935|5935|"WGS 84 / EPSG Arctic Regional zone C5"|Projected|[132.0, 59.0, -138.0, 71.0]|Some([-2379893.805, -679520.842, 2379893.805, 1217856.199])| + EPSG_5936|5936|"WGS 84 / EPSG Alaska Polar Stereographic"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-1405880.717, -1387222.946, 5405880.717, 5387222.946])| + EPSG_5937|5937|"WGS 84 / EPSG Canada Polar Stereographic"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-1403805.947, -1397584.163, 5403805.947, 5397584.163])| + EPSG_5938|5938|"WGS 84 / EPSG Greenland Polar Stereographic"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-1401213.076, -1401213.076, 5401213.076, 5401213.076])| + EPSG_5939|5939|"WGS 84 / EPSG Norway Polar Stereographic"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-1405880.717, -1387222.946, 5405880.717, 5387222.946])| + EPSG_5940|5940|"WGS 84 / EPSG Russia Polar Stereographic"|Projected|[-180.0, 60.0, 180.0, 90.0]|Some([-1401213.076, -1401213.076, 5401213.076, 5401213.076])| + EPSG_6050|6050|"GR96 / EPSG Arctic zone 1-25"|Projected|[-60.0, 82.83, 0.0, 87.84]|Some([25100338.008, 1208867.143, 25899661.992, 1800596.541])| + EPSG_6051|6051|"GR96 / EPSG Arctic zone 2-18"|Projected|[-72.0, 79.5, -32.0, 84.51]|Some([18100819.545, 2214346.86, 18899180.455, 2810361.182])| + EPSG_6052|6052|"GR96 / EPSG Arctic zone 2-20"|Projected|[-32.0, 79.5, 8.01, 84.51]|Some([20100819.545, 2214346.864, 20899372.023, 2810397.424])| + EPSG_6053|6053|"GR96 / EPSG Arctic zone 3-29"|Projected|[-75.0, 76.16, -54.0, 81.17]|Some([29339921.413, 3215666.359, 29896511.272, 3807801.266])| + EPSG_6054|6054|"GR96 / EPSG Arctic zone 3-31"|Projected|[-54.0, 76.16, -24.0, 81.17]|Some([31103488.728, 3215645.774, 31896511.272, 3807801.266])| + EPSG_6055|6055|"GR96 / EPSG Arctic zone 3-33"|Projected|[-24.0, 76.16, 1.89, 81.17]|Some([33129396.294, 3215654.206, 33815591.801, 3803587.73])| + EPSG_6056|6056|"GR96 / EPSG Arctic zone 4-20"|Projected|[-76.0, 72.83, -51.0, 77.84]|Some([20106943.949, 4217199.595, 20925303.561, 4809783.683])| + EPSG_6057|6057|"GR96 / EPSG Arctic zone 4-22"|Projected|[-51.0, 72.83, -26.0, 77.84]|Some([22106943.949, 4217199.595, 22925303.561, 4809783.683])| + EPSG_6058|6058|"GR96 / EPSG Arctic zone 4-24"|Projected|[-26.0, 72.83, -2.0, 77.84]|Some([24106943.949, 4217168.655, 24893056.051, 4804852.123])| + EPSG_6059|6059|"GR96 / EPSG Arctic zone 5-41"|Projected|[-71.89, 69.48, -51.99, 74.51]|Some([41114476.793, 5216081.131, 41890158.063, 5802060.086])| + EPSG_6060|6060|"GR96 / EPSG Arctic zone 5-43"|Projected|[-52.0, 69.5, -32.0, 74.51]|Some([43110597.003, 5218312.219, 43889402.997, 5802010.751])| + EPSG_6061|6061|"GR96 / EPSG Arctic zone 5-45"|Projected|[-32.0, 69.5, -12.0, 74.51]|Some([45110597.003, 5218312.219, 45889402.997, 5802010.751])| + EPSG_6062|6062|"GR96 / EPSG Arctic zone 6-26"|Projected|[-65.0, 66.16, -47.0, 71.17]|Some([26095158.422, 6218146.939, 26904841.578, 6800627.269])| + EPSG_6063|6063|"GR96 / EPSG Arctic zone 6-28"|Projected|[-47.0, 66.16, -29.0, 71.17]|Some([28095158.422, 6218146.939, 28904841.578, 6800627.269])| + EPSG_6064|6064|"GR96 / EPSG Arctic zone 6-30"|Projected|[-29.0, 66.16, -11.0, 71.17]|Some([30095158.422, 6218146.939, 30904841.578, 6800627.269])| + EPSG_6065|6065|"GR96 / EPSG Arctic zone 7-11"|Projected|[-59.0, 62.83, -42.0, 67.84]|Some([11093145.783, 7218990.082, 11957384.383, 7804553.489])| + EPSG_6066|6066|"GR96 / EPSG Arctic zone 7-13"|Projected|[-42.0, 62.83, -25.0, 67.84]|Some([13093145.783, 7218990.082, 13957384.383, 7804553.489])| + EPSG_6067|6067|"GR96 / EPSG Arctic zone 8-20"|Projected|[-59.0, 59.5, -44.0, 64.51]|Some([20104086.126, 8219762.325, 20952204.13, 8801720.306])| + EPSG_6068|6068|"GR96 / EPSG Arctic zone 8-22"|Projected|[-44.0, 59.5, -29.0, 64.51]|Some([22104086.126, 8219762.325, 22952204.13, 8801720.306])| + EPSG_6069|6069|"ETRS89 / EPSG Arctic zone 2-22"|Projected|[-4.0, 79.5, 36.01, 84.51]|Some([22100819.545, 2214346.864, 22899372.023, 2810397.424])| + EPSG_6070|6070|"ETRS89 / EPSG Arctic zone 3-11"|Projected|[-3.35, 76.16, 38.01, 81.17]|Some([10867880.862, 3215695.441, 11948226.427, 3860740.154])| + EPSG_6071|6071|"ETRS89 / EPSG Arctic zone 4-26"|Projected|[-2.0, 72.83, 22.01, 77.84]|Some([26106943.949, 4217168.662, 26893379.096, 4804899.514])| + EPSG_6072|6072|"ETRS89 / EPSG Arctic zone 4-28"|Projected|[22.0, 72.83, 46.01, 77.84]|Some([28106943.949, 4217168.662, 28893379.096, 4804899.514])| + EPSG_6073|6073|"ETRS89 / EPSG Arctic zone 5-11"|Projected|[4.0, 69.5, 24.01, 74.51]|Some([11110597.003, 5218312.227, 11889788.811, 5802060.086])| + EPSG_6074|6074|"ETRS89 / EPSG Arctic zone 5-13"|Projected|[24.0, 69.5, 44.01, 74.51]|Some([13110597.003, 5218312.227, 13889788.811, 5802060.086])| + EPSG_6075|6075|"WGS 84 / EPSG Arctic zone 2-24"|Projected|[33.0, 79.5, 73.01, 84.51]|Some([24100819.545, 2214346.864, 24899372.023, 2810397.424])| + EPSG_6076|6076|"WGS 84 / EPSG Arctic zone 2-26"|Projected|[73.0, 79.5, 113.01, 84.51]|Some([26100819.545, 2214346.864, 26899372.023, 2810397.424])| + EPSG_6077|6077|"WGS 84 / EPSG Arctic zone 3-13"|Projected|[34.99, 76.16, 67.01, 81.17]|Some([13051773.573, 3215646.811, 13896769.774, 3817128.146])| + EPSG_6078|6078|"WGS 84 / EPSG Arctic zone 3-15"|Projected|[67.0, 76.16, 98.01, 81.17]|Some([15077697.311, 3215701.816, 15896769.774, 3812298.769])| + EPSG_6079|6079|"WGS 84 / EPSG Arctic zone 3-17"|Projected|[98.0, 76.16, 129.01, 81.17]|Some([17077697.311, 3215701.816, 17896769.774, 3812298.769])| + EPSG_6080|6080|"WGS 84 / EPSG Arctic zone 3-19"|Projected|[129.0, 76.16, 160.01, 81.17]|Some([19077697.311, 3215701.816, 19896769.774, 3812298.769])| + EPSG_6081|6081|"WGS 84 / EPSG Arctic zone 4-30"|Projected|[46.0, 72.83, 70.01, 77.84]|Some([30106943.949, 4217168.662, 30893379.096, 4804899.514])| + EPSG_6082|6082|"WGS 84 / EPSG Arctic zone 4-32"|Projected|[70.0, 72.83, 94.01, 77.84]|Some([32106943.949, 4217168.662, 32893379.096, 4804899.514])| + EPSG_6083|6083|"WGS 84 / EPSG Arctic zone 4-34"|Projected|[94.0, 72.83, 118.01, 77.84]|Some([34106943.949, 4217168.662, 34893379.096, 4804899.514])| + EPSG_6084|6084|"WGS 84 / EPSG Arctic zone 4-36"|Projected|[118.0, 72.83, 142.01, 77.84]|Some([36106943.949, 4217168.662, 36893379.096, 4804899.514])| + EPSG_6085|6085|"WGS 84 / EPSG Arctic zone 4-38"|Projected|[142.0, 72.83, 166.01, 77.84]|Some([38106943.949, 4217168.662, 38893379.096, 4804899.514])| + EPSG_6086|6086|"WGS 84 / EPSG Arctic zone 4-40"|Projected|[166.0, 72.83, -168.99, 77.84]|Some([40074696.439, 4217200.593, 40893379.096, 4809783.683])| + EPSG_6087|6087|"WGS 84 / EPSG Arctic zone 5-15"|Projected|[44.0, 69.5, 64.01, 74.51]|Some([15110597.003, 5218312.228, 15889788.811, 5802060.086])| + EPSG_6088|6088|"WGS 84 / EPSG Arctic zone 5-17"|Projected|[64.0, 69.5, 85.01, 74.51]|Some([17110597.003, 5218324.609, 17928314.151, 5807240.122])| + EPSG_6089|6089|"WGS 84 / EPSG Arctic zone 5-19"|Projected|[85.0, 69.5, 106.01, 74.51]|Some([19110597.003, 5218324.609, 19928314.151, 5807240.122])| + EPSG_6090|6090|"WGS 84 / EPSG Arctic zone 5-21"|Projected|[106.0, 69.5, 127.01, 74.51]|Some([21110597.003, 5218324.609, 21928314.151, 5807240.122])| + EPSG_6091|6091|"WGS 84 / EPSG Arctic zone 5-23"|Projected|[127.0, 69.5, 148.0, 74.51]|Some([23110597.003, 5218325.208, 23927929.465, 5807185.908])| + EPSG_6092|6092|"WGS 84 / EPSG Arctic zone 5-25"|Projected|[148.0, 69.5, 169.01, 74.51]|Some([25110597.003, 5218324.609, 25928314.151, 5807240.122])| + EPSG_6093|6093|"WGS 84 / EPSG Arctic zone 5-27"|Projected|[169.0, 69.5, -169.0, 74.51]|Some([27110597.003, 5218335.311, 27966337.995, 5812848.2])| + EPSG_6094|6094|"NAD83(NSRS2007) / EPSG Arctic zone 5-29"|Projected|[-173.0, 69.5, -153.0, 74.51]|Some([29110597.003, 5218312.219, 29889402.997, 5802010.751])| + EPSG_6095|6095|"NAD83(NSRS2007) / EPSG Arctic zone 5-31"|Projected|[-157.0, 69.5, -137.0, 74.51]|Some([31110597.003, 5218312.219, 31889402.997, 5802010.751])| + EPSG_6096|6096|"NAD83(NSRS2007) / EPSG Arctic zone 6-14"|Projected|[-174.0, 66.16, -156.0, 71.17]|Some([14095158.422, 6218146.939, 14904841.578, 6800627.269])| + EPSG_6097|6097|"NAD83(NSRS2007) / EPSG Arctic zone 6-16"|Projected|[-156.0, 66.16, -138.0, 71.17]|Some([16095158.422, 6218146.939, 16904841.578, 6800627.269])| + EPSG_6098|6098|"NAD83(CSRS) / EPSG Arctic zone 1-23"|Projected|[-120.0, 82.83, -60.0, 87.84]|Some([23100338.642, 1208865.981, 23899662.668, 1800596.541])| + EPSG_6099|6099|"NAD83(CSRS) / EPSG Arctic zone 2-14"|Projected|[-135.0, 79.5, -95.0, 84.51]|Some([14100820.569, 2214346.073, 14899181.517, 2810360.437])| + EPSG_6100|6100|"NAD83(CSRS) / EPSG Arctic zone 2-16"|Projected|[-95.0, 79.5, -55.0, 84.51]|Some([16100819.854, 2214345.547, 16899180.787, 2810359.901])| + EPSG_6101|6101|"NAD83(CSRS) / EPSG Arctic zone 3-25"|Projected|[-144.0, 76.16, -114.0, 81.17]|Some([25103488.728, 3215645.248, 25896512.464, 3807801.266])| + EPSG_6102|6102|"NAD83(CSRS) / EPSG Arctic zone 3-27"|Projected|[-114.0, 76.16, -84.0, 81.17]|Some([27103489.504, 3215644.711, 27896512.065, 3807800.231])| + EPSG_6103|6103|"NAD83(CSRS) / EPSG Arctic zone 3-29"|Projected|[-84.0, 76.16, -64.78, 81.17]|Some([29103488.889, 3215660.448, 29612688.863, 3807799.939])| + EPSG_6104|6104|"NAD83(CSRS) / EPSG Arctic zone 4-14"|Projected|[-141.0, 72.83, -116.0, 77.84]|Some([14106945.096, 4217199.054, 14925304.741, 4809783.155])| + EPSG_6105|6105|"NAD83(CSRS) / EPSG Arctic zone 4-16"|Projected|[-116.0, 72.83, -91.0, 77.84]|Some([16106944.801, 4217198.604, 16925304.421, 4809782.685])| + EPSG_6106|6106|"NAD83(CSRS) / EPSG Arctic zone 4-18"|Projected|[-91.0, 72.83, -67.0, 77.84]|Some([18106944.322, 4217167.371, 18893056.417, 4804850.851])| + EPSG_6107|6107|"NAD83(CSRS) / EPSG Arctic zone 5-33"|Projected|[-141.0, 69.5, -121.0, 74.51]|Some([33110598.161, 5218311.705, 33889404.182, 5802010.268])| + EPSG_6108|6108|"NAD83(CSRS) / EPSG Arctic zone 5-35"|Projected|[-121.0, 69.5, -101.0, 74.51]|Some([35110597.955, 5218311.34, 35889403.952, 5802009.887])| + EPSG_6109|6109|"NAD83(CSRS) / EPSG Arctic zone 5-37"|Projected|[-101.0, 69.5, -81.0, 74.51]|Some([37110597.617, 5218311.059, 37889403.595, 5802009.595])| + EPSG_6110|6110|"NAD83(CSRS) / EPSG Arctic zone 5-39"|Projected|[-81.0, 69.5, -61.0, 74.51]|Some([39110597.185, 5218310.897, 39889403.154, 5802009.428])| + EPSG_6111|6111|"NAD83(CSRS) / EPSG Arctic zone 6-18"|Projected|[-141.0, 66.16, -122.0, 71.17]|Some([18095159.585, 6218152.958, 18949448.128, 6806174.331])| + EPSG_6112|6112|"NAD83(CSRS) / EPSG Arctic zone 6-20"|Projected|[-122.0, 66.16, -103.0, 71.17]|Some([20095159.399, 6218152.62, 20949447.912, 6806173.969])| + EPSG_6113|6113|"NAD83(CSRS) / EPSG Arctic zone 6-22"|Projected|[-103.0, 66.16, -84.0, 71.17]|Some([22095159.088, 6218152.351, 22949447.578, 6806173.681])| + EPSG_6114|6114|"NAD83(CSRS) / EPSG Arctic zone 6-24"|Projected|[-84.0, 66.16, -65.0, 71.17]|Some([24095158.686, 6218152.181, 24949447.162, 6806173.499])| + EPSG_6115|6115|"WGS 84 / EPSG Arctic zone 1-27"|Projected|[0.0, 82.83, 60.01, 87.84]|Some([27100338.008, 1208867.146, 27899782.884, 1800617.547])| + EPSG_6116|6116|"WGS 84 / EPSG Arctic zone 1-29"|Projected|[60.0, 82.83, 120.01, 87.84]|Some([29100338.008, 1208867.146, 29899782.884, 1800617.547])| + EPSG_6117|6117|"WGS 84 / EPSG Arctic zone 1-31"|Projected|[120.0, 82.83, 180.0, 87.83]|Some([31100338.008, 1208867.143, 31899661.992, 1799627.648])| + EPSG_6118|6118|"WGS 84 / EPSG Arctic zone 1-21"|Projected|[-180.0, 82.83, -120.0, 87.84]|Some([21100338.008, 1208867.143, 21899661.992, 1800596.541])| + EPSG_6119|6119|"WGS 84 / EPSG Arctic zone 2-28"|Projected|[113.0, 79.5, 153.01, 84.51]|Some([28100819.545, 2214346.864, 28899372.023, 2810397.424])| + EPSG_6120|6120|"WGS 84 / EPSG Arctic zone 2-10"|Projected|[146.0, 79.5, -173.99, 84.51]|Some([10100819.545, 2214346.864, 10899372.023, 2810397.424])| + EPSG_6121|6121|"WGS 84 / EPSG Arctic zone 2-12"|Projected|[-174.0, 79.5, -134.99, 84.51]|Some([12100819.545, 2214389.981, 12880157.065, 2810361.182])| + EPSG_6122|6122|"WGS 84 / EPSG Arctic zone 3-21"|Projected|[160.0, 76.16, -168.99, 81.17]|Some([21077697.311, 3215701.816, 21896769.774, 3812298.769])| + EPSG_6123|6123|"WGS 84 / EPSG Arctic zone 3-23"|Projected|[-169.0, 76.16, -138.0, 81.17]|Some([23077697.311, 3215702.953, 23896511.272, 3812298.769])| + EPSG_6124|6124|"WGS 84 / EPSG Arctic zone 4-12"|Projected|[-169.0, 72.83, -141.0, 77.84]|Some([12042570.21, 4217168.655, 12957429.79, 4815103.169])| + EPSG_6125|6125|"ETRS89 / EPSG Arctic zone 5-47"|Projected|[-15.0, 69.5, 5.01, 74.51]|Some([47110597.003, 5218312.227, 47889788.811, 5802060.086])| + EPSG_6128|6128|"Grand Cayman National Grid 1959"|Projected|[-81.46, 19.21, -81.04, 19.41]|Some([1481505.428, 6968251.734, 1626369.322, 7041063.632])| + EPSG_6129|6129|"Sister Islands National Grid 1961"|Projected|[-80.14, 19.63, -79.69, 19.78]|Some([1935781.423, 7121421.4, 2090876.412, 7176873.372])| + EPSG_6135|6135|"CIGD11"|Geographic2d|[-83.6, 17.58, -78.72, 20.68]|Some([-83.6, 17.58, -78.72, 20.68])| + EPSG_6201|6201|"NAD27 / Michigan Central"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([1280098.134, 176221.772, 2544765.51, 960831.503])| + EPSG_6202|6202|"NAD27 / Michigan South"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([1217018.127, 69240.903, 2601805.637, 1004175.282])| + EPSG_6204|6204|"Macedonia State Coordinate System"|Projected|[20.45, 40.85, 23.04, 42.36]|Some([453158.728, 4522953.224, 672459.609, 4692669.407])| + EPSG_6207|6207|"Nepal 1981"|Geographic2d|[80.06, 26.34, 88.21, 30.43]|Some([80.062, 26.34, 88.213, 30.43])| + EPSG_6210|6210|"SIRGAS 2000 / UTM zone 23N"|Projected|[-48.0, 0.0, -41.99, 5.13]|Some([166021.443, 0.0, 835092.849, 567818.771])| + EPSG_6211|6211|"SIRGAS 2000 / UTM zone 24N"|Projected|[-42.0, 0.0, -38.22, 0.74]|Some([166021.443, 0.0, 586797.17, 81905.273])| + EPSG_6244|6244|"MAGNA-SIRGAS / Arauca urban grid"|Projected|[-70.78, 7.05, -70.71, 7.1]|Some([1032866.982, 1271367.62, 1040600.943, 1276897.544])| + EPSG_6245|6245|"MAGNA-SIRGAS / Armenia urban grid"|Projected|[-75.73, 4.5, -75.65, 4.55]|Some([1149551.726, 989512.098, 1158432.062, 995042.686])| + EPSG_6246|6246|"MAGNA-SIRGAS / Barranquilla urban grid"|Projected|[-74.87, 10.85, -74.75, 11.06]|Some([913364.246, 1691744.722, 926485.544, 1714975.346])| + EPSG_6247|6247|"MAGNA-SIRGAS / Bogota urban grid"|Projected|[-74.27, 4.45, -73.98, 4.85]|Some([78632.788, 83823.168, 110831.638, 128075.831])| + EPSG_6248|6248|"MAGNA-SIRGAS / Bucaramanga urban grid"|Projected|[-73.19, 7.03, -73.06, 7.17]|Some([1098052.517, 1269235.005, 1112418.319, 1284722.274])| + EPSG_6249|6249|"MAGNA-SIRGAS / Cali urban grid"|Projected|[-76.61, 3.32, -76.42, 3.56]|Some([1051959.083, 858884.854, 1073077.836, 885428.418])| + EPSG_6250|6250|"MAGNA-SIRGAS / Cartagena urban grid"|Projected|[-75.57, 10.27, -75.42, 10.47]|Some([836540.769, 1627834.309, 852972.916, 1649957.833])| + EPSG_6251|6251|"MAGNA-SIRGAS / Cucuta urban grid"|Projected|[-72.56, 7.81, -72.46, 7.97]|Some([836504.126, 1355674.122, 847534.031, 1373370.644])| + EPSG_6252|6252|"MAGNA-SIRGAS / Florencia urban grid"|Projected|[-75.63, 1.57, -75.59, 1.65]|Some([1161089.32, 665427.756, 1165540.648, 674274.211])| + EPSG_6253|6253|"MAGNA-SIRGAS / Ibague urban grid"|Projected|[-75.27, 4.39, -75.11, 4.47]|Some([867634.828, 977288.287, 885397.102, 986136.898])| + EPSG_6254|6254|"MAGNA-SIRGAS / Inirida urban grid"|Projected|[-67.94, 3.8, -67.88, 3.9]|Some([1015315.735, 486766.729, 1021980.418, 497824.902])| + EPSG_6255|6255|"MAGNA-SIRGAS / Leticia urban grid"|Projected|[-69.98, -4.23, -69.92, -4.17]|Some([21849.191, 23927.711, 28510.89, 30562.716])| + EPSG_6256|6256|"MAGNA-SIRGAS / Manizales urban grid"|Projected|[-75.54, 5.02, -75.44, 5.11]|Some([1170520.524, 1047064.366, 1181613.709, 1057020.562])| + EPSG_6257|6257|"MAGNA-SIRGAS / Medellin urban grid"|Projected|[-75.66, 6.12, -75.5, 6.37]|Some([824848.16, 1168736.875, 842562.636, 1196391.249])| + EPSG_6258|6258|"MAGNA-SIRGAS / Mitu urban grid"|Projected|[-70.25, 1.23, -70.21, 1.29]|Some([1092099.324, 627789.068, 1096551.203, 634423.748])| + EPSG_6259|6259|"MAGNA-SIRGAS / Mocoa urban grid"|Projected|[-76.66, 1.12, -76.63, 1.18]|Some([1046467.962, 615614.168, 1049807.256, 622249.345])| + EPSG_6260|6260|"MAGNA-SIRGAS / Monteria urban grid"|Projected|[-75.94, 8.7, -75.82, 8.81]|Some([1125162.94, 1454047.807, 1138368.619, 1466214.383])| + EPSG_6261|6261|"MAGNA-SIRGAS / Neiva urban grid"|Projected|[-75.32, 2.87, -75.23, 2.99]|Some([861856.962, 809191.829, 871863.909, 822462.214])| + EPSG_6262|6262|"MAGNA-SIRGAS / Pasto urban grid"|Projected|[-77.32, 1.16, -77.23, 1.26]|Some([973023.838, 620021.116, 983044.526, 631083.099])| + EPSG_6263|6263|"MAGNA-SIRGAS / Pereira urban grid"|Projected|[-75.77, 4.78, -75.64, 4.87]|Some([1145053.583, 1020479.524, 1159478.512, 1030434.738])| + EPSG_6264|6264|"MAGNA-SIRGAS / Popayan urban grid"|Projected|[-76.65, 2.41, -76.55, 2.49]|Some([1047545.63, 758260.956, 1058670.833, 767109.622])| + EPSG_6265|6265|"MAGNA-SIRGAS / Puerto Carreno urban grid"|Projected|[-67.7, 5.98, -67.41, 6.3]|Some([1041774.041, 1153060.093, 1073882.447, 1188452.393])| + EPSG_6266|6266|"MAGNA-SIRGAS / Quibdo urban grid"|Projected|[-76.68, 5.66, -76.63, 5.72]|Some([1044033.696, 1117655.779, 1049572.753, 1124291.02])| + EPSG_6267|6267|"MAGNA-SIRGAS / Riohacha urban grid"|Projected|[-72.96, 11.42, -72.84, 11.58]|Some([1121909.095, 1754955.101, 1135004.698, 1772654.86])| + EPSG_6268|6268|"MAGNA-SIRGAS / San Andres urban grid"|Projected|[-81.82, 12.43, -81.6, 12.65]|Some([810586.008, 1866981.681, 834505.969, 1891322.973])| + EPSG_6269|6269|"MAGNA-SIRGAS / San Jose del Guaviare urban grid"|Projected|[-72.66, 2.54, -72.6, 2.61]|Some([1157656.045, 772717.699, 1164328.889, 780458.35])| + EPSG_6270|6270|"MAGNA-SIRGAS / Santa Marta urban grid"|Projected|[-74.24, 11.16, -74.13, 11.3]|Some([982254.555, 1725935.992, 994269.711, 1741424.021])| + EPSG_6271|6271|"MAGNA-SIRGAS / Sucre urban grid"|Projected|[-74.74, 8.79, -74.69, 8.83]|Some([927114.59, 1463852.774, 932615.639, 1468276.956])| + EPSG_6272|6272|"MAGNA-SIRGAS / Tunja urban grid"|Projected|[-73.39, 5.5, -73.3, 5.61]|Some([1076295.493, 1099988.945, 1086272.807, 1112158.879])| + EPSG_6273|6273|"MAGNA-SIRGAS / Valledupar urban grid"|Projected|[-73.3, 10.39, -73.19, 10.51]|Some([1085128.718, 1640875.034, 1097174.767, 1654149.302])| + EPSG_6274|6274|"MAGNA-SIRGAS / Villavicencio urban grid"|Projected|[-73.69, 4.07, -73.56, 4.21]|Some([1043403.553, 941510.7, 1057839.801, 956993.26])| + EPSG_6275|6275|"MAGNA-SIRGAS / Yopal urban grid"|Projected|[-72.43, 5.3, -72.35, 5.37]|Some([850080.122, 1077990.37, 858948.278, 1085732.055])| + EPSG_6307|6307|"NAD83(CORS96) / Puerto Rico and Virgin Is."|Projected|[-67.97, 17.62, -64.51, 18.57]|Some([36907.961, 176388.461, 404129.026, 282602.555])| + EPSG_6311|6311|"CGRS93"|Geographic2d|[32.2, 34.59, 34.65, 35.74]|Some([32.2, 34.59, 34.65, 35.74])| + EPSG_6312|6312|"CGRS93 / Cyprus Local Transverse Mercator"|Projected|[32.2, 34.59, 34.65, 35.74]|Some([126613.588, 328917.51, 351376.697, 457751.751])| + EPSG_6316|6316|"MGI 1901 / Balkans zone 7"|Projected|[19.5, 41.85, 22.51, 46.19]|Some([7375876.586, 4634174.884, 7625830.168, 5117298.997])| + EPSG_6318|6318|"NAD83(2011)"|Geographic2d|[167.65, 14.92, -63.88, 74.71]|Some([-179.503, 14.92, 176.215, 74.71])| + EPSG_6322|6322|"NAD83(PA11)"|Geographic2d|[157.47, -17.56, -151.27, 31.8]|Some([-178.609, -17.56, 179.683, 31.8])| + EPSG_6325|6325|"NAD83(MA11)"|Geographic2d|[129.48, 1.64, 149.55, 23.9]|Some([129.48, 1.64, 149.55, 23.9])| + EPSG_6328|6328|"NAD83(2011) / UTM zone 59N"|Projected|[167.65, 49.01, 174.01, 56.28]|Some([255040.976, 5428568.284, 720100.876, 6242288.35])| + EPSG_6329|6329|"NAD83(2011) / UTM zone 60N"|Projected|[174.0, 47.92, 180.0, 56.67]|Some([275878.052, 5307408.619, 724121.948, 6284675.176])| + EPSG_6330|6330|"NAD83(2011) / UTM zone 1N"|Projected|[-180.0, 47.88, -173.99, 63.21]|Some([275705.201, 5302962.875, 725042.38, 7012536.576])| + EPSG_6331|6331|"NAD83(2011) / UTM zone 2N"|Projected|[-174.0, 48.66, -167.99, 73.05]|Some([279095.534, 5389660.727, 721640.728, 8108518.215])| + EPSG_6332|6332|"NAD83(2011) / UTM zone 3N"|Projected|[-168.0, 49.52, -161.99, 74.29]|Some([282881.311, 5485264.341, 717842.313, 8246692.024])| + EPSG_6333|6333|"NAD83(2011) / UTM zone 4N"|Projected|[-162.0, 50.98, -155.99, 74.71]|Some([289420.598, 5647600.828, 711281.2, 8293495.466])| + EPSG_6334|6334|"NAD83(2011) / UTM zone 5N"|Projected|[-156.0, 52.15, -149.99, 74.71]|Some([294760.555, 5777721.865, 705923.421, 8293495.466])| + EPSG_6335|6335|"NAD83(2011) / UTM zone 6N"|Projected|[-150.0, 54.05, -143.99, 74.13]|Some([303614.943, 5989084.721, 697039.487, 8228862.485])| + EPSG_6336|6336|"NAD83(2011) / UTM zone 7N"|Projected|[-144.0, 53.47, -137.99, 73.59]|Some([300888.495, 5924556.24, 699775.033, 8168689.221])| + EPSG_6337|6337|"NAD83(2011) / UTM zone 8N"|Projected|[-138.0, 53.6, -131.99, 73.04]|Some([301497.817, 5939018.973, 699163.677, 8107403.958])| + EPSG_6338|6338|"NAD83(2011) / UTM zone 9N"|Projected|[-132.0, 35.38, -126.0, 56.84]|Some([227475.274, 3915184.943, 772524.726, 6303587.687])| + EPSG_6339|6339|"NAD83(2011) / UTM zone 10N"|Projected|[-126.0, 30.54, -119.99, 49.09]|Some([212172.222, 3378624.203, 788787.633, 5441824.853])| + EPSG_6340|6340|"NAD83(2011) / UTM zone 11N"|Projected|[-120.0, 30.88, -114.0, 49.01]|Some([213181.345, 3416303.043, 786818.655, 5432904.306])| + EPSG_6341|6341|"NAD83(2011) / UTM zone 12N"|Projected|[-114.0, 31.33, -108.0, 49.01]|Some([214532.476, 3466175.159, 785467.524, 5432904.306])| + EPSG_6342|6342|"NAD83(2011) / UTM zone 13N"|Projected|[-108.0, 28.98, -102.0, 49.01]|Some([207672.606, 3205769.791, 792327.394, 5432904.306])| + EPSG_6343|6343|"NAD83(2011) / UTM zone 14N"|Projected|[-102.0, 25.83, -96.0, 49.01]|Some([199250.354, 2856857.634, 800749.646, 5432904.306])| + EPSG_6344|6344|"NAD83(2011) / UTM zone 15N"|Projected|[-96.01, 25.61, -90.0, 49.38]|Some([197690.847, 2832494.974, 801304.224, 5474057.564])| + EPSG_6345|6345|"NAD83(2011) / UTM zone 16N"|Projected|[-90.0, 23.97, -84.0, 48.32]|Some([194701.986, 2650905.127, 805298.014, 5356218.118])| + EPSG_6346|6346|"NAD83(2011) / UTM zone 17N"|Projected|[-84.0, 23.81, -78.0, 46.13]|Some([194325.668, 2633191.154, 805674.332, 5112867.417])| + EPSG_6347|6347|"NAD83(2011) / UTM zone 18N"|Projected|[-78.0, 28.28, -72.0, 45.03]|Some([205723.769, 3128220.038, 794276.231, 4990662.135])| + EPSG_6348|6348|"NAD83(2011) / UTM zone 19N"|Projected|[-72.0, 33.61, -65.99, 47.47]|Some([221647.209, 3718914.933, 779280.966, 5261788.015])| + EPSG_6350|6350|"NAD83(2011) / Conus Albers"|Projected|[-124.79, 24.41, -66.91, 49.38]|Some([-2916311.1, 153628.723, 2945749.913, 3255274.794])| + EPSG_6351|6351|"NAD83(2011) / EPSG Arctic zone 5-29"|Projected|[-173.0, 69.5, -153.0, 74.51]|Some([29110597.003, 5218312.219, 29889402.997, 5802010.751])| + EPSG_6352|6352|"NAD83(2011) / EPSG Arctic zone 5-31"|Projected|[-157.0, 69.5, -137.0, 74.51]|Some([31110597.003, 5218312.219, 31889402.997, 5802010.751])| + EPSG_6353|6353|"NAD83(2011) / EPSG Arctic zone 6-14"|Projected|[-174.0, 66.16, -156.0, 71.17]|Some([14095158.422, 6218146.939, 14904841.578, 6800627.269])| + EPSG_6354|6354|"NAD83(2011) / EPSG Arctic zone 6-16"|Projected|[-156.0, 66.16, -138.0, 71.17]|Some([16095158.422, 6218146.939, 16904841.578, 6800627.269])| + EPSG_6355|6355|"NAD83(2011) / Alabama East"|Projected|[-86.79, 30.99, -84.89, 35.0]|Some([108626.368, 54321.743, 290100.077, 499449.41])| + EPSG_6356|6356|"NAD83(2011) / Alabama West"|Projected|[-88.48, 30.14, -86.3, 35.02]|Some([505580.446, 15519.0, 715617.187, 557319.387])| + EPSG_6362|6362|"Mexico ITRF92 / LCC"|Projected|[-122.19, 12.1, -84.64, 32.72]|Some([278545.21, 11229.843, 4411724.301, 2428997.54])| + EPSG_6365|6365|"Mexico ITRF2008"|Geographic2d|[-122.19, 12.1, -84.64, 32.72]|Some([-122.19, 12.1, -84.64, 32.72])| + EPSG_6366|6366|"Mexico ITRF2008 / UTM zone 11N"|Projected|[-122.19, 15.01, -114.0, 32.72]|Some([-58603.971, 1659432.041, 822635.936, 3632180.201])| + EPSG_6367|6367|"Mexico ITRF2008 / UTM zone 12N"|Projected|[-114.0, 15.09, -108.0, 32.27]|Some([177484.621, 1668280.432, 822515.379, 3574315.953])| + EPSG_6368|6368|"Mexico ITRF2008 / UTM zone 13N"|Projected|[-108.0, 14.05, -102.0, 31.79]|Some([175966.325, 1553256.07, 824033.675, 3521079.182])| + EPSG_6369|6369|"Mexico ITRF2008 / UTM zone 14N"|Projected|[-102.0, 12.3, -96.0, 29.81]|Some([173651.794, 1359727.406, 826348.206, 3301508.05])| + EPSG_6370|6370|"Mexico ITRF2008 / UTM zone 15N"|Projected|[-96.0, 12.1, -90.0, 26.0]|Some([173406.546, 1337611.479, 826593.454, 2879132.534])| + EPSG_6371|6371|"Mexico ITRF2008 / UTM zone 16N"|Projected|[-90.0, 17.81, -84.64, 25.77]|Some([181954.812, 1969165.279, 750159.873, 2853640.128])| + EPSG_6372|6372|"Mexico ITRF2008 / LCC"|Projected|[-122.19, 12.1, -84.64, 32.72]|Some([278545.21, 11229.843, 4411724.301, 2428997.54])| + EPSG_6381|6381|"UCS-2000 / Ukraine TM zone 7"|Projected|[22.15, 48.24, 22.5, 48.98]|Some([384302.544, 5345879.713, 411538.68, 5428614.846])| + EPSG_6382|6382|"UCS-2000 / Ukraine TM zone 8"|Projected|[22.5, 47.71, 25.5, 51.96]|Some([187556.957, 5286304.555, 412683.646, 5760079.799])| + EPSG_6383|6383|"UCS-2000 / Ukraine TM zone 9"|Projected|[25.5, 45.26, 28.5, 51.94]|Some([182384.087, 5013954.671, 417853.33, 5757849.713])| + EPSG_6384|6384|"UCS-2000 / Ukraine TM zone 10"|Projected|[28.5, 43.42, 31.5, 52.12]|Some([178640.391, 4809490.649, 421593.189, 5777871.499])| + EPSG_6385|6385|"UCS-2000 / Ukraine TM zone 11"|Projected|[31.5, 43.18, 34.5, 52.38]|Some([178159.375, 4782822.726, 422069.727, 5806795.085])| + EPSG_6386|6386|"UCS-2000 / Ukraine TM zone 12"|Projected|[34.5, 43.24, 37.5, 51.25]|Some([178276.419, 4789484.496, 421947.578, 5681072.024])| + EPSG_6387|6387|"UCS-2000 / Ukraine TM zone 13"|Projected|[37.5, 46.77, 40.18, 50.39]|Some([185537.026, 5181777.15, 390239.438, 5585403.328])| + EPSG_6391|6391|"Cayman Islands National Grid 2011"|Projected|[-83.6, 17.58, -78.72, 20.68]|Some([1893042.065, 1263164.71, 3593487.814, 2398311.764])| + EPSG_6393|6393|"NAD83(2011) / Alaska Albers"|Projected|[172.42, 51.3, -129.99, 71.4]|Some([-2257390.059, 144002.179, 1648473.206, 2681390.481])| + EPSG_6394|6394|"NAD83(2011) / Alaska zone 1"|Projected|[-141.0, 54.61, -129.99, 60.35]|Some([345065.001, 308998.437, 1056160.285, 970307.741])| + EPSG_6395|6395|"NAD83(2011) / Alaska zone 2"|Projected|[-144.01, 59.72, -140.98, 70.16]|Some([386918.31, 636898.364, 557389.032, 1801991.227])| + EPSG_6396|6396|"NAD83(2011) / Alaska zone 3"|Projected|[-148.0, 59.72, -144.0, 70.38]|Some([387480.793, 636898.322, 612519.207, 1826509.149])| + EPSG_6397|6397|"NAD83(2011) / Alaska zone 4"|Projected|[-152.01, 59.11, -147.99, 70.63]|Some([384866.006, 568949.725, 615133.994, 1854397.462])| + EPSG_6398|6398|"NAD83(2011) / Alaska zone 5"|Projected|[-156.0, 55.72, -151.86, 71.28]|Some([374335.663, 191454.933, 634459.395, 1927037.849])| + EPSG_6399|6399|"NAD83(2011) / Alaska zone 6"|Projected|[-160.0, 54.89, -155.99, 71.4]|Some([371683.471, 99058.912, 628958.023, 1940259.62])| + EPSG_6400|6400|"NAD83(2011) / Alaska zone 7"|Projected|[-164.01, 54.32, -160.0, 70.74]|Some([369227.276, 35615.004, 630122.198, 1866663.097])| + EPSG_6401|6401|"NAD83(2011) / Alaska zone 8"|Projected|[-168.26, 54.34, -164.0, 69.05]|Some([353035.906, 37841.059, 630059.06, 1678580.299])| + EPSG_6402|6402|"NAD83(2011) / Alaska zone 9"|Projected|[-173.16, 56.49, -168.0, 65.82]|Some([305398.633, 277180.772, 623180.037, 1320354.895])| + EPSG_6403|6403|"NAD83(2011) / Alaska zone 10"|Projected|[172.42, 51.3, -164.84, 54.34]|Some([195688.4, 33405.959, 1775378.814, 432207.768])| + EPSG_6404|6404|"NAD83(2011) / Arizona Central"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([76951.283, 36584.405, 353893.118, 667599.273])| + EPSG_6405|6405|"NAD83(2011) / Arizona Central (ft)"|Projected|[-113.35, 31.33, -110.44, 37.01]|Some([252464.84, 120027.575, 1161066.659, 2190286.329])| + EPSG_6406|6406|"NAD83(2011) / Arizona East"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([66481.577, 36584.599, 320581.618, 667693.422])| + EPSG_6407|6407|"NAD83(2011) / Arizona East (ft)"|Projected|[-111.71, 31.33, -109.04, 37.01]|Some([218115.411, 120028.214, 1051776.963, 2190595.217])| + EPSG_6408|6408|"NAD83(2011) / Arizona West"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([113255.823, 116414.744, 329519.631, 666199.554])| + EPSG_6409|6409|"NAD83(2011) / Arizona West (ft)"|Projected|[-114.81, 32.05, -112.52, 37.0]|Some([371574.223, 381938.138, 1081101.152, 2185694.073])| + EPSG_6410|6410|"NAD83(2011) / Arkansas North"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([159882.152, 37351.802, 616294.174, 243510.715])| + EPSG_6411|6411|"NAD83(2011) / Arkansas North (ftUS)"|Projected|[-94.62, 34.67, -89.64, 36.5]|Some([524546.693, 122545.038, 2021958.47, 798918.07])| + EPSG_6412|6412|"NAD83(2011) / Arkansas South"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([168267.017, 438081.85, 549513.688, 672644.321])| + EPSG_6413|6413|"NAD83(2011) / Arkansas South (ftUS)"|Projected|[-94.48, 33.01, -90.4, 35.1]|Some([552056.037, 1437273.536, 1802862.824, 2206833.911])| + EPSG_6414|6414|"NAD83(2011) / California Albers"|Projected|[-124.45, 32.53, -114.12, 42.01]|Some([-418604.909, -609074.348, 552971.381, 458666.86])| + EPSG_6415|6415|"NAD83(2011) / California zone 1"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([1789539.567, 528503.854, 2172670.813, 800071.591])| + EPSG_6416|6416|"NAD83(2011) / California zone 1 (ftUS)"|Projected|[-124.45, 39.59, -119.99, 42.01]|Some([5871181.062, 1733933.063, 7128170.827, 2624901.545])| + EPSG_6417|6417|"NAD83(2011) / California zone 2"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([1819114.258, 539224.713, 2216001.305, 779630.525])| + EPSG_6418|6418|"NAD83(2011) / California zone 2 (ftUS)"|Projected|[-124.06, 38.02, -119.54, 40.16]|Some([5968210.696, 1769106.414, 7270330.95, 2557837.816])| + EPSG_6419|6419|"NAD83(2011) / California zone 3"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([1774907.68, 525529.067, 2238487.139, 748597.178])| + EPSG_6420|6420|"NAD83(2011) / California zone 3 (ftUS)"|Projected|[-123.02, 36.73, -117.83, 38.71]|Some([5823176.279, 1724173.279, 7344103.224, 2456022.577])| + EPSG_6421|6421|"NAD83(2011) / California zone 4"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([1727886.776, 549564.135, 2305549.302, 754562.842])| + EPSG_6422|6422|"NAD83(2011) / California zone 4 (ftUS)"|Projected|[-122.01, 35.78, -115.62, 37.58]|Some([5668908.533, 1803028.335, 7564123.003, 2475594.923])| + EPSG_6423|6423|"NAD83(2011) / California zone 5"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([1679423.165, 417902.407, 2363675.21, 763023.807])| + EPSG_6424|6424|"NAD83(2011) / California zone 5 (ftUS)"|Projected|[-121.42, 32.76, -114.12, 35.81]|Some([5509907.499, 1371068.148, 7754824.418, 2503353.939])| + EPSG_6425|6425|"NAD83(2011) / California zone 6"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([1821504.087, 540295.778, 2171920.436, 713800.804])| + EPSG_6426|6426|"NAD83(2011) / California zone 6 (ftUS)"|Projected|[-118.15, 32.53, -114.42, 34.08]|Some([5976051.325, 1772620.398, 7125708.965, 2341861.472])| + EPSG_6427|6427|"NAD83(2011) / Colorado Central"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([602369.994, 338844.895, 1217673.025, 561273.139])| + EPSG_6428|6428|"NAD83(2011) / Colorado Central (ftUS)"|Projected|[-109.06, 38.14, -102.04, 40.09]|Some([1976275.555, 1111693.626, 3994982.248, 1841443.624])| + EPSG_6429|6429|"NAD83(2011) / Colorado North"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([608530.815, 329968.654, 1211685.384, 496983.93])| + EPSG_6430|6430|"NAD83(2011) / Colorado North (ftUS)"|Projected|[-109.06, 39.56, -102.04, 41.01]|Some([1996488.183, 1082572.159, 3975337.797, 1630521.443])| + EPSG_6431|6431|"NAD83(2011) / Colorado South"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([597496.398, 339577.268, 1222409.552, 534163.828])| + EPSG_6432|6432|"NAD83(2011) / Colorado South (ftUS)"|Projected|[-109.06, 36.98, -102.04, 38.68]|Some([1960286.099, 1114096.419, 4010522.005, 1752502.494])| + EPSG_6433|6433|"NAD83(2011) / Connecticut"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([222322.543, 168688.657, 386437.098, 287986.778])| + EPSG_6434|6434|"NAD83(2011) / Connecticut (ftUS)"|Projected|[-73.73, 40.98, -71.78, 42.05]|Some([729403.211, 553439.369, 1267835.713, 944836.621])| + EPSG_6435|6435|"NAD83(2011) / Delaware"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([166533.162, 48840.047, 238996.166, 205470.483])| + EPSG_6436|6436|"NAD83(2011) / Delaware (ftUS)"|Projected|[-75.8, 38.44, -74.97, 39.85]|Some([546367.548, 160236.055, 784106.588, 674114.41])| + EPSG_6437|6437|"NAD83(2011) / Florida East"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([65102.243, 8491.398, 304467.201, 720632.506])| + EPSG_6438|6438|"NAD83(2011) / Florida East (ftUS)"|Projected|[-82.33, 24.41, -79.97, 30.83]|Some([213589.61, 27858.86, 998906.144, 2364275.146])| + EPSG_6439|6439|"NAD83(2011) / Florida GDL Albers"|Projected|[-87.63, 24.41, -79.97, 31.01]|Some([32031.289, 45423.238, 808502.508, 784246.475])| + EPSG_6440|6440|"NAD83(2011) / Florida North"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([295646.502, 23279.259, 839215.833, 226917.137])| + EPSG_6441|6441|"NAD83(2011) / Florida North (ftUS)"|Projected|[-87.63, 29.21, -82.04, 31.01]|Some([969966.9, 76375.368, 2753327.28, 744477.307])| + EPSG_6442|6442|"NAD83(2011) / Florida West"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([66150.824, 214526.481, 286899.27, 584280.418])| + EPSG_6443|6443|"NAD83(2011) / Florida West (ftUS)"|Projected|[-83.34, 26.27, -81.13, 29.6]|Some([217029.83, 703825.629, 941268.688, 1916926.67])| + EPSG_6444|6444|"NAD83(2011) / Georgia East"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([74709.789, 39904.779, 334263.246, 519815.24])| + EPSG_6445|6445|"NAD83(2011) / Georgia East (ftUS)"|Projected|[-83.47, 30.36, -80.77, 34.68]|Some([245110.367, 130920.93, 1096662.0, 1705427.168])| + EPSG_6446|6446|"NAD83(2011) / Georgia West"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([561618.683, 68725.622, 812812.39, 556485.421])| + EPSG_6447|6447|"NAD83(2011) / Georgia West (ftUS)"|Projected|[-85.61, 30.62, -82.99, 35.01]|Some([1842577.296, 225477.311, 2666701.982, 1825735.918])| + EPSG_6448|6448|"NAD83(2011) / Idaho Central"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([392281.83, 35910.836, 610204.021, 448964.739])| + EPSG_6449|6449|"NAD83(2011) / Idaho Central (ftUS)"|Projected|[-115.3, 41.99, -112.67, 45.7]|Some([1287011.305, 117817.469, 2001977.692, 1472978.482])| + EPSG_6450|6450|"NAD83(2011) / Idaho East"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([111063.719, 35911.072, 293355.534, 343148.061])| + EPSG_6451|6451|"NAD83(2011) / Idaho East (ftUS)"|Projected|[-113.24, 41.99, -111.04, 44.75]|Some([364381.553, 117818.241, 962450.615, 1125811.597])| + EPSG_6452|6452|"NAD83(2011) / Idaho West"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([676539.784, 35910.659, 918488.551, 817141.665])| + EPSG_6453|6453|"NAD83(2011) / Idaho West (ftUS)"|Projected|[-117.24, 41.99, -114.32, 49.01]|Some([2219614.274, 117816.887, 3013407.856, 2680905.614])| + EPSG_6454|6454|"NAD83(2011) / Illinois East"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([216692.348, 43649.737, 416809.982, 648476.24])| + EPSG_6455|6455|"NAD83(2011) / Illinois East (ftUS)"|Projected|[-89.27, 37.06, -87.02, 42.5]|Some([710931.478, 143207.513, 1367484.084, 2127542.465])| + EPSG_6456|6456|"NAD83(2011) / Illinois West"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([579494.187, 33660.726, 811007.382, 649616.799])| + EPSG_6457|6457|"NAD83(2011) / Illinois West (ftUS)"|Projected|[-91.52, 36.97, -88.92, 42.51]|Some([1901223.844, 110435.231, 2660780.051, 2131284.449])| + EPSG_6458|6458|"NAD83(2011) / Indiana East"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([18848.17, 299944.57, 177929.13, 724484.683])| + EPSG_6459|6459|"NAD83(2011) / Indiana East (ftUS)"|Projected|[-86.59, 37.95, -84.78, 41.77]|Some([61837.703, 984068.143, 583755.82, 2376913.498])| + EPSG_6460|6460|"NAD83(2011) / Indiana West"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([810427.296, 279966.475, 974300.985, 724572.21])| + EPSG_6461|6461|"NAD83(2011) / Indiana West (ftUS)"|Projected|[-88.1, 37.77, -86.24, 41.77]|Some([2658876.887, 918523.342, 3196519.15, 2377200.659])| + EPSG_6462|6462|"NAD83(2011) / Iowa North"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([1238456.484, 1038882.072, 1778141.02, 1228643.996])| + EPSG_6463|6463|"NAD83(2011) / Iowa North (ftUS)"|Projected|[-96.65, 41.85, -90.15, 43.51]|Some([4063169.315, 3408398.93, 5833784.33, 4030976.178])| + EPSG_6464|6464|"NAD83(2011) / Iowa South"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([275770.616, 39978.785, 785355.693, 231925.011])| + EPSG_6465|6465|"NAD83(2011) / Iowa South (ftUS)"|Projected|[-96.14, 40.36, -90.14, 42.04]|Some([904757.43, 131163.729, 2576621.138, 760907.307])| + EPSG_6466|6466|"NAD83(2011) / Kansas North"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([46038.237, 20724.498, 698193.85, 193910.164])| + EPSG_6467|6467|"NAD83(2011) / Kansas North (ftUS)"|Projected|[-102.06, 38.52, -94.58, 40.01]|Some([151043.784, 67993.624, 2290657.656, 636186.931])| + EPSG_6468|6468|"NAD83(2011) / Kansas South"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([84022.727, 435888.473, 747112.606, 652740.363])| + EPSG_6469|6469|"NAD83(2011) / Kansas South (ftUS)"|Projected|[-102.05, 36.99, -94.6, 38.88]|Some([275664.564, 1430077.432, 2451151.942, 2141532.339])| + EPSG_6470|6470|"NAD83(2011) / Kentucky North"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([349218.279, 23310.027, 656953.319, 184641.099])| + EPSG_6471|6471|"NAD83(2011) / Kentucky North (ftUS)"|Projected|[-85.96, 37.71, -82.47, 39.15]|Some([1145726.971, 76476.313, 2155354.347, 605776.672])| + EPSG_6472|6472|"NAD83(2011) / Kentucky Single Zone"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([1157746.949, 1017388.797, 1840462.143, 1319387.434])| + EPSG_6473|6473|"NAD83(2011) / Kentucky Single Zone (ftUS)"|Projected|[-89.57, 36.49, -81.95, 39.15]|Some([3798374.782, 3337883.077, 6038249.548, 4328690.274])| + EPSG_6474|6474|"NAD83(2011) / Kentucky South"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([157793.092, 517386.113, 840416.218, 710602.335])| + EPSG_6475|6475|"NAD83(2011) / Kentucky South (ftUS)"|Projected|[-89.57, 36.49, -81.95, 38.17]|Some([517692.837, 1697457.607, 2757265.541, 2331367.827])| + EPSG_6476|6476|"NAD83(2011) / Louisiana North"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([851729.364, 38809.051, 1156879.26, 281692.556])| + EPSG_6477|6477|"NAD83(2011) / Louisiana North (ftUS)"|Projected|[-94.05, 30.85, -90.86, 33.03]|Some([2794382.087, 127326.029, 3795528.039, 924186.327])| + EPSG_6478|6478|"NAD83(2011) / Louisiana South"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([745633.391, 38798.174, 1252090.057, 287715.924])| + EPSG_6479|6479|"NAD83(2011) / Louisiana South (ftUS)"|Projected|[-93.94, 28.85, -88.75, 31.07]|Some([2446298.884, 127290.341, 4107898.796, 943947.993])| + EPSG_6480|6480|"NAD83(2011) / Maine CS2000 Central"|Projected|[-70.03, 43.75, -68.33, 47.47]|Some([427110.623, 27775.677, 564029.867, 441619.015])| + EPSG_6481|6481|"NAD83(2011) / Maine CS2000 East"|Projected|[-68.58, 44.18, -66.91, 47.37]|Some([643626.95, 38518.355, 777163.163, 393521.359])| + EPSG_6482|6482|"NAD83(2011) / Maine CS2000 West"|Projected|[-71.09, 43.07, -69.6, 46.58]|Some([241765.496, 26291.387, 363121.336, 416635.763])| + EPSG_6483|6483|"NAD83(2011) / Maine East"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([177049.162, 23701.068, 427772.489, 423896.787])| + EPSG_6484|6484|"NAD83(2011) / Maine East (ftUS)"|Projected|[-70.03, 43.88, -66.91, 47.47]|Some([580868.792, 77759.255, 1403450.241, 1390734.707])| + EPSG_6485|6485|"NAD83(2011) / Maine West"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([824761.643, 22958.179, 973880.256, 416752.605])| + EPSG_6486|6486|"NAD83(2011) / Maine West (ftUS)"|Projected|[-71.09, 43.04, -69.26, 46.58]|Some([2705905.49, 75321.959, 3195138.806, 1367295.839])| + EPSG_6487|6487|"NAD83(2011) / Maryland"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([181219.097, 33674.371, 578370.965, 231963.298])| + EPSG_6488|6488|"NAD83(2011) / Maryland (ftUS)"|Projected|[-79.49, 37.97, -74.97, 39.73]|Some([594549.654, 110480.0, 1897538.739, 761032.921])| + EPSG_6489|6489|"NAD83(2011) / Massachusetts Island"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([465603.809, 21100.837, 551174.59, 56819.463])| + EPSG_6490|6490|"NAD83(2011) / Massachusetts Island (ftUS)"|Projected|[-70.91, 41.19, -69.89, 41.51]|Some([1527568.495, 69228.329, 1808311.967, 186415.188])| + EPSG_6491|6491|"NAD83(2011) / Massachusetts Mainland"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([32912.329, 801094.12, 337016.003, 961845.933])| + EPSG_6492|6492|"NAD83(2011) / Massachusetts Mainland (ftUS)"|Projected|[-73.5, 41.46, -69.86, 42.89]|Some([107979.867, 2628256.293, 1105693.337, 3155656.2])| + EPSG_6493|6493|"NAD83(2011) / Michigan Central"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([5783269.684, 53711.284, 6168729.258, 292766.575])| + EPSG_6494|6494|"NAD83(2011) / Michigan Central (ft)"|Projected|[-87.06, 43.8, -82.27, 45.92]|Some([18973981.902, 176218.125, 20238613.052, 960520.258])| + EPSG_6495|6495|"NAD83(2011) / Michigan North"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([7730770.077, 32977.062, 8280243.766, 399102.506])| + EPSG_6496|6496|"NAD83(2011) / Michigan North (ft)"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([25363418.889, 108192.459, 27166154.088, 1309391.423])| + EPSG_6497|6497|"NAD83(2011) / Michigan Oblique Mercator"|Projected|[-90.42, 41.69, -82.13, 48.32]|Some([131754.146, 126622.954, 821906.052, 872458.276])| + EPSG_6498|6498|"NAD83(2011) / Michigan South"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([3764135.981, 21106.546, 4186207.076, 305975.012])| + EPSG_6499|6499|"NAD83(2011) / Michigan South (ft)"|Projected|[-87.2, 41.69, -82.13, 44.22]|Some([12349527.498, 69247.199, 13734275.183, 1003855.027])| + EPSG_6500|6500|"NAD83(2011) / Minnesota Central"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([595233.186, 131122.17, 953783.384, 378908.518])| + EPSG_6501|6501|"NAD83(2011) / Minnesota Central (ftUS)"|Projected|[-96.86, 45.28, -92.29, 47.48]|Some([1952860.877, 430189.984, 3129204.319, 1243135.696])| + EPSG_6502|6502|"NAD83(2011) / Minnesota North"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([484662.883, 115564.992, 1076333.058, 428200.772])| + EPSG_6503|6503|"NAD83(2011) / Minnesota North (ftUS)"|Projected|[-97.22, 46.64, -89.49, 49.38]|Some([1590098.143, 379149.477, 3531269.373, 1404855.366])| + EPSG_6504|6504|"NAD83(2011) / Minnesota South"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([569492.513, 154447.097, 1025656.603, 391676.367])| + EPSG_6505|6505|"NAD83(2011) / Minnesota South (ftUS)"|Projected|[-96.85, 43.49, -91.21, 45.59]|Some([1868410.019, 506715.185, 3365008.371, 1285024.882])| + EPSG_6506|6506|"NAD83(2011) / Mississippi East"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([190340.121, 56529.852, 371711.704, 611572.95])| + EPSG_6507|6507|"NAD83(2011) / Mississippi East (ftUS)"|Projected|[-89.97, 30.01, -88.09, 35.01]|Some([624474.212, 185465.023, 1219524.147, 2006468.92])| + EPSG_6508|6508|"NAD83(2011) / Mississippi TM"|Projected|[-91.65, 30.01, -88.09, 35.01]|Some([316708.059, 1023969.858, 660135.778, 1580007.849])| + EPSG_6509|6509|"NAD83(2011) / Mississippi West"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([574253.773, 166276.994, 791999.89, 611774.795])| + EPSG_6510|6510|"NAD83(2011) / Mississippi West (ftUS)"|Projected|[-91.65, 31.0, -89.37, 35.01]|Some([1884030.919, 545527.105, 2598419.639, 2007131.139])| + EPSG_6511|6511|"NAD83(2011) / Missouri Central"|Projected|[-93.79, 36.48, -91.41, 40.61]|Some([384402.583, 71750.784, 597674.639, 530978.613])| + EPSG_6512|6512|"NAD83(2011) / Missouri East"|Projected|[-91.97, 35.98, -89.1, 40.61]|Some([117430.214, 16273.215, 376256.537, 531217.473])| + EPSG_6513|6513|"NAD83(2011) / Missouri West"|Projected|[-95.77, 36.48, -93.48, 40.59]|Some([736193.986, 34767.346, 941402.47, 491753.126])| + EPSG_6514|6514|"NAD83(2011) / Montana"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([76530.54, 11129.352, 1035187.319, 549126.161])| + EPSG_6515|6515|"NAD83(2011) / Montana (ft)"|Projected|[-116.07, 44.35, -104.04, 49.01]|Some([251084.45, 36513.623, 3396283.855, 1801595.017])| + EPSG_6516|6516|"NAD83(2011) / Nebraska"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([153376.225, 17396.267, 901213.811, 363144.755])| + EPSG_6518|6518|"NAD83(2011) / Nevada Central"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([362659.338, 6138670.888, 651166.06, 6694999.823])| + EPSG_6519|6519|"NAD83(2011) / Nevada Central (ftUS)"|Projected|[-118.19, 36.0, -114.99, 41.0]|Some([1189824.846, 20139956.073, 2136367.315, 21965178.587])| + EPSG_6520|6520|"NAD83(2011) / Nevada East"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([69754.746, 8026623.097, 341810.023, 8805865.426])| + EPSG_6521|6521|"NAD83(2011) / Nevada East (ftUS)"|Projected|[-117.01, 34.99, -114.03, 42.0]|Some([228853.697, 26334012.61, 1121421.717, 28890576.82])| + EPSG_6522|6522|"NAD83(2011) / Nevada West"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([673826.63, 4244079.443, 941909.004, 4805926.331])| + EPSG_6523|6523|"NAD83(2011) / Nevada West (ftUS)"|Projected|[-120.0, 36.95, -116.99, 42.0]|Some([2210712.869, 13924117.307, 3090246.456, 15767443.304])| + EPSG_6524|6524|"NAD83(2011) / New Hampshire"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([226793.802, 21105.444, 384952.066, 312732.672])| + EPSG_6525|6525|"NAD83(2011) / New Hampshire (ftUS)"|Projected|[-72.56, 42.69, -70.63, 45.31]|Some([744072.667, 69243.444, 1262963.571, 1026023.774])| + EPSG_6526|6526|"NAD83(2011) / New Jersey"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([54545.12, 4070.111, 203801.357, 281108.052])| + EPSG_6527|6527|"NAD83(2011) / New Jersey (ftUS)"|Projected|[-75.6, 38.87, -73.88, 41.36]|Some([178953.448, 13353.356, 668638.286, 922268.668])| + EPSG_6528|6528|"NAD83(2011) / New Mexico Central"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([359824.279, 86475.321, 633545.173, 666494.112])| + EPSG_6529|6529|"NAD83(2011) / New Mexico Central (ftUS)"|Projected|[-107.73, 31.78, -104.84, 37.0]|Some([1180523.49, 283711.114, 2078556.121, 2186656.1])| + EPSG_6530|6530|"NAD83(2011) / New Mexico East"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([33975.771, 110868.236, 291929.385, 666375.071])| + EPSG_6531|6531|"NAD83(2011) / New Mexico East (ftUS)"|Projected|[-105.72, 32.0, -102.99, 37.0]|Some([111468.841, 363740.204, 957771.658, 2186265.545])| + EPSG_6532|6532|"NAD83(2011) / New Mexico West"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([713259.079, 36585.48, 974025.425, 666551.877])| + EPSG_6533|6533|"NAD83(2011) / New Mexico West (ftUS)"|Projected|[-109.06, 31.33, -106.32, 37.0]|Some([2340084.161, 120030.862, 3195615.082, 2186845.616])| + EPSG_6534|6534|"NAD83(2011) / New York Central"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([153330.974, 220983.306, 376222.785, 490949.206])| + EPSG_6535|6535|"NAD83(2011) / New York Central (ftUS)"|Projected|[-77.75, 41.99, -75.06, 44.41]|Some([503053.371, 725009.395, 1234324.252, 1610722.52])| + EPSG_6536|6536|"NAD83(2011) / New York East"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([34535.945, 227223.262, 257035.813, 688009.811])| + EPSG_6537|6537|"NAD83(2011) / New York East (ftUS)"|Projected|[-75.87, 40.88, -73.23, 45.02]|Some([113306.679, 745481.654, 843291.664, 2257245.523])| + EPSG_6538|6538|"NAD83(2011) / New York Long Island"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([277950.229, 33683.766, 486555.644, 128170.646])| + EPSG_6539|6539|"NAD83(2011) / New York Long Island (ftUS)"|Projected|[-74.26, 40.47, -71.8, 41.3]|Some([911908.377, 110510.822, 1596307.976, 420506.527])| + EPSG_6540|6540|"NAD83(2011) / New York West"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([251673.766, 220983.468, 451364.453, 404996.19])| + EPSG_6541|6541|"NAD83(2011) / New York West (ftUS)"|Projected|[-79.77, 41.99, -77.36, 43.64]|Some([825699.68, 725009.927, 1480851.543, 1328725.001])| + EPSG_6542|6542|"NAD83(2011) / North Carolina"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([116361.135, 8875.962, 944684.034, 327875.984])| + EPSG_6543|6543|"NAD83(2011) / North Carolina (ftUS)"|Projected|[-84.33, 33.83, -75.38, 36.59]|Some([381761.488, 29120.552, 3099350.865, 1075706.457])| + EPSG_6544|6544|"NAD83(2011) / North Dakota North"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([329319.593, 16678.577, 878256.827, 229887.853])| + EPSG_6545|6545|"NAD83(2011) / North Dakota North (ft)"|Projected|[-104.07, 47.15, -96.83, 49.01]|Some([1080444.859, 54719.74, 2881420.035, 754225.239])| + EPSG_6546|6546|"NAD83(2011) / North Dakota South"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([324736.074, 29272.934, 906254.759, 247922.521])| + EPSG_6547|6547|"NAD83(2011) / North Dakota South (ft)"|Projected|[-104.05, 45.93, -96.55, 47.83]|Some([1065407.067, 96039.809, 2973276.77, 813394.098])| + EPSG_6548|6548|"NAD83(2011) / Ohio North"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([403035.411, 48121.536, 769684.568, 298304.008])| + EPSG_6549|6549|"NAD83(2011) / Ohio North (ftUS)"|Projected|[-84.81, 40.1, -80.51, 42.33]|Some([1322292.01, 157878.74, 2525206.786, 978685.731])| + EPSG_6550|6550|"NAD83(2011) / Ohio South"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([396472.805, 44406.796, 757238.344, 264561.547])| + EPSG_6551|6551|"NAD83(2011) / Ohio South (ftUS)"|Projected|[-84.83, 38.4, -80.7, 40.36]|Some([1300761.196, 145691.296, 2484372.802, 867982.343])| + EPSG_6552|6552|"NAD83(2011) / Oklahoma North"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([145234.111, 29966.761, 925682.539, 234484.772])| + EPSG_6553|6553|"NAD83(2011) / Oklahoma North (ftUS)"|Projected|[-103.0, 35.27, -94.42, 37.01]|Some([476488.914, 98315.947, 3037010.129, 769305.456])| + EPSG_6554|6554|"NAD83(2011) / Oklahoma South"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([414409.72, 31799.907, 932158.693, 253870.049])| + EPSG_6555|6555|"NAD83(2011) / Oklahoma South (ftUS)"|Projected|[-100.0, 33.62, -94.42, 35.57]|Some([1359609.223, 104330.196, 3058257.314, 832905.32])| + EPSG_6556|6556|"NAD83(2011) / Oregon LCC (m)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([60163.867, 25562.683, 734038.753, 509025.159])| + EPSG_6557|6557|"NAD83(2011) / Oregon GIC Lambert (ft)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([197388.016, 83867.072, 2408263.626, 1670030.051])| + EPSG_6558|6558|"NAD83(2011) / Oregon North"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([2205462.606, 31489.15, 2823406.45, 295944.206])| + EPSG_6559|6559|"NAD83(2011) / Oregon North (ft)"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([7235769.707, 103310.859, 9263144.52, 970945.557])| + EPSG_6560|6560|"NAD83(2011) / Oregon South"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([1160304.687, 34807.774, 1798296.348, 329408.434])| + EPSG_6561|6561|"NAD83(2011) / Oregon South (ft)"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([3806773.909, 114198.733, 5899922.402, 1080736.333])| + EPSG_6562|6562|"NAD83(2011) / Pennsylvania North"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([364722.698, 48125.726, 858118.947, 266893.215])| + EPSG_6563|6563|"NAD83(2011) / Pennsylvania North (ftUS)"|Projected|[-80.53, 40.6, -74.7, 42.53]|Some([1196594.385, 157892.487, 2815345.245, 875632.155])| + EPSG_6564|6564|"NAD83(2011) / Pennsylvania South"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([361633.135, 41825.709, 859794.669, 209415.985])| + EPSG_6565|6565|"NAD83(2011) / Pennsylvania South (ftUS)"|Projected|[-80.53, 39.71, -74.72, 41.18]|Some([1186458.044, 137223.18, 2820843.01, 687058.943])| + EPSG_6566|6566|"NAD83(2011) / Puerto Rico and Virgin Is."|Projected|[-67.97, 17.62, -64.51, 18.57]|Some([36907.961, 176388.461, 404129.026, 282602.555])| + EPSG_6567|6567|"NAD83(2011) / Rhode Island"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([70610.778, 5182.622, 135267.079, 104115.195])| + EPSG_6568|6568|"NAD83(2011) / Rhode Island (ftUS)"|Projected|[-71.85, 41.13, -71.08, 42.02]|Some([231662.195, 17003.318, 443788.743, 341584.603])| + EPSG_6569|6569|"NAD83(2011) / South Carolina"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([386694.594, 24032.757, 843837.456, 377206.795])| + EPSG_6570|6570|"NAD83(2011) / South Carolina (ft)"|Projected|[-83.36, 32.05, -78.52, 35.21]|Some([1268683.052, 78847.627, 2768495.59, 1237555.103])| + EPSG_6571|6571|"NAD83(2011) / South Dakota North"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([274445.551, 34078.149, 883988.887, 243155.392])| + EPSG_6572|6572|"NAD83(2011) / South Dakota North (ftUS)"|Projected|[-104.07, 44.14, -96.45, 45.95]|Some([900410.112, 111804.729, 2900220.208, 797752.314])| + EPSG_6573|6573|"NAD83(2011) / South Dakota South"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([293645.541, 16297.864, 920867.071, 280198.165])| + EPSG_6574|6574|"NAD83(2011) / South Dakota South (ftUS)"|Projected|[-104.06, 42.48, -96.43, 44.79]|Some([963402.081, 53470.575, 3021211.384, 919283.478])| + EPSG_6575|6575|"NAD83(2011) / Tennessee"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([206556.417, 71749.383, 997092.638, 269011.428])| + EPSG_6576|6576|"NAD83(2011) / Tennessee (ftUS)"|Projected|[-90.31, 34.98, -81.65, 36.68]|Some([677677.177, 235397.768, 3271294.763, 882581.658])| + EPSG_6577|6577|"NAD83(2011) / Texas Central"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([88485.924, 3012579.293, 1360427.427, 3308389.708])| + EPSG_6578|6578|"NAD83(2011) / Texas Central (ftUS)"|Projected|[-106.66, 29.78, -93.5, 32.27]|Some([290307.569, 9883770.564, 4463335.649, 10854275.232])| + EPSG_6579|6579|"NAD83(2011) / Texas Centric Albers Equal Area"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831192.278, 6857764.52, 2152758.974, 8059593.093])| + EPSG_6580|6580|"NAD83(2011) / Texas Centric Lambert Conformal"|Projected|[-106.66, 25.83, -93.5, 36.5]|Some([831139.345, 5877072.129, 2152810.743, 7077309.646])| + EPSG_6581|6581|"NAD83(2011) / Texas North"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([59141.726, 1033282.718, 339017.133, 1278422.106])| + EPSG_6582|6582|"NAD83(2011) / Texas North (ftUS)"|Projected|[-103.03, 34.3, -99.99, 36.5]|Some([194034.147, 3390028.382, 1112258.712, 4194289.858])| + EPSG_6583|6583|"NAD83(2011) / Texas North Central"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([166935.124, 2005915.123, 1026435.587, 2332223.008])| + EPSG_6584|6584|"NAD83(2011) / Texas North Central (ftUS)"|Projected|[-103.07, 31.72, -94.0, 34.58]|Some([547686.318, 6581073.199, 3367564.088, 7651634.987])| + EPSG_6585|6585|"NAD83(2011) / Texas South"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([129543.463, 5018097.381, 465443.4, 5282925.792])| + EPSG_6586|6586|"NAD83(2011) / Texas South (ftUS)"|Projected|[-100.2, 25.83, -96.85, 28.21]|Some([425010.511, 16463541.159, 1527042.22, 17332399.035])| + EPSG_6587|6587|"NAD83(2011) / Texas South Central"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([8759.289, 3994088.534, 1116403.98, 4329153.767])| + EPSG_6588|6588|"NAD83(2011) / Texas South Central (ftUS)"|Projected|[-105.0, 27.78, -93.76, 30.67]|Some([28737.767, 13103938.798, 3662735.393, 14203231.984])| + EPSG_6589|6589|"NAD83(2011) / Vermont"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([423006.838, 24438.295, 581907.658, 281578.897])| + EPSG_6590|6590|"NAD83(2011) / Vermont (ftUS)"|Projected|[-73.44, 42.72, -71.5, 45.03]|Some([1387814.935, 80177.973, 1909142.042, 923813.43])| + EPSG_6591|6591|"NAD83(2011) / Virginia Lambert"|Projected|[-83.68, 36.54, -75.31, 39.46]|Some([-374247.52, 59942.043, 375142.238, 392175.157])| + EPSG_6592|6592|"NAD83(2011) / Virginia North"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([3362553.326, 2011470.121, 3675327.323, 2200927.06])| + EPSG_6593|6593|"NAD83(2011) / Virginia North (ftUS)"|Projected|[-80.06, 37.77, -76.51, 39.46]|Some([11031977.038, 6599298.223, 12058136.392, 7220874.863])| + EPSG_6594|6594|"NAD83(2011) / Virginia South"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([3036366.981, 1022941.909, 3785608.14, 1228478.938])| + EPSG_6595|6595|"NAD83(2011) / Virginia South (ftUS)"|Projected|[-83.68, 36.54, -75.31, 38.28]|Some([9961814.003, 3356101.914, 12419949.373, 4030434.647])| + EPSG_6596|6596|"NAD83(2011) / Washington North"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([199622.139, 8897.31, 789505.514, 235374.47])| + EPSG_6597|6597|"NAD83(2011) / Washington North (ftUS)"|Projected|[-124.79, 47.08, -117.02, 49.05]|Some([654926.968, 29190.59, 2590236.007, 772224.407])| + EPSG_6598|6598|"NAD83(2011) / Washington South"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([195499.391, 22976.053, 780314.152, 260319.122])| + EPSG_6599|6599|"NAD83(2011) / Washington South (ftUS)"|Projected|[-124.4, 45.54, -116.91, 47.61]|Some([641400.92, 75380.599, 2560080.681, 854063.654])| + EPSG_6600|6600|"NAD83(2011) / West Virginia North"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([403574.778, 28865.245, 754712.786, 240001.069])| + EPSG_6601|6601|"NAD83(2011) / West Virginia North (ftUS)"|Projected|[-81.76, 38.76, -77.72, 40.64]|Some([1324061.585, 94702.058, 2476086.866, 787403.506])| + EPSG_6602|6602|"NAD83(2011) / West Virginia South"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([453513.498, 22198.636, 773116.784, 242635.52])| + EPSG_6603|6603|"NAD83(2011) / West Virginia South (ftUS)"|Projected|[-82.65, 37.2, -79.05, 39.17]|Some([1487902.2, 72830.025, 2536467.316, 796046.703])| + EPSG_6605|6605|"NAD83(2011) / Wisconsin Central (ftUS)"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([1207874.319, 53469.789, 2955328.583, 739108.439])| + EPSG_6606|6606|"NAD83(2011) / Wisconsin North"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([373644.539, 22599.495, 752749.553, 242221.119])| + EPSG_6607|6607|"NAD83(2011) / Wisconsin North (ftUS)"|Projected|[-92.89, 45.37, -88.05, 47.31]|Some([1225865.459, 74145.175, 2469645.826, 794687.121])| + EPSG_6608|6608|"NAD83(2011) / Wisconsin South"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([482416.586, 53326.791, 850746.174, 263304.749])| + EPSG_6609|6609|"NAD83(2011) / Wisconsin South (ftUS)"|Projected|[-91.43, 42.48, -86.95, 44.33]|Some([1582728.415, 174956.315, 2791156.407, 863858.997])| + EPSG_6610|6610|"NAD83(2011) / Wisconsin Transverse Mercator"|Projected|[-92.89, 42.48, -86.25, 47.31]|Some([282455.202, 223072.39, 828240.811, 766436.435])| + EPSG_6611|6611|"NAD83(2011) / Wyoming East"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([102113.109, 54410.863, 293960.125, 501634.091])| + EPSG_6612|6612|"NAD83(2011) / Wyoming East (ftUS)"|Projected|[-106.33, 40.99, -104.05, 45.01]|Some([335016.091, 178512.974, 964434.176, 1645777.845])| + EPSG_6613|6613|"NAD83(2011) / Wyoming East Central"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([290893.723, 154410.763, 512191.622, 601840.614])| + EPSG_6614|6614|"NAD83(2011) / Wyoming East Central (ftUS)"|Projected|[-108.63, 40.99, -106.0, 45.01]|Some([954373.824, 506595.978, 1680415.346, 1974538.747])| + EPSG_6615|6615|"NAD83(2011) / Wyoming West"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([717820.124, 154411.136, 887789.509, 563723.822])| + EPSG_6616|6616|"NAD83(2011) / Wyoming West (ftUS)"|Projected|[-111.06, 40.99, -109.04, 44.67]|Some([2355048.189, 506597.202, 2912689.414, 1849483.907])| + EPSG_6617|6617|"NAD83(2011) / Wyoming West Central"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([405623.049, 54412.076, 705179.483, 503572.395])| + EPSG_6618|6618|"NAD83(2011) / Wyoming West Central (ftUS)"|Projected|[-111.06, 40.99, -107.5, 45.01]|Some([1330781.622, 178516.951, 2313576.353, 1652137.098])| + EPSG_6619|6619|"NAD83(2011) / Utah Central"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([277517.684, 2017395.213, 714632.015, 2308013.497])| + EPSG_6620|6620|"NAD83(2011) / Utah North"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([284873.767, 1024061.481, 708352.443, 1189279.766])| + EPSG_6621|6621|"NAD83(2011) / Utah South"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([273008.072, 3035885.945, 718982.33, 3215389.407])| + EPSG_6622|6622|"NAD83(CSRS)v2 / Quebec Lambert"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([-893275.479, 110383.071, 897177.342, 2112794.117])| + EPSG_6623|6623|"NAD83 / Quebec Albers"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([-893137.688, 109682.4, 897039.607, 2124842.873])| + EPSG_6624|6624|"NAD83(CSRS)v2 / Quebec Albers"|Projected|[-79.85, 44.99, -57.1, 62.62]|Some([-893137.688, 109682.4, 897039.607, 2124842.873])| + EPSG_6625|6625|"NAD83(2011) / Utah Central (ftUS)"|Projected|[-114.05, 38.49, -109.04, 41.08]|Some([910489.268, 6618737.46, 2344588.537, 7572207.614])| + EPSG_6626|6626|"NAD83(2011) / Utah North (ftUS)"|Projected|[-114.04, 40.55, -109.04, 42.01]|Some([934623.35, 3359775.041, 2323986.308, 3901828.698])| + EPSG_6627|6627|"NAD83(2011) / Utah South (ftUS)"|Projected|[-114.05, 36.99, -109.04, 38.58]|Some([895693.984, 9960235.804, 2358861.194, 10549156.745])| + EPSG_6628|6628|"NAD83(PA11) / Hawaii zone 1"|Projected|[-156.1, 18.87, -154.74, 20.33]|Some([436777.084, 4058.543, 580083.065, 165857.102])| + EPSG_6629|6629|"NAD83(PA11) / Hawaii zone 2"|Projected|[-157.36, 20.45, -155.93, 21.26]|Some([427654.163, 12915.794, 576867.636, 102770.167])| + EPSG_6630|6630|"NAD83(PA11) / Hawaii zone 3"|Projected|[-158.33, 21.2, -157.61, 21.75]|Some([465735.883, 3690.622, 540494.023, 64638.674])| + EPSG_6631|6631|"NAD83(PA11) / Hawaii zone 4"|Projected|[-159.85, 21.81, -159.23, 22.29]|Some([463810.5, -2583.624, 527917.563, 50608.486])| + EPSG_6632|6632|"NAD83(PA11) / Hawaii zone 5"|Projected|[-160.3, 21.73, -159.99, 22.07]|Some([486205.794, 7012.661, 518277.332, 44671.059])| + EPSG_6633|6633|"NAD83(PA11) / Hawaii zone 3 (ftUS)"|Projected|[-158.33, 21.2, -157.61, 21.75]|Some([1528001.809, 12108.316, 1773270.806, 212068.718])| + EPSG_6634|6634|"NAD83(PA11) / UTM zone 4N"|Projected|[-160.3, 19.51, -155.99, 22.29]|Some([363588.225, 2157259.407, 815938.21, 2468018.556])| + EPSG_6635|6635|"NAD83(PA11) / UTM zone 5N"|Projected|[-156.0, 18.87, -154.74, 20.86]|Some([183890.923, 2087343.709, 318974.866, 2309565.702])| + EPSG_6636|6636|"NAD83(PA11) / UTM zone 2S"|Projected|[-170.88, -14.59, -168.09, -14.11]|Some([512925.157, 8385013.991, 814222.967, 8440104.877])| + EPSG_6637|6637|"NAD83(MA11) / Guam Map Grid"|Projected|[144.58, 13.18, 145.01, 13.7]|Some([81570.949, 164597.308, 128185.658, 222142.253])| + EPSG_6646|6646|"Karbala 1979 / Iraq National Grid"|Projected|[38.79, 29.06, 48.61, 37.39]|Some([48710.405, 3716.972, 1005660.814, 955023.258])| + EPSG_6668|6668|"JGD2011"|Geographic2d|[122.38, 17.09, 157.65, 46.05]|Some([122.38, 17.09, 157.65, 46.05])| + EPSG_6669|6669|"JGD2011 / Japan Plane Rectangular CS I"|Projected|[128.17, 26.96, 130.46, 34.74]|Some([-132049.508, -669482.511, 95311.499, 193787.213])| + EPSG_6670|6670|"JGD2011 / Japan Plane Rectangular CS II"|Projected|[129.76, 30.18, 132.05, 33.99]|Some([-119419.183, -312649.517, 101119.969, 110486.304])| + EPSG_6671|6671|"JGD2011 / Japan Plane Rectangular CS III"|Projected|[130.81, 33.72, 133.49, 36.38]|Some([-125737.064, -252913.371, 122647.486, 43016.411])| + EPSG_6672|6672|"JGD2011 / Japan Plane Rectangular CS IV"|Projected|[131.95, 32.69, 134.81, 34.45]|Some([-145349.824, -34375.821, 122842.247, 161904.197])| + EPSG_6673|6673|"JGD2011 / Japan Plane Rectangular CS V"|Projected|[133.13, 34.13, 135.47, 35.71]|Some([-110992.003, -207439.917, 104842.551, -31506.672])| + EPSG_6674|6674|"JGD2011 / Japan Plane Rectangular CS VI"|Projected|[134.86, 33.4, 136.99, 36.33]|Some([-106045.514, -288402.568, 92091.564, 37217.083])| + EPSG_6675|6675|"JGD2011 / Japan Plane Rectangular CS VII"|Projected|[136.22, 34.51, 137.84, 37.58]|Some([-86924.201, -165291.634, 61825.867, 175742.246])| + EPSG_6676|6676|"JGD2011 / Japan Plane Rectangular CS VIII"|Projected|[137.32, 34.54, 139.91, 38.58]|Some([-108311.316, -161963.928, 129424.253, 287250.64])| + EPSG_6677|6677|"JGD2011 / Japan Plane Rectangular CS IX"|Projected|[138.4, 29.31, 141.11, 37.98]|Some([-139237.374, -741839.214, 124017.003, 220682.801])| + EPSG_6678|6678|"JGD2011 / Japan Plane Rectangular CS X"|Projected|[139.49, 37.73, 142.14, 41.58]|Some([-118410.456, -251974.013, 115178.264, 176312.8])| + EPSG_6679|6679|"JGD2011 / Japan Plane Rectangular CS XI"|Projected|[139.34, 41.34, 141.46, 43.42]|Some([-76161.028, -295460.047, 101269.482, -63724.196])| + EPSG_6680|6680|"JGD2011 / Japan Plane Rectangular CS XII"|Projected|[140.89, 42.15, 143.61, 45.54]|Some([-112401.852, -205503.727, 112401.852, 172018.666])| + EPSG_6681|6681|"JGD2011 / Japan Plane Rectangular CS XIII"|Projected|[142.61, 41.87, 145.87, 44.4]|Some([-136139.903, -236601.142, 134479.611, 45750.328])| + EPSG_6682|6682|"JGD2011 / Japan Plane Rectangular CS XIV"|Projected|[141.2, 24.67, 142.33, 27.8]|Some([-80968.069, -147319.884, 33398.738, 199680.336])| + EPSG_6683|6683|"JGD2011 / Japan Plane Rectangular CS XV"|Projected|[126.63, 26.02, 128.4, 26.91]|Some([-87081.022, 2215.628, 90083.966, 101131.166])| + EPSG_6684|6684|"JGD2011 / Japan Plane Rectangular CS XVI"|Projected|[122.83, 23.98, 125.51, 24.94]|Some([-119061.641, -223738.677, 153665.544, -116567.494])| + EPSG_6685|6685|"JGD2011 / Japan Plane Rectangular CS XVII"|Projected|[131.12, 24.4, 131.38, 26.01]|Some([12011.913, -177218.482, 38541.557, 1163.086])| + EPSG_6686|6686|"JGD2011 / Japan Plane Rectangular CS XVIII"|Projected|[136.02, 20.37, 136.16, 20.48]|Some([2086.316, 40957.476, 16702.411, 53142.346])| + EPSG_6687|6687|"JGD2011 / Japan Plane Rectangular CS XIX"|Projected|[153.91, 24.22, 154.05, 24.35]|Some([-9141.113, -197159.042, 5078.395, -182758.419])| + EPSG_6688|6688|"JGD2011 / UTM zone 51N"|Projected|[122.38, 21.1, 126.0, 29.71]|Some([435606.214, 2333215.068, 811684.528, 3290419.882])| + EPSG_6689|6689|"JGD2011 / UTM zone 52N"|Projected|[126.0, 21.12, 132.0, 38.63]|Some([188357.296, 2335428.461, 811642.704, 4279988.956])| + EPSG_6690|6690|"JGD2011 / UTM zone 53N"|Projected|[132.0, 17.09, 138.0, 43.55]|Some([180701.572, 1889511.329, 819298.428, 4826267.586])| + EPSG_6691|6691|"JGD2011 / UTM zone 54N"|Projected|[138.0, 17.63, 144.0, 46.05]|Some([181636.801, 1949250.955, 818363.199, 5103979.181])| + EPSG_6692|6692|"JGD2011 / UTM zone 55N"|Projected|[144.0, 23.03, 147.86, 45.65]|Some([192525.243, 2546841.34, 588116.936, 5059539.363])| + EPSG_6703|6703|"WGS 84 / TM 60 SW"|Projected|[-63.01, -56.25, -56.99, -47.68]|Some([274092.134, 3762021.194, 725907.866, 4719265.451])| + EPSG_6706|6706|"RDN2008"|Geographic2d|[5.93, 34.76, 18.99, 47.1]|Some([5.93, 34.76, 18.99, 47.1])| + EPSG_6707|6707|"RDN2008 / UTM zone 32N (N-E)"|Projected|[5.93, 36.53, 12.0, 47.04]|Some([225132.787, 4042736.378, 768598.181, 5214183.815])| + EPSG_6708|6708|"RDN2008 / UTM zone 33N (N-E)"|Projected|[12.0, 34.79, 18.0, 47.1]|Some([225503.37, 3849755.266, 774496.63, 5220644.505])| + EPSG_6709|6709|"RDN2008 / UTM zone 34N (N-E)"|Projected|[17.99, 34.76, 18.99, 41.64]|Some([224488.264, 3848268.688, 332597.547, 4614184.666])| + EPSG_6720|6720|"WGS 84 / CIG92"|Projected|[105.48, -10.63, 105.77, -10.36]|Some([34119.706, 124429.016, 65880.294, 154298.44])| + EPSG_6721|6721|"GDA94 / CIG94"|Projected|[105.48, -10.63, 105.77, -10.36]|Some([34119.687, 124427.676, 65880.313, 154297.134])| + EPSG_6722|6722|"WGS 84 / CKIG92"|Projected|[96.76, -12.27, 96.99, -11.76]|Some([37465.217, 43044.554, 62534.783, 99464.654])| + EPSG_6723|6723|"GDA94 / CKIG94"|Projected|[96.76, -12.27, 96.99, -11.76]|Some([37465.294, 143052.872, 62534.706, 199472.627])| + EPSG_6736|6736|"GDA94 / MGA zone 46"|Projected|[93.41, -15.46, 96.01, -8.57]|Some([543982.744, 8288531.631, 831369.191, 9052662.824])| + EPSG_6737|6737|"GDA94 / MGA zone 47"|Projected|[96.0, -15.56, 100.34, -8.47]|Some([169646.719, 8277472.622, 647505.804, 9063742.116])| + EPSG_6738|6738|"GDA94 / MGA zone 59"|Projected|[168.0, -32.48, 173.35, -25.61]|Some([198695.776, 6402392.036, 735995.359, 7167504.655])| + EPSG_6783|6783|"NAD83(CORS96)"|Geographic2d|[167.65, 14.92, -63.88, 74.71]|Some([-179.503, 14.92, 176.215, 74.71])| + EPSG_6784|6784|"NAD83(CORS96) / Oregon Baker zone (m)"|Projected|[-118.15, 44.6, -117.37, 45.19]|Some([14854.7, 11114.087, 76791.548, 76795.571])| + EPSG_6785|6785|"NAD83(CORS96) / Oregon Baker zone (ft)"|Projected|[-118.15, 44.6, -117.37, 45.19]|Some([48735.891, 36463.54, 251940.775, 251953.972])| + EPSG_6786|6786|"NAD83(2011) / Oregon Baker zone (m)"|Projected|[-118.15, 44.6, -117.37, 45.19]|Some([14854.7, 11114.087, 76791.548, 76795.571])| + EPSG_6787|6787|"NAD83(2011) / Oregon Baker zone (ft)"|Projected|[-118.15, 44.6, -117.37, 45.19]|Some([48735.891, 36463.54, 251940.775, 251953.972])| + EPSG_6788|6788|"NAD83(CORS96) / Oregon Bend-Klamath Falls zone (m)"|Projected|[-122.45, 41.88, -120.77, 43.89]|Some([21883.851, 14442.327, 161362.823, 238245.53])| + EPSG_6789|6789|"NAD83(CORS96) / Oregon Bend-Klamath Falls zone (ft)"|Projected|[-122.45, 41.88, -120.77, 43.89]|Some([71797.412, 47382.963, 529405.588, 781645.439])| + EPSG_6790|6790|"NAD83(2011) / Oregon Bend-Klamath Falls zone (m)"|Projected|[-122.45, 41.88, -120.77, 43.89]|Some([21883.851, 14442.327, 161362.823, 238245.53])| + EPSG_6791|6791|"NAD83(2011) / Oregon Bend-Klamath Falls zone (ft)"|Projected|[-122.45, 41.88, -120.77, 43.89]|Some([71797.412, 47382.963, 529405.588, 781645.439])| + EPSG_6792|6792|"NAD83(CORS96) / Oregon Bend-Redmond-Prineville zone (m)"|Projected|[-121.88, 43.76, -119.79, 44.98]|Some([29254.977, 29238.197, 197594.459, 165856.083])| + EPSG_6793|6793|"NAD83(CORS96) / Oregon Bend-Redmond-Prineville zone (ft)"|Projected|[-121.88, 43.76, -119.79, 44.98]|Some([95980.896, 95925.842, 648275.784, 544147.255])| + EPSG_6794|6794|"NAD83(2011) / Oregon Bend-Redmond-Prineville zone (m)"|Projected|[-121.88, 43.76, -119.79, 44.98]|Some([29254.977, 29238.197, 197594.459, 165856.083])| + EPSG_6795|6795|"NAD83(2011) / Oregon Bend-Redmond-Prineville zone (ft)"|Projected|[-121.88, 43.76, -119.79, 44.98]|Some([95980.896, 95925.842, 648275.784, 544147.255])| + EPSG_6796|6796|"NAD83(CORS96) / Oregon Bend-Burns zone (m)"|Projected|[-120.95, 43.34, -118.8, 44.28]|Some([22674.444, 23699.21, 197050.401, 128856.255])| + EPSG_6797|6797|"NAD83(CORS96) / Oregon Bend-Burns zone (ft)"|Projected|[-120.95, 43.34, -118.8, 44.28]|Some([74391.221, 77753.315, 646490.816, 422756.742])| + EPSG_6798|6798|"NAD83(2011) / Oregon Bend-Burns zone (m)"|Projected|[-120.95, 43.34, -118.8, 44.28]|Some([22674.444, 23699.21, 197050.401, 128856.255])| + EPSG_6799|6799|"NAD83(2011) / Oregon Bend-Burns zone (ft)"|Projected|[-120.95, 43.34, -118.8, 44.28]|Some([74391.221, 77753.315, 646490.816, 422756.742])| + EPSG_6800|6800|"NAD83(CORS96) / Oregon Canyonville-Grants Pass zone (m)"|Projected|[-123.83, 42.49, -122.43, 43.24]|Some([-834.918, -1110.772, 114270.412, 82608.783])| + EPSG_6801|6801|"NAD83(CORS96) / Oregon Canyonville-Grants Pass zone (ft)"|Projected|[-123.83, 42.49, -122.43, 43.24]|Some([-2739.232, -3644.266, 374902.926, 271026.189])| + EPSG_6802|6802|"NAD83(2011) / Oregon Canyonville-Grants Pass zone (m)"|Projected|[-123.83, 42.49, -122.43, 43.24]|Some([-834.918, -1110.772, 114270.412, 82608.783])| + EPSG_6803|6803|"NAD83(2011) / Oregon Canyonville-Grants Pass zone (ft)"|Projected|[-123.83, 42.49, -122.43, 43.24]|Some([-2739.232, -3644.266, 374902.926, 271026.189])| + EPSG_6804|6804|"NAD83(CORS96) / Oregon Columbia River East zone (m)"|Projected|[-122.05, 45.49, -118.89, 46.08]|Some([28839.577, 10364.974, 275849.884, 77193.838])| + EPSG_6805|6805|"NAD83(CORS96) / Oregon Columbia River East zone (ft)"|Projected|[-122.05, 45.49, -118.89, 46.08]|Some([94618.034, 34005.819, 905019.306, 253260.624])| + EPSG_6806|6806|"NAD83(2011) / Oregon Columbia River East zone (m)"|Projected|[-122.05, 45.49, -118.89, 46.08]|Some([28839.577, 10364.974, 275849.884, 77193.838])| + EPSG_6807|6807|"NAD83(2011) / Oregon Columbia River East zone (ft)"|Projected|[-122.05, 45.49, -118.89, 46.08]|Some([94618.034, 34005.819, 905019.306, 253260.624])| + EPSG_6808|6808|"NAD83(CORS96) / Oregon Columbia River West zone (m)"|Projected|[-124.33, 45.17, -121.47, 46.56]|Some([63738.105, 102685.61, 288576.314, 258313.232])| + EPSG_6809|6809|"NAD83(CORS96) / Oregon Columbia River West zone (ft)"|Projected|[-124.33, 45.17, -121.47, 46.56]|Some([209114.518, 336895.047, 946772.682, 847484.356])| + EPSG_6810|6810|"NAD83(2011) / Oregon Columbia River West zone (m)"|Projected|[-124.33, 45.17, -121.47, 46.56]|Some([63738.105, 102685.61, 288576.314, 258313.232])| + EPSG_6811|6811|"NAD83(2011) / Oregon Columbia River West zone (ft)"|Projected|[-124.33, 45.17, -121.47, 46.56]|Some([209114.518, 336895.047, 946772.682, 847484.356])| + EPSG_6812|6812|"NAD83(CORS96) / Oregon Cottage Grove-Canyonville zone (m)"|Projected|[-123.96, 42.82, -122.4, 43.88]|Some([-1249.172, -1481.222, 126328.699, 116710.524])| + EPSG_6813|6813|"NAD83(CORS96) / Oregon Cottage Grove-Canyonville zone (ft)"|Projected|[-123.96, 42.82, -122.4, 43.88]|Some([-4098.333, -4859.653, 414464.234, 382908.543])| + EPSG_6814|6814|"NAD83(2011) / Oregon Cottage Grove-Canyonville zone (m)"|Projected|[-123.96, 42.82, -122.4, 43.88]|Some([-1249.172, -1481.222, 126328.699, 116710.524])| + EPSG_6815|6815|"NAD83(2011) / Oregon Cottage Grove-Canyonville zone (ft)"|Projected|[-123.96, 42.82, -122.4, 43.88]|Some([-4098.333, -4859.653, 414464.234, 382908.543])| + EPSG_6816|6816|"NAD83(CORS96) / Oregon Dufur-Madras zone (m)"|Projected|[-121.95, 44.63, -120.46, 45.55]|Some([4606.652, 14447.635, 122855.147, 117140.817])| + EPSG_6817|6817|"NAD83(CORS96) / Oregon Dufur-Madras zone (ft)"|Projected|[-121.95, 44.63, -120.46, 45.55]|Some([15113.687, 47400.376, 403068.067, 384320.265])| + EPSG_6818|6818|"NAD83(2011) / Oregon Dufur-Madras zone (m)"|Projected|[-121.95, 44.63, -120.46, 45.55]|Some([4606.652, 14447.635, 122855.147, 117140.817])| + EPSG_6819|6819|"NAD83(2011) / Oregon Dufur-Madras zone (ft)"|Projected|[-121.95, 44.63, -120.46, 45.55]|Some([15113.687, 47400.376, 403068.067, 384320.265])| + EPSG_6820|6820|"NAD83(CORS96) / Oregon Eugene zone (m)"|Projected|[-123.8, 43.74, -122.18, 44.71]|Some([-1019.369, -1110.985, 129482.913, 107147.314])| + EPSG_6821|6821|"NAD83(CORS96) / Oregon Eugene zone (ft)"|Projected|[-123.8, 43.74, -122.18, 44.71]|Some([-3344.388, -3644.963, 424812.706, 351533.182])| + EPSG_6822|6822|"NAD83(2011) / Oregon Eugene zone (m)"|Projected|[-123.8, 43.74, -122.18, 44.71]|Some([-1019.369, -1110.985, 129482.913, 107147.314])| + EPSG_6823|6823|"NAD83(2011) / Oregon Eugene zone (ft)"|Projected|[-123.8, 43.74, -122.18, 44.71]|Some([-3344.388, -3644.963, 424812.706, 351533.182])| + EPSG_6824|6824|"NAD83(CORS96) / Oregon Grants Pass-Ashland zone (m)"|Projected|[-123.95, 41.88, -122.37, 42.85]|Some([-1189.492, 14439.795, 129966.533, 122642.51])| + EPSG_6825|6825|"NAD83(CORS96) / Oregon Grants Pass-Ashland zone (ft)"|Projected|[-123.95, 41.88, -122.37, 42.85]|Some([-3902.531, 47374.654, 426399.387, 402370.438])| + EPSG_6826|6826|"NAD83(2011) / Oregon Grants Pass-Ashland zone (m)"|Projected|[-123.95, 41.88, -122.37, 42.85]|Some([-1189.492, 14439.795, 129966.533, 122642.51])| + EPSG_6827|6827|"NAD83(2011) / Oregon Grants Pass-Ashland zone (ft)"|Projected|[-123.95, 41.88, -122.37, 42.85]|Some([-3902.531, 47374.654, 426399.387, 402370.438])| + EPSG_6828|6828|"NAD83(CORS96) / Oregon Gresham-Warm Springs zone (m)"|Projected|[-122.43, 45.02, -121.68, 45.55]|Some([2380.41, 2222.756, 61497.917, 61336.149])| + EPSG_6829|6829|"NAD83(CORS96) / Oregon Gresham-Warm Springs zone (ft)"|Projected|[-122.43, 45.02, -121.68, 45.55]|Some([7809.745, 7292.507, 201764.82, 201234.084])| + EPSG_6830|6830|"NAD83(2011) / Oregon Gresham-Warm Springs zone (m)"|Projected|[-122.43, 45.02, -121.68, 45.55]|Some([2380.41, 2222.756, 61497.917, 61336.149])| + EPSG_6831|6831|"NAD83(2011) / Oregon Gresham-Warm Springs zone (ft)"|Projected|[-122.43, 45.02, -121.68, 45.55]|Some([7809.745, 7292.507, 201764.82, 201234.084])| + EPSG_6832|6832|"NAD83(CORS96) / Oregon La Grande zone (m)"|Projected|[-118.17, 45.13, -117.14, 45.8]|Some([26624.645, 14449.176, 107663.555, 89283.014])| + EPSG_6833|6833|"NAD83(CORS96) / Oregon La Grande zone (ft)"|Projected|[-118.17, 45.13, -117.14, 45.8]|Some([87351.197, 47405.432, 353226.886, 292923.274])| + EPSG_6834|6834|"NAD83(2011) / Oregon La Grande zone (m)"|Projected|[-118.17, 45.13, -117.14, 45.8]|Some([26624.645, 14449.176, 107663.555, 89283.014])| + EPSG_6835|6835|"NAD83(2011) / Oregon La Grande zone (ft)"|Projected|[-118.17, 45.13, -117.14, 45.8]|Some([87351.197, 47405.432, 353226.886, 292923.274])| + EPSG_6836|6836|"NAD83(CORS96) / Oregon Ontario zone (m)"|Projected|[-117.9, 43.41, -116.7, 44.65]|Some([7095.5, 17777.839, 104301.449, 155965.445])| + EPSG_6837|6837|"NAD83(CORS96) / Oregon Ontario zone (ft)"|Projected|[-117.9, 43.41, -116.7, 44.65]|Some([23279.2, 58326.243, 342196.355, 511697.656])| + EPSG_6838|6838|"NAD83(2011) / Oregon Ontario zone (m)"|Projected|[-117.9, 43.41, -116.7, 44.65]|Some([7095.5, 17777.839, 104301.449, 155965.445])| + EPSG_6839|6839|"NAD83(2011) / Oregon Ontario zone (ft)"|Projected|[-117.9, 43.41, -116.7, 44.65]|Some([23279.2, 58326.243, 342196.355, 511697.656])| + EPSG_6840|6840|"NAD83(CORS96) / Oregon Coast zone (m)"|Projected|[-124.84, 41.89, -123.35, 46.4]|Some([69167.852, 51394.869, 192829.113, 552830.556])| + EPSG_6841|6841|"NAD83(CORS96) / Oregon Coast zone (ft)"|Projected|[-124.84, 41.89, -123.35, 46.4]|Some([226928.647, 168618.336, 632641.448, 1813748.544])| + EPSG_6842|6842|"NAD83(2011) / Oregon Coast zone (m)"|Projected|[-124.84, 41.89, -123.35, 46.4]|Some([69167.852, 51394.869, 192829.113, 552830.556])| + EPSG_6843|6843|"NAD83(2011) / Oregon Coast zone (ft)"|Projected|[-124.84, 41.89, -123.35, 46.4]|Some([226928.647, 168618.336, 632641.448, 1813748.544])| + EPSG_6844|6844|"NAD83(CORS96) / Oregon Pendleton zone (m)"|Projected|[-119.36, 45.46, -118.17, 46.02]|Some([44878.065, 23340.193, 137956.126, 86067.942])| + EPSG_6845|6845|"NAD83(CORS96) / Oregon Pendleton zone (ft)"|Projected|[-119.36, 45.46, -118.17, 46.02]|Some([147237.747, 76575.435, 452611.963, 282375.138])| + EPSG_6846|6846|"NAD83(2011) / Oregon Pendleton zone (m)"|Projected|[-119.36, 45.46, -118.17, 46.02]|Some([44878.065, 23340.193, 137956.126, 86067.942])| + EPSG_6847|6847|"NAD83(2011) / Oregon Pendleton zone (ft)"|Projected|[-119.36, 45.46, -118.17, 46.02]|Some([147237.747, 76575.435, 452611.963, 282375.138])| + EPSG_6848|6848|"NAD83(CORS96) / Oregon Pendleton-La Grande zone (m)"|Projected|[-118.64, 45.13, -118.09, 45.64]|Some([5870.823, 5187.167, 49145.978, 61923.872])| + EPSG_6849|6849|"NAD83(CORS96) / Oregon Pendleton-La Grande zone (ft)"|Projected|[-118.64, 45.13, -118.09, 45.64]|Some([19261.229, 17018.263, 161240.084, 203162.31])| + EPSG_6850|6850|"NAD83(2011) / Oregon Pendleton-La Grande zone (m)"|Projected|[-118.64, 45.13, -118.09, 45.64]|Some([5870.823, 5187.167, 49145.978, 61923.872])| + EPSG_6851|6851|"NAD83(2011) / Oregon Pendleton-La Grande zone (ft)"|Projected|[-118.64, 45.13, -118.09, 45.64]|Some([19261.229, 17018.263, 161240.084, 203162.31])| + EPSG_6852|6852|"NAD83(CORS96) / Oregon Portland zone (m)"|Projected|[-123.53, 45.23, -122.11, 46.01]|Some([38746.18, 19992.575, 150259.803, 106978.892])| + EPSG_6853|6853|"NAD83(CORS96) / Oregon Portland zone (ft)"|Projected|[-123.53, 45.23, -122.11, 46.01]|Some([127120.013, 65592.437, 492978.356, 350980.615])| + EPSG_6854|6854|"NAD83(2011) / Oregon Portland zone (m)"|Projected|[-123.53, 45.23, -122.11, 46.01]|Some([38746.18, 19992.575, 150259.803, 106978.892])| + EPSG_6855|6855|"NAD83(2011) / Oregon Portland zone (ft)"|Projected|[-123.53, 45.23, -122.11, 46.01]|Some([127120.013, 65592.437, 492978.356, 350980.615])| + EPSG_6856|6856|"NAD83(CORS96) / Oregon Salem zone (m)"|Projected|[-123.73, 44.32, -121.89, 45.3]|Some([-1587.636, -1481.215, 145197.926, 108117.879])| + EPSG_6857|6857|"NAD83(CORS96) / Oregon Salem zone (ft)"|Projected|[-123.73, 44.32, -121.89, 45.3]|Some([-5208.78, -4859.63, 476371.149, 354717.449])| + EPSG_6858|6858|"NAD83(2011) / Oregon Salem zone (m)"|Projected|[-123.73, 44.32, -121.89, 45.3]|Some([-1587.636, -1481.215, 145197.926, 108117.879])| + EPSG_6859|6859|"NAD83(2011) / Oregon Salem zone (ft)"|Projected|[-123.73, 44.32, -121.89, 45.3]|Some([-5208.78, -4859.63, 476371.149, 354717.449])| + EPSG_6860|6860|"NAD83(CORS96) / Oregon Santiam Pass zone (m)"|Projected|[-122.51, 44.1, -121.69, 44.66]|Some([-800.838, 1852.236, 64867.924, 64408.093])| + EPSG_6861|6861|"NAD83(CORS96) / Oregon Santiam Pass zone (ft)"|Projected|[-122.51, 44.1, -121.69, 44.66]|Some([-2627.42, 6076.89, 212821.272, 211312.642])| + EPSG_6862|6862|"NAD83(2011) / Oregon Santiam Pass zone (m)"|Projected|[-122.51, 44.1, -121.69, 44.66]|Some([-800.838, 1852.236, 64867.924, 64408.093])| + EPSG_6863|6863|"NAD83(2011) / Oregon Santiam Pass zone (ft)"|Projected|[-122.51, 44.1, -121.69, 44.66]|Some([-2627.42, 6076.89, 212821.272, 211312.642])| + EPSG_6867|6867|"NAD83(CORS96) / Oregon LCC (m)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([60163.867, 25562.683, 734038.753, 509025.159])| + EPSG_6868|6868|"NAD83(CORS96) / Oregon GIC Lambert (ft)"|Projected|[-124.6, 41.98, -116.47, 46.26]|Some([197388.016, 83867.072, 2408263.626, 1670030.051])| + EPSG_6870|6870|"ETRS89 / Albania TM 2010"|Projected|[19.22, 39.64, 21.06, 42.67]|Some([433043.94, 4389557.95, 590992.016, 4726605.169])| + EPSG_6875|6875|"RDN2008 / Italy zone (N-E)"|Projected|[5.93, 34.76, 18.99, 47.1]|Some([6444735.932, 3842196.016, 7639559.465, 5234265.085])| + EPSG_6876|6876|"RDN2008 / Zone 12 (N-E)"|Projected|[5.93, 34.76, 18.99, 47.1]|Some([2443901.785, 3847967.968, 3640520.245, 5242128.278])| + EPSG_6879|6879|"NAD83(2011) / Wisconsin Central"|Projected|[-92.89, 43.98, -86.25, 45.8]|Some([368160.829, 16297.624, 900785.954, 225280.703])| + EPSG_6880|6880|"NAD83(2011) / Nebraska (ftUS)"|Projected|[-104.06, 39.99, -95.3, 43.01]|Some([503201.832, 57074.252, 2956732.313, 1191417.417])| + EPSG_6881|6881|"Aden 1925"|Geographic2d|[43.37, 12.54, 53.14, 19.0]|Some([43.371, 12.539, 53.141, 18.999])| + EPSG_6882|6882|"Bekaa Valley 1920"|Geographic2d|[35.04, 33.06, 36.63, 34.65]|Some([35.039, 33.06, 36.629, 34.65])| + EPSG_6883|6883|"Bioko"|Geographic2d|[8.37, 3.14, 9.02, 3.82]|Some([8.371, 3.136, 9.021, 3.816])| + EPSG_6884|6884|"NAD83(CORS96) / Oregon North"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([2205462.606, 31489.15, 2823406.45, 295944.206])| + EPSG_6885|6885|"NAD83(CORS96) / Oregon North (ft)"|Projected|[-124.17, 43.95, -116.47, 46.26]|Some([7235769.707, 103310.859, 9263144.52, 970945.557])| + EPSG_6886|6886|"NAD83(CORS96) / Oregon South"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([1160304.687, 34807.774, 1798296.348, 329408.434])| + EPSG_6887|6887|"NAD83(CORS96) / Oregon South (ft)"|Projected|[-124.6, 41.98, -116.9, 44.56]|Some([3806773.909, 114198.733, 5899922.402, 1080736.333])| + EPSG_6892|6892|"South East Island 1943"|Geographic2d|[55.15, -4.86, 56.01, -3.66]|Some([55.151, -4.858, 56.011, -3.658])| + EPSG_6894|6894|"Gambia"|Geographic2d|[-16.88, 13.05, -13.79, 13.83]|Some([-16.881, 13.05, -13.791, 13.83])| + EPSG_6915|6915|"South East Island 1943 / UTM zone 40N"|Projected|[55.15, -4.86, 56.01, -3.66]|Some([294587.868, -537196.896, 390288.028, -404335.022])| + EPSG_6922|6922|"NAD83 / Kansas LCC"|Projected|[-102.06, 36.99, -94.58, 40.01]|Some([60852.597, 109912.11, 726692.026, 451869.446])| + EPSG_6923|6923|"NAD83 / Kansas LCC (ftUS)"|Projected|[-102.06, 36.99, -94.58, 40.01]|Some([199647.228, 360603.315, 2384155.422, 1482508.34])| + EPSG_6924|6924|"NAD83(2011) / Kansas LCC"|Projected|[-102.06, 36.99, -94.58, 40.01]|Some([60852.597, 109912.11, 726692.026, 451869.446])| + EPSG_6925|6925|"NAD83(2011) / Kansas LCC (ftUS)"|Projected|[-102.06, 36.99, -94.58, 40.01]|Some([199647.228, 360603.315, 2384155.422, 1482508.34])| + EPSG_6931|6931|"WGS 84 / NSIDC EASE-Grid 2.0 North"|Projected|[-180.0, 0.0, 180.0, 90.0]|Some([-8960607.232, -9009964.761, 8960607.232, 9009964.761])| + EPSG_6932|6932|"WGS 84 / NSIDC EASE-Grid 2.0 South"|Projected|[-180.0, -90.0, 180.0, 0.0]|Some([-8960607.232, -9009964.761, 8960607.232, 9009964.761])| + EPSG_6933|6933|"WGS 84 / NSIDC EASE-Grid 2.0 Global"|Projected|[-180.0, -86.0, 180.0, 86.0]|Some([-17367530.445, -7324184.564, 17367530.445, 7324184.564])| + EPSG_6962|6962|"ETRS89 / Albania LCC 2010"|Projected|[19.22, 39.64, 21.06, 42.67]|Some([-66933.08, -150938.051, 90959.315, 185927.66])| + EPSG_6966|6966|"NAD27 / Michigan North"|Projected|[-90.42, 45.08, -83.44, 48.32]|Some([1116671.943, 108196.156, 2919461.461, 1309431.425])| + EPSG_6983|6983|"IG05 Intermediate CRS"|Geographic2d|[34.17, 29.45, 35.69, 33.28]|Some([34.17, 29.45, 35.69, 33.28])| + EPSG_6984|6984|"Israeli Grid 05"|Projected|[34.17, 29.45, 35.69, 33.28]|Some([119163.57, 373652.168, 266628.788, 798787.184])| + EPSG_6990|6990|"IG05/12 Intermediate CRS"|Geographic2d|[34.17, 29.45, 35.69, 33.28]|Some([34.169, 29.45, 35.689, 33.28])| + EPSG_6991|6991|"Israeli Grid 05/12"|Projected|[34.17, 29.45, 35.69, 33.28]|Some([119097.334, 373609.826, 266563.359, 798747.065])| + EPSG_7005|7005|"Nahrwan 1934 / UTM zone 37N"|Projected|[38.79, 31.14, 42.0, 36.75]|Some([479941.748, 3444699.725, 785999.078, 4070917.09])| + EPSG_7006|7006|"Nahrwan 1934 / UTM zone 38N"|Projected|[42.0, 29.06, 48.0, 37.39]|Some([207830.68, 3214219.142, 792031.772, 4141945.335])| + EPSG_7007|7007|"Nahrwan 1934 / UTM zone 39N"|Projected|[48.0, 29.6, 48.75, 31.0]|Some([209339.078, 3276320.621, 285168.352, 3433056.086])| + EPSG_7035|7035|"RGSPM06 (lon-lat)"|Geographic2d|[-57.1, 43.41, -55.9, 47.37]|Some([-57.1, 43.41, -55.9, 47.37])| + EPSG_7037|7037|"RGR92 (lon-lat)"|Geographic2d|[51.83, -24.72, 58.24, -18.28]|Some([51.83, -24.72, 58.24, -18.28])| + EPSG_7039|7039|"RGM04 (lon-lat)"|Geographic2d|[43.68, -14.49, 46.7, -11.33]|Some([43.68, -14.49, 46.7, -11.33])| + EPSG_7041|7041|"RGFG95 (lon-lat)"|Geographic2d|[-54.61, 2.11, -49.45, 8.88]|Some([-54.61, 2.11, -49.45, 8.88])| + EPSG_7057|7057|"NAD83(2011) / IaRCS zone 1"|Projected|[-96.6, 42.9, -93.97, 43.51]|Some([11138251.671, 9490651.793, 11842992.51, 9715889.765])| + EPSG_7058|7058|"NAD83(2011) / IaRCS zone 2"|Projected|[-93.98, 42.9, -91.6, 43.51]|Some([12170408.545, 9702803.578, 12808156.007, 9927546.903])| + EPSG_7059|7059|"NAD83(2011) / IaRCS zone 3"|Projected|[-91.62, 42.29, -90.89, 43.51]|Some([13386351.543, 9043333.843, 13583883.352, 9488284.381])| + EPSG_7060|7060|"NAD83(2011) / IaRCS zone 4"|Projected|[-96.65, 42.2, -93.0, 42.92]|Some([14007751.057, 8478515.193, 14996764.287, 8746243.255])| + EPSG_7061|7061|"NAD83(2011) / IaRCS zone 5"|Projected|[-93.03, 42.2, -91.59, 43.09]|Some([15288635.716, 8735995.292, 15678847.422, 9061333.783])| + EPSG_7062|7062|"NAD83(2011) / IaRCS zone 6"|Projected|[-96.37, 40.58, -95.04, 42.22]|Some([16323140.371, 6720228.16, 16692601.177, 7318589.366])| + EPSG_7063|7063|"NAD83(2011) / IaRCS zone 7"|Projected|[-95.16, 41.15, -94.16, 42.22]|Some([17354945.625, 7127912.154, 17630365.277, 7518270.657])| + EPSG_7064|7064|"NAD83(2011) / IaRCS zone 8"|Projected|[-94.29, 41.15, -93.23, 42.22]|Some([18342094.544, 7327908.25, 18634035.941, 7718343.586])| + EPSG_7065|7065|"NAD83(2011) / IaRCS zone 9"|Projected|[-93.35, 41.16, -92.29, 42.3]|Some([19353134.452, 7531549.889, 19645029.722, 7947424.226])| + EPSG_7066|7066|"NAD83(2011) / IaRCS zone 10"|Projected|[-92.31, 41.42, -90.89, 42.3]|Some([20323544.034, 7849382.096, 20713026.239, 8171016.513])| + EPSG_7067|7067|"NAD83(2011) / IaRCS zone 11"|Projected|[-91.14, 41.44, -90.14, 42.68]|Some([21333653.363, 8033575.85, 21607850.969, 8486050.289])| + EPSG_7068|7068|"NAD83(2011) / IaRCS zone 12"|Projected|[-95.39, 40.57, -92.17, 41.17]|Some([22044377.349, 6073693.848, 22938955.377, 6296538.784])| + EPSG_7069|7069|"NAD83(2011) / IaRCS zone 13"|Projected|[-92.42, 40.59, -91.48, 41.52]|Some([23360202.684, 6523868.782, 23621281.055, 6863125.068])| + EPSG_7070|7070|"NAD83(2011) / IaRCS zone 14"|Projected|[-91.72, 40.36, -90.78, 41.6]|Some([24369014.885, 6240074.312, 24630985.115, 6692224.18])| + EPSG_7073|7073|"RGTAAF07"|Geographic2d|[37.98, -67.13, 142.0, -20.91]|Some([37.98, -67.13, 142.0, -20.91])| + EPSG_7074|7074|"RGTAAF07 / UTM zone 37S"|Projected|[37.98, -25.7, 41.82, -20.91]|Some([393924.991, 7154516.748, 793343.449, 7687812.851])| + EPSG_7075|7075|"RGTAAF07 / UTM zone 38S"|Projected|[45.37, -49.38, 48.01, -43.12]|Some([526856.228, 4525942.436, 744869.712, 5225792.838])| + EPSG_7076|7076|"RGTAAF07 / UTM zone 39S"|Projected|[48.0, -49.82, 54.01, -42.61]|Some([253924.577, 4477035.381, 746895.739, 5282492.548])| + EPSG_7077|7077|"RGTAAF07 / UTM zone 40S"|Projected|[54.0, -49.61, 57.16, -43.3]|Some([256661.191, 4500406.602, 512977.71, 5205868.592])| + EPSG_7078|7078|"RGTAAF07 / UTM zone 41S"|Projected|[62.96, -53.03, 66.0, -45.73]|Some([496887.677, 4120182.861, 733421.641, 4935949.87])| + EPSG_7079|7079|"RGTAAF07 / UTM zone 42S"|Projected|[66.0, -53.24, 72.01, -45.11]|Some([264007.247, 4096802.47, 736779.393, 5004829.864])| + EPSG_7080|7080|"RGTAAF07 / UTM zone 43S"|Projected|[72.0, -51.19, 78.01, -34.47]|Some([224446.042, 4324739.287, 776472.776, 6185729.345])| + EPSG_7081|7081|"RGTAAF07 / UTM zone 44S"|Projected|[78.0, -42.04, 81.83, -34.5]|Some([224544.801, 5341427.298, 576197.657, 6182400.912])| + EPSG_7084|7084|"RGF93 v1 (lon-lat)"|Geographic2d|[-9.86, 41.15, 10.38, 51.56]|Some([-9.86, 41.15, 10.38, 51.56])| + EPSG_7086|7086|"RGAF09 (lon-lat)"|Geographic2d|[-63.66, 14.08, -57.52, 18.53]|Some([-63.66, 14.08, -57.52, 18.53])| + EPSG_7109|7109|"NAD83(2011) / RMTCRS St Mary (m)"|Projected|[-113.97, 48.55, -113.0, 49.01]|Some([41455.899, 5681.663, 113415.64, 57765.196])| + EPSG_7110|7110|"NAD83(2011) / RMTCRS Blackfeet (m)"|Projected|[-113.84, 48.0, -112.0, 49.01]|Some([-16.032, 0.042, 137319.717, 113198.987])| + EPSG_7111|7111|"NAD83(2011) / RMTCRS Milk River (m)"|Projected|[-112.5, 47.78, -108.74, 49.01]|Some([37568.98, 119927.314, 319382.284, 259165.966])| + EPSG_7112|7112|"NAD83(2011) / RMTCRS Fort Belknap (m)"|Projected|[-110.84, 47.78, -106.99, 49.01]|Some([24628.104, 69929.749, 313177.634, 209340.447])| + EPSG_7113|7113|"NAD83(2011) / RMTCRS Fort Peck Assiniboine (m)"|Projected|[-107.57, 48.01, -104.78, 48.88]|Some([45553.144, 64043.159, 253726.379, 162847.759])| + EPSG_7114|7114|"NAD83(2011) / RMTCRS Fort Peck Sioux (m)"|Projected|[-107.76, 47.75, -104.04, 49.01]|Some([-69465.958, -14868.062, 209487.24, 127692.231])| + EPSG_7115|7115|"NAD83(2011) / RMTCRS Crow (m)"|Projected|[-108.84, 44.99, -106.66, 46.09]|Some([114029.269, 26674.964, 285970.731, 149527.414])| + EPSG_7116|7116|"NAD83(2011) / RMTCRS Bobcat (m)"|Projected|[-112.34, 45.36, -110.75, 46.59]|Some([14573.508, 1056.729, 139187.438, 138375.527])| + EPSG_7117|7117|"NAD83(2011) / RMTCRS Billings (m)"|Projected|[-109.42, 44.99, -107.99, 47.41]|Some([120859.147, -38186.692, 233655.306, 231352.342])| + EPSG_7118|7118|"NAD83(2011) / RMTCRS Wind River (m)"|Projected|[-109.5, 42.69, -107.94, 43.86]|Some([4368.533, 2592.889, 132241.303, 133270.483])| + EPSG_7119|7119|"NAD83(2011) / RMTCRS St Mary (ft)"|Projected|[-113.97, 48.55, -113.0, 49.01]|Some([136010.167, 18640.628, 372098.556, 189518.361])| + EPSG_7120|7120|"NAD83(2011) / RMTCRS Blackfeet (ft)"|Projected|[-113.84, 48.0, -112.0, 49.01]|Some([-52.598, 0.138, 450524.007, 371387.753])| + EPSG_7121|7121|"NAD83(2011) / RMTCRS Milk River (ft)"|Projected|[-112.5, 47.78, -108.74, 49.01]|Some([123257.809, 393462.317, 1047842.141, 850282.042])| + EPSG_7122|7122|"NAD83(2011) / RMTCRS Fort Belknap (ft)"|Projected|[-110.84, 47.78, -106.99, 49.01]|Some([80800.867, 229428.309, 1027485.675, 686812.489])| + EPSG_7123|7123|"NAD83(2011) / RMTCRS Fort Peck Assiniboine (ft)"|Projected|[-107.57, 48.01, -104.78, 48.88]|Some([149452.573, 210115.351, 832435.625, 534277.423])| + EPSG_7124|7124|"NAD83(2011) / RMTCRS Fort Peck Sioux (ft)"|Projected|[-107.76, 47.75, -104.04, 49.01]|Some([-227906.686, -48779.732, 687294.093, 418937.765])| + EPSG_7125|7125|"NAD83(2011) / RMTCRS Crow (ft)"|Projected|[-108.84, 44.99, -106.66, 46.09]|Some([374111.777, 87516.287, 938224.181, 490575.505])| + EPSG_7126|7126|"NAD83(2011) / RMTCRS Bobcat (ft)"|Projected|[-112.34, 45.36, -110.75, 46.59]|Some([47813.348, 3466.957, 456651.699, 453987.949])| + EPSG_7127|7127|"NAD83(2011) / RMTCRS Billings (ft)"|Projected|[-109.42, 44.99, -107.99, 47.41]|Some([396519.512, -125284.422, 766585.649, 759029.994])| + EPSG_7128|7128|"NAD83(2011) / RMTCRS Wind River (ftUS)"|Projected|[-109.5, 42.69, -107.94, 43.86]|Some([14332.429, 8506.836, 433861.676, 437238.242])| + EPSG_7131|7131|"NAD83(2011) / San Francisco CS13"|Projected|[-123.56, 36.85, -121.2, 38.87]|Some([-50999.148, -75885.616, 159486.057, 149066.21])| + EPSG_7132|7132|"NAD83(2011) / San Francisco CS13 (ftUS)"|Projected|[-123.56, 36.85, -121.2, 38.87]|Some([-167319.704, -248968.058, 523247.171, 489061.389])| + EPSG_7133|7133|"RGTAAF07 (lon-lat)"|Geographic2d|[37.98, -67.13, 142.0, -20.91]|Some([37.98, -67.13, 142.0, -20.91])| + EPSG_7136|7136|"IGD05"|Geographic2d|[32.99, 29.45, 35.69, 33.53]|Some([32.99, 29.45, 35.69, 33.53])| + EPSG_7139|7139|"IGD05/12"|Geographic2d|[32.99, 29.45, 35.69, 33.53]|Some([32.99, 29.45, 35.69, 33.53])| + EPSG_7142|7142|"Palestine 1923 / Palestine Grid modified"|Projected|[34.17, 29.18, 39.31, 33.38]|Some([68881.905, -156267.991, 569049.626, 316899.422])| + EPSG_7257|7257|"NAD83(2011) / InGCS Adams (m)"|Projected|[-85.08, 40.56, -84.8, 40.93]|Some([228989.639, 37110.491, 252704.264, 78210.846])| + EPSG_7258|7258|"NAD83(2011) / InGCS Adams (ftUS)"|Projected|[-85.08, 40.56, -84.8, 40.93]|Some([751276.84, 121753.336, 829080.571, 256596.751])| + EPSG_7259|7259|"NAD83(2011) / InGCS Allen (m)"|Projected|[-85.34, 40.91, -84.8, 41.28]|Some([215566.865, 37110.557, 261063.044, 78243.021])| + EPSG_7260|7260|"NAD83(2011) / InGCS Allen (ftUS)"|Projected|[-85.34, 40.91, -84.8, 41.28]|Some([707238.956, 121753.553, 856504.337, 256702.31])| + EPSG_7261|7261|"NAD83(2011) / InGCS Bartholomew (m)"|Projected|[-86.09, 39.03, -85.68, 39.36]|Some([219217.892, 39330.577, 254720.655, 75995.325])| + EPSG_7262|7262|"NAD83(2011) / InGCS Bartholomew (ftUS)"|Projected|[-86.09, 39.03, -85.68, 39.36]|Some([719217.368, 129037.067, 835696.016, 249327.995])| + EPSG_7263|7263|"NAD83(2011) / InGCS Benton (m)"|Projected|[-87.53, 40.47, -87.09, 40.74]|Some([220494.158, 38220.945, 257809.68, 68229.748])| + EPSG_7264|7264|"NAD83(2011) / InGCS Benton (ftUS)"|Projected|[-87.53, 40.47, -87.09, 40.74]|Some([723404.584, 125396.549, 845830.593, 223850.431])| + EPSG_7265|7265|"NAD83(2011) / InGCS Blackford-Delaware (m)"|Projected|[-85.58, 40.07, -85.2, 40.57]|Some([224644.225, 38220.804, 257061.974, 93762.53])| + EPSG_7266|7266|"NAD83(2011) / InGCS Blackford-Delaware (ftUS)"|Projected|[-85.58, 40.07, -85.2, 40.57]|Some([737020.261, 125396.086, 843377.492, 307619.233])| + EPSG_7267|7267|"NAD83(2011) / InGCS Boone-Hendricks (m)"|Projected|[-86.7, 39.6, -86.24, 40.19]|Some([222821.369, 36000.0, 262332.227, 101544.022])| + EPSG_7268|7268|"NAD83(2011) / InGCS Boone-Hendricks (ftUS)"|Projected|[-86.7, 39.6, -86.24, 40.19]|Some([731039.774, 118110.001, 860668.314, 333149.013])| + EPSG_7269|7269|"NAD83(2011) / InGCS Brown (m)"|Projected|[-86.39, 39.04, -86.07, 39.35]|Some([232207.781, 40440.778, 259913.458, 74882.985])| + EPSG_7270|7270|"NAD83(2011) / InGCS Brown (ftUS)"|Projected|[-86.39, 39.04, -86.07, 39.35]|Some([761835.028, 132679.453, 852732.738, 245678.594])| + EPSG_7271|7271|"NAD83(2011) / InGCS Carroll (m)"|Projected|[-86.78, 40.43, -86.37, 40.74]|Some([228968.457, 39331.387, 263760.259, 73794.203])| + EPSG_7272|7272|"NAD83(2011) / InGCS Carroll (ftUS)"|Projected|[-86.78, 40.43, -86.37, 40.74]|Some([751207.345, 129039.724, 865353.451, 242106.482])| + EPSG_7273|7273|"NAD83(2011) / InGCS Cass (m)"|Projected|[-86.59, 40.56, -86.16, 40.92]|Some([223908.028, 37110.491, 260326.705, 77116.938])| + EPSG_7274|7274|"NAD83(2011) / InGCS Cass (ftUS)"|Projected|[-86.59, 40.56, -86.16, 40.92]|Some([734604.921, 121753.335, 854088.533, 253007.82])| + EPSG_7275|7275|"NAD83(2011) / InGCS Clark-Floyd-Scott (m)"|Projected|[-86.04, 38.17, -85.41, 38.84]|Some([201442.201, 38220.037, 256649.927, 112687.652])| + EPSG_7276|7276|"NAD83(2011) / InGCS Clark-Floyd-Scott (ftUS)"|Projected|[-86.04, 38.17, -85.41, 38.84]|Some([660898.288, 125393.572, 842025.635, 369709.403])| + EPSG_7277|7277|"NAD83(2011) / InGCS Clay (m)"|Projected|[-87.25, 39.16, -86.93, 39.61]|Some([231356.695, 37110.219, 259015.279, 87094.814])| + EPSG_7278|7278|"NAD83(2011) / InGCS Clay (ftUS)"|Projected|[-87.25, 39.16, -86.93, 39.61]|Some([759042.757, 121752.442, 849785.96, 285743.569])| + EPSG_7279|7279|"NAD83(2011) / InGCS Clinton (m)"|Projected|[-86.7, 40.17, -86.24, 40.44]|Some([231481.555, 38220.851, 270666.433, 68264.964])| + EPSG_7280|7280|"NAD83(2011) / InGCS Clinton (ftUS)"|Projected|[-86.7, 40.17, -86.24, 40.44]|Some([759452.402, 125396.242, 888011.456, 223965.968])| + EPSG_7281|7281|"NAD83(2011) / InGCS Crawford-Lawrence-Orange (m)"|Projected|[-86.69, 38.1, -86.24, 39.0]|Some([223334.095, 36000.012, 262805.984, 135940.882])| + EPSG_7282|7282|"NAD83(2011) / InGCS Crawford-Lawrence-Orange (ftUS)"|Projected|[-86.69, 38.1, -86.24, 39.0]|Some([732721.944, 118110.039, 862222.632, 445999.377])| + EPSG_7283|7283|"NAD83(2011) / InGCS Daviess-Greene (m)"|Projected|[-87.28, 38.49, -86.68, 39.18]|Some([224295.643, 40440.295, 276643.561, 117124.221])| + EPSG_7284|7284|"NAD83(2011) / InGCS Daviess-Greene (ftUS)"|Projected|[-87.28, 38.49, -86.68, 39.18]|Some([735876.622, 132677.869, 907621.417, 384265.05])| + EPSG_7285|7285|"NAD83(2011) / InGCS Dearborn-Ohio-Switzerland (m)"|Projected|[-85.21, 38.68, -84.78, 39.31]|Some([213024.464, 39330.383, 250442.133, 109317.916])| + EPSG_7286|7286|"NAD83(2011) / InGCS Dearborn-Ohio-Switzerland (ftUS)"|Projected|[-85.21, 38.68, -84.78, 39.31]|Some([698897.761, 129036.432, 821658.9, 358653.862])| + EPSG_7287|7287|"NAD83(2011) / InGCS Decatur-Rush (m)"|Projected|[-85.69, 39.13, -85.29, 39.79]|Some([236541.169, 39330.65, 271129.522, 112671.31])| + EPSG_7288|7288|"NAD83(2011) / InGCS Decatur-Rush (ftUS)"|Projected|[-85.69, 39.13, -85.29, 39.79]|Some([776052.152, 129037.308, 889530.772, 369655.789])| + EPSG_7289|7289|"NAD83(2011) / InGCS DeKalb (m)"|Projected|[-85.2, 41.26, -84.8, 41.54]|Some([219048.313, 37110.634, 252571.009, 68239.191])| + EPSG_7290|7290|"NAD83(2011) / InGCS DeKalb (ftUS)"|Projected|[-85.2, 41.26, -84.8, 41.54]|Some([718661.006, 121753.804, 828643.385, 223881.414])| + EPSG_7291|7291|"NAD83(2011) / InGCS Dubois-Martin (m)"|Projected|[-87.08, 38.2, -86.67, 38.91]|Some([228612.63, 36000.021, 264526.662, 114853.826])| + EPSG_7292|7292|"NAD83(2011) / InGCS Dubois-Martin (ftUS)"|Projected|[-87.08, 38.2, -86.67, 38.91]|Some([750039.936, 118110.069, 867867.89, 376816.259])| + EPSG_7293|7293|"NAD83(2011) / InGCS Elkhart-Kosciusko-Wabash (m)"|Projected|[-86.08, 40.65, -85.63, 41.77]|Some([220546.256, 36000.012, 258607.928, 160414.606])| + EPSG_7294|7294|"NAD83(2011) / InGCS Elkhart-Kosciusko-Wabash (ftUS)"|Projected|[-86.08, 40.65, -85.63, 41.77]|Some([723575.509, 118110.039, 848449.511, 526293.588])| + EPSG_7295|7295|"NAD83(2011) / InGCS Fayette-Franklin-Union (m)"|Projected|[-85.31, 39.26, -84.81, 39.79]|Some([217558.928, 37110.267, 260714.833, 95988.347])| + EPSG_7296|7296|"NAD83(2011) / InGCS Fayette-Franklin-Union (ftUS)"|Projected|[-85.31, 39.26, -84.81, 39.79]|Some([713774.583, 121752.601, 855361.916, 314921.767])| + EPSG_7297|7297|"NAD83(2011) / InGCS Fountain-Warren (m)"|Projected|[-87.54, 39.95, -87.09, 40.48]|Some([219490.011, 36000.0, 257946.238, 94879.689])| + EPSG_7298|7298|"NAD83(2011) / InGCS Fountain-Warren (ftUS)"|Projected|[-87.54, 39.95, -87.09, 40.48]|Some([720110.144, 118110.0, 846278.617, 311284.445])| + EPSG_7299|7299|"NAD83(2011) / InGCS Fulton-Marshall-St. Joseph (m)"|Projected|[-86.53, 40.9, -85.94, 41.77]|Some([220619.082, 36000.017, 270335.368, 132688.182])| + EPSG_7300|7300|"NAD83(2011) / InGCS Fulton-Marshall-St. Joseph (ftUS)"|Projected|[-86.53, 40.9, -85.94, 41.77]|Some([723814.437, 118110.057, 886925.285, 435327.809])| + EPSG_7301|7301|"NAD83(2011) / InGCS Gibson (m)"|Projected|[-87.99, 38.16, -87.31, 38.54]|Some([210201.533, 37110.009, 269798.467, 79346.54])| + EPSG_7302|7302|"NAD83(2011) / InGCS Gibson (ftUS)"|Projected|[-87.99, 38.16, -87.31, 38.54]|Some([689636.197, 121751.753, 885163.803, 260322.772])| + EPSG_7303|7303|"NAD83(2011) / InGCS Grant (m)"|Projected|[-85.87, 40.37, -85.44, 40.66]|Some([225561.206, 38220.909, 262082.868, 70457.426])| + EPSG_7304|7304|"NAD83(2011) / InGCS Grant (ftUS)"|Projected|[-85.87, 40.37, -85.44, 40.66]|Some([740028.724, 125396.431, 859850.21, 231159.072])| + EPSG_7305|7305|"NAD83(2011) / InGCS Hamilton-Tipton (m)"|Projected|[-86.25, 39.92, -85.86, 40.41]|Some([218625.904, 38220.738, 251969.489, 92661.121])| + EPSG_7306|7306|"NAD83(2011) / InGCS Hamilton-Tipton (ftUS)"|Projected|[-86.25, 39.92, -85.86, 40.41]|Some([717275.155, 125395.871, 826669.898, 304005.694])| + EPSG_7307|7307|"NAD83(2011) / InGCS Hancock-Madison (m)"|Projected|[-85.96, 39.69, -85.57, 40.38]|Some([226274.901, 40441.299, 259729.834, 117083.81])| + EPSG_7308|7308|"NAD83(2011) / InGCS Hancock-Madison (ftUS)"|Projected|[-85.96, 39.69, -85.57, 40.38]|Some([742370.239, 132681.163, 852130.298, 384132.467])| + EPSG_7309|7309|"NAD83(2011) / InGCS Harrison-Washington (m)"|Projected|[-86.33, 37.95, -85.84, 38.79]|Some([224178.995, 36000.0, 267247.308, 129291.1])| + EPSG_7310|7310|"NAD83(2011) / InGCS Harrison-Washington (ftUS)"|Projected|[-86.33, 37.95, -85.84, 38.79]|Some([735493.92, 118110.0, 876793.875, 424182.551])| + EPSG_7311|7311|"NAD83(2011) / InGCS Henry (m)"|Projected|[-85.6, 39.78, -85.2, 40.08]|Some([227149.354, 39331.052, 261417.751, 72672.42])| + EPSG_7312|7312|"NAD83(2011) / InGCS Henry (ftUS)"|Projected|[-85.6, 39.78, -85.2, 40.08]|Some([745239.172, 129038.627, 857668.072, 238426.097])| + EPSG_7313|7313|"NAD83(2011) / InGCS Howard-Miami (m)"|Projected|[-86.38, 40.37, -85.86, 41.0]|Some([220465.216, 38220.91, 264630.821, 108223.711])| + EPSG_7314|7314|"NAD83(2011) / InGCS Howard-Miami (ftUS)"|Projected|[-86.38, 40.37, -85.86, 41.0]|Some([723309.629, 125396.437, 868209.619, 355063.958])| + EPSG_7315|7315|"NAD83(2011) / InGCS Huntington-Whitley (m)"|Projected|[-85.69, 40.65, -85.3, 41.3]|Some([223929.502, 36000.012, 256916.314, 108206.477])| + EPSG_7316|7316|"NAD83(2011) / InGCS Huntington-Whitley (ftUS)"|Projected|[-85.69, 40.65, -85.3, 41.3]|Some([734675.375, 118110.039, 842899.607, 355007.418])| + EPSG_7317|7317|"NAD83(2011) / InGCS Jackson (m)"|Projected|[-86.32, 38.72, -85.79, 39.08]|Some([207821.532, 38220.248, 253914.996, 78251.183])| + EPSG_7318|7318|"NAD83(2011) / InGCS Jackson (ftUS)"|Projected|[-86.32, 38.72, -85.79, 39.08]|Some([681827.81, 125394.265, 833052.782, 256729.091])| + EPSG_7319|7319|"NAD83(2011) / InGCS Jasper-Porter (m)"|Projected|[-87.28, 40.73, -86.92, 41.77]|Some([224793.621, 39331.542, 255206.379, 154851.419])| + EPSG_7320|7320|"NAD83(2011) / InGCS Jasper-Porter (ftUS)"|Projected|[-87.28, 40.73, -86.92, 41.77]|Some([737510.406, 129040.234, 837289.594, 508041.698])| + EPSG_7321|7321|"NAD83(2011) / InGCS Jay (m)"|Projected|[-85.22, 40.3, -84.8, 40.58]|Some([221295.106, 36000.008, 257004.448, 67116.513])| + EPSG_7322|7322|"NAD83(2011) / InGCS Jay (ftUS)"|Projected|[-85.22, 40.3, -84.8, 40.58]|Some([726032.359, 118110.025, 843188.76, 220198.094])| + EPSG_7323|7323|"NAD83(2011) / InGCS Jefferson (m)"|Projected|[-85.69, 38.58, -85.2, 38.92]|Some([210372.825, 39330.313, 253070.799, 77129.974])| + EPSG_7324|7324|"NAD83(2011) / InGCS Jefferson (ftUS)"|Projected|[-85.69, 38.58, -85.2, 38.92]|Some([690198.177, 129036.203, 830283.112, 253050.589])| + EPSG_7325|7325|"NAD83(2011) / InGCS Jennings (m)"|Projected|[-85.8, 38.8, -85.43, 39.2]|Some([240000.0, 36000.0, 272142.658, 80472.53])| + EPSG_7326|7326|"NAD83(2011) / InGCS Jennings (ftUS)"|Projected|[-85.8, 38.8, -85.43, 39.2]|Some([787400.0, 118110.0, 892854.704, 264016.958])| + EPSG_7327|7327|"NAD83(2011) / InGCS Johnson-Marion (m)"|Projected|[-86.33, 39.34, -85.93, 39.93]|Some([224481.661, 40441.023, 258966.861, 105972.508])| + EPSG_7328|7328|"NAD83(2011) / InGCS Johnson-Marion (ftUS)"|Projected|[-86.33, 39.34, -85.93, 39.93]|Some([736486.917, 132680.255, 849627.111, 347678.135])| + EPSG_7329|7329|"NAD83(2011) / InGCS Knox (m)"|Projected|[-87.76, 38.41, -87.09, 38.91]|Some([212923.79, 37110.062, 271443.353, 92677.008])| + EPSG_7330|7330|"NAD83(2011) / InGCS Knox (ftUS)"|Projected|[-87.76, 38.41, -87.09, 38.91]|Some([698567.468, 121751.927, 890560.401, 304057.817])| + EPSG_7331|7331|"NAD83(2011) / InGCS LaGrange-Noble (m)"|Projected|[-85.66, 41.26, -85.19, 41.77]|Some([222400.567, 37110.649, 261789.777, 93787.978])| + EPSG_7332|7332|"NAD83(2011) / InGCS LaGrange-Noble (ftUS)"|Projected|[-85.66, 41.26, -85.19, 41.77]|Some([729659.194, 121753.854, 858888.628, 307702.726])| + EPSG_7333|7333|"NAD83(2011) / InGCS Lake-Newton (m)"|Projected|[-87.53, 40.73, -87.21, 41.77]|Some([229017.628, 39331.541, 256051.162, 154853.089])| + EPSG_7334|7334|"NAD83(2011) / InGCS Lake-Newton (ftUS)"|Projected|[-87.53, 40.73, -87.21, 41.77]|Some([751368.667, 129040.23, 840061.187, 508047.176])| + EPSG_7335|7335|"NAD83(2011) / InGCS LaPorte-Pulaski-Starke (m)"|Projected|[-86.94, 40.9, -86.46, 41.77]|Some([223989.742, 36000.002, 264436.718, 132665.801])| + EPSG_7336|7336|"NAD83(2011) / InGCS LaPorte-Pulaski-Starke (ftUS)"|Projected|[-86.94, 40.9, -86.46, 41.77]|Some([734873.012, 118110.006, 867572.799, 435254.383])| + EPSG_7337|7337|"NAD83(2011) / InGCS Monroe-Morgan (m)"|Projected|[-86.69, 38.99, -86.24, 39.64]|Some([223538.199, 40440.733, 262526.682, 112639.024])| + EPSG_7338|7338|"NAD83(2011) / InGCS Monroe-Morgan (ftUS)"|Projected|[-86.69, 38.99, -86.24, 39.64]|Some([733391.576, 132679.303, 861306.289, 369549.865])| + EPSG_7339|7339|"NAD83(2011) / InGCS Montgomery-Putnam (m)"|Projected|[-87.1, 39.47, -86.64, 40.22]|Some([227092.039, 38220.56, 266676.471, 121542.975])| + EPSG_7340|7340|"NAD83(2011) / InGCS Montgomery-Putnam (ftUS)"|Projected|[-87.1, 39.47, -86.64, 40.22]|Some([745051.133, 125395.286, 874921.056, 398762.245])| + EPSG_7341|7341|"NAD83(2011) / InGCS Owen (m)"|Projected|[-87.06, 39.16, -86.63, 39.48]|Some([226170.682, 37110.216, 263336.986, 72672.85])| + EPSG_7342|7342|"NAD83(2011) / InGCS Owen (ftUS)"|Projected|[-87.06, 39.16, -86.63, 39.48]|Some([742028.313, 121752.433, 863964.76, 238427.507])| + EPSG_7343|7343|"NAD83(2011) / InGCS Parke-Vermillion (m)"|Projected|[-87.54, 39.6, -87.0, 40.15]|Some([223680.529, 36000.031, 270062.208, 97127.806])| + EPSG_7344|7344|"NAD83(2011) / InGCS Parke-Vermillion (ftUS)"|Projected|[-87.54, 39.6, -87.0, 40.15]|Some([733858.537, 118110.1, 886029.093, 318660.142])| + EPSG_7345|7345|"NAD83(2011) / InGCS Perry (m)"|Projected|[-86.82, 37.84, -86.43, 38.27]|Some([229437.033, 40439.816, 263766.693, 88204.181])| + EPSG_7346|7346|"NAD83(2011) / InGCS Perry (ftUS)"|Projected|[-86.82, 37.84, -86.43, 38.27]|Some([752744.666, 132676.296, 865374.559, 289383.218])| + EPSG_7347|7347|"NAD83(2011) / InGCS Pike-Warrick (m)"|Projected|[-87.48, 37.87, -87.01, 38.56]|Some([224162.045, 38219.938, 265516.722, 114851.314])| + EPSG_7348|7348|"NAD83(2011) / InGCS Pike-Warrick (ftUS)"|Projected|[-87.48, 37.87, -87.01, 38.56]|Some([735438.309, 125393.245, 871116.113, 376808.019])| + EPSG_7349|7349|"NAD83(2011) / InGCS Posey (m)"|Projected|[-88.1, 37.77, -87.68, 38.24]|Some([226783.914, 38219.875, 263788.97, 90423.408])| + EPSG_7350|7350|"NAD83(2011) / InGCS Posey (ftUS)"|Projected|[-88.1, 37.77, -87.68, 38.24]|Some([744040.224, 125393.041, 865447.647, 296664.131])| + EPSG_7351|7351|"NAD83(2011) / InGCS Randolph-Wayne (m)"|Projected|[-85.23, 39.71, -84.8, 40.32]|Some([224563.596, 37110.358, 261439.456, 104874.619])| + EPSG_7352|7352|"NAD83(2011) / InGCS Randolph-Wayne (ftUS)"|Projected|[-85.23, 39.71, -84.8, 40.32]|Some([736755.731, 121752.899, 857739.283, 344076.145])| + EPSG_7353|7353|"NAD83(2011) / InGCS Ripley (m)"|Projected|[-85.45, 38.91, -85.06, 39.32]|Some([226989.097, 37110.196, 260817.453, 82656.624])| + EPSG_7354|7354|"NAD83(2011) / InGCS Ripley (ftUS)"|Projected|[-85.45, 38.91, -85.06, 39.32]|Some([744713.394, 121752.368, 855698.595, 271182.607])| + EPSG_7355|7355|"NAD83(2011) / InGCS Shelby (m)"|Projected|[-85.96, 39.34, -85.62, 39.7]|Some([234827.227, 40441.002, 264139.625, 80448.832])| + EPSG_7356|7356|"NAD83(2011) / InGCS Shelby (ftUS)"|Projected|[-85.96, 39.34, -85.62, 39.7]|Some([770428.994, 132680.188, 866598.086, 263939.21])| + EPSG_7357|7357|"NAD83(2011) / InGCS Spencer (m)"|Projected|[-87.27, 37.78, -86.76, 38.21]|Some([220618.992, 39329.808, 265547.704, 87098.68])| + EPSG_7358|7358|"NAD83(2011) / InGCS Spencer (ftUS)"|Projected|[-87.27, 37.78, -86.76, 38.21]|Some([723814.143, 129034.545, 871217.759, 285756.252])| + EPSG_7359|7359|"NAD83(2011) / InGCS Steuben (m)"|Projected|[-85.2, 41.52, -84.8, 41.77]|Some([223305.216, 38221.367, 256694.784, 66008.439])| + EPSG_7360|7360|"NAD83(2011) / InGCS Steuben (ftUS)"|Projected|[-85.2, 41.52, -84.8, 41.77]|Some([732627.198, 125397.934, 842172.802, 216562.687])| + EPSG_7361|7361|"NAD83(2011) / InGCS Sullivan (m)"|Projected|[-87.66, 38.9, -87.24, 39.26]|Some([226120.047, 36000.017, 262554.934, 75999.025])| + EPSG_7362|7362|"NAD83(2011) / InGCS Sullivan (ftUS)"|Projected|[-87.66, 38.9, -87.24, 39.26]|Some([741862.187, 118110.056, 861398.979, 249340.133])| + EPSG_7363|7363|"NAD83(2011) / InGCS Tippecanoe-White (m)"|Projected|[-87.1, 40.21, -86.58, 40.92]|Some([222973.213, 37110.445, 267242.873, 116004.085])| + EPSG_7364|7364|"NAD83(2011) / InGCS Tippecanoe-White (ftUS)"|Projected|[-87.1, 40.21, -86.58, 40.92]|Some([731537.951, 121753.186, 876779.326, 380590.068])| + EPSG_7365|7365|"NAD83(2011) / InGCS Vanderburgh (m)"|Projected|[-87.71, 37.82, -87.44, 38.17]|Some([225912.31, 38219.893, 249685.285, 77081.306])| + EPSG_7366|7366|"NAD83(2011) / InGCS Vanderburgh (ftUS)"|Projected|[-87.71, 37.82, -87.44, 38.17]|Some([741180.638, 125393.099, 819175.806, 252890.919])| + EPSG_7367|7367|"NAD83(2011) / InGCS Vigo (m)"|Projected|[-87.63, 39.25, -87.19, 39.61]|Some([224461.952, 36000.008, 262443.856, 76001.632])| + EPSG_7368|7368|"NAD83(2011) / InGCS Vigo (ftUS)"|Projected|[-87.63, 39.25, -87.19, 39.61]|Some([736422.253, 118110.025, 861034.551, 249348.687])| + EPSG_7369|7369|"NAD83(2011) / InGCS Wells (m)"|Projected|[-85.45, 40.56, -85.06, 40.92]|Some([223060.98, 37110.503, 256092.069, 77108.71])| + EPSG_7370|7370|"NAD83(2011) / InGCS Wells (ftUS)"|Projected|[-85.45, 40.56, -85.06, 40.92]|Some([731825.897, 121753.375, 840195.396, 252980.825])| + EPSG_7373|7373|"ONGD14"|Geographic2d|[51.99, 14.33, 63.38, 26.74]|Some([51.99, 14.33, 63.38, 26.74])| + EPSG_7374|7374|"ONGD14 / UTM zone 39N"|Projected|[51.99, 14.94, 54.01, 19.67]|Some([603777.292, 1651926.895, 823817.576, 2177756.624])| + EPSG_7375|7375|"ONGD14 / UTM zone 40N"|Projected|[54.0, 14.33, 60.01, 26.74]|Some([176364.653, 1584223.174, 824715.007, 2961179.095])| + EPSG_7376|7376|"ONGD14 / UTM zone 41N"|Projected|[60.0, 16.37, 63.38, 24.09]|Some([179498.591, 1809863.885, 540581.387, 2667452.211])| + EPSG_7528|7528|"NAD83(2011) / WISCRS Adams and Juneau (m)"|Projected|[-90.32, 43.64, -89.59, 44.25]|Some([121397.084, 30369.15, 180302.638, 98231.191])| + EPSG_7529|7529|"NAD83(2011) / WISCRS Ashland (m)"|Projected|[-90.93, 45.98, -90.3, 47.09]|Some([148970.697, 30443.807, 197792.567, 153889.848])| + EPSG_7530|7530|"NAD83(2011) / WISCRS Barron (m)"|Projected|[-92.16, 45.2, -91.53, 45.65]|Some([68791.48, 7409.378, 118294.279, 57474.65])| + EPSG_7531|7531|"NAD83(2011) / WISCRS Bayfield (m)"|Projected|[-91.56, 46.15, -90.75, 47.01]|Some([197138.843, 90785.024, 259718.701, 186469.122])| + EPSG_7532|7532|"NAD83(2011) / WISCRS Brown (m)"|Projected|[-88.26, 44.24, -87.76, 44.68]|Some([10830.217, 142372.763, 50772.107, 191299.947])| + EPSG_7533|7533|"NAD83(2011) / WISCRS Buffalo (m)"|Projected|[-92.09, 44.02, -91.52, 44.6]|Some([151784.747, 59845.992, 197488.673, 124338.744])| + EPSG_7534|7534|"NAD83(2011) / WISCRS Burnett (m)"|Projected|[-92.89, 45.63, -92.03, 46.16]|Some([30302.806, 29577.877, 97366.869, 88579.887])| + EPSG_7535|7535|"NAD83(2011) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (m)"|Projected|[-88.89, 43.54, -88.04, 44.6]|Some([213232.899, 91162.476, 281934.679, 209049.2])| + EPSG_7536|7536|"NAD83(2011) / WISCRS Chippewa (m)"|Projected|[-91.67, 44.85, -90.92, 45.3]|Some([30356.065, 29882.118, 89647.537, 79962.474])| + EPSG_7537|7537|"NAD83(2011) / WISCRS Clark (m)"|Projected|[-90.93, 44.42, -90.31, 45.04]|Some([182295.239, 91116.442, 231673.236, 160095.25])| + EPSG_7538|7538|"NAD83(2011) / WISCRS Columbia (m)"|Projected|[-89.79, 43.28, -89.0, 43.65]|Some([137056.035, 91287.942, 201182.45, 132472.895])| + EPSG_7539|7539|"NAD83(2011) / WISCRS Crawford (m)"|Projected|[-91.22, 42.98, -90.66, 43.43]|Some([90760.196, 29255.242, 136439.791, 79288.87])| + EPSG_7540|7540|"NAD83(2011) / WISCRS Dane (m)"|Projected|[-89.84, 42.84, -89.0, 43.3]|Some([213037.586, 121093.581, 281712.358, 172285.176])| + EPSG_7541|7541|"NAD83(2011) / WISCRS Dodge and Jefferson (m)"|Projected|[-89.02, 42.84, -88.4, 43.64]|Some([243317.733, 151932.999, 294005.889, 240882.346])| + EPSG_7542|7542|"NAD83(2011) / WISCRS Door (m)"|Projected|[-87.74, 44.67, -86.8, 45.43]|Some([121706.52, 30003.694, 196248.158, 114574.658])| + EPSG_7543|7543|"NAD83(2011) / WISCRS Douglas (m)"|Projected|[-92.3, 46.15, -91.55, 46.76]|Some([29516.264, 29641.619, 87458.762, 97523.293])| + EPSG_7544|7544|"NAD83(2011) / WISCRS Dunn (m)"|Projected|[-92.16, 44.68, -91.64, 45.21]|Some([30760.805, 30189.637, 71990.428, 89125.622])| + EPSG_7545|7545|"NAD83(2011) / WISCRS Eau Claire (m)"|Projected|[-91.66, 44.59, -90.92, 44.86]|Some([90621.289, 60317.778, 149385.125, 90390.108])| + EPSG_7546|7546|"NAD83(2011) / WISCRS Florence (m)"|Projected|[-88.69, 45.71, -88.05, 46.03]|Some([90803.26, 30133.793, 140640.87, 65849.557])| + EPSG_7547|7547|"NAD83(2011) / WISCRS Forest (m)"|Projected|[-89.05, 45.37, -88.42, 46.08]|Some([243201.642, 151635.039, 292557.725, 230638.385])| + EPSG_7548|7548|"NAD83(2011) / WISCRS Grant (m)"|Projected|[-91.16, 42.5, -90.42, 43.22]|Some([212723.788, 120949.814, 273553.221, 201007.484])| + EPSG_7549|7549|"NAD83(2011) / WISCRS Green and Lafayette (m)"|Projected|[-90.43, 42.5, -89.36, 42.86]|Some([121488.35, 30548.781, 209444.374, 70710.252])| + EPSG_7550|7550|"NAD83(2011) / WISCRS Green Lake and Marquette (m)"|Projected|[-89.6, 43.63, -88.88, 43.99]|Some([121956.704, 59504.166, 180064.917, 99568.067])| + EPSG_7551|7551|"NAD83(2011) / WISCRS Iowa (m)"|Projected|[-90.43, 42.81, -89.83, 43.21]|Some([91087.255, 30117.989, 140164.269, 74610.112])| + EPSG_7552|7552|"NAD83(2011) / WISCRS Iron (m)"|Projected|[-90.56, 45.98, -89.92, 46.6]|Some([197387.081, 60763.727, 246984.802, 129740.413])| + EPSG_7553|7553|"NAD83(2011) / WISCRS Jackson (m)"|Projected|[-91.17, 44.07, -90.31, 44.6]|Some([906.371, 4627.977, 69805.006, 63661.843])| + EPSG_7554|7554|"NAD83(2011) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (m)"|Projected|[-88.31, 42.49, -87.75, 43.55]|Some([151763.757, 30362.711, 197803.766, 148208.392])| + EPSG_7555|7555|"NAD83(2011) / WISCRS Kewaunee, Manitowoc and Sheboygan (m)"|Projected|[-88.17, 43.54, -87.37, 44.68]|Some([29746.113, 30368.229, 94406.291, 157228.592])| + EPSG_7556|7556|"NAD83(2011) / WISCRS La Crosse (m)"|Projected|[-91.43, 43.72, -90.91, 44.1]|Some([121321.692, 29875.663, 163225.907, 72179.389])| + EPSG_7557|7557|"NAD83(2011) / WISCRS Langlade (m)"|Projected|[-89.43, 45.02, -88.63, 45.48]|Some([167158.248, 90360.603, 230217.637, 141565.578])| + EPSG_7558|7558|"NAD83(2011) / WISCRS Lincoln (m)"|Projected|[-90.05, 45.11, -89.42, 45.56]|Some([91207.196, 29513.318, 140788.533, 79577.342])| + EPSG_7559|7559|"NAD83(2011) / WISCRS Marathon (m)"|Projected|[-90.32, 44.68, -89.22, 45.13]|Some([31067.373, 30499.302, 118284.926, 80657.158])| + EPSG_7560|7560|"NAD83(2011) / WISCRS Marinette (m)"|Projected|[-88.43, 44.96, -87.48, 45.8]|Some([181936.004, 29820.194, 256894.362, 123430.645])| + EPSG_7561|7561|"NAD83(2011) / WISCRS Menominee (m)"|Projected|[-88.99, 44.85, -88.48, 45.12]|Some([60136.051, 14819.497, 100477.633, 44984.073])| + EPSG_7562|7562|"NAD83(2011) / WISCRS Monroe (m)"|Projected|[-90.98, 43.72, -90.31, 44.17]|Some([177256.057, 90803.64, 231249.119, 140861.482])| + EPSG_7563|7563|"NAD83(2011) / WISCRS Oconto (m)"|Projected|[-88.69, 44.67, -87.76, 45.38]|Some([120894.18, 30312.536, 194643.2, 109515.565])| + EPSG_7564|7564|"NAD83(2011) / WISCRS Oneida (m)"|Projected|[-90.05, 45.46, -89.04, 45.91]|Some([30560.117, 30442.288, 109561.254, 80585.051])| + EPSG_7565|7565|"NAD83(2011) / WISCRS Pepin and Pierce (m)"|Projected|[-92.81, 44.4, -91.65, 44.87]|Some([121255.701, 59790.643, 213670.895, 112185.52])| + EPSG_7566|7566|"NAD83(2011) / WISCRS Polk (m)"|Projected|[-92.89, 45.2, -92.15, 45.73]|Some([121564.582, 59889.602, 179710.418, 118910.402])| + EPSG_7567|7567|"NAD83(2011) / WISCRS Portage (m)"|Projected|[-89.85, 44.24, -89.22, 44.69]|Some([28428.188, 30372.953, 78756.077, 80438.979])| + EPSG_7568|7568|"NAD83(2011) / WISCRS Price (m)"|Projected|[-90.68, 45.37, -90.04, 45.99]|Some([213018.673, 90515.957, 263158.068, 159528.366])| + EPSG_7569|7569|"NAD83(2011) / WISCRS Richland (m)"|Projected|[-90.68, 43.16, -90.19, 43.56]|Some([182099.707, 116222.183, 221952.551, 160693.946])| + EPSG_7570|7570|"NAD83(2011) / WISCRS Rock (m)"|Projected|[-89.37, 42.49, -88.77, 42.85]|Some([121822.518, 60601.003, 171151.467, 100637.732])| + EPSG_7571|7571|"NAD83(2011) / WISCRS Rusk (m)"|Projected|[-91.55, 45.29, -90.67, 45.64]|Some([212627.652, 152309.363, 281665.381, 191324.143])| + EPSG_7572|7572|"NAD83(2011) / WISCRS Sauk (m)"|Projected|[-90.32, 43.14, -89.59, 43.65]|Some([151452.924, 35612.623, 210844.757, 92361.672])| + EPSG_7573|7573|"NAD83(2011) / WISCRS Sawyer (m)"|Projected|[-91.56, 45.63, -90.67, 46.16]|Some([182140.8, 90711.697, 251545.607, 149720.975])| + EPSG_7574|7574|"NAD83(2011) / WISCRS Shawano (m)"|Projected|[-89.23, 44.58, -88.24, 45.03]|Some([212837.879, 60437.941, 291466.952, 110636.857])| + EPSG_7575|7575|"NAD83(2011) / WISCRS St. Croix (m)"|Projected|[-92.81, 44.85, -92.13, 45.22]|Some([151540.292, 90443.536, 205297.906, 131687.372])| + EPSG_7576|7576|"NAD83(2011) / WISCRS Taylor (m)"|Projected|[-90.93, 45.03, -90.04, 45.39]|Some([151945.664, 91317.722, 222086.787, 131425.815])| + EPSG_7577|7577|"NAD83(2011) / WISCRS Trempealeau (m)"|Projected|[-91.62, 43.98, -91.15, 44.6]|Some([236620.45, 90985.015, 274331.389, 159911.826])| + EPSG_7578|7578|"NAD83(2011) / WISCRS Vernon (m)"|Projected|[-91.28, 43.42, -90.31, 43.74]|Some([182281.15, 30306.865, 260838.076, 65981.128])| + EPSG_7579|7579|"NAD83(2011) / WISCRS Vilas (m)"|Projected|[-90.05, 45.85, -88.93, 46.3]|Some([90831.084, 25010.184, 177830.439, 75185.198])| + EPSG_7580|7580|"NAD83(2011) / WISCRS Walworth (m)"|Projected|[-88.78, 42.49, -88.3, 42.85]|Some([212968.19, 91151.992, 252431.591, 131172.833])| + EPSG_7581|7581|"NAD83(2011) / WISCRS Washburn (m)"|Projected|[-92.06, 45.63, -91.54, 46.16]|Some([212511.614, 151542.453, 253062.703, 210491.705])| + EPSG_7582|7582|"NAD83(2011) / WISCRS Washington (m)"|Projected|[-88.42, 43.19, -88.03, 43.55]|Some([91142.335, 30212.484, 122846.348, 70271.566])| + EPSG_7583|7583|"NAD83(2011) / WISCRS Waukesha (m)"|Projected|[-88.55, 42.84, -88.06, 43.2]|Some([182218.015, 30056.246, 222278.003, 70102.441])| + EPSG_7584|7584|"NAD83(2011) / WISCRS Waupaca (m)"|Projected|[-89.23, 44.24, -88.6, 44.69]|Some([151994.896, 91081.524, 202322.354, 141170.886])| + EPSG_7585|7585|"NAD83(2011) / WISCRS Waushara (m)"|Projected|[-89.6, 43.98, -88.88, 44.25]|Some([91340.12, 30186.217, 149110.213, 60251.794])| + EPSG_7586|7586|"NAD83(2011) / WISCRS Wood (m)"|Projected|[-90.32, 44.24, -89.72, 44.69]|Some([182920.252, 120966.591, 230851.576, 171023.033])| + EPSG_7587|7587|"NAD83(2011) / WISCRS Adams and Juneau (ftUS)"|Projected|[-90.32, 43.64, -89.59, 44.25]|Some([398283.598, 99636.118, 591542.905, 322280.165])| + EPSG_7588|7588|"NAD83(2011) / WISCRS Ashland (ftUS)"|Projected|[-90.93, 45.98, -90.3, 47.09]|Some([488748.028, 99881.058, 648924.448, 504886.944])| + EPSG_7589|7589|"NAD83(2011) / WISCRS Barron (ftUS)"|Projected|[-92.16, 45.2, -91.53, 45.65]|Some([225693.381, 24308.935, 388103.812, 188564.749])| + EPSG_7590|7590|"NAD83(2011) / WISCRS Bayfield (ftUS)"|Projected|[-91.56, 46.15, -90.75, 47.01]|Some([646779.688, 297850.532, 852093.772, 611774.11])| + EPSG_7591|7591|"NAD83(2011) / WISCRS Brown (ftUS)"|Projected|[-88.26, 44.24, -87.76, 44.68]|Some([35532.138, 467101.307, 166574.82, 627623.242])| + EPSG_7592|7592|"NAD83(2011) / WISCRS Buffalo (ftUS)"|Projected|[-92.09, 44.02, -91.52, 44.6]|Some([497980.456, 196344.726, 647927.421, 407934.695])| + EPSG_7593|7593|"NAD83(2011) / WISCRS Burnett (ftUS)"|Projected|[-92.89, 45.63, -92.03, 46.16]|Some([99418.455, 97040.084, 319444.468, 290615.845])| + EPSG_7594|7594|"NAD83(2011) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (ftUS)"|Projected|[-88.89, 43.54, -88.04, 44.6]|Some([699581.603, 299088.89, 924980.691, 685855.582])| + EPSG_7595|7595|"NAD83(2011) / WISCRS Chippewa (ftUS)"|Projected|[-91.67, 44.85, -90.92, 45.3]|Some([99593.189, 98038.248, 294118.627, 262343.549])| + EPSG_7596|7596|"NAD83(2011) / WISCRS Clark (ftUS)"|Projected|[-90.93, 44.42, -90.31, 45.04]|Some([598080.296, 298937.86, 760081.277, 525245.834])| + EPSG_7597|7597|"NAD83(2011) / WISCRS Columbia (ftUS)"|Projected|[-89.79, 43.28, -89.0, 43.65]|Some([449658.008, 299500.525, 660046.088, 434621.49])| + EPSG_7598|7598|"NAD83(2011) / WISCRS Crawford (ftUS)"|Projected|[-91.22, 42.98, -90.66, 43.43]|Some([297769.078, 95981.574, 447636.214, 260133.567])| + EPSG_7599|7599|"NAD83(2011) / WISCRS Dane (ftUS)"|Projected|[-89.84, 42.84, -89.0, 43.3]|Some([698940.814, 397287.856, 924251.295, 565238.948])| + EPSG_7600|7600|"NAD83(2011) / WISCRS Dodge and Jefferson (ftUS)"|Projected|[-89.02, 42.84, -88.4, 43.64]|Some([798284.929, 498466.849, 964584.322, 790294.829])| + EPSG_7601|7601|"NAD83(2011) / WISCRS Door (ftUS)"|Projected|[-87.74, 44.67, -86.8, 45.43]|Some([399298.808, 98437.12, 643857.498, 375900.356])| + EPSG_7602|7602|"NAD83(2011) / WISCRS Douglas (ftUS)"|Projected|[-92.3, 46.15, -91.55, 46.76]|Some([96837.942, 97249.21, 286937.623, 319957.67])| + EPSG_7603|7603|"NAD83(2011) / WISCRS Dunn (ftUS)"|Projected|[-92.16, 44.68, -91.64, 45.21]|Some([100921.074, 99047.166, 236188.596, 292406.313])| + EPSG_7604|7604|"NAD83(2011) / WISCRS Eau Claire (ftUS)"|Projected|[-91.66, 44.59, -90.92, 44.86]|Some([297313.345, 197892.575, 490107.697, 296554.878])| + EPSG_7605|7605|"NAD83(2011) / WISCRS Florence (ftUS)"|Projected|[-88.69, 45.71, -88.05, 46.03]|Some([297910.363, 98863.951, 461419.253, 216041.423])| + EPSG_7606|7606|"NAD83(2011) / WISCRS Forest (ftUS)"|Projected|[-89.05, 45.37, -88.42, 46.08]|Some([797904.053, 497489.29, 959833.136, 756686.103])| + EPSG_7607|7607|"NAD83(2011) / WISCRS Grant (ftUS)"|Projected|[-91.16, 42.5, -90.42, 43.22]|Some([697911.296, 396816.181, 897482.524, 659472.054])| + EPSG_7608|7608|"NAD83(2011) / WISCRS Green and Lafayette (ftUS)"|Projected|[-90.43, 42.5, -89.36, 42.86]|Some([398583.027, 100225.46, 687152.082, 231988.553])| + EPSG_7609|7609|"NAD83(2011) / WISCRS Green Lake and Marquette (ftUS)"|Projected|[-89.6, 43.63, -88.88, 43.99]|Some([400119.62, 195223.25, 590762.983, 326666.233])| + EPSG_7610|7610|"NAD83(2011) / WISCRS Iowa (ftUS)"|Projected|[-90.43, 42.81, -89.83, 43.21]|Some([298842.102, 98812.102, 459855.606, 244783.343])| + EPSG_7611|7611|"NAD83(2011) / WISCRS Iron (ftUS)"|Projected|[-90.56, 45.98, -89.92, 46.6]|Some([647594.116, 199355.66, 810315.971, 425656.672])| + EPSG_7612|7612|"NAD83(2011) / WISCRS Jackson (ftUS)"|Projected|[-91.17, 44.07, -90.31, 44.6]|Some([2973.652, 15183.622, 229018.591, 208863.897])| + EPSG_7613|7613|"NAD83(2011) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (ftUS)"|Projected|[-88.31, 42.49, -87.75, 43.55]|Some([497911.593, 99614.996, 648961.188, 486247.033])| + EPSG_7614|7614|"NAD83(2011) / WISCRS Kewaunee, Manitowoc and Sheboygan (ftUS)"|Projected|[-88.17, 43.54, -87.37, 44.68]|Some([97592.04, 99633.1, 309731.306, 515840.807])| + EPSG_7615|7615|"NAD83(2011) / WISCRS La Crosse (ftUS)"|Projected|[-91.43, 43.72, -90.91, 44.1]|Some([398036.252, 98017.07, 535516.998, 236808.545])| + EPSG_7616|7616|"NAD83(2011) / WISCRS Langlade (ftUS)"|Projected|[-89.43, 45.02, -88.63, 45.48]|Some([548418.352, 296458.077, 755305.695, 464453.068])| + EPSG_7617|7617|"NAD83(2011) / WISCRS Lincoln (ftUS)"|Projected|[-90.05, 45.11, -89.42, 45.56]|Some([299235.609, 96828.276, 461903.713, 261079.997])| + EPSG_7618|7618|"NAD83(2011) / WISCRS Marathon (ftUS)"|Projected|[-90.32, 44.68, -89.22, 45.13]|Some([101926.872, 100063.125, 388073.128, 264622.692])| + EPSG_7619|7619|"NAD83(2011) / WISCRS Marinette (ftUS)"|Projected|[-88.43, 44.96, -87.48, 45.8]|Some([596901.705, 97835.087, 842827.587, 404955.374])| + EPSG_7620|7620|"NAD83(2011) / WISCRS Menominee (ftUS)"|Projected|[-88.99, 44.85, -88.48, 45.12]|Some([197296.36, 48620.302, 329650.368, 147585.246])| + EPSG_7621|7621|"NAD83(2011) / WISCRS Monroe (ftUS)"|Projected|[-90.98, 43.72, -90.31, 44.17]|Some([581547.581, 297911.61, 758689.819, 462143.047])| + EPSG_7622|7622|"NAD83(2011) / WISCRS Oconto (ftUS)"|Projected|[-88.69, 44.67, -87.76, 45.38]|Some([396633.655, 99450.38, 638591.9, 359302.317])| + EPSG_7623|7623|"NAD83(2011) / WISCRS Oneida (ftUS)"|Projected|[-90.05, 45.46, -89.04, 45.91]|Some([100262.651, 99876.075, 359452.216, 264386.121])| + EPSG_7624|7624|"NAD83(2011) / WISCRS Pepin and Pierce (ftUS)"|Projected|[-92.81, 44.4, -91.65, 44.87]|Some([397819.744, 196163.136, 701018.594, 368061.994])| + EPSG_7625|7625|"NAD83(2011) / WISCRS Polk (ftUS)"|Projected|[-92.89, 45.2, -92.15, 45.73]|Some([398833.132, 196487.801, 589599.929, 390125.209])| + EPSG_7626|7626|"NAD83(2011) / WISCRS Portage (ftUS)"|Projected|[-89.85, 44.24, -89.22, 44.69]|Some([93268.147, 99648.597, 258385.563, 263906.884])| + EPSG_7627|7627|"NAD83(2011) / WISCRS Price (ftUS)"|Projected|[-90.68, 45.37, -90.04, 45.99]|Some([698878.763, 296967.769, 863377.761, 523385.981])| + EPSG_7628|7628|"NAD83(2011) / WISCRS Richland (ftUS)"|Projected|[-90.68, 43.16, -90.19, 43.56]|Some([597438.787, 381305.614, 728189.328, 527210.056])| + EPSG_7629|7629|"NAD83(2011) / WISCRS Rock (ftUS)"|Projected|[-89.37, 42.49, -88.77, 42.85]|Some([399679.378, 198821.79, 561519.439, 330175.624])| + EPSG_7630|7630|"NAD83(2011) / WISCRS Rusk (ftUS)"|Projected|[-91.55, 45.29, -90.67, 45.64]|Some([697595.889, 499701.636, 924097.172, 627702.627])| + EPSG_7631|7631|"NAD83(2011) / WISCRS Sauk (ftUS)"|Projected|[-90.32, 43.14, -89.59, 43.65]|Some([496891.802, 116839.081, 691746.506, 303023.252])| + EPSG_7632|7632|"NAD83(2011) / WISCRS Sawyer (ftUS)"|Projected|[-91.56, 45.63, -90.67, 46.16]|Some([597573.61, 297609.961, 825279.213, 491209.565])| + EPSG_7633|7633|"NAD83(2011) / WISCRS Shawano (ftUS)"|Projected|[-89.23, 44.58, -88.24, 45.03]|Some([698285.608, 198286.812, 956254.492, 362981.088])| + EPSG_7634|7634|"NAD83(2011) / WISCRS St. Croix (ftUS)"|Projected|[-92.81, 44.85, -92.13, 45.22]|Some([497178.442, 296730.168, 673548.212, 432044.32])| + EPSG_7635|7635|"NAD83(2011) / WISCRS Taylor (ftUS)"|Projected|[-90.93, 45.03, -90.04, 45.39]|Some([498508.398, 299598.227, 728629.733, 431186.194])| + EPSG_7636|7636|"NAD83(2011) / WISCRS Trempealeau (ftUS)"|Projected|[-91.62, 43.98, -91.15, 44.6]|Some([776312.26, 298506.668, 900035.565, 524644.048])| + EPSG_7637|7637|"NAD83(2011) / WISCRS Vernon (ftUS)"|Projected|[-91.28, 43.42, -90.31, 43.74]|Some([598034.071, 99431.775, 855766.256, 216473.083])| + EPSG_7638|7638|"NAD83(2011) / WISCRS Vilas (ftUS)"|Projected|[-90.05, 45.85, -88.93, 46.3]|Some([298001.648, 82054.245, 583432.032, 246670.103])| + EPSG_7639|7639|"NAD83(2011) / WISCRS Walworth (ftUS)"|Projected|[-88.78, 42.49, -88.3, 42.85]|Some([698713.137, 299054.495, 828185.978, 430356.203])| + EPSG_7640|7640|"NAD83(2011) / WISCRS Washburn (ftUS)"|Projected|[-92.06, 45.63, -91.54, 46.16]|Some([697215.187, 497185.533, 830256.55, 690588.202])| + EPSG_7641|7641|"NAD83(2011) / WISCRS Washington (ftUS)"|Projected|[-88.42, 43.19, -88.03, 43.55]|Some([299022.81, 99122.124, 403038.392, 230549.298])| + EPSG_7642|7642|"NAD83(2011) / WISCRS Waukesha (ftUS)"|Projected|[-88.55, 42.84, -88.06, 43.2]|Some([597826.936, 98609.535, 729257.082, 229994.424])| + EPSG_7643|7643|"NAD83(2011) / WISCRS Waupaca (ftUS)"|Projected|[-89.23, 44.24, -88.6, 44.69]|Some([498669.921, 298823.3, 663785.923, 463158.148])| + EPSG_7644|7644|"NAD83(2011) / WISCRS Waushara (ftUS)"|Projected|[-89.6, 43.98, -88.88, 44.25]|Some([299671.711, 99035.947, 489205.756, 197676.096])| + EPSG_7645|7645|"NAD83(2011) / WISCRS Wood (ftUS)"|Projected|[-90.32, 44.24, -89.72, 44.69]|Some([600130.859, 396871.224, 757385.544, 561098.067])| + EPSG_7683|7683|"GSK-2011"|Geographic2d|[18.92, 39.87, -168.97, 85.19]|Some([-174.707, 39.87, 179.556, 85.19])| + EPSG_7686|7686|"Kyrg-06"|Geographic2d|[69.24, 39.19, 80.29, 43.22]|Some([69.24, 39.19, 80.29, 43.22])| + EPSG_7692|7692|"Kyrg-06 / zone 1"|Projected|[69.24, 39.51, 70.02, 40.22]|Some([1361569.814, 4390117.695, 1429291.168, 4469784.779])| + EPSG_7693|7693|"Kyrg-06 / zone 2"|Projected|[70.01, 39.19, 73.02, 42.83]|Some([2169829.61, 4354340.772, 2429882.389, 4759679.198])| + EPSG_7694|7694|"Kyrg-06 / zone 3"|Projected|[73.01, 39.35, 76.02, 43.22]|Some([3170125.368, 4372104.074, 3429587.285, 4803006.614])| + EPSG_7695|7695|"Kyrg-06 / zone 4"|Projected|[76.01, 40.35, 79.02, 43.0]|Some([4171996.797, 4483135.831, 4427720.0, 4778565.168])| + EPSG_7696|7696|"Kyrg-06 / zone 5"|Projected|[79.01, 41.66, 80.29, 42.8]|Some([5174507.523, 4628641.408, 5281457.544, 4756346.434])| + EPSG_7755|7755|"WGS 84 / India NSF LCC"|Projected|[65.6, 3.87, 97.42, 35.51]|Some([2338453.379, 1771797.38, 6008387.455, 5357415.548])| + EPSG_7756|7756|"WGS 84 / Andhra Pradesh"|Projected|[76.75, 12.61, 84.81, 19.92]|Some([551369.68, 596770.865, 1427968.743, 1409849.549])| + EPSG_7757|7757|"WGS 84 / Arunachal Pradesh"|Projected|[91.55, 26.65, 97.42, 29.47]|Some([706289.188, 850242.306, 1290724.495, 1166199.179])| + EPSG_7758|7758|"WGS 84 / Assam"|Projected|[89.69, 24.13, 96.03, 27.98]|Some([688900.968, 792590.545, 1333461.09, 1223140.182])| + EPSG_7759|7759|"WGS 84 / Bihar"|Projected|[83.31, 24.28, 88.3, 27.86]|Some([739552.513, 823086.848, 1246233.693, 1222152.027])| + EPSG_7760|7760|"WGS 84 / Delhi"|Projected|[76.83, 28.4, 77.34, 28.89]|Some([983340.671, 975052.703, 1033318.624, 1029406.165])| + EPSG_7761|7761|"WGS 84 / Gujarat"|Projected|[68.13, 20.05, 74.48, 24.71]|Some([660405.599, 742265.446, 1324945.305, 1261779.854])| + EPSG_7762|7762|"WGS 84 / Haryana"|Projected|[74.46, 27.65, 77.6, 30.94]|Some([848013.019, 822440.419, 1157908.191, 1188124.522])| + EPSG_7763|7763|"WGS 84 / Himachal Pradesh"|Projected|[75.57, 30.38, 79.0, 33.26]|Some([826494.607, 847912.71, 1156204.272, 1168637.375])| + EPSG_7764|7764|"WGS 84 / Jammu and Kashmir"|Projected|[73.76, 32.27, 79.57, 35.51]|Some([741751.592, 724331.934, 1289342.164, 1087902.325])| + EPSG_7765|7765|"WGS 84 / Jharkhand"|Projected|[83.32, 21.96, 87.98, 25.35]|Some([761866.956, 815454.081, 1243298.157, 1192848.964])| + EPSG_7766|7766|"WGS 84 / Madhya Pradesh"|Projected|[74.03, 21.07, 82.81, 26.88]|Some([548219.766, 675008.705, 1461135.105, 1325341.47])| + EPSG_7767|7767|"WGS 84 / Maharashtra"|Projected|[72.6, 15.6, 80.9, 22.04]|Some([554595.606, 637060.034, 1445404.394, 1354760.323])| + EPSG_7768|7768|"WGS 84 / Manipur"|Projected|[92.97, 23.84, 94.76, 25.7]|Some([895061.411, 899141.308, 1077430.748, 1105556.766])| + EPSG_7769|7769|"WGS 84 / Meghalaya"|Projected|[89.82, 25.03, 92.81, 26.12]|Some([843060.069, 934060.954, 1144829.304, 1055723.651])| + EPSG_7770|7770|"WGS 84 / Nagaland"|Projected|[93.33, 25.2, 95.25, 27.05]|Some([894675.032, 897439.515, 1088191.046, 1102808.52])| + EPSG_7771|7771|"WGS 84 / India Northeast"|Projected|[89.69, 21.94, 97.42, 29.47]|Some([606052.138, 590946.73, 1405318.409, 1430541.518])| + EPSG_7772|7772|"WGS 84 / Orissa"|Projected|[81.38, 17.8, 87.5, 22.57]|Some([682321.947, 728498.935, 1331465.479, 1259535.361])| + EPSG_7773|7773|"WGS 84 / Punjab"|Projected|[73.87, 29.54, 76.94, 32.58]|Some([854102.68, 837958.657, 1151713.448, 1176027.278])| + EPSG_7774|7774|"WGS 84 / Rajasthan"|Projected|[69.48, 23.06, 78.27, 30.2]|Some([549170.684, 576406.049, 1450829.316, 1374568.174])| + EPSG_7775|7775|"WGS 84 / Uttar Pradesh"|Projected|[77.08, 23.87, 84.64, 30.42]|Some([613202.693, 638639.651, 1383740.54, 1369770.286])| + EPSG_7776|7776|"WGS 84 / Uttaranchal"|Projected|[77.56, 28.71, 81.02, 31.48]|Some([822646.66, 856834.397, 1160742.933, 1165249.79])| + EPSG_7777|7777|"WGS 84 / Andaman and Nicobar"|Projected|[92.15, 6.7, 94.33, 13.73]|Some([878378.922, 607396.953, 1119409.527, 1385215.455])| + EPSG_7778|7778|"WGS 84 / Chhattisgarh"|Projected|[80.23, 17.78, 84.39, 24.11]|Some([785807.609, 643616.127, 1226921.403, 1345998.028])| + EPSG_7779|7779|"WGS 84 / Goa"|Projected|[73.61, 14.86, 74.35, 15.8]|Some([958028.225, 943015.83, 1037666.928, 1047066.475])| + EPSG_7780|7780|"WGS 84 / Karnataka"|Projected|[74.0, 11.57, 78.58, 18.46]|Some([740936.311, 606775.916, 1240511.377, 1370649.064])| + EPSG_7781|7781|"WGS 84 / Kerala"|Projected|[74.81, 8.25, 77.4, 12.8]|Some([868893.344, 751161.798, 1154247.237, 1254815.865])| + EPSG_7782|7782|"WGS 84 / Lakshadweep"|Projected|[72.04, 8.21, 73.76, 11.76]|Some([880446.691, 802031.301, 1069966.332, 1194899.723])| + EPSG_7783|7783|"WGS 84 / Mizoram"|Projected|[92.25, 21.94, 93.45, 24.53]|Some([948347.683, 868778.48, 1072313.883, 1155788.972])| + EPSG_7784|7784|"WGS 84 / Sikkim"|Projected|[88.01, 27.08, 88.92, 28.14]|Some([951399.578, 939609.498, 1041657.425, 1057167.698])| + EPSG_7785|7785|"WGS 84 / Tamil Nadu"|Projected|[76.22, 8.02, 80.4, 13.59]|Some([762432.004, 684289.945, 1223230.787, 1301314.178])| + EPSG_7786|7786|"WGS 84 / Tripura"|Projected|[91.15, 22.94, 92.34, 24.54]|Some([938459.631, 910295.019, 1060514.671, 1087631.311])| + EPSG_7787|7787|"WGS 84 / West Bengal"|Projected|[85.82, 21.49, 89.88, 27.23]|Some([787042.441, 680551.224, 1207774.533, 1317917.439])| + EPSG_7791|7791|"RDN2008 / UTM zone 32N"|Projected|[5.93, 36.53, 12.0, 47.04]|Some([225132.787, 4042736.378, 768598.181, 5214183.815])| + EPSG_7792|7792|"RDN2008 / UTM zone 33N"|Projected|[12.0, 34.79, 18.0, 47.1]|Some([225503.37, 3849755.266, 774496.63, 5220644.505])| + EPSG_7793|7793|"RDN2008 / UTM zone 34N"|Projected|[17.99, 34.76, 18.99, 41.64]|Some([224488.264, 3848268.688, 332597.547, 4614184.666])| + EPSG_7794|7794|"RDN2008 / Italy zone (E-N)"|Projected|[5.93, 34.76, 18.99, 47.1]|Some([6444735.932, 3842196.016, 7639559.465, 5234265.085])| + EPSG_7795|7795|"RDN2008 / Zone 12 (E-N)"|Projected|[5.93, 34.76, 18.99, 47.1]|Some([2443901.785, 3847967.968, 3640520.245, 5242128.278])| + EPSG_7798|7798|"BGS2005"|Geographic2d|[22.36, 41.24, 31.35, 44.23]|Some([22.36, 41.24, 31.35, 44.23])| + EPSG_7799|7799|"BGS2005 / UTM zone 34N (N-E)"|Projected|[22.36, 41.32, 24.0, 44.23]|Some([608614.984, 4575173.125, 751095.288, 4901796.357])| + EPSG_7800|7800|"BGS2005 / UTM zone 35N (N-E)"|Projected|[24.0, 41.24, 30.01, 44.15]|Some([248597.584, 4565399.907, 752240.526, 4892939.522])| + EPSG_7801|7801|"BGS2005 / CCS2005"|Projected|[22.36, 41.24, 28.68, 44.23]|Some([236773.582, 4567221.418, 766578.049, 4904167.977])| + EPSG_7803|7803|"BGS2005 / UTM zone 34N"|Projected|[22.36, 41.32, 24.0, 44.23]|Some([608614.984, 4575173.125, 751095.288, 4901796.357])| + EPSG_7805|7805|"BGS2005 / UTM zone 36N"|Projected|[30.0, 42.56, 31.35, 43.67]|Some([253727.659, 4713274.717, 366980.824, 4839595.716])| + EPSG_7825|7825|"Pulkovo 1942 / CS63 zone X1"|Projected|[22.15, 47.71, 25.01, 51.92]|Some([1198818.277, 5277097.044, 1413438.983, 5746435.171])| + EPSG_7826|7826|"Pulkovo 1942 / CS63 zone X2"|Projected|[25.0, 47.72, 28.01, 51.96]|Some([2187582.167, 5278203.217, 2413415.596, 5750879.545])| + EPSG_7827|7827|"Pulkovo 1942 / CS63 zone X3"|Projected|[28.0, 45.18, 31.01, 52.09]|Some([3182221.168, 4995850.256, 3418806.424, 5765337.325])| + EPSG_7828|7828|"Pulkovo 1942 / CS63 zone X4"|Projected|[31.0, 44.32, 34.01, 52.38]|Some([4180456.696, 4900275.698, 4420577.207, 5797597.634])| + EPSG_7829|7829|"Pulkovo 1942 / CS63 zone X5"|Projected|[34.0, 44.33, 37.01, 52.25]|Some([5180474.345, 4901381.813, 5420553.875, 5783127.633])| + EPSG_7830|7830|"Pulkovo 1942 / CS63 zone X6"|Projected|[37.0, 46.8, 40.01, 50.44]|Some([6185604.844, 5175899.759, 6415384.77, 5581765.916])| + EPSG_7831|7831|"Pulkovo 1942 / CS63 zone X7"|Projected|[40.0, 48.8, 40.18, 49.62]|Some([7189915.615, 5399108.403, 7204729.88, 5490544.107])| + EPSG_7844|7844|"GDA2020"|Geographic2d|[93.41, -60.55, 173.34, -8.47]|Some([93.41, -60.55, 173.34, -8.47])| + EPSG_7845|7845|"GDA2020 / GA LCC"|Projected|[112.85, -43.7, 153.69, -9.86]|Some([-2382330.288, -5065635.719, 2219274.762, -1158972.006])| + EPSG_7846|7846|"GDA2020 / MGA zone 46"|Projected|[93.41, -15.46, 96.01, -8.57]|Some([543982.744, 8288531.631, 831369.191, 9052662.824])| + EPSG_7847|7847|"GDA2020 / MGA zone 47"|Projected|[96.0, -15.56, 100.34, -8.47]|Some([169646.719, 8277472.622, 647505.804, 9063742.116])| + EPSG_7848|7848|"GDA2020 / MGA zone 48"|Projected|[102.14, -34.68, 108.01, -8.87]|Some([185409.281, 6158318.803, 831104.436, 9019519.378])| + EPSG_7849|7849|"GDA2020 / MGA zone 49"|Projected|[108.0, -37.84, 114.01, -10.72]|Some([171822.512, 5807666.716, 829272.355, 8814980.011])| + EPSG_7850|7850|"GDA2020 / MGA zone 50"|Projected|[114.0, -38.53, 120.01, -12.06]|Some([173357.972, 5731082.25, 827731.754, 8666811.663])| + EPSG_7851|7851|"GDA2020 / MGA zone 51"|Projected|[120.0, -38.07, 126.01, -10.46]|Some([171545.269, 5782139.287, 829550.527, 8843727.573])| + EPSG_7852|7852|"GDA2020 / MGA zone 52"|Projected|[125.99, -37.38, 132.0, -9.1]|Some([169104.664, 5858719.403, 829795.052, 8994092.068])| + EPSG_7853|7853|"GDA2020 / MGA zone 53"|Projected|[132.0, -40.71, 138.01, -8.88]|Some([170005.496, 5489076.49, 831095.455, 9018414.642])| + EPSG_7854|7854|"GDA2020 / MGA zone 54"|Projected|[138.0, -48.19, 144.01, -9.08]|Some([170186.616, 4658200.177, 830913.729, 8996303.223])| + EPSG_7855|7855|"GDA2020 / MGA zone 55"|Projected|[144.0, -50.89, 150.01, -9.23]|Some([170325.081, 4358091.03, 830774.8, 8979719.5])| + EPSG_7856|7856|"GDA2020 / MGA zone 56"|Projected|[150.0, -58.96, 156.0, -13.87]|Some([175714.327, 3460530.811, 824285.673, 8466650.971])| + EPSG_7857|7857|"GDA2020 / MGA zone 57"|Projected|[156.0, -60.56, 162.01, -14.08]|Some([176008.634, 3282444.37, 825072.218, 8443426.056])| + EPSG_7858|7858|"GDA2020 / MGA zone 58"|Projected|[162.0, -59.39, 168.0, -25.94]|Some([199529.304, 3412679.943, 800470.696, 7130960.754])| + EPSG_7859|7859|"GDA2020 / MGA zone 59"|Projected|[168.0, -32.48, 173.35, -25.61]|Some([198695.776, 6402392.036, 735995.359, 7167504.655])| + EPSG_7877|7877|"Astro DOS 71 / SHLG71"|Projected|[-5.85, -16.08, -5.59, -15.85]|Some([285201.694, 1987981.8, 313051.39, 2013437.876])| + EPSG_7878|7878|"Astro DOS 71 / UTM zone 30S"|Projected|[-5.85, -16.08, -5.59, -15.85]|Some([194204.338, 8220613.853, 222386.618, 8246443.639])| + EPSG_7881|7881|"St. Helena Tritan"|Geographic2d|[-5.85, -16.08, -5.59, -15.85]|Some([-5.85, -16.08, -5.59, -15.85])| + EPSG_7882|7882|"St. Helena Tritan / SHLG(Tritan)"|Projected|[-5.85, -16.08, -5.59, -15.85]|Some([285201.805, 1987981.877, 313051.433, 2013437.887])| + EPSG_7883|7883|"St. Helena Tritan / UTM zone 30S"|Projected|[-5.85, -16.08, -5.59, -15.85]|Some([194739.662, 8220113.671, 222921.854, 8245943.393])| + EPSG_7886|7886|"SHGD2015"|Geographic2d|[-5.85, -16.08, -5.59, -15.85]|Some([-5.85, -16.08, -5.59, -15.85])| + EPSG_7887|7887|"SHMG2015"|Projected|[-5.85, -16.08, -5.59, -15.85]|Some([194739.732, 8220113.731, 222921.925, 8245943.454])| + EPSG_7899|7899|"GDA2020 / Vicgrid"|Projected|[140.96, -39.2, 150.04, -33.98]|Some([2126335.946, 2244252.056, 2966077.314, 2835165.132])| + EPSG_7991|7991|"NAD27 / MTM zone 10"|Projected|[-81.0, 42.26, -77.99, 47.33]|Some([181021.47, 4679834.26, 429361.018, 5244286.946])| + EPSG_7992|7992|"Malongo 1987 / UTM zone 33S"|Projected|[11.79, -6.04, 12.37, -5.79]|Some([144372.665, 9331424.74, 208798.754, 9359433.86])| + EPSG_8013|8013|"GDA2020 / ALB2020"|Projected|[117.55, -35.21, 118.22, -34.75]|Some([19478.155, 202040.094, 80827.064, 253124.743])| + EPSG_8014|8014|"GDA2020 / BIO2020"|Projected|[114.9, -22.2, 115.59, -20.21]|Some([23422.081, 244006.143, 95532.826, 464380.643])| + EPSG_8015|8015|"GDA2020 / BRO2020"|Projected|[122.08, -18.09, 122.62, -16.75]|Some([22987.925, 299027.25, 80566.319, 447354.297])| + EPSG_8016|8016|"GDA2020 / BCG2020"|Projected|[115.18, -33.75, 115.87, -33.4]|Some([26432.657, 263998.177, 90622.761, 302903.832])| + EPSG_8017|8017|"GDA2020 / CARN2020"|Projected|[113.33, -25.5, 114.0, -23.0]|Some([15484.011, 228520.461, 84174.244, 505467.611])| + EPSG_8018|8018|"GDA2020 / CIG2020"|Projected|[105.48, -10.63, 105.77, -10.36]|Some([34119.687, 224427.676, 65880.313, 254297.134])| + EPSG_8019|8019|"GDA2020 / CKIG2020"|Projected|[96.76, -12.27, 96.99, -11.76]|Some([37465.294, 243052.872, 62534.706, 299472.627])| + EPSG_8020|8020|"GDA2020 / COL2020"|Projected|[115.73, -34.67, 116.4, -33.25]|Some([21051.241, 261844.483, 83489.111, 419455.354])| + EPSG_8021|8021|"GDA2020 / ESP2020"|Projected|[121.56, -34.5, 122.2, -33.33]|Some([19896.219, 230806.638, 79483.082, 360632.219])| + EPSG_8022|8022|"GDA2020 / EXM2020"|Projected|[113.75, -22.42, 114.25, -21.75]|Some([17243.117, 269651.396, 68964.465, 343875.992])| + EPSG_8023|8023|"GDA2020 / GCG2020"|Projected|[114.51, -29.1, 115.0, -28.48]|Some([42818.966, 229555.066, 90801.523, 298343.233])| + EPSG_8024|8024|"GDA2020 / GOLD2020"|Projected|[121.0, -32.25, 121.84, -28.75]|Some([11161.027, 230138.944, 93210.379, 618281.641])| + EPSG_8025|8025|"GDA2020 / JCG2020"|Projected|[114.83, -30.74, 115.34, -29.08]|Some([35070.83, 247786.135, 84726.645, 431853.657])| + EPSG_8026|8026|"GDA2020 / KALB2020"|Projected|[113.9, -28.5, 114.75, -27.16]|Some([13839.358, 546025.304, 98087.917, 694598.828])| + EPSG_8027|8027|"GDA2020 / KAR2020"|Projected|[116.58, -20.92, 117.25, -20.25]|Some([13083.282, 235742.53, 83085.706, 309959.741])| + EPSG_8028|8028|"GDA2020 / KUN2020"|Projected|[128.5, -16.75, 129.0, -14.75]|Some([23080.874, 247312.489, 76919.126, 468645.366])| + EPSG_8029|8029|"GDA2020 / LCG2020"|Projected|[115.15, -31.49, 115.62, -30.71]|Some([29244.998, 264664.302, 74267.397, 351171.761])| + EPSG_8030|8030|"GDA2020 / MRCG2020"|Projected|[114.96, -34.42, 115.24, -33.51]|Some([30798.094, 239709.235, 56813.574, 340667.962])| + EPSG_8031|8031|"GDA2020 / PCG2020"|Projected|[115.44, -33.42, 116.09, -31.33]|Some([14151.118, 200610.565, 76014.192, 432441.045])| + EPSG_8032|8032|"GDA2020 / PHG2020"|Projected|[118.25, -20.79, 118.97, -20.1]|Some([13396.48, 200126.166, 88695.171, 276560.222])| + EPSG_8035|8035|"WGS 84 / TM Zone 20N (ftUS)"|Projected|[-62.09, 9.83, -59.99, 12.34]|Some([1965021.312, 3565452.466, 2723736.422, 4481584.57])| + EPSG_8036|8036|"WGS 84 / TM Zone 21N (ftUS)"|Projected|[-60.0, 9.95, -57.28, 12.19]|Some([561091.441, 3608579.665, 1540485.296, 4427056.856])| + EPSG_8042|8042|"Gusterberg (Ferro)"|Geographic2d|[12.07, 46.93, 16.83, 51.06]|Some([29.737, 46.93, 34.497, 51.06])| + EPSG_8043|8043|"St. Stephen (Ferro)"|Geographic2d|[14.41, 47.42, 18.86, 50.45]|Some([32.077, 47.42, 36.527, 50.45])| + EPSG_8044|8044|"Gusterberg Grid (Ferro)"|Projected|[12.07, 46.93, 16.83, 51.06]|Some([-339406.688, -204925.595, 123204.778, 157371.541])| + EPSG_8045|8045|"St. Stephen Grid (Ferro)"|Projected|[14.41, 47.42, 18.86, 50.45]|Some([-252141.396, -187462.835, 87674.359, 148144.131])| + EPSG_8058|8058|"GDA2020 / NSW Lambert"|Projected|[140.99, -37.53, 153.69, -28.15]|Some([8708252.164, 4006187.691, 9958614.108, 5065609.188])| + EPSG_8059|8059|"GDA2020 / SA Lambert"|Projected|[128.99, -38.13, 141.01, -25.99]|Some([396749.099, 1305574.976, 1603250.901, 2665724.452])| + EPSG_8065|8065|"NAD83(2011) / PCCS zone 1 (ft)"|Projected|[-111.57, 31.43, -110.44, 32.52]|Some([106960.577, 501652.277, 459502.247, 899574.775])| + EPSG_8066|8066|"NAD83(2011) / PCCS zone 2 (ft)"|Projected|[-112.52, 31.5, -111.56, 32.51]|Some([1689860.218, 1090949.733, 1989108.995, 1458955.242])| + EPSG_8067|8067|"NAD83(2011) / PCCS zone 3 (ft)"|Projected|[-113.33, 31.8, -112.51, 32.51]|Some([549251.483, 109140.554, 804031.628, 368083.976])| + EPSG_8068|8068|"NAD83(2011) / PCCS zone 4 (ft)"|Projected|[-110.87, 32.33, -110.61, 32.49]|Some([-7079.683, 45676.576, 73259.627, 103936.219])| + EPSG_8082|8082|"NAD83(CSRS)v6 / MTM NS 2010 zone 4"|Projected|[-63.0, 44.64, -59.73, 47.08]|Some([24381003.192, 4944444.039, 24640416.322, 5217139.633])| + EPSG_8083|8083|"NAD83(CSRS)v6 / MTM NS 2010 zone 5"|Projected|[-66.28, 43.41, -63.0, 46.02]|Some([25355838.948, 4807789.174, 25621483.709, 5099339.87])| + EPSG_8086|8086|"ISN2016"|Geographic2d|[-30.87, 59.96, -5.55, 69.59]|Some([-30.87, 59.96, -5.55, 69.59])| + EPSG_8088|8088|"ISN2016 / Lambert 2016"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([2038384.289, -262368.597, 3448429.41, 868116.034])| + EPSG_8090|8090|"NAD83(HARN) / WISCRS Florence (m)"|Projected|[-88.69, 45.71, -88.05, 46.03]|Some([90803.749, 30132.852, 140641.351, 65848.612])| + EPSG_8091|8091|"NAD83(HARN) / WISCRS Florence (ftUS)"|Projected|[-88.69, 45.71, -88.05, 46.03]|Some([297911.967, 98860.867, 461420.832, 216038.322])| + EPSG_8092|8092|"NAD83(HARN) / WISCRS Eau Claire (m)"|Projected|[-91.66, 44.59, -90.92, 44.86]|Some([90621.849, 60316.879, 149385.676, 90389.204])| + EPSG_8093|8093|"NAD83(HARN) / WISCRS Eau Claire (ftUS)"|Projected|[-91.66, 44.59, -90.92, 44.86]|Some([297315.183, 197889.626, 490109.505, 296551.915])| + EPSG_8095|8095|"NAD83(HARN) / WISCRS Wood (m)"|Projected|[-90.32, 44.24, -89.72, 44.69]|Some([182920.781, 120965.689, 230852.097, 171022.123])| + EPSG_8096|8096|"NAD83(HARN) / WISCRS Wood (ftUS)"|Projected|[-90.32, 44.24, -89.72, 44.69]|Some([600132.595, 396868.265, 757387.256, 561095.083])| + EPSG_8097|8097|"NAD83(HARN) / WISCRS Waushara (m)"|Projected|[-89.6, 43.98, -88.88, 44.25]|Some([91340.632, 30185.315, 149110.715, 60250.887])| + EPSG_8098|8098|"NAD83(HARN) / WISCRS Waushara (ftUS)"|Projected|[-89.6, 43.98, -88.88, 44.25]|Some([299673.39, 99032.987, 489207.404, 197673.119])| + EPSG_8099|8099|"NAD83(HARN) / WISCRS Waupaca (m)"|Projected|[-89.23, 44.24, -88.6, 44.69]|Some([151995.398, 91080.614, 202322.848, 141169.968])| + EPSG_8100|8100|"NAD83(HARN) / WISCRS Waupaca (ftUS)"|Projected|[-89.23, 44.24, -88.6, 44.69]|Some([498671.569, 298820.315, 663787.544, 463155.138])| + EPSG_8101|8101|"NAD83(HARN) / WISCRS Waukesha (m)"|Projected|[-88.55, 42.84, -88.06, 43.2]|Some([182218.501, 30055.358, 222278.483, 70101.546])| + EPSG_8102|8102|"NAD83(HARN) / WISCRS Waukesha (ftUS)"|Projected|[-88.55, 42.84, -88.06, 43.2]|Some([597828.53, 98606.621, 729258.655, 229991.489])| + EPSG_8103|8103|"NAD83(HARN) / WISCRS Washington (m)"|Projected|[-88.42, 43.19, -88.03, 43.55]|Some([91142.818, 30211.588, 122846.825, 70270.664])| + EPSG_8104|8104|"NAD83(HARN) / WISCRS Washington (ftUS)"|Projected|[-88.42, 43.19, -88.03, 43.55]|Some([299024.394, 99119.186, 403039.958, 230546.338])| + EPSG_8105|8105|"NAD83(HARN) / WISCRS Washburn (m)"|Projected|[-92.06, 45.63, -91.54, 46.16]|Some([212512.185, 151541.541, 253063.267, 210490.783])| + EPSG_8106|8106|"NAD83(HARN) / WISCRS Washburn (ftUS)"|Projected|[-92.06, 45.63, -91.54, 46.16]|Some([697217.06, 497182.538, 830258.403, 690585.179])| + EPSG_8107|8107|"NAD83(HARN) / WISCRS Walworth (m)"|Projected|[-88.78, 42.49, -88.3, 42.85]|Some([212968.682, 91151.113, 252432.077, 131171.947])| + EPSG_8108|8108|"NAD83(HARN) / WISCRS Walworth (ftUS)"|Projected|[-88.78, 42.49, -88.3, 42.85]|Some([698714.752, 299051.61, 828187.571, 430353.295])| + EPSG_8109|8109|"NAD83(HARN) / WISCRS Vilas (m)"|Projected|[-90.05, 45.85, -88.93, 46.3]|Some([90831.605, 25009.251, 177830.947, 75184.257])| + EPSG_8110|8110|"NAD83(HARN) / WISCRS Vilas (ftUS)"|Projected|[-90.05, 45.85, -88.93, 46.3]|Some([298003.357, 82051.183, 583433.698, 246667.018])| + EPSG_8111|8111|"NAD83(HARN) / WISCRS Vernon (m)"|Projected|[-91.28, 43.42, -90.31, 43.74]|Some([182281.699, 30305.983, 260838.614, 65980.24])| + EPSG_8112|8112|"NAD83(HARN) / WISCRS Vernon (ftUS)"|Projected|[-91.28, 43.42, -90.31, 43.74]|Some([598035.875, 99428.881, 855768.018, 216470.172])| + EPSG_8113|8113|"NAD83(HARN) / WISCRS Trempealeau (m)"|Projected|[-91.62, 43.98, -91.15, 44.6]|Some([236621.01, 90984.127, 274331.943, 159910.927])| + EPSG_8114|8114|"NAD83(HARN) / WISCRS Trempealeau (ftUS)"|Projected|[-91.62, 43.98, -91.15, 44.6]|Some([776314.098, 298503.756, 900037.384, 524641.1])| + EPSG_8115|8115|"NAD83(HARN) / WISCRS Taylor (m)"|Projected|[-90.93, 45.03, -90.04, 45.39]|Some([151946.206, 91316.81, 222087.318, 131424.897])| + EPSG_8116|8116|"NAD83(HARN) / WISCRS Taylor (ftUS)"|Projected|[-90.93, 45.03, -90.04, 45.39]|Some([498510.178, 299595.235, 728631.477, 431183.182])| + EPSG_8117|8117|"NAD83(HARN) / WISCRS St. Croix (m)"|Projected|[-92.81, 44.85, -92.13, 45.22]|Some([151540.881, 90442.643, 205298.486, 131686.472])| + EPSG_8118|8118|"NAD83(HARN) / WISCRS St. Croix (ftUS)"|Projected|[-92.81, 44.85, -92.13, 45.22]|Some([497180.373, 296727.237, 673550.117, 432041.368])| + EPSG_8119|8119|"NAD83(HARN) / WISCRS Shawano (m)"|Projected|[-89.23, 44.58, -88.24, 45.03]|Some([212838.379, 60437.024, 291467.44, 110635.932])| + EPSG_8120|8120|"NAD83(HARN) / WISCRS Shawano (ftUS)"|Projected|[-89.23, 44.58, -88.24, 45.03]|Some([698287.249, 198283.803, 956256.092, 362978.054])| + EPSG_8121|8121|"NAD83(HARN) / WISCRS Sawyer (m)"|Projected|[-91.56, 45.63, -90.67, 46.16]|Some([182141.358, 90710.78, 251546.154, 149720.048])| + EPSG_8122|8122|"NAD83(HARN) / WISCRS Sawyer (ftUS)"|Projected|[-91.56, 45.63, -90.67, 46.16]|Some([597575.438, 297606.95, 825281.008, 491206.525])| + EPSG_8123|8123|"NAD83(HARN) / WISCRS Sauk (m)"|Projected|[-90.32, 43.14, -89.59, 43.65]|Some([151453.452, 35611.74, 210845.275, 92360.78])| + EPSG_8124|8124|"NAD83(HARN) / WISCRS Sauk (ftUS)"|Projected|[-90.32, 43.14, -89.59, 43.65]|Some([496893.533, 116836.185, 691748.206, 303020.326])| + EPSG_8125|8125|"NAD83(HARN) / WISCRS Rusk (m)"|Projected|[-91.55, 45.29, -90.67, 45.64]|Some([212628.209, 152308.451, 281665.927, 191323.225])| + EPSG_8126|8126|"NAD83(HARN) / WISCRS Rusk (ftUS)"|Projected|[-91.55, 45.29, -90.67, 45.64]|Some([697597.715, 499698.642, 924098.963, 627699.615])| + EPSG_8127|8127|"NAD83(HARN) / WISCRS Rock (m)"|Projected|[-89.37, 42.49, -88.77, 42.85]|Some([121823.024, 60600.127, 171151.965, 100636.849])| + EPSG_8128|8128|"NAD83(HARN) / WISCRS Rock (ftUS)"|Projected|[-89.37, 42.49, -88.77, 42.85]|Some([399681.038, 198818.916, 561521.072, 330172.727])| + EPSG_8129|8129|"NAD83(HARN) / WISCRS Richland (m)"|Projected|[-90.68, 43.16, -90.19, 43.56]|Some([182100.245, 116221.304, 221953.083, 160693.06])| + EPSG_8130|8130|"NAD83(HARN) / WISCRS Richland (ftUS)"|Projected|[-90.68, 43.16, -90.19, 43.56]|Some([597440.553, 381302.728, 728191.072, 527207.147])| + EPSG_8131|8131|"NAD83(HARN) / WISCRS Price (m)"|Projected|[-90.68, 45.37, -90.04, 45.99]|Some([213019.213, 90515.039, 263158.6, 159527.437])| + EPSG_8132|8132|"NAD83(HARN) / WISCRS Price (ftUS)"|Projected|[-90.68, 45.37, -90.04, 45.99]|Some([698880.534, 296964.757, 863379.507, 523382.934])| + EPSG_8133|8133|"NAD83(HARN) / WISCRS Portage (m)"|Projected|[-89.85, 44.24, -89.22, 44.69]|Some([28428.706, 30372.048, 78756.587, 80438.066])| + EPSG_8134|8134|"NAD83(HARN) / WISCRS Portage (ftUS)"|Projected|[-89.85, 44.24, -89.22, 44.69]|Some([93269.845, 99645.627, 258387.236, 263903.889])| + EPSG_8135|8135|"NAD83(HARN) / WISCRS Polk (m)"|Projected|[-92.89, 45.2, -92.15, 45.73]|Some([121565.172, 59888.702, 179710.999, 118909.493])| + EPSG_8136|8136|"NAD83(HARN) / WISCRS Polk (ftUS)"|Projected|[-92.89, 45.2, -92.15, 45.73]|Some([398835.067, 196484.85, 589601.836, 390122.229])| + EPSG_8137|8137|"NAD83(HARN) / WISCRS Pepin and Pierce (m)"|Projected|[-92.81, 44.4, -91.65, 44.87]|Some([121256.285, 59789.755, 213671.465, 112184.623])| + EPSG_8138|8138|"NAD83(HARN) / WISCRS Pepin and Pierce (ftUS)"|Projected|[-92.81, 44.4, -91.65, 44.87]|Some([397821.661, 196160.22, 701020.464, 368059.052])| + EPSG_8139|8139|"NAD83(HARN) / WISCRS Oneida (m)"|Projected|[-90.05, 45.46, -89.04, 45.91]|Some([30560.638, 30441.362, 109561.764, 80584.117])| + EPSG_8140|8140|"NAD83(HARN) / WISCRS Oneida (ftUS)"|Projected|[-90.05, 45.46, -89.04, 45.91]|Some([100264.361, 99873.036, 359453.887, 264383.058])| + EPSG_8141|8141|"NAD83(HARN) / WISCRS Oconto (m)"|Projected|[-88.69, 44.67, -87.76, 45.38]|Some([120894.665, 30311.613, 194643.674, 109514.63])| + EPSG_8142|8142|"NAD83(HARN) / WISCRS Oconto (ftUS)"|Projected|[-88.69, 44.67, -87.76, 45.38]|Some([396635.248, 99447.35, 638593.454, 359299.248])| + EPSG_8143|8143|"NAD83(HARN) / WISCRS Monroe (m)"|Projected|[-90.98, 43.72, -90.31, 44.17]|Some([177256.602, 90802.752, 231249.655, 140860.586])| + EPSG_8144|8144|"NAD83(HARN) / WISCRS Monroe (ftUS)"|Projected|[-90.98, 43.72, -90.31, 44.17]|Some([581549.367, 297908.696, 758691.577, 462140.107])| + EPSG_8145|8145|"NAD83(HARN) / WISCRS Menominee (m)"|Projected|[-88.99, 44.85, -88.48, 45.12]|Some([60136.546, 14818.574, 100478.122, 44983.145])| + EPSG_8146|8146|"NAD83(HARN) / WISCRS Menominee (ftUS)"|Projected|[-88.99, 44.85, -88.48, 45.12]|Some([197297.985, 48617.272, 329651.973, 147582.202])| + EPSG_8147|8147|"NAD83(HARN) / WISCRS Marinette (m)"|Projected|[-88.43, 44.96, -87.48, 45.8]|Some([181936.484, 29819.264, 256894.83, 123429.701])| + EPSG_8148|8148|"NAD83(HARN) / WISCRS Marinette (ftUS)"|Projected|[-88.43, 44.96, -87.48, 45.8]|Some([596903.281, 97832.036, 842829.123, 404952.276])| + EPSG_8149|8149|"NAD83(HARN) / WISCRS Marathon (m)"|Projected|[-90.32, 44.68, -89.22, 45.13]|Some([31067.9, 30498.39, 118285.439, 80656.239])| + EPSG_8150|8150|"NAD83(HARN) / WISCRS Marathon (ftUS)"|Projected|[-90.32, 44.68, -89.22, 45.13]|Some([101928.601, 100060.136, 388074.811, 264619.678])| + EPSG_8151|8151|"NAD83(HARN) / WISCRS Lincoln (m)"|Projected|[-90.05, 45.11, -89.42, 45.56]|Some([91207.72, 29512.399, 140789.049, 79576.416])| + EPSG_8152|8152|"NAD83(HARN) / WISCRS Lincoln (ftUS)"|Projected|[-90.05, 45.11, -89.42, 45.56]|Some([299237.326, 96825.262, 461905.405, 261076.958])| + EPSG_8153|8153|"NAD83(HARN) / WISCRS Langlade (m)"|Projected|[-89.43, 45.02, -88.63, 45.48]|Some([167158.756, 90359.681, 230218.134, 141564.648])| + EPSG_8154|8154|"NAD83(HARN) / WISCRS Langlade (ftUS)"|Projected|[-89.43, 45.02, -88.63, 45.48]|Some([548420.018, 296455.052, 755307.329, 464450.016])| + EPSG_8155|8155|"NAD83(HARN) / WISCRS La Crosse (m)"|Projected|[-91.43, 43.72, -90.91, 44.1]|Some([121322.25, 29874.779, 163226.458, 72178.498])| + EPSG_8156|8156|"NAD83(HARN) / WISCRS La Crosse (ftUS)"|Projected|[-91.43, 43.72, -90.91, 44.1]|Some([398038.081, 98014.171, 535518.804, 236805.624])| + EPSG_8157|8157|"NAD83(HARN) / WISCRS Kewaunee, Manitowoc and Sheboygan (m)"|Projected|[-88.17, 43.54, -87.37, 44.68]|Some([29746.587, 30367.324, 94406.754, 157227.667])| + EPSG_8158|8158|"NAD83(HARN) / WISCRS Kewaunee, Manitowoc and Sheboygan (ftUS)"|Projected|[-88.17, 43.54, -87.37, 44.68]|Some([97593.596, 99630.129, 309732.826, 515837.77])| + EPSG_8159|8159|"NAD83(HARN) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (m)"|Projected|[-88.31, 42.49, -87.75, 43.55]|Some([151764.236, 30361.828, 197804.237, 148207.489])| + EPSG_8160|8160|"NAD83(HARN) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (ftUS)"|Projected|[-88.31, 42.49, -87.75, 43.55]|Some([497913.165, 99612.097, 648962.734, 486244.07])| + EPSG_8161|8161|"NAD83(HARN) / WISCRS Jackson (m)"|Projected|[-91.17, 44.07, -90.31, 44.6]|Some([906.92, 4627.084, 69805.544, 63660.941])| + EPSG_8162|8162|"NAD83(HARN) / WISCRS Jackson (ftUS)"|Projected|[-91.17, 44.07, -90.31, 44.6]|Some([2975.454, 15180.692, 229020.356, 208860.936])| + EPSG_8163|8163|"NAD83(HARN) / WISCRS Iron (m)"|Projected|[-90.56, 45.98, -89.92, 46.6]|Some([197387.617, 60762.797, 246985.33, 129739.473])| + EPSG_8164|8164|"NAD83(HARN) / WISCRS Iron (ftUS)"|Projected|[-90.56, 45.98, -89.92, 46.6]|Some([647595.875, 199352.609, 810317.705, 425653.587])| + EPSG_8165|8165|"NAD83(HARN) / WISCRS Iowa (m)"|Projected|[-90.43, 42.81, -89.83, 43.21]|Some([91087.787, 30117.114, 140164.793, 74609.23])| + EPSG_8166|8166|"NAD83(HARN) / WISCRS Iowa (ftUS)"|Projected|[-90.43, 42.81, -89.83, 43.21]|Some([298843.847, 98809.231, 459857.325, 244780.448])| + EPSG_8167|8167|"NAD83(HARN) / WISCRS Green Lake and Marquette (m)"|Projected|[-89.6, 43.63, -88.88, 43.99]|Some([121957.215, 59503.27, 180065.419, 99567.164])| + EPSG_8168|8168|"NAD83(HARN) / WISCRS Green Lake and Marquette (ftUS)"|Projected|[-89.6, 43.63, -88.88, 43.99]|Some([400121.298, 195220.31, 590764.63, 326663.271])| + EPSG_8169|8169|"NAD83(HARN) / WISCRS Green and Lafayette (m)"|Projected|[-90.43, 42.5, -89.36, 42.86]|Some([121488.878, 30547.91, 209444.887, 70709.374])| + EPSG_8170|8170|"NAD83(HARN) / WISCRS Green and Lafayette (ftUS)"|Projected|[-90.43, 42.5, -89.36, 42.86]|Some([398584.761, 100222.601, 687153.767, 231985.673])| + EPSG_8171|8171|"NAD83(HARN) / WISCRS Grant (m)"|Projected|[-91.16, 42.5, -90.42, 43.22]|Some([212724.337, 120948.949, 273553.759, 201006.606])| + EPSG_8172|8172|"NAD83(HARN) / WISCRS Grant (ftUS)"|Projected|[-91.16, 42.5, -90.42, 43.22]|Some([697913.094, 396813.343, 897484.29, 659469.173])| + EPSG_8173|8173|"NAD83(HARN) / WISCRS Forest (m)"|Projected|[-89.05, 45.37, -88.42, 46.08]|Some([243202.141, 151634.108, 292558.216, 230637.442])| + EPSG_8177|8177|"NAD83(HARN) / WISCRS Forest (ftUS)"|Projected|[-89.05, 45.37, -88.42, 46.08]|Some([797905.69, 497486.235, 959834.748, 756683.01])| + EPSG_8179|8179|"NAD83(HARN) / WISCRS Dunn (m)"|Projected|[-92.16, 44.68, -91.64, 45.21]|Some([30761.378, 30188.74, 71990.995, 89124.717])| + EPSG_8180|8180|"NAD83(HARN) / WISCRS Dunn (ftUS)"|Projected|[-92.16, 44.68, -91.64, 45.21]|Some([100922.954, 99044.226, 236190.455, 292403.344])| + EPSG_8181|8181|"NAD83(HARN) / WISCRS Douglas (m)"|Projected|[-92.3, 46.15, -91.55, 46.76]|Some([29516.839, 29640.698, 87459.329, 97522.363])| + EPSG_8182|8182|"NAD83(HARN) / WISCRS Douglas (ftUS)"|Projected|[-92.3, 46.15, -91.55, 46.76]|Some([96839.829, 97246.191, 286939.483, 319954.619])| + EPSG_8184|8184|"NAD83(HARN) / WISCRS Door (m)"|Projected|[-87.74, 44.67, -86.8, 45.43]|Some([121706.986, 30002.766, 196248.612, 114573.716])| + EPSG_8185|8185|"NAD83(HARN) / WISCRS Door (ftUS)"|Projected|[-87.74, 44.67, -86.8, 45.43]|Some([399300.338, 98434.076, 643858.989, 375897.267])| + EPSG_8187|8187|"NAD83(HARN) / WISCRS Dodge and Jefferson (m)"|Projected|[-89.02, 42.84, -88.4, 43.64]|Some([243318.231, 151932.115, 294006.379, 240881.446])| + EPSG_8189|8189|"NAD83(HARN) / WISCRS Dodge and Jefferson (ftUS)"|Projected|[-89.02, 42.84, -88.4, 43.64]|Some([798286.564, 498463.947, 964585.929, 790291.878])| + EPSG_8191|8191|"NAD83(HARN) / WISCRS Dane (m)"|Projected|[-89.84, 42.84, -89.0, 43.3]|Some([213038.102, 121092.7, 281712.863, 172284.287])| + EPSG_8193|8193|"NAD83(HARN) / WISCRS Dane (ftUS)"|Projected|[-89.84, 42.84, -89.0, 43.3]|Some([698942.508, 397284.968, 924252.951, 565236.031])| + EPSG_8196|8196|"NAD83(HARN) / WISCRS Crawford (m)"|Projected|[-91.22, 42.98, -90.66, 43.43]|Some([90760.747, 29254.369, 136440.334, 79287.989])| + EPSG_8197|8197|"NAD83(HARN) / WISCRS Crawford (ftUS)"|Projected|[-91.22, 42.98, -90.66, 43.43]|Some([297770.884, 95978.71, 447637.995, 260130.677])| + EPSG_8198|8198|"NAD83(HARN) / WISCRS Columbia (m)"|Projected|[-89.79, 43.28, -89.0, 43.65]|Some([137056.55, 91287.054, 201182.955, 132472.0])| + EPSG_8200|8200|"NAD83(HARN) / WISCRS Columbia (ftUS)"|Projected|[-89.79, 43.28, -89.0, 43.65]|Some([449659.699, 299497.61, 660047.744, 434618.553])| + EPSG_8201|8201|"NAD83(HARN) / WISCRS Clark (m)"|Projected|[-90.93, 44.42, -90.31, 45.04]|Some([182295.783, 91115.542, 231673.773, 160094.339])| + EPSG_8202|8202|"NAD83(HARN) / WISCRS Clark (ftUS)"|Projected|[-90.93, 44.42, -90.31, 45.04]|Some([598082.083, 298934.907, 760083.039, 525242.845])| + EPSG_8203|8203|"NAD83(HARN) / WISCRS Chippewa (m)"|Projected|[-91.67, 44.85, -90.92, 45.3]|Some([30356.625, 29881.215, 89648.088, 79961.563])| + EPSG_8204|8204|"NAD83(HARN) / WISCRS Chippewa (ftUS)"|Projected|[-91.67, 44.85, -90.92, 45.3]|Some([99595.028, 98035.285, 294120.436, 262340.561])| + EPSG_8205|8205|"NAD83(HARN) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (m)"|Projected|[-88.89, 43.54, -88.04, 44.6]|Some([213233.393, 91161.577, 281935.161, 209048.281])| + EPSG_8206|8206|"NAD83(HARN) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (ftUS)"|Projected|[-88.89, 43.54, -88.04, 44.6]|Some([699583.223, 299085.94, 924982.275, 685852.568])| + EPSG_8207|8207|"NAD83(HARN) / WISCRS Burnett (m)"|Projected|[-92.89, 45.63, -92.03, 46.16]|Some([30303.394, 29576.969, 97367.447, 88578.971])| + EPSG_8208|8208|"NAD83(HARN) / WISCRS Burnett (ftUS)"|Projected|[-92.89, 45.63, -92.03, 46.16]|Some([99420.384, 97037.106, 319446.365, 290612.839])| + EPSG_8209|8209|"NAD83(HARN) / WISCRS Buffalo (m)"|Projected|[-92.09, 44.02, -91.52, 44.6]|Some([151785.317, 59845.107, 197489.236, 124337.848])| + EPSG_8210|8210|"NAD83(HARN) / WISCRS Buffalo (ftUS)"|Projected|[-92.09, 44.02, -91.52, 44.6]|Some([497982.329, 196341.821, 647929.27, 407931.758])| + EPSG_8212|8212|"NAD83(HARN) / WISCRS Brown (m)"|Projected|[-88.26, 44.24, -87.76, 44.68]|Some([10830.698, 142371.848, 50772.581, 191299.024])| + EPSG_8213|8213|"NAD83(HARN) / WISCRS Brown (ftUS)"|Projected|[-88.26, 44.24, -87.76, 44.68]|Some([35533.715, 467098.304, 166576.376, 627620.215])| + EPSG_8214|8214|"NAD83(HARN) / WISCRS Bayfield (m)"|Projected|[-91.56, 46.15, -90.75, 47.01]|Some([197139.401, 90784.098, 259719.25, 186468.182])| + EPSG_8216|8216|"NAD83(HARN) / WISCRS Bayfield (ftUS)"|Projected|[-91.56, 46.15, -90.75, 47.01]|Some([646781.518, 297847.494, 852095.573, 611771.027])| + EPSG_8218|8218|"NAD83(HARN) / WISCRS Barron (m)"|Projected|[-92.16, 45.2, -91.53, 45.65]|Some([68792.053, 7408.473, 118294.844, 57473.738])| + EPSG_8220|8220|"NAD83(HARN) / WISCRS Barron (ftUS)"|Projected|[-92.16, 45.2, -91.53, 45.65]|Some([225695.259, 24305.965, 388105.666, 188561.754])| + EPSG_8222|8222|"NAD83(HARN) / WISCRS Ashland (m)"|Projected|[-90.93, 45.98, -90.3, 47.09]|Some([148971.242, 30442.88, 197793.105, 153888.903])| + EPSG_8224|8224|"NAD83(HARN) / WISCRS Ashland (ftUS)"|Projected|[-90.93, 45.98, -90.3, 47.09]|Some([488749.815, 99878.016, 648926.211, 504883.842])| + EPSG_8225|8225|"NAD83(HARN) / WISCRS Adams and Juneau (m)"|Projected|[-90.32, 43.64, -89.59, 44.25]|Some([121397.613, 30368.259, 180303.158, 98230.289])| + EPSG_8226|8226|"NAD83(HARN) / WISCRS Adams and Juneau (ftUS)"|Projected|[-90.32, 43.64, -89.59, 44.25]|Some([398285.334, 99633.195, 591544.61, 322277.206])| + EPSG_8232|8232|"NAD83(CSRS96)"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_8237|8237|"NAD83(CSRS)v2"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_8240|8240|"NAD83(CSRS)v3"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_8246|8246|"NAD83(CSRS)v4"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_8249|8249|"NAD83(CSRS)v5"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_8252|8252|"NAD83(CSRS)v6"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_8255|8255|"NAD83(CSRS)v7"|Geographic2d|[-141.01, 38.21, -40.73, 86.46]|Some([-141.01, 38.21, -40.73, 86.46])| + EPSG_8311|8311|"NAD83(2011) / Oregon Burns-Harper zone (m)"|Projected|[-118.61, 43.49, -117.49, 44.19]|Some([13682.872, -1111.131, 104292.573, 77108.939])| + EPSG_8312|8312|"NAD83(2011) / Oregon Burns-Harper zone (ft)"|Projected|[-118.61, 43.49, -117.49, 44.19]|Some([44891.313, -3645.442, 342167.234, 252982.085])| + EPSG_8313|8313|"NAD83(2011) / Oregon Canyon City-Burns zone (m)"|Projected|[-119.22, 43.52, -118.52, 44.36]|Some([2209.089, 2222.591, 58816.551, 95688.468])| + EPSG_8314|8314|"NAD83(2011) / Oregon Canyon City-Burns zone (ft)"|Projected|[-119.22, 43.52, -118.52, 44.36]|Some([7247.669, 7291.965, 192967.688, 313938.543])| + EPSG_8315|8315|"NAD83(2011) / Oregon Coast Range North zone (m)"|Projected|[-123.81, 45.4, -123.01, 45.98]|Some([-797.964, -376.851, 61841.954, 64170.552])| + EPSG_8316|8316|"NAD83(2011) / Oregon Coast Range North zone (ft)"|Projected|[-123.81, 45.4, -123.01, 45.98]|Some([-2617.991, -1236.386, 202893.549, 210533.307])| + EPSG_8317|8317|"NAD83(2011) / Oregon Dayville-Prairie City zone (m)"|Projected|[-119.89, 44.24, -118.61, 44.94]|Some([-505.554, -1111.303, 101756.024, 77194.261])| + EPSG_8318|8318|"NAD83(2011) / Oregon Dayville-Prairie City zone (ft)"|Projected|[-119.89, 44.24, -118.61, 44.94]|Some([-1658.641, -3646.008, 333845.222, 253262.01])| + EPSG_8319|8319|"NAD83(2011) / Oregon Denio-Burns zone (m)"|Projected|[-119.41, 41.88, -117.67, 43.54]|Some([-2468.988, 14441.829, 141989.962, 199360.944])| + EPSG_8320|8320|"NAD83(2011) / Oregon Denio-Burns zone (ft)"|Projected|[-119.41, 41.88, -117.67, 43.54]|Some([-8100.355, 47381.33, 465846.333, 654071.338])| + EPSG_8321|8321|"NAD83(2011) / Oregon Halfway zone (m)"|Projected|[-117.61, 44.61, -116.63, 45.28]|Some([11419.137, -1130.977, 89222.275, 73521.33])| + EPSG_8322|8322|"NAD83(2011) / Oregon Halfway zone (ft)"|Projected|[-117.61, 44.61, -116.63, 45.28]|Some([37464.362, -3710.556, 292724.001, 241211.711])| + EPSG_8323|8323|"NAD83(2011) / Oregon Medford-Diamond Lake zone (m)"|Projected|[-122.73, 42.53, -121.71, 43.34]|Some([20560.284, -1125.23, 104369.618, 89013.329])| + EPSG_8324|8324|"NAD83(2011) / Oregon Medford-Diamond Lake zone (ft)"|Projected|[-122.73, 42.53, -121.71, 43.34]|Some([67455.001, -3691.699, 342420.008, 292038.48])| + EPSG_8325|8325|"NAD83(2011) / Oregon Mitchell zone (m)"|Projected|[-120.56, 44.38, -119.82, 44.78]|Some([5288.08, -1087.856, 64277.741, 43461.493])| + EPSG_8326|8326|"NAD83(2011) / Oregon Mitchell zone (ft)"|Projected|[-120.56, 44.38, -119.82, 44.78]|Some([17349.345, -3569.083, 210884.978, 142590.201])| + EPSG_8327|8327|"NAD83(2011) / Oregon North Central zone (m)"|Projected|[-121.79, 44.89, -119.03, 45.95]|Some([-1927.393, -1903.051, 216148.295, 116971.564])| + EPSG_8328|8328|"NAD83(2011) / Oregon North Central zone (ft)"|Projected|[-121.79, 44.89, -119.03, 45.95]|Some([-6323.466, -6243.606, 709147.949, 383764.975])| + EPSG_8329|8329|"NAD83(2011) / Oregon Ochoco Summit zone (m)"|Projected|[-121.01, 44.21, -120.31, 44.61]|Some([-765.926, -1105.561, 55187.388, 43474.964])| + EPSG_8330|8330|"NAD83(2011) / Oregon Ochoco Summit zone (ft)"|Projected|[-121.01, 44.21, -120.31, 44.61]|Some([-2512.882, -3627.168, 181060.983, 142634.396])| + EPSG_8331|8331|"NAD83(2011) / Oregon Owyhee zone (m)"|Projected|[-118.14, 41.88, -116.85, 43.54]|Some([23784.796, 14441.662, 130882.383, 199140.772])| + EPSG_8332|8332|"NAD83(2011) / Oregon Owyhee zone (ft)"|Projected|[-118.14, 41.88, -116.85, 43.54]|Some([78034.106, 47380.78, 429404.145, 653348.99])| + EPSG_8333|8333|"NAD83(2011) / Oregon Pilot Rock-Ukiah zone (m)"|Projected|[-119.65, 44.99, -118.11, 46.04]|Some([-1270.766, -790.591, 120200.825, 116306.197])| + EPSG_8334|8334|"NAD83(2011) / Oregon Pilot Rock-Ukiah zone (ft)"|Projected|[-119.65, 44.99, -118.11, 46.04]|Some([-4169.181, -2593.804, 394359.663, 381582.011])| + EPSG_8335|8335|"NAD83(2011) / Oregon Prairie City-Brogan zone (m)"|Projected|[-118.77, 44.16, -117.48, 45.02]|Some([-1602.375, 17781.262, 101601.933, 113653.293])| + EPSG_8336|8336|"NAD83(2011) / Oregon Prairie City-Brogan zone (ft)"|Projected|[-118.77, 44.16, -117.48, 45.02]|Some([-5257.137, 58337.473, 333339.674, 372878.257])| + EPSG_8337|8337|"NAD83(2011) / Oregon Riley-Lakeview zone (m)"|Projected|[-120.97, 41.88, -119.15, 43.45]|Some([17141.212, 14442.6, 168245.942, 189565.453])| + EPSG_8338|8338|"NAD83(2011) / Oregon Riley-Lakeview zone (ft)"|Projected|[-120.97, 41.88, -119.15, 43.45]|Some([56237.571, 47383.857, 551987.997, 621933.902])| + EPSG_8339|8339|"NAD83(2011) / Oregon Siskiyou Pass zone (m)"|Projected|[-122.71, 41.95, -121.96, 42.46]|Some([-504.693, -1102.806, 61693.703, 55744.471])| + EPSG_8340|8340|"NAD83(2011) / Oregon Siskiyou Pass zone (ft)"|Projected|[-122.71, 41.95, -121.96, 42.46]|Some([-1655.818, -3618.13, 202407.163, 182888.684])| + EPSG_8341|8341|"NAD83(2011) / Oregon Ukiah-Fox zone (m)"|Projected|[-119.35, 44.52, -118.64, 45.21]|Some([2168.121, 8861.917, 58627.071, 85617.033])| + EPSG_8342|8342|"NAD83(2011) / Oregon Ukiah-Fox zone (ft)"|Projected|[-119.35, 44.52, -118.64, 45.21]|Some([7113.257, 29074.531, 192346.033, 280895.778])| + EPSG_8343|8343|"NAD83(2011) / Oregon Wallowa zone (m)"|Projected|[-118.16, 45.27, -116.42, 46.05]|Some([8195.983, 2223.406, 144770.185, 89500.216])| + EPSG_8344|8344|"NAD83(2011) / Oregon Wallowa zone (ft)"|Projected|[-118.16, 45.27, -116.42, 46.05]|Some([26889.707, 7294.64, 474967.8, 293635.881])| + EPSG_8345|8345|"NAD83(2011) / Oregon Warner Highway zone (m)"|Projected|[-120.42, 41.95, -119.3, 42.43]|Some([5165.469, -1108.569, 98057.13, 52460.114])| + EPSG_8346|8346|"NAD83(2011) / Oregon Warner Highway zone (ft)"|Projected|[-120.42, 41.95, -119.3, 42.43]|Some([16947.076, -3637.036, 321709.744, 172113.235])| + EPSG_8347|8347|"NAD83(2011) / Oregon Willamette Pass zone (m)"|Projected|[-122.26, 42.99, -121.48, 44.18]|Some([-1208.823, -1111.174, 62417.678, 131263.772])| + EPSG_8348|8348|"NAD83(2011) / Oregon Willamette Pass zone (ft)"|Projected|[-122.26, 42.99, -121.48, 44.18]|Some([-3965.955, -3645.584, 204782.407, 430655.421])| + EPSG_8351|8351|"S-JTSK [JTSK03]"|Geographic2d|[16.84, 47.73, 22.56, 49.61]|Some([16.841, 47.73, 22.562, 49.611])| + EPSG_8352|8352|"S-JTSK [JTSK03] / Krovak"|Projected|[16.84, 47.73, 22.56, 49.61]|Some([1116657.704, 164132.175, 1353306.188, 598360.445])| + EPSG_8353|8353|"S-JTSK [JTSK03] / Krovak East North"|Projected|[16.84, 47.73, 22.56, 49.61]|Some([-598360.445, -1353306.188, -164132.175, -1116657.704])| + EPSG_8379|8379|"NAD83 / NCRS Las Vegas (m)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([51834.918, 158940.638, 123179.364, 220107.951])| + EPSG_8380|8380|"NAD83 / NCRS Las Vegas (ftUS)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([170061.728, 521457.744, 404130.964, 722137.504])| + EPSG_8381|8381|"NAD83 / NCRS Las Vegas high (m)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([251833.233, 358939.201, 323180.175, 420108.655])| + EPSG_8382|8382|"NAD83 / NCRS Las Vegas high (ftUS)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([826222.865, 1177619.696, 1060300.292, 1378306.48])| + EPSG_8383|8383|"NAD83(2011) / NCRS Las Vegas (m)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([51834.918, 158940.638, 123179.364, 220107.951])| + EPSG_8384|8384|"NAD83(2011) / NCRS Las Vegas (ftUS)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([170061.728, 521457.744, 404130.964, 722137.504])| + EPSG_8385|8385|"NAD83(2011) / NCRS Las Vegas high (m)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([251833.233, 358939.201, 323180.175, 420108.655])| + EPSG_8387|8387|"NAD83(2011) / NCRS Las Vegas high (ftUS)"|Projected|[-115.5, 35.88, -114.71, 36.43]|Some([826222.865, 1177619.696, 1060300.292, 1378306.48])| + EPSG_8391|8391|"GDA94 / WEIPA94"|Projected|[141.5, -13.5, 142.51, -11.49]|Some([354128.51, 506575.893, 464751.635, 729374.692])| + EPSG_8395|8395|"ETRS89 / Gauss-Kruger CM 9E"|Projected|[8.33, 53.36, 10.41, 53.99]|Some([455393.394, 5914684.905, 593871.499, 5985723.555])| + EPSG_8427|8427|"Hong Kong Geodetic CS"|Geographic2d|[113.76, 22.13, 114.51, 22.58]|Some([113.76, 22.13, 114.51, 22.58])| + EPSG_8428|8428|"Macao 1920"|Geographic2d|[113.52, 22.06, 113.68, 22.23]|Some([113.517, 22.061, 113.677, 22.231])| + EPSG_8431|8431|"Macao 2008"|Geographic2d|[113.52, 22.06, 113.68, 22.23]|Some([113.52, 22.06, 113.68, 22.23])| + EPSG_8433|8433|"Macao 1920 / Macao Grid"|Projected|[113.52, 22.06, 113.68, 22.23]|Some([17991.638, 3256.675, 34512.655, 22095.112])| + EPSG_8441|8441|"Tananarive / Laborde Grid"|Projected|[43.18, -25.64, 50.56, -11.89]|Some([45119.744, 46899.845, 849613.925, 1575714.152])| + EPSG_8455|8455|"RGTAAF07 / UTM zone 53S"|Projected|[136.0, -66.73, 138.01, -65.61]|Some([544083.962, 2595516.223, 638654.557, 2723193.122])| + EPSG_8456|8456|"RGTAAF07 / UTM zone 54S"|Projected|[138.0, -67.13, 142.0, -66.1]|Some([364415.299, 2550989.342, 545207.235, 2668941.996])| + EPSG_8518|8518|"NAD83(2011) / KS RCS zone 1"|Projected|[-102.06, 38.69, -101.38, 40.01]|Some([1368675.529, 433426.253, 1562807.241, 914637.553])| + EPSG_8519|8519|"NAD83(2011) / KS RCS zone 2"|Projected|[-101.49, 38.69, -100.71, 40.01]|Some([2345839.743, 433416.705, 2568515.49, 914743.186])| + EPSG_8520|8520|"NAD83(2011) / KS RCS zone 3"|Projected|[-100.82, 38.69, -100.14, 40.01]|Some([3365826.001, 433408.905, 3559949.966, 914615.591])| + EPSG_8521|8521|"NAD83(2011) / KS RCS zone 4"|Projected|[-100.19, 38.69, -99.03, 40.01]|Some([4288753.729, 433394.159, 4619896.033, 915098.02])| + EPSG_8522|8522|"NAD83(2011) / KS RCS zone 5"|Projected|[-99.07, 38.26, -98.47, 40.01]|Some([5384176.274, 276778.27, 5556475.948, 914489.326])| + EPSG_8523|8523|"NAD83(2011) / KS RCS zone 6"|Projected|[-98.51, 38.15, -97.92, 40.01]|Some([6396465.537, 236713.713, 6566146.956, 914428.191])| + EPSG_8524|8524|"NAD83(2011) / KS RCS zone 7"|Projected|[-97.94, 38.08, -96.8, 40.01]|Some([7325361.447, 211216.147, 7653528.241, 914785.597])| + EPSG_8525|8525|"NAD83(2011) / KS RCS zone 8"|Projected|[-96.97, 38.73, -95.94, 39.57]|Some([8365907.461, 440948.89, 8659769.534, 747407.68])| + EPSG_8526|8526|"NAD83(2011) / KS RCS zone 9"|Projected|[-96.94, 38.08, -95.94, 38.88]|Some([9373336.617, 147035.891, 9661207.562, 438890.448])| + EPSG_8527|8527|"NAD83(2011) / KS RCS zone 10"|Projected|[-96.81, 39.21, -94.85, 40.01]|Some([10199620.153, 545793.722, 10755041.145, 838968.794])| + EPSG_8528|8528|"NAD83(2011) / KS RCS zone 11"|Projected|[-96.04, 38.73, -94.58, 39.43]|Some([11274616.558, 465234.173, 11691148.659, 721174.11])| + EPSG_8529|8529|"NAD83(2011) / KS RCS zone 12"|Projected|[-102.05, 36.99, -101.06, 38.71]|Some([12314994.944, 87394.174, 12604186.654, 714447.51])| + EPSG_8531|8531|"NAD83(2011) / KS RCS zone 13"|Projected|[-101.13, 36.99, -100.08, 38.71]|Some([13286763.542, 87391.442, 13593472.947, 714630.643])| + EPSG_8533|8533|"NAD83(2011) / KS RCS zone 14"|Projected|[-100.25, 36.99, -99.54, 38.7]|Some([14329608.079, 87390.461, 14536999.219, 710679.596])| + EPSG_8534|8534|"NAD83(2011) / KS RCS zone 15"|Projected|[-99.59, 36.99, -98.91, 38.7]|Some([15386082.272, 87389.514, 15584707.972, 710379.417])| + EPSG_8535|8535|"NAD83(2011) / KS RCS zone 16"|Projected|[-99.03, 36.99, -98.34, 38.27]|Some([16359795.934, 87387.922, 16561339.117, 553874.335])| + EPSG_8536|8536|"NAD83(2011) / KS RCS zone 17"|Projected|[-98.48, 37.38, -96.52, 38.18]|Some([17215220.649, 259191.954, 17784779.351, 552004.978])| + EPSG_8538|8538|"NAD83(2011) / KS RCS zone 18"|Projected|[-98.35, 36.99, -96.52, 37.48]|Some([18251728.172, 129602.487, 18786241.551, 309498.517])| + EPSG_8539|8539|"NAD83(2011) / KS RCS zone 19"|Projected|[-96.53, 36.99, -95.5, 38.88]|Some([19335460.507, 87385.15, 19636304.714, 776159.859])| + EPSG_8540|8540|"NAD83(2011) / KS RCS zone 20"|Projected|[-95.53, 36.99, -94.6, 38.74]|Some([20369537.342, 87384.834, 20641172.347, 725035.376])| + EPSG_8545|8545|"NAD83(HARN Corrected)"|Geographic2d|[-67.97, 17.62, -64.51, 18.57]|Some([-67.97, 17.62, -64.51, 18.57])| + EPSG_8677|8677|"MGI 1901 / Balkans zone 5"|Projected|[13.38, 42.95, 16.5, 46.88]|Some([5368179.787, 4756203.746, 5622776.612, 5194172.126])| + EPSG_8678|8678|"MGI 1901 / Balkans zone 6"|Projected|[16.5, 41.79, 19.51, 46.55]|Some([6375729.807, 4627349.792, 6625908.928, 5157318.823])| + EPSG_8679|8679|"MGI 1901 / Balkans zone 8"|Projected|[22.5, 41.11, 23.04, 44.7]|Some([8374491.808, 4552257.727, 8424383.276, 4951686.065])| + EPSG_8682|8682|"SRB_ETRS89 / UTM zone 34N"|Projected|[18.81, 42.23, 23.01, 46.19]|Some([319279.673, 4675314.585, 665865.976, 5117489.385])| + EPSG_8685|8685|"SRB_ETRS89"|Geographic2d|[18.81, 42.23, 23.01, 46.19]|Some([18.81, 42.23, 23.01, 46.19])| + EPSG_8686|8686|"MGI 1901 / Slovenia Grid"|Projected|[13.38, 45.42, 16.61, 46.88]|Some([373591.224, 5030633.629, 626370.811, 5194172.126])| + EPSG_8687|8687|"Slovenia 1996 / UTM zone 33N"|Projected|[13.38, 45.42, 16.61, 46.88]|Some([373255.693, 5029608.812, 625961.938, 5193102.992])| + EPSG_8692|8692|"NAD83(MA11) / UTM zone 54N"|Projected|[141.19, 10.95, 144.01, 23.9]|Some([519340.032, 1210457.457, 829020.652, 2646418.809])| + EPSG_8693|8693|"NAD83(MA11) / UTM zone 55N"|Projected|[144.0, 11.05, 149.55, 23.9]|Some([172184.088, 1221508.258, 778610.549, 2646397.14])| + EPSG_8694|8694|"Camacupa 2015"|Geographic2d|[8.2, -18.02, 24.09, -4.38]|Some([8.203, -18.019, 24.093, -4.378])| + EPSG_8699|8699|"RSAO13"|Geographic2d|[8.2, -18.02, 24.09, -4.38]|Some([8.2, -18.02, 24.09, -4.38])| + EPSG_8818|8818|"MTRF-2000"|Geographic2d|[34.44, 16.29, 55.67, 32.16]|Some([34.44, 16.29, 55.67, 32.16])| + EPSG_8826|8826|"NAD83 / Idaho Transverse Mercator"|Projected|[-117.24, 41.99, -111.04, 49.01]|Some([2231613.668, 1198891.862, 2745190.158, 1983850.043])| + EPSG_8836|8836|"MTRF-2000 / UTM zone 36N"|Projected|[34.44, 24.92, 36.01, 29.38]|Some([639747.529, 2756859.565, 804025.419, 3253854.8])| + EPSG_8837|8837|"MTRF-2000 / UTM zone 37N"|Projected|[36.0, 16.29, 42.0, 32.16]|Some([179368.037, 1801013.994, 820631.963, 3562115.601])| + EPSG_8838|8838|"MTRF-2000 / UTM zone 38N"|Projected|[41.99, 16.35, 48.0, 31.15]|Some([178396.615, 1807651.065, 820534.106, 3450127.254])| + EPSG_8839|8839|"MTRF-2000 / UTM zone 39N"|Projected|[47.99, 17.94, 54.01, 28.94]|Some([181126.261, 1983547.396, 818873.739, 3205069.819])| + EPSG_8840|8840|"MTRF-2000 / UTM zone 40N"|Projected|[54.0, 19.66, 55.67, 22.77]|Some([185404.074, 2174402.148, 363461.087, 2521182.59])| + EPSG_8857|8857|"WGS 84 / Equal Earth Greenwich"|Projected|[-180.0, -90.0, 180.0, 90.0]|Some([-17243959.062, -8392927.598, 17243959.062, 8392927.598])| + EPSG_8858|8858|"WGS 84 / Equal Earth Americas"|Projected|[90.01, -90.0, 89.99, 90.0]|Some([-17243001.064, -8392927.598, 17243001.064, 8392927.598])| + EPSG_8859|8859|"WGS 84 / Equal Earth Asia-Pacific"|Projected|[-29.99, -90.0, -30.01, 90.0]|Some([-17243001.064, -8392927.598, 17243001.064, 8392927.598])| + EPSG_8860|8860|"NAD83(FBN)"|Geographic2d|[144.58, -14.59, -64.51, 49.38]|Some([-175.177, -14.59, 179.792, 49.38])| + EPSG_8888|8888|"WGS 84 (Transit)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8900|8900|"RGWF96"|Geographic2d|[179.49, -15.94, -174.27, -9.84]|Some([-179.886, -15.94, 179.906, -9.84])| + EPSG_8902|8902|"RGWF96 (lon-lat)"|Geographic2d|[179.49, -15.94, -174.27, -9.84]|Some([-179.886, -15.94, 179.906, -9.84])| + EPSG_8903|8903|"RGWF96 / UTM zone 1S"|Projected|[179.49, -15.94, -174.27, -9.84]|Some([114904.681, 8234536.903, 799448.368, 8912277.477])| + EPSG_8907|8907|"CR-SIRGAS"|Geographic2d|[-90.45, 2.15, -81.43, 11.77]|Some([-90.45, 2.15, -81.43, 11.77])| + EPSG_8908|8908|"CR-SIRGAS / CRTM05"|Projected|[-85.97, 7.98, -82.53, 11.22]|Some([282790.448, 882351.61, 662066.521, 1241397.265])| + EPSG_8909|8909|"CR-SIRGAS / UTM zone 16N"|Projected|[-90.45, 2.15, -82.92, 11.11]|Some([116136.881, 237640.868, 954069.797, 1231205.301])| + EPSG_8910|8910|"CR-SIRGAS / UTM zone 17N"|Projected|[-83.6, 9.6, -81.43, 11.77]|Some([214616.959, 1061217.133, 453150.876, 1302433.023])| + EPSG_8949|8949|"SIRGAS-Chile 2010"|Geographic2d|[-113.21, -59.87, -65.72, -17.5]|Some([-113.21, -59.87, -65.72, -17.5])| + EPSG_8950|8950|"SIRGAS-Chile 2010 / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, -18.35]|Some([182927.612, 3415992.79, 818130.08, 7971091.108])| + EPSG_8951|8951|"SIRGAS-Chile 2010 / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, -17.5]|Some([181409.073, 3359262.29, 818590.927, 8065119.517])| + EPSG_8972|8972|"SIRGAS-CON DGF00P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8973|8973|"SIRGAS-CON DGF01P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8974|8974|"SIRGAS-CON DGF01P02"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8975|8975|"SIRGAS-CON DGF02P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8976|8976|"SIRGAS-CON DGF04P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8977|8977|"SIRGAS-CON DGF05P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8978|8978|"SIRGAS-CON DGF06P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8979|8979|"SIRGAS-CON DGF07P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8980|8980|"SIRGAS-CON DGF08P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8981|8981|"SIRGAS-CON SIR09P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8982|8982|"SIRGAS-CON SIR10P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8983|8983|"SIRGAS-CON SIR11P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8984|8984|"SIRGAS-CON SIR13P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8985|8985|"SIRGAS-CON SIR14P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8986|8986|"SIRGAS-CON SIR15P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8987|8987|"SIRGAS-CON SIR17P01"|Geographic2d|[-122.19, -59.87, -25.28, 32.72]|Some([-122.19, -59.87, -25.28, 32.72])| + EPSG_8988|8988|"ITRF88"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8989|8989|"ITRF89"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8990|8990|"ITRF90"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8991|8991|"ITRF91"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8992|8992|"ITRF92"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8993|8993|"ITRF93"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8994|8994|"ITRF94"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8995|8995|"ITRF96"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8996|8996|"ITRF97"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8997|8997|"ITRF2000"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8998|8998|"ITRF2005"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_8999|8999|"ITRF2008"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9000|9000|"ITRF2014"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9003|9003|"IGS97"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9006|9006|"IGS00"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9009|9009|"IGb00"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9012|9012|"IGS05"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9014|9014|"IGS08"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9017|9017|"IGb08"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9019|9019|"IGS14"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9039|9039|"ISN2016 / LAEA Europe"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([2180027.087, 4190621.841, 3714401.462, 5627446.31])| + EPSG_9040|9040|"ISN2016 / LCC Europe"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([1877057.884, 3749340.562, 3380297.711, 5177062.581])| + EPSG_9053|9053|"WGS 84 (G730)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9054|9054|"WGS 84 (G873)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9055|9055|"WGS 84 (G1150)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9056|9056|"WGS 84 (G1674)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9057|9057|"WGS 84 (G1762)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9059|9059|"ETRF89"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9060|9060|"ETRF90"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9061|9061|"ETRF91"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9062|9062|"ETRF92"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9063|9063|"ETRF93"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9064|9064|"ETRF94"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9065|9065|"ETRF96"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9066|9066|"ETRF97"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9067|9067|"ETRF2000"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9068|9068|"ETRF2005"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9069|9069|"ETRF2014"|Geographic2d|[-16.1, 33.26, 38.01, 84.73]|Some([-16.1, 33.26, 38.01, 84.73])| + EPSG_9072|9072|"NAD83(MARP00)"|Geographic2d|[129.48, 1.64, 149.55, 23.9]|Some([129.48, 1.64, 149.55, 23.9])| + EPSG_9075|9075|"NAD83(PACP00)"|Geographic2d|[157.47, -17.56, -151.27, 31.8]|Some([-178.609, -17.56, 179.683, 31.8])| + EPSG_9140|9140|"KOSOVAREF01"|Geographic2d|[19.97, 41.85, 21.8, 43.25]|Some([19.97, 41.85, 21.8, 43.25])| + EPSG_9141|9141|"KOSOVAREF01 / Balkans zone 7"|Projected|[19.97, 41.85, 21.8, 43.25]|Some([7414471.635, 4634512.631, 7566429.639, 4790529.759])| + EPSG_9148|9148|"SIRGAS-Chile 2013"|Geographic2d|[-113.21, -59.87, -65.72, -17.5]|Some([-113.21, -59.87, -65.72, -17.5])| + EPSG_9149|9149|"SIRGAS-Chile 2013 / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, -18.35]|Some([182927.612, 3415992.79, 818130.08, 7971091.108])| + EPSG_9150|9150|"SIRGAS-Chile 2013 / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, -17.5]|Some([181409.073, 3359262.29, 818590.927, 8065119.517])| + EPSG_9153|9153|"SIRGAS-Chile 2016"|Geographic2d|[-113.21, -59.87, -65.72, -17.5]|Some([-113.21, -59.87, -65.72, -17.5])| + EPSG_9154|9154|"SIRGAS-Chile 2016 / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, -18.35]|Some([182927.612, 3415992.79, 818130.08, 7971091.108])| + EPSG_9155|9155|"SIRGAS-Chile 2016 / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, -17.5]|Some([181409.073, 3359262.29, 818590.927, 8065119.517])| + EPSG_9156|9156|"RSAO13 / UTM zone 32S"|Projected|[8.2, -17.28, 12.01, -5.03]|Some([411317.714, 8086970.824, 833808.591, 9444019.425])| + EPSG_9157|9157|"RSAO13 / UTM zone 33S"|Projected|[12.0, -17.44, 18.0, -4.38]|Some([166992.125, 8069266.693, 833007.875, 9515868.939])| + EPSG_9158|9158|"RSAO13 / UTM zone 34S"|Projected|[17.99, -18.02, 24.09, -6.91]|Some([167329.493, 8004870.38, 841520.527, 9236200.118])| + EPSG_9159|9159|"RSAO13 / TM 12 SE"|Projected|[8.2, -17.26, 13.86, -6.01]|Some([79159.92, 8087701.22, 705877.003, 9335689.939])| + EPSG_9191|9191|"WGS 84 / NIWA Albers"|Projected|[155.0, -60.0, -169.99, -25.0]|Some([-2034601.007, -2337463.863, 1532420.407, 1670045.586])| + EPSG_9205|9205|"VN-2000 / TM-3 103-00"|Projected|[102.14, 20.89, 103.99, 22.82]|Some([410330.864, 2310772.7, 602802.737, 2524796.445])| + EPSG_9206|9206|"VN-2000 / TM-3 104-00"|Projected|[103.21, 20.57, 105.03, 22.04]|Some([417441.064, 2275349.314, 607190.285, 2438450.828])| + EPSG_9207|9207|"VN-2000 / TM-3 104-30"|Projected|[103.4, 8.33, 105.54, 10.55]|Some([378641.119, 921168.214, 614357.589, 1166896.339])| + EPSG_9208|9208|"VN-2000 / TM-3 104-45"|Projected|[103.53, 10.18, 105.86, 22.85]|Some([366122.456, 1125763.303, 621430.344, 2528297.838])| + EPSG_9209|9209|"VN-2000 / TM-3 105-30"|Projected|[104.33, 9.19, 106.69, 23.4]|Some([371225.775, 1016275.09, 630581.159, 2589186.635])| + EPSG_9210|9210|"VN-2000 / TM-3 105-45"|Projected|[105.26, 9.75, 107.8, 23.12]|Some([446044.545, 1078207.427, 724757.667, 2559154.771])| + EPSG_9211|9211|"VN-2000 / TM-3 106-00"|Projected|[104.83, 16.92, 107.03, 22.7]|Some([375168.809, 1871377.419, 609524.89, 2511646.1])| + EPSG_9212|9212|"VN-2000 / TM-3 106-15"|Projected|[106.41, 11.3, 107.43, 17.22]|Some([516820.46, 1249642.884, 628627.137, 1904957.917])| + EPSG_9213|9213|"VN-2000 / TM-3 106-30"|Projected|[105.43, 21.32, 106.25, 22.75]|Some([388801.757, 2358401.534, 474129.507, 2517106.354])| + EPSG_9214|9214|"VN-2000 / TM-3 107-00"|Projected|[105.88, 15.99, 108.24, 21.63]|Some([379925.938, 1768472.018, 632529.243, 2393214.36])| + EPSG_9215|9215|"VN-2000 / TM-3 107-15"|Projected|[106.09, 21.32, 107.37, 22.47]|Some([379464.362, 2358381.922, 512253.108, 2486168.838])| + EPSG_9216|9216|"VN-2000 / TM-3 107-30"|Projected|[107.33, 13.92, 108.55, 15.42]|Some([481434.61, 1539453.536, 613273.892, 1705679.701])| + EPSG_9217|9217|"VN-2000 / TM-3 108-15"|Projected|[108.55, 11.25, 109.53, 14.71]|Some([532110.25, 1244125.91, 639570.919, 1627243.74])| + EPSG_9218|9218|"VN-2000 / TM-3 108-30"|Projected|[107.2, 10.43, 109.52, 14.61]|Some([357468.191, 1153415.286, 611481.738, 1616190.323])| + EPSG_9221|9221|"Hartebeesthoek94 / ZAF BSU Albers 25E"|Projected|[13.33, -38.17, 36.54, -22.13]|Some([198422.564, 336238.857, 2588236.92, 2177513.913])| + EPSG_9222|9222|"Hartebeesthoek94 / ZAF BSU Albers 44E"|Projected|[32.71, -50.32, 42.85, -43.26]|Some([294681.694, 316663.187, 1118025.52, 1158049.934])| + EPSG_9248|9248|"Tapi Aike"|Geographic2d|[-73.28, -52.43, -68.3, -50.33]|Some([-73.278, -52.431, -68.298, -50.331])| + EPSG_9249|9249|"Tapi Aike / Argentina 1"|Projected|[-73.28, -52.0, -70.5, -50.33]|Some([1409022.821, 4237688.247, 1606966.293, 4424541.161])| + EPSG_9250|9250|"Tapi Aike / Argentina 2"|Projected|[-70.5, -52.43, -68.3, -50.33]|Some([2393352.277, 4189852.104, 2549998.467, 4424547.193])| + EPSG_9251|9251|"MMN"|Geographic2d|[-68.64, -55.11, -63.73, -52.59]|Some([-68.641, -55.111, -63.731, -52.591])| + EPSG_9252|9252|"MMN / Argentina 2"|Projected|[-68.64, -55.11, -63.73, -52.59]|Some([2522939.622, 3879915.226, 2856989.077, 4173032.17])| + EPSG_9253|9253|"MMS"|Geographic2d|[-68.64, -55.11, -63.73, -52.59]|Some([-68.639, -55.111, -63.73, -52.591])| + EPSG_9254|9254|"MMS / Argentina 2"|Projected|[-68.64, -55.11, -63.73, -52.59]|Some([2523011.303, 3879921.889, 2857060.813, 4173038.88])| + EPSG_9265|9265|"POSGAR 2007 / UTM zone 19S"|Projected|[-68.62, -54.61, -66.0, -51.65]|Some([524543.407, 3944470.294, 707532.37, 4277820.917])| + EPSG_9271|9271|"MGI / Austria West"|Projected|[9.53, 46.77, 11.84, 47.61]|Some([-61338.468, 5181209.678, 115103.428, 5275692.205])| + EPSG_9272|9272|"MGI / Austria Central"|Projected|[11.83, 46.4, 14.84, 48.79]|Some([-115559.833, 5140068.852, 115918.74, 5406890.523])| + EPSG_9273|9273|"MGI / Austria East"|Projected|[14.83, 46.56, 17.17, 49.02]|Some([-115190.122, 5157843.997, 64236.661, 5432456.225])| + EPSG_9284|9284|"Pampa del Castillo / Argentina 1"|Projected|[-73.59, -50.34, -70.5, -44.94]|Some([1374496.872, 4422299.973, 1618614.13, 5023817.629])| + EPSG_9285|9285|"Pampa del Castillo / Argentina 3"|Projected|[-67.5, -49.05, -65.47, -43.58]|Some([3378838.981, 4565908.042, 3542810.007, 5175043.519])| + EPSG_9294|9294|"ONGD17"|Geographic2d|[51.99, 14.33, 63.38, 26.74]|Some([51.99, 14.33, 63.38, 26.74])| + EPSG_9295|9295|"ONGD17 / UTM zone 39N"|Projected|[51.99, 14.94, 54.01, 19.67]|Some([603778.05, 1651928.118, 823818.405, 2177757.804])| + EPSG_9296|9296|"ONGD17 / UTM zone 40N"|Projected|[54.0, 14.33, 60.01, 26.74]|Some([176365.53, 1584224.499, 824715.773, 2961180.195])| + EPSG_9297|9297|"ONGD17 / UTM zone 41N"|Projected|[60.0, 16.37, 63.38, 24.09]|Some([179499.345, 1809865.288, 540582.067, 2667453.358])| + EPSG_9299|9299|"HS2-IRF"|Geographic2d|[-2.75, 51.45, 0.0, 53.3]|Some([-2.75, 51.45, 0.0, 53.3])| + EPSG_9300|9300|"HS2 Survey Grid"|Projected|[-2.75, 51.45, 0.0, 53.3]|Some([111978.945, 280489.688, 303145.067, 487396.538])| + EPSG_9309|9309|"ATRF2014"|Geographic2d|[93.41, -60.55, 173.34, -8.47]|Some([93.41, -60.55, 173.34, -8.47])| + EPSG_9311|9311|"NAD27 / US National Atlas Equal Area"|Projected|[167.65, 15.56, -65.69, 74.71]|Some([-8046095.112, -3237606.513, 3695884.723, 4802459.513])| + EPSG_9333|9333|"KSA-GRF17"|Geographic2d|[34.44, 16.29, 55.67, 32.16]|Some([34.44, 16.29, 55.67, 32.16])| + EPSG_9354|9354|"WGS 84 / IBCSO Polar Stereographic"|Projected|[-180.0, -90.0, 180.0, -50.0]|Some([-4409148.188, -4433435.009, 4409148.188, 4433435.009])| + EPSG_9356|9356|"KSA-GRF17 / UTM zone 36N"|Projected|[34.44, 24.92, 36.01, 29.38]|Some([639747.706, 2756859.733, 804025.621, 3253854.976])| + EPSG_9357|9357|"KSA-GRF17 / UTM zone 37N"|Projected|[36.0, 16.29, 42.0, 32.16]|Some([179368.29, 1801014.175, 820632.216, 3562115.797])| + EPSG_9358|9358|"KSA-GRF17 / UTM zone 38N"|Projected|[41.99, 16.35, 48.0, 31.15]|Some([178396.874, 1807651.265, 820534.364, 3450127.439])| + EPSG_9359|9359|"KSA-GRF17 / UTM zone 39N"|Projected|[47.99, 17.94, 54.01, 28.94]|Some([181126.519, 1983547.612, 818873.997, 3205070.048])| + EPSG_9360|9360|"KSA-GRF17 / UTM zone 40N"|Projected|[54.0, 19.66, 55.67, 22.77]|Some([185404.333, 2174402.374, 363461.333, 2521182.809])| + EPSG_9364|9364|"TPEN11-IRF"|Geographic2d|[-3.14, 53.32, -1.34, 53.9]|Some([-3.14, 53.32, -1.34, 53.9])| + EPSG_9367|9367|"TPEN11 Grid"|Projected|[-3.14, 53.32, -1.34, 53.9]|Some([143943.445, 387479.896, 263893.654, 452416.347])| + EPSG_9372|9372|"MML07-IRF"|Geographic2d|[-1.89, 51.46, 0.16, 53.42]|Some([-1.89, 51.46, 0.16, 53.42])| + EPSG_9373|9373|"MML07 Grid"|Projected|[-1.89, 51.46, 0.16, 53.42]|Some([-22930.298, -1755.508, 119545.647, 216848.633])| + EPSG_9377|9377|"MAGNA-SIRGAS 2018 / Origen-Nacional"|Projected|[-84.77, -4.23, -66.87, 15.51]|Some([3681463.997, 1080616.361, 5683147.651, 3307327.961])| + EPSG_9380|9380|"IGb14"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9384|9384|"AbInvA96_2020-IRF"|Geographic2d|[-4.31, 57.1, -2.1, 57.71]|Some([-4.31, 57.1, -2.1, 57.71])| + EPSG_9387|9387|"AbInvA96_2020 Grid"|Projected|[-4.31, 57.1, -2.1, 57.71]|Some([88554.337, 81816.415, 222497.022, 150292.166])| + EPSG_9391|9391|"BGS2005 / UTM zone 35N"|Projected|[24.0, 41.24, 30.01, 44.15]|Some([248597.584, 4565399.907, 752240.526, 4892939.522])| + EPSG_9403|9403|"PN68"|Geographic2d|[-18.22, 27.58, -13.37, 29.47]|Some([-18.218, 27.579, -13.368, 29.469])| + EPSG_9404|9404|"PN68 / UTM zone 27N"|Projected|[-18.22, 27.59, -18.0, 27.88]|Some([773898.344, 3054758.434, 796353.137, 3087415.47])| + EPSG_9405|9405|"PN68 / UTM zone 28N"|Projected|[-18.06, 27.58, -13.37, 29.47]|Some([198057.96, 3050558.806, 661064.766, 3263840.718])| + EPSG_9406|9406|"PN84 / UTM zone 27N"|Projected|[-18.22, 27.59, -18.0, 27.88]|Some([773898.344, 3054758.434, 796353.137, 3087415.47])| + EPSG_9407|9407|"PN84 / UTM zone 28N"|Projected|[-18.06, 27.58, -16.08, 28.9]|Some([198057.96, 3051022.208, 394871.033, 3200639.747])| + EPSG_9453|9453|"GBK19-IRF"|Geographic2d|[-4.65, 55.55, -4.05, 55.95]|Some([-4.65, 55.55, -4.05, 55.95])| + EPSG_9456|9456|"GBK19 Grid"|Projected|[-4.65, 55.55, -4.05, 55.95]|Some([74785.688, 91603.403, 112655.1, 136178.967])| + EPSG_9470|9470|"SRGI2013"|Geographic2d|[92.01, -13.95, 141.46, 7.79]|Some([92.01, -13.95, 141.46, 7.79])| + EPSG_9473|9473|"GDA2020 / Australian Albers"|Projected|[112.85, -43.7, 153.69, -9.86]|Some([-2153255.276, -4928351.64, 2436270.293, -1033174.082])| + EPSG_9474|9474|"PZ-90.02"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9475|9475|"PZ-90.11"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9476|9476|"SRGI2013 / UTM zone 46N"|Projected|[92.01, 0.0, 96.0, 7.79]|Some([389832.481, -0.108, 833978.766, 862257.082])| + EPSG_9477|9477|"SRGI2013 / UTM zone 47N"|Projected|[96.0, 0.0, 102.0, 7.49]|Some([166021.652, -0.092, 833978.755, 829048.009])| + EPSG_9478|9478|"SRGI2013 / UTM zone 48N"|Projected|[102.0, 0.0, 108.0, 6.94]|Some([166021.642, -0.064, 833978.743, 768165.535])| + EPSG_9479|9479|"SRGI2013 / UTM zone 49N"|Projected|[108.0, 0.0, 114.0, 7.37]|Some([166021.629, -0.029, 833978.727, 815764.531])| + EPSG_9480|9480|"SRGI2013 / UTM zone 50N"|Projected|[114.0, 0.0, 120.0, 4.37]|Some([166021.613, 0.011, 833978.709, 483690.502])| + EPSG_9481|9481|"SRGI2013 / UTM zone 51N"|Projected|[120.0, 0.0, 126.0, 5.48]|Some([166021.595, 0.057, 833978.688, 606555.022])| + EPSG_9482|9482|"SRGI2013 / UTM zone 52N"|Projected|[126.0, 0.0, 132.0, 6.68]|Some([166021.574, 0.107, 833978.666, 739385.253])| + EPSG_9487|9487|"SRGI2013 / UTM zone 47S"|Projected|[96.0, -8.86, 102.0, 0.0]|Some([166021.652, 9019294.086, 833978.755, 9999999.936])| + EPSG_9488|9488|"SRGI2013 / UTM zone 48S"|Projected|[102.0, -10.73, 108.01, 0.0]|Some([166021.642, 8812262.974, 835093.035, 9999999.971])| + EPSG_9489|9489|"SRGI2013 / UTM zone 49S"|Projected|[108.0, -12.07, 114.0, 0.0]|Some([166021.629, 8663916.563, 833978.727, 10000000.011])| + EPSG_9490|9490|"SRGI2013 / UTM zone 50S"|Projected|[114.0, -13.06, 120.01, 0.0]|Some([166021.613, 8554290.079, 835093.001, 10000000.057])| + EPSG_9491|9491|"SRGI2013 / UTM zone 51S"|Projected|[120.0, -13.95, 126.01, 0.01]|Some([166021.6, 8455742.548, 835092.975, 10001106.945])| + EPSG_9492|9492|"SRGI2013 / UTM zone 52S"|Projected|[126.0, -9.45, 132.0, 0.0]|Some([166021.574, 8953979.443, 833978.666, 10000000.161])| + EPSG_9493|9493|"SRGI2013 / UTM zone 53S"|Projected|[132.0, -10.06, 138.01, 0.0]|Some([166021.552, 8886438.905, 835092.933, 10000000.219])| + EPSG_9494|9494|"SRGI2013 / UTM zone 54S"|Projected|[138.0, -10.84, 141.46, 0.0]|Some([166021.527, 8800095.74, 551187.106, 10000000.253])| + EPSG_9498|9498|"POSGAR 2007 / CABA 2019"|Projected|[-58.54, -34.71, -58.29, -34.5]|Some([12956.747, 61030.845, 35916.751, 84340.521])| + EPSG_9547|9547|"LTF2004(G)"|Geographic2d|[4.65, 44.87, 7.88, 45.89]|Some([4.65, 44.87, 7.88, 45.89])| + EPSG_9549|9549|"LTF2004(C)"|Projected|[4.65, 44.87, 7.88, 45.89]|Some([-21206.994, 15176.364, 234030.562, 130781.668])| + EPSG_9674|9674|"NAD83 / USFS R6 Albers"|Projected|[-124.79, 41.98, -116.47, 49.05]|Some([202906.727, 879293.035, 892717.785, 1675748.171])| + EPSG_9678|9678|"Gulshan 303 / Bangladesh Transverse Mercator"|Projected|[88.01, 20.52, 92.67, 26.64]|Some([292781.483, 268734.499, 778725.025, 949043.525])| + EPSG_9680|9680|"WGS 84 / TM 90 NE"|Projected|[88.01, 18.56, 92.67, 26.64]|Some([289974.458, 2052143.824, 781829.472, 2949341.194])| + EPSG_9696|9696|"REDGEOMIN"|Geographic2d|[-113.21, -59.87, -65.72, -17.5]|Some([-113.21, -59.87, -65.72, -17.5])| + EPSG_9697|9697|"REDGEOMIN / UTM zone 12S"|Projected|[-109.51, -27.25, -109.16, -27.01]|Some([647512.039, 6984534.842, 682558.363, 7011584.097])| + EPSG_9698|9698|"REDGEOMIN / UTM zone 18S"|Projected|[-78.0, -59.36, -71.99, -18.35]|Some([182927.612, 3415992.79, 818130.08, 7971091.108])| + EPSG_9699|9699|"REDGEOMIN / UTM zone 19S"|Projected|[-72.0, -59.87, -66.0, -17.5]|Some([181409.073, 3359262.29, 818590.927, 8065131.102])| + EPSG_9702|9702|"ETRF2000-PL"|Geographic2d|[14.14, 49.0, 24.15, 55.93]|Some([14.14, 49.0, 24.15, 55.93])| + EPSG_9709|9709|"NAD83(CSRS) / UTM zone 23N"|Projected|[-48.0, 39.85, -42.0, 54.47]|Some([243339.605, 4411107.969, 756658.996, 6039958.308])| + EPSG_9712|9712|"NAD83 / UTM zone 24N"|Projected|[-42.0, 45.53, -40.73, 49.53]|Some([265745.952, 5043285.015, 374814.454, 5490701.227])| + EPSG_9713|9713|"NAD83(CSRS) / UTM zone 24N"|Projected|[-42.0, 45.53, -40.73, 49.53]|Some([265745.186, 5043283.994, 374813.706, 5490700.145])| + EPSG_9716|9716|"IGM95 / UTM zone 34N"|Projected|[17.99, 34.76, 18.99, 41.64]|Some([224488.264, 3848268.688, 332597.547, 4614184.666])| + EPSG_9739|9739|"EOS21-IRF"|Geographic2d|[-3.56, 55.55, -1.94, 57.21]|Some([-3.56, 55.55, -1.94, 57.21])| + EPSG_9741|9741|"EOS21 Grid"|Projected|[-3.56, 55.55, -1.94, 57.21]|Some([23873.748, 44435.62, 126120.106, 229565.307])| + EPSG_9748|9748|"NAD83(2011) / Alabama East (ftUS)"|Projected|[-86.79, 30.99, -84.89, 35.0]|Some([356385.009, 178220.585, 951770.004, 1638610.274])| + EPSG_9749|9749|"NAD83(2011) / Alabama West (ftUS)"|Projected|[-88.48, 30.14, -86.3, 35.02]|Some([1658725.18, 50915.251, 2347820.72, 1828472.023])| + EPSG_9755|9755|"WGS 84 (G2139)"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| + EPSG_9758|9758|"ECML14_NB-IRF"|Geographic2d|[-1.9, 54.85, -1.3, 55.3]|Some([-1.9, 54.85, -1.3, 55.3])| + EPSG_9761|9761|"ECML14_NB Grid"|Projected|[-1.9, 54.85, -1.3, 55.3]|Some([89761.473, 380049.267, 128300.995, 430201.254])| + EPSG_9763|9763|"EWR2-IRF"|Geographic2d|[-1.43, 51.7, -0.36, 52.24]|Some([-1.43, 51.7, -0.36, 52.24])| + EPSG_9766|9766|"EWR2 Grid"|Projected|[-1.43, 51.7, -0.36, 52.24]|Some([155877.93, 119126.686, 229853.367, 179348.163])| + EPSG_9777|9777|"RGF93 v2"|Geographic2d|[-9.86, 41.15, 10.38, 51.56]|Some([-9.86, 41.15, 10.38, 51.56])| + EPSG_9779|9779|"RGF93 v2 (lon-lat)"|Geographic2d|[-9.86, 41.15, 10.38, 51.56]|Some([-9.86, 41.15, 10.38, 51.56])| + EPSG_9782|9782|"RGF93 v2b"|Geographic2d|[-9.86, 41.15, 10.38, 51.56]|Some([-9.86, 41.15, 10.38, 51.56])| + EPSG_9784|9784|"RGF93 v2b (lon-lat)"|Geographic2d|[-9.86, 41.15, 10.38, 51.56]|Some([-9.86, 41.15, 10.38, 51.56])| + EPSG_9793|9793|"RGF93 v2 / Lambert-93"|Projected|[-9.86, 41.15, 10.38, 51.56]|Some([-378305.81, 6005281.901, 1320649.571, 7235612.725])| + EPSG_9794|9794|"RGF93 v2b / Lambert-93"|Projected|[-9.86, 41.15, 10.38, 51.56]|Some([-378305.81, 6005281.901, 1320649.571, 7235612.725])| + EPSG_9821|9821|"UCS-2000 / LCS-32 Kyiv region"|Projected|[29.26, 49.17, 32.16, 51.55]|Some([209693.269, 5448645.744, 421165.733, 5714694.692])| + EPSG_9822|9822|"RGF93 v2 / CC42"|Projected|[-1.06, 41.31, 9.63, 43.07]|Some([1360148.436, 1123378.371, 2254632.683, 1339754.14])| + EPSG_9823|9823|"RGF93 v2 / CC43"|Projected|[-1.79, 42.33, 7.65, 44.01]|Some([1305422.558, 2125579.297, 2083056.865, 2323158.503])| + EPSG_9824|9824|"RGF93 v2 / CC44"|Projected|[-1.79, 42.92, 7.71, 45.0]|Some([1309096.616, 3080014.115, 2084381.885, 3322082.757])| + EPSG_9825|9825|"RGF93 v2 / CC45"|Projected|[-1.46, 44.0, 7.71, 46.0]|Some([1342437.381, 4088889.588, 2077583.387, 4321739.72])| + EPSG_9826|9826|"RGF93 v2 / CC46"|Projected|[-2.21, 45.0, 7.16, 47.0]|Some([1289473.773, 5088867.232, 2027875.373, 5324113.396])| + EPSG_9827|9827|"RGF93 v2 / CC47"|Projected|[-4.77, 46.0, 7.63, 48.0]|Some([1099056.839, 6088846.216, 2058469.98, 6339910.234])| + EPSG_9828|9828|"RGF93 v2 / CC48"|Projected|[-4.87, 47.0, 8.23, 49.0]|Some([1102438.827, 7088823.329, 2097494.1, 7340564.439])| + EPSG_9829|9829|"RGF93 v2 / CC49"|Projected|[-4.87, 48.0, 8.23, 50.0]|Some([1113711.165, 8088803.879, 2090007.684, 8340438.398])| + EPSG_9830|9830|"RGF93 v2 / CC50"|Projected|[-2.03, 49.0, 8.08, 51.14]|Some([1332198.993, 9088784.764, 2071451.484, 9338881.939])| + EPSG_9831|9831|"UCS-2000 / LCS-01 Crimea"|Projected|[32.47, 44.38, 36.65, 46.24]|Some([138336.191, 4916152.425, 471453.716, 5125115.319])| + EPSG_9832|9832|"UCS-2000 / LCS-05 Vinnytsia"|Projected|[27.37, 48.06, 30.02, 49.89]|Some([203466.17, 5325213.772, 400992.958, 5529605.654])| + EPSG_9833|9833|"UCS-2000 / LCS-07 Volyn"|Projected|[23.6, 50.28, 26.11, 51.97]|Some([212210.538, 5572113.688, 391117.861, 5760898.019])| + EPSG_9834|9834|"UCS-2000 / LCS-12 Dnipropetrovsk"|Projected|[32.95, 47.45, 36.94, 49.19]|Some([145514.285, 5257381.964, 446416.223, 5452886.753])| + EPSG_9835|9835|"UCS-2000 / LCS-14 Donetsk"|Projected|[36.54, 46.86, 39.1, 49.24]|Some([226906.375, 5191784.375, 422117.013, 5457652.438])| + EPSG_9836|9836|"UCS-2000 / LCS-18 Zhytomyr"|Projected|[27.19, 49.58, 29.74, 51.68]|Some([205381.576, 5494248.462, 389791.891, 5728668.039])| + EPSG_9837|9837|"UCS-2000 / LCS-21 Zakarpattia"|Projected|[22.13, 47.89, 24.63, 49.1]|Some([197545.383, 5306319.652, 384626.215, 5441748.567])| + EPSG_9838|9838|"UCS-2000 / LCS-23 Zaporizhzhia"|Projected|[34.17, 46.07, 37.25, 48.15]|Some([158533.478, 5103969.464, 396818.954, 5336829.841])| + EPSG_9839|9839|"UCS-2000 / LCS-26 Ivano-Frankivsk"|Projected|[23.53, 47.72, 25.66, 49.56]|Some([208585.616, 5287415.327, 368395.746, 5492744.617])| + EPSG_9840|9840|"UCS-2000 / LCS-35 Kirovohrad"|Projected|[29.74, 47.74, 33.9, 49.25]|Some([130619.882, 5289628.755, 442612.532, 5459999.32])| + EPSG_9841|9841|"UCS-2000 / LCS-44 Luhansk"|Projected|[37.83, 47.82, 40.23, 50.09]|Some([212494.579, 5298513.106, 392108.874, 5551680.096])| + EPSG_9842|9842|"RGF93 v2b / CC42"|Projected|[-1.06, 41.31, 9.63, 43.07]|Some([1360148.436, 1123378.371, 2254632.683, 1339754.14])| + EPSG_9843|9843|"RGF93 v2b / CC43"|Projected|[-1.79, 42.33, 7.65, 44.01]|Some([1305422.558, 2125579.297, 2083056.865, 2323158.503])| + EPSG_9844|9844|"RGF93 v2b / CC44"|Projected|[-1.79, 42.92, 7.71, 45.0]|Some([1309096.616, 3080014.115, 2084381.885, 3322082.757])| + EPSG_9845|9845|"RGF93 v2b / CC45"|Projected|[-1.46, 44.0, 7.71, 46.0]|Some([1342437.381, 4088889.588, 2077583.387, 4321739.72])| + EPSG_9846|9846|"RGF93 v2b / CC46"|Projected|[-2.21, 45.0, 7.16, 47.0]|Some([1289473.773, 5088867.232, 2027875.373, 5324113.396])| + EPSG_9847|9847|"RGF93 v2b / CC47"|Projected|[-4.77, 46.0, 7.63, 48.0]|Some([1099056.839, 6088846.216, 2058469.98, 6339910.234])| + EPSG_9848|9848|"RGF93 v2b / CC48"|Projected|[-4.87, 47.0, 8.23, 49.0]|Some([1102438.827, 7088823.329, 2097494.1, 7340564.439])| + EPSG_9849|9849|"RGF93 v2b / CC49"|Projected|[-4.87, 48.0, 8.23, 50.0]|Some([1113711.165, 8088803.879, 2090007.684, 8340438.398])| + EPSG_9850|9850|"RGF93 v2b / CC50"|Projected|[-2.03, 49.0, 8.08, 51.14]|Some([1332198.993, 9088784.764, 2071451.484, 9338881.939])| + EPSG_9851|9851|"UCS-2000 / LCS-46 Lviv"|Projected|[22.64, 48.71, 25.43, 50.65]|Some([200030.868, 5397500.154, 405361.263, 5614249.125])| + EPSG_9852|9852|"UCS-2000 / LCS-48 Mykolaiv"|Projected|[30.2, 46.36, 33.19, 48.23]|Some([174415.431, 5136211.217, 404523.644, 5345401.627])| + EPSG_9853|9853|"UCS-2000 / LCS-51 Odessa"|Projected|[28.21, 45.2, 31.3, 48.24]|Some([159472.343, 5007282.825, 402260.845, 5346775.692])| + EPSG_9854|9854|"UCS-2000 / LCS-53 Poltava"|Projected|[32.08, 48.74, 35.49, 50.55]|Some([171155.048, 5400821.697, 421963.064, 5603601.783])| + EPSG_9855|9855|"UCS-2000 / LCS-56 Rivne"|Projected|[25.08, 50.0, 27.74, 51.95]|Some([162466.668, 5540965.405, 353173.559, 5759641.097])| + EPSG_9856|9856|"UCS-2000 / LCS-59 Sumy"|Projected|[32.93, 50.1, 35.67, 52.37]|Some([187786.756, 5552076.487, 383822.46, 5805781.245])| + EPSG_9857|9857|"UCS-2000 / LCS-61 Ternopil"|Projected|[24.71, 48.5, 26.45, 50.27]|Some([241737.358, 5374144.882, 370325.854, 5571431.885])| + EPSG_9858|9858|"UCS-2000 / LCS-63 Kharkiv"|Projected|[34.84, 48.52, 38.1, 50.46]|Some([177485.211, 5376352.429, 418305.74, 5593434.829])| + EPSG_9859|9859|"UCS-2000 / LCS-65 Kherson"|Projected|[31.51, 45.75, 35.11, 47.6]|Some([145269.637, 5068406.093, 425390.575, 5275979.352])| + EPSG_9860|9860|"UCS-2000 / LCS-68 Khmelnytsky"|Projected|[26.13, 48.45, 27.9, 50.59]|Some([235761.263, 5368582.417, 366695.354, 5606980.516])| + EPSG_9861|9861|"UCS-2000 / LCS-71 Cherkasy"|Projected|[29.6, 48.45, 32.89, 50.23]|Some([159567.525, 5368576.03, 402938.846, 5568269.171])| + EPSG_9862|9862|"UCS-2000 / LCS-73 Chernivtsi"|Projected|[24.9, 47.72, 27.54, 48.68]|Some([217588.247, 5287414.252, 415662.078, 5395305.023])| + EPSG_9863|9863|"UCS-2000 / LCS-74 Chernihiv"|Projected|[30.48, 50.34, 33.51, 52.38]|Some([191910.216, 5578776.271, 407608.604, 5806825.108])| + EPSG_9864|9864|"UCS-2000 / LCS-80 Kyiv city"|Projected|[30.23, 50.21, 30.83, 50.59]|Some([280842.973, 5564318.119, 323672.888, 5606640.204])| + EPSG_9865|9865|"UCS-2000 / LCS-85 Sevastopol"|Projected|[33.37, 44.38, 33.93, 44.85]|Some([329364.006, 4916219.351, 374228.934, 4968802.171])| + EPSG_9866|9866|"MRH21-IRF"|Geographic2d|[-3.27, 51.35, -0.36, 53.26]|Some([-3.27, 51.35, -0.36, 53.26])| + EPSG_9869|9869|"MRH21 Grid"|Projected|[-3.27, 51.35, -0.36, 53.26]|Some([124877.152, 160050.575, 327606.925, 373590.625])| + EPSG_9871|9871|"MOLDOR11-IRF"|Geographic2d|[-2.4, 53.25, -1.39, 53.55]|Some([-2.4, 53.25, -1.39, 53.55])| + EPSG_9874|9874|"PNG94 / PNGMG94 zone 57"|Projected|[156.0, -14.26, 162.01, -1.11]|Some([166083.811, 8421415.639, 835030.272, 9877311.494])| + EPSG_9875|9875|"PNG94 / PNGMG94 zone 58"|Projected|[162.0, -4.36, 162.81, -2.34]|Some([166298.587, 9517416.414, 256949.46, 9741168.135])| + EPSG_9880|9880|"MOLDOR11 Grid"|Projected|[-2.4, 53.25, -1.39, 53.55]|Some([193199.932, 379765.659, 260615.955, 413274.798])| + EPSG_9895|9895|"LUREF / Luxembourg TM (3D)"|Projected|[5.73, 49.44, 6.53, 50.19]|Some([48225.166, 56133.744, 106245.562, 139644.912])| + EPSG_9939|9939|"EBBWV14-IRF"|Geographic2d|[-3.3, 51.5, -2.89, 51.85]|Some([-3.3, 51.5, -2.89, 51.85])| + EPSG_9943|9943|"EBBWV14 Grid"|Projected|[-3.3, 51.5, -2.89, 51.85]|Some([92814.229, 92153.097, 121284.827, 131115.349])| + EPSG_9945|9945|"Macedonia State Coordinate System truncated"|Projected|[20.45, 40.85, 23.04, 42.36]|Some([453158.728, 522953.224, 672459.609, 692669.407])| + EPSG_9947|9947|"ISN2004 / LAEA Iceland"|Projected|[-30.87, 59.96, -5.55, 69.59]|Some([1040247.367, 738438.301, 2446290.924, 1867761.072])| + EPSG_9964|9964|"HULLEE13-IRF"|Geographic2d|[-1.7, 53.6, -0.27, 53.9]|Some([-1.7, 53.6, -0.27, 53.9])| + EPSG_9967|9967|"HULLEE13 Grid"|Projected|[-1.7, 53.6, -0.27, 53.9]|Some([91208.054, 230817.515, 185877.335, 264468.256])| + EPSG_9969|9969|"SCM22-IRF"|Geographic2d|[-4.4, 55.7, -3.3, 57.55]|Some([-4.4, 55.7, -3.3, 57.55])| + EPSG_9972|9972|"SCM22 Grid"|Projected|[-4.4, 55.7, -3.3, 57.55]|Some([74019.794, 138877.26, 143182.15, 345013.872])| + EPSG_9974|9974|"FNL22-IRF"|Geographic2d|[-4.6, 57.4, -3.0, 58.64]|Some([-4.6, 57.4, -3.0, 58.64])| + EPSG_9977|9977|"FNL22 Grid"|Projected|[-4.6, 57.4, -3.0, 58.64]|Some([91524.663, 116287.389, 187713.236, 254673.054])| + EPSG_9990|9990|"ITRF2020"|Geographic2d|[-180.0, -90.0, 180.0, 90.0]|Some([-180.0, -90.0, 180.0, 90.0])| +} \ No newline at end of file diff --git a/geoengine/crs-bounds/src/epsg/mod.rs b/geoengine/crs-bounds/src/epsg/mod.rs new file mode 100644 index 0000000000..e5a04cb5d7 --- /dev/null +++ b/geoengine/crs-bounds/src/epsg/mod.rs @@ -0,0 +1,34 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CrsType { + Projected, + Geographic2d, +} + +#[derive(Debug, Clone, Copy)] +pub struct EpsgBounds { + #[cfg(feature = "metadata")] + pub code: u16, + #[cfg(feature = "metadata")] + pub name: &'static str, + + pub crs_type: CrsType, + pub wgs84_bounds: [f64; 4], + pub native_bounds: Option<[f64; 4]>, +} + +impl EpsgBounds { + #[inline] + pub const fn from_code_const() -> Option<&'static Self> { + registry::get_epsg_bounds(CODE) + } + + pub const fn from_code(code: u16) -> Option<&'static Self> { + registry::get_epsg_bounds(code) + } +} + +pub mod registry { + #![allow(clippy::unreadable_literal, clippy::approx_constant)] + + include!("epsg_registry.rs"); +} diff --git a/geoengine/crs-bounds/src/lib.rs b/geoengine/crs-bounds/src/lib.rs new file mode 100644 index 0000000000..ac76f2ee22 --- /dev/null +++ b/geoengine/crs-bounds/src/lib.rs @@ -0,0 +1,9 @@ +mod epsg; + +pub use epsg::EpsgBounds; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CrsType { + Projected, + Geographic2d, +} diff --git a/geoengine/datatypes/Cargo.toml b/geoengine/datatypes/Cargo.toml index 3aeaa0ec48..2506316095 100644 --- a/geoengine/datatypes/Cargo.toml +++ b/geoengine/datatypes/Cargo.toml @@ -14,10 +14,12 @@ bench = false [features] [dependencies] +geodesy = { workspace = true } arrow = { workspace = true } arrow-array = { workspace = true } arrow-ord = { workspace = true } arrow-schema = { workspace = true } +bytemuck = { workspace = true } bytes = { workspace = true } chrono = { workspace = true } fallible-iterator = { workspace = true } @@ -43,6 +45,9 @@ uuid = { workspace = true } wkt = { workspace = true } strum = { workspace = true } tempfile = { workspace = true } +tracing = { workspace = true } +crs-bounds = { path = "../crs-bounds", features = [] } +crs-definitions = { workspace = true } [dev-dependencies] criterion = { workspace = true } diff --git a/geoengine/datatypes/src/collections/feature_collection.rs b/geoengine/datatypes/src/collections/feature_collection.rs index 02fa672a7a..cc8118dad1 100644 --- a/geoengine/datatypes/src/collections/feature_collection.rs +++ b/geoengine/datatypes/src/collections/feature_collection.rs @@ -33,7 +33,7 @@ use crate::util::arrow::{ArrowTyped, downcast_array}; use crate::util::helpers::SomeIter; use crate::{ collections::{FeatureCollectionError, IntoGeometryOptionsIterator}, - operations::reproject::CoordinateProjection, + spatial_reference::CoordinateProjection, }; use crate::{ collections::{IntoGeometryIterator, VectorDataType, VectorDataTyped, error}, diff --git a/geoengine/datatypes/src/collections/multi_line_string_collection.rs b/geoengine/datatypes/src/collections/multi_line_string_collection.rs index 2515bac982..15b287f637 100644 --- a/geoengine/datatypes/src/collections/multi_line_string_collection.rs +++ b/geoengine/datatypes/src/collections/multi_line_string_collection.rs @@ -398,6 +398,7 @@ mod tests { use crate::collections::{BuilderProvider, FeatureCollectionModifications}; use crate::primitives::{FeatureData, FeatureDataRef, TimeInterval}; + use crate::spatial_reference::CoordinateProjection; #[test] fn single_line() { @@ -629,18 +630,20 @@ mod tests { #[test] fn reproject_multi_lines_epsg4326_epsg900913_collection() { - use crate::operations::reproject::{CoordinateProjection, CoordinateProjector, Reproject}; + use crate::operations::reproject::Reproject; use crate::primitives::FeatureData; - use crate::spatial_reference::{SpatialReference, SpatialReferenceAuthority}; + use crate::spatial_reference::{ + DefaultCoordinateProjector, SpatialReference, SpatialReferenceAuthority, + }; use crate::util::well_known_data::{ - COLOGNE_EPSG_900_913, COLOGNE_EPSG_4326, HAMBURG_EPSG_900_913, HAMBURG_EPSG_4326, - MARBURG_EPSG_900_913, MARBURG_EPSG_4326, + COLOGNE_EPSG_3857, COLOGNE_EPSG_4326, HAMBURG_EPSG_3857, HAMBURG_EPSG_4326, + MARBURG_EPSG_3857, MARBURG_EPSG_4326, }; let from = SpatialReference::epsg_4326(); let to = SpatialReference::new(SpatialReferenceAuthority::Epsg, 900_913); - let projector = CoordinateProjector::from_known_srs(from, to).unwrap(); + let projector = DefaultCoordinateProjector::from_known_srs(from, to).unwrap(); let collection = MultiLineStringCollection::from_slices( &[ @@ -657,14 +660,10 @@ mod tests { .unwrap(); let expected = [ - MultiLineString::new(vec![vec![MARBURG_EPSG_900_913, HAMBURG_EPSG_900_913]]).unwrap(), + MultiLineString::new(vec![vec![MARBURG_EPSG_3857, HAMBURG_EPSG_3857]]).unwrap(), MultiLineString::new(vec![ - vec![ - COLOGNE_EPSG_900_913, - MARBURG_EPSG_900_913, - HAMBURG_EPSG_900_913, - ], - vec![HAMBURG_EPSG_900_913, COLOGNE_EPSG_900_913], + vec![COLOGNE_EPSG_3857, MARBURG_EPSG_3857, HAMBURG_EPSG_3857], + vec![HAMBURG_EPSG_3857, COLOGNE_EPSG_3857], ]) .unwrap(), ]; diff --git a/geoengine/datatypes/src/collections/multi_point_collection.rs b/geoengine/datatypes/src/collections/multi_point_collection.rs index c6b31899c5..5e461a8284 100755 --- a/geoengine/datatypes/src/collections/multi_point_collection.rs +++ b/geoengine/datatypes/src/collections/multi_point_collection.rs @@ -344,6 +344,7 @@ mod tests { DataRef, FeatureData, FeatureDataRef, FeatureDataType, FeatureDataValue, MultiPointAccess, TimeInstance, TimeInterval, }; + use crate::spatial_reference::CoordinateProjection; use float_cmp::approx_eq; use serde_json::{from_str, json}; use std::collections::HashMap; @@ -1253,17 +1254,18 @@ mod tests { #[test] fn reproject_epsg4326_epsg900913() { - use crate::operations::reproject::{CoordinateProjection, CoordinateProjector}; - use crate::spatial_reference::{SpatialReference, SpatialReferenceAuthority}; + use crate::spatial_reference::{ + DefaultCoordinateProjector, SpatialReference, SpatialReferenceAuthority, + }; use crate::util::well_known_data::{ - COLOGNE_EPSG_900_913, COLOGNE_EPSG_4326, HAMBURG_EPSG_900_913, HAMBURG_EPSG_4326, - MARBURG_EPSG_900_913, MARBURG_EPSG_4326, + COLOGNE_EPSG_3857, COLOGNE_EPSG_4326, HAMBURG_EPSG_3857, HAMBURG_EPSG_4326, + MARBURG_EPSG_3857, MARBURG_EPSG_4326, }; let from = SpatialReference::epsg_4326(); let to = SpatialReference::new(SpatialReferenceAuthority::Epsg, 900_913); - let projector = CoordinateProjector::from_known_srs(from, to).unwrap(); + let projector = DefaultCoordinateProjector::from_known_srs(from, to).unwrap(); let pc = MultiPointCollection::from_data( MultiPoint::many(vec![ @@ -1292,12 +1294,12 @@ mod tests { let coords = proj_pc.coordinates(); assert_eq!(coords.len(), 3); - assert!(approx_eq!(f64, coords[0].x, MARBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, coords[0].y, MARBURG_EPSG_900_913.y)); - assert!(approx_eq!(f64, coords[1].x, COLOGNE_EPSG_900_913.x)); - assert!(approx_eq!(f64, coords[1].y, COLOGNE_EPSG_900_913.y)); - assert!(approx_eq!(f64, coords[2].x, HAMBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, coords[2].y, HAMBURG_EPSG_900_913.y)); + assert!(approx_eq!(f64, coords[0].x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, coords[0].y, MARBURG_EPSG_3857.y)); + assert!(approx_eq!(f64, coords[1].x, COLOGNE_EPSG_3857.x)); + assert!(approx_eq!(f64, coords[1].y, COLOGNE_EPSG_3857.y)); + assert!(approx_eq!(f64, coords[2].x, HAMBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, coords[2].y, HAMBURG_EPSG_3857.y)); let offsets = proj_pc.feature_offsets(); assert_eq!(offsets.len(), 3); @@ -1306,17 +1308,18 @@ mod tests { #[test] fn reproject_epsg4326_epsg900913_collections_equal() { - use crate::operations::reproject::{CoordinateProjection, CoordinateProjector}; - use crate::spatial_reference::{SpatialReference, SpatialReferenceAuthority}; + use crate::spatial_reference::{ + DefaultCoordinateProjector, SpatialReference, SpatialReferenceAuthority, + }; use crate::util::well_known_data::{ - COLOGNE_EPSG_900_913, COLOGNE_EPSG_4326, HAMBURG_EPSG_900_913, HAMBURG_EPSG_4326, - MARBURG_EPSG_900_913, MARBURG_EPSG_4326, + COLOGNE_EPSG_3857, COLOGNE_EPSG_4326, HAMBURG_EPSG_3857, HAMBURG_EPSG_4326, + MARBURG_EPSG_3857, MARBURG_EPSG_4326, }; let from = SpatialReference::epsg_4326(); let to = SpatialReference::new(SpatialReferenceAuthority::Epsg, 900_913); - let projector = CoordinateProjector::from_known_srs(from, to).unwrap(); + let projector = DefaultCoordinateProjector::from_known_srs(from, to).unwrap(); let pc = MultiPointCollection::from_data( MultiPoint::many(vec![ @@ -1342,8 +1345,8 @@ mod tests { .unwrap(); let expected_points = MultiPoint::many(vec![ - vec![MARBURG_EPSG_900_913, COLOGNE_EPSG_900_913], - vec![HAMBURG_EPSG_900_913], + vec![MARBURG_EPSG_3857, COLOGNE_EPSG_3857], + vec![HAMBURG_EPSG_3857], ]) .unwrap(); diff --git a/geoengine/datatypes/src/collections/multi_polygon_collection.rs b/geoengine/datatypes/src/collections/multi_polygon_collection.rs index ff24afd080..c92bafc6ab 100644 --- a/geoengine/datatypes/src/collections/multi_polygon_collection.rs +++ b/geoengine/datatypes/src/collections/multi_polygon_collection.rs @@ -525,6 +525,7 @@ mod tests { use crate::collections::{BuilderProvider, FeatureCollectionModifications}; use crate::primitives::CacheHint; use crate::primitives::{FeatureData, FeatureDataRef, TimeInterval}; +use crate::spatial_reference::CoordinateProjection; #[test] fn single_polygons() { @@ -918,18 +919,20 @@ mod tests { #[test] fn reproject_multi_polygons_epsg4326_epsg900913_collection() { use crate::operations::reproject::Reproject; - use crate::operations::reproject::{CoordinateProjection, CoordinateProjector}; + use crate::primitives::FeatureData; - use crate::spatial_reference::{SpatialReference, SpatialReferenceAuthority}; + use crate::spatial_reference::{ + DefaultCoordinateProjector, SpatialReference, SpatialReferenceAuthority, + }; use crate::util::well_known_data::{ - COLOGNE_EPSG_900_913, COLOGNE_EPSG_4326, HAMBURG_EPSG_900_913, HAMBURG_EPSG_4326, - MARBURG_EPSG_900_913, MARBURG_EPSG_4326, + COLOGNE_EPSG_3857, COLOGNE_EPSG_4326, HAMBURG_EPSG_3857, HAMBURG_EPSG_4326, + MARBURG_EPSG_3857, MARBURG_EPSG_4326, }; let from = SpatialReference::epsg_4326(); let to = SpatialReference::new(SpatialReferenceAuthority::Epsg, 900_913); - let projector = CoordinateProjector::from_known_srs(from, to).unwrap(); + let projector = DefaultCoordinateProjector::from_known_srs(from, to).unwrap(); let collection = MultiPolygonCollection::from_slices( &[ @@ -964,24 +967,24 @@ mod tests { let expected = [ MultiPolygon::new(vec![ vec![vec![ - HAMBURG_EPSG_900_913, - MARBURG_EPSG_900_913, - COLOGNE_EPSG_900_913, - HAMBURG_EPSG_900_913, + HAMBURG_EPSG_3857, + MARBURG_EPSG_3857, + COLOGNE_EPSG_3857, + HAMBURG_EPSG_3857, ]], vec![vec![ - COLOGNE_EPSG_900_913, - HAMBURG_EPSG_900_913, - MARBURG_EPSG_900_913, - COLOGNE_EPSG_900_913, + COLOGNE_EPSG_3857, + HAMBURG_EPSG_3857, + MARBURG_EPSG_3857, + COLOGNE_EPSG_3857, ]], ]) .unwrap(), MultiPolygon::new(vec![vec![vec![ - MARBURG_EPSG_900_913, - COLOGNE_EPSG_900_913, - HAMBURG_EPSG_900_913, - MARBURG_EPSG_900_913, + MARBURG_EPSG_3857, + COLOGNE_EPSG_3857, + HAMBURG_EPSG_3857, + MARBURG_EPSG_3857, ]]]) .unwrap(), ]; diff --git a/geoengine/datatypes/src/error.rs b/geoengine/datatypes/src/error.rs index 5928ac1268..3a4c606443 100644 --- a/geoengine/datatypes/src/error.rs +++ b/geoengine/datatypes/src/error.rs @@ -2,7 +2,7 @@ use crate::{ collections::FeatureCollectionError, primitives::{BoundingBox2D, Coordinate2D, PrimitivesError, TimeInstance, TimeInterval}, raster::RasterDataType, - spatial_reference::SpatialReference, + spatial_reference::{self, SpatialReference}, }; use snafu::{AsErrorSource, ErrorCompat, IntoError, prelude::*}; use std::{any::Any, convert::Infallible, path::PathBuf, sync::Arc}; @@ -375,6 +375,9 @@ pub enum Error { srs_out: SpatialReference, bounds: BoundingBox2D, }, + GeodesyProjector { + source: spatial_reference::GeodesyProjectorError, + }, } impl From for Error { @@ -406,3 +409,9 @@ impl From for Error { Self::Io { source: e } } } + +impl From for Error { + fn from(source: crate::spatial_reference::GeodesyProjectorError) -> Self { + Self::GeodesyProjector { source } + } +} diff --git a/geoengine/datatypes/src/operations/reproject.rs b/geoengine/datatypes/src/operations/reproject.rs index 7268ee948e..e4c300c9e0 100644 --- a/geoengine/datatypes/src/operations/reproject.rs +++ b/geoengine/datatypes/src/operations/reproject.rs @@ -9,84 +9,12 @@ use crate::{ BoundedGrid, GeoTransform, GridBoundingBox, GridBounds, GridIdx, GridIdx2D, GridShape, GridSize, SamplePoints, SpatialGridDefinition, }, - spatial_reference::SpatialReference, + spatial_reference::{CoordinateProjection, SpatialReference}, util::Result, }; use num_traits::Zero; -use proj::Proj; use snafu::ensure; -pub trait CoordinateProjection { - fn from_known_srs(from: SpatialReference, to: SpatialReference) -> Result - where - Self: Sized; - /// project a single coord - fn project_coordinate(&self, c: Coordinate2D) -> Result; - - /// project a set of coords - fn project_coordinates>(&self, coords: A) - -> Result>; - - fn source_srs(&self) -> SpatialReference; - - fn target_srs(&self) -> SpatialReference; -} - -pub struct CoordinateProjector { - pub from: SpatialReference, - pub to: SpatialReference, - p: Proj, -} - -impl CoordinateProjection for CoordinateProjector { - fn from_known_srs(from: SpatialReference, to: SpatialReference) -> Result { - let p = Proj::new_known_crs(&from.proj_string()?, &to.proj_string()?, None) - .map_err(|_| error::Error::NoCoordinateProjector { from, to })?; - Ok(CoordinateProjector { from, to, p }) - } - - fn project_coordinate(&self, c: Coordinate2D) -> Result { - self.p.convert(c).map_err(Into::into) - } - - fn project_coordinates>( - &self, - coords: A, - ) -> Result> { - let c_ref = coords.as_ref(); - - let mut cc = Vec::from(c_ref); - self.p.convert_array(&mut cc)?; - - Ok(cc) - } - - fn source_srs(&self) -> SpatialReference { - self.from - } - - fn target_srs(&self) -> SpatialReference { - self.to - } -} - -impl Clone for CoordinateProjector { - fn clone(&self) -> Self { - CoordinateProjector { - from: self.from, - to: self.to, - p: Proj::new_known_crs(&self.from.to_string(), &self.to.to_string(), None) - .expect("the Proj object creation should work because it already worked in the creation of the `CoordinateProjector`"), - } - } -} - -impl AsRef for CoordinateProjector { - fn as_ref(&self) -> &CoordinateProjector { - self - } -} - pub trait Reproject { type Out; fn reproject(&self, projector: &P) -> Result; @@ -259,10 +187,8 @@ where const POINTS_PER_LINE: i32 = 7; // clip bbox to the area of use of the target projection - let area_of_use_projector = CoordinateProjector::from_known_srs( - SpatialReference::epsg_4326(), - projector.source_srs(), - )?; + let area_of_use_projector = + P::from_known_srs(SpatialReference::epsg_4326(), projector.source_srs())?; let source_area_of_use = projector.source_srs().area_of_use::()?; let target_area_of_use = projector.target_srs().area_of_use::()?; let area_of_use = source_area_of_use.intersection(&target_area_of_use); @@ -355,13 +281,13 @@ fn diag_distance(ul_coord: Coordinate2D, lr_coord: Coordinate2D) -> f64 { proj_ul_lr_vector.x.hypot(proj_ul_lr_vector.y) } -pub fn suggest_pixel_size_like_gdal_helper( +pub fn suggest_pixel_size_like_gdal_helper( bbox: B, spatial_resolution: SpatialResolution, source_srs: SpatialReference, target: SpatialReference, ) -> Result { - let projector = CoordinateProjector::from_known_srs(source_srs, target)?; + let projector = P::from_known_srs(source_srs, target)?; suggest_pixel_size_like_gdal(bbox, spatial_resolution, &projector) } @@ -389,12 +315,12 @@ pub fn suggest_pixel_size_like_gdal( spatial_grid: &SpatialGridDefinition, source_srs: SpatialReference, target_srs: SpatialReference, ) -> Result { - let projector = CoordinateProjector::from_known_srs(source_srs, target_srs)?; + let projector = P::from_known_srs(source_srs, target_srs)?; suggest_output_spatial_grid_like_gdal(spatial_grid, &projector) } @@ -522,13 +448,16 @@ pub fn suggest_output_spatial_grid_like_gdal( Ok(out_spatial_grid_moved_origin.replace_origin(proj_origin)) } -pub fn suggest_pixel_size_from_diag_cross_helper( +pub fn suggest_pixel_size_from_diag_cross_helper< + B: AxisAlignedRectangle, + P: CoordinateProjection, +>( bbox: B, spatial_resolution: SpatialResolution, source_srs: SpatialReference, target: SpatialReference, ) -> Result { - let projector = CoordinateProjector::from_known_srs(source_srs, target)?; + let projector = P::from_known_srs(source_srs, target)?; suggest_pixel_size_from_diag_cross(bbox, spatial_resolution, &projector) } @@ -614,14 +543,14 @@ pub fn project_coordinates_fail_tolerant( /// this method performs the transformation of a query rectangle in `target` projection /// to a new query rectangle with coordinates in the `source` projection -pub fn reproject_spatial_query( +pub fn reproject_spatial_query( spatial_bounds: S, source: SpatialReference, target: SpatialReference, force_clipping: bool, ) -> Result> { let (Some(_s_bbox), Some(p_bbox)) = - reproject_and_unify_bbox_internal(spatial_bounds, target, source, force_clipping)? + reproject_and_unify_bbox_internal::<_, P>(spatial_bounds, target, source, force_clipping)? else { return Ok(None); }; @@ -632,22 +561,22 @@ pub fn reproject_spatial_query( /// Reproject a bounding box to the `target` projection and return the input and output bounding box /// as a pair where both elements cover the same area of the original `source_bbox` in WGS84. /// The pair is structured as `(source_bbox_clipped, target_bbox_clipped)` -pub fn reproject_and_unify_bbox( +pub fn reproject_and_unify_bbox( source_bbox: T, source: SpatialReference, target: SpatialReference, ) -> Result<(Option, Option)> { - reproject_and_unify_bbox_internal(source_bbox, source, target, true) + reproject_and_unify_bbox_internal::(source_bbox, source, target, true) } -fn reproject_and_unify_bbox_internal( +fn reproject_and_unify_bbox_internal( source_bbox: T, source: SpatialReference, target: SpatialReference, force_clipping: bool, ) -> Result<(Option, Option)> { - let proj_from_to = CoordinateProjector::from_known_srs(source, target)?; - let proj_to_from = CoordinateProjector::from_known_srs(target, source)?; + let proj_from_to = P::from_known_srs(source, target)?; + let proj_to_from = P::from_known_srs(target, source)?; let target_bbox_clipped = source_bbox.reproject_clipped(&proj_from_to)?; @@ -686,12 +615,12 @@ fn reproject_and_unify_bbox_internal( } /// Reproject the area of use of the `source` projection to the `target` projection and back. Return the back projected bounds and the area of use in the `target` projection. -pub fn reproject_and_unify_proj_bounds( +pub fn reproject_and_unify_proj_bounds( source: SpatialReference, target: SpatialReference, ) -> Result<(Option, Option)> { - let proj_from_to = CoordinateProjector::from_known_srs(source, target)?; - let proj_to_from = CoordinateProjector::from_known_srs(target, source)?; + let proj_from_to = P::from_known_srs(source, target)?; + let proj_to_from = P::from_known_srs(target, source)?; let target_bbox_clipped = source .area_of_use_projected::()? @@ -709,15 +638,19 @@ pub fn reproject_and_unify_proj_bounds( mod tests { use crate::primitives::{BoundingBox2D, SpatialPartition2D}; - use crate::spatial_reference::SpatialReferenceAuthority; + use crate::spatial_reference::{DefaultCoordinateProjector, SpatialReferenceAuthority}; use crate::util::well_known_data::{ - COLOGNE_EPSG_900_913, COLOGNE_EPSG_4326, HAMBURG_EPSG_900_913, HAMBURG_EPSG_4326, - MARBURG_EPSG_900_913, MARBURG_EPSG_4326, + COLOGNE_EPSG_3857, COLOGNE_EPSG_4326, HAMBURG_EPSG_3857, HAMBURG_EPSG_4326, + MARBURG_EPSG_3857, MARBURG_EPSG_4326, }; - use float_cmp::approx_eq; + use float_cmp::{approx_eq, assert_approx_eq}; use super::*; + // For testing, we use the default coordinate projector which may change! + // TODO: we need to test all the different projectors! + type CoordinateProjector = DefaultCoordinateProjector; + #[test] fn new_proj() { let from = SpatialReference::epsg_4326(); @@ -741,8 +674,8 @@ mod tests { let p = CoordinateProjector::from_known_srs(from, to).unwrap(); let rp = p.project_coordinate(MARBURG_EPSG_4326).unwrap(); - assert!(approx_eq!(f64, rp.x, MARBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, rp.y, MARBURG_EPSG_900_913.y)); + assert!(approx_eq!(f64, rp.x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.y, MARBURG_EPSG_3857.y)); } #[test] @@ -752,8 +685,8 @@ mod tests { let p = CoordinateProjector::from_known_srs(from, to).unwrap(); let rp = MARBURG_EPSG_4326.reproject(&p).unwrap(); - assert!(approx_eq!(f64, rp.x, MARBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, rp.y, MARBURG_EPSG_900_913.y)); + assert!(approx_eq!(f64, rp.x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.y, MARBURG_EPSG_3857.y)); } #[test] @@ -768,10 +701,10 @@ mod tests { }; let rl = l.reproject(&p).unwrap(); - assert!(approx_eq!(f64, rl.start.x, MARBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, rl.start.y, MARBURG_EPSG_900_913.y)); - assert!(approx_eq!(f64, rl.end.x, COLOGNE_EPSG_900_913.x)); - assert!(approx_eq!(f64, rl.end.y, COLOGNE_EPSG_900_913.y)); + assert!(approx_eq!(f64, rl.start.x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rl.start.y, MARBURG_EPSG_3857.y)); + assert!(approx_eq!(f64, rl.end.x, COLOGNE_EPSG_3857.x)); + assert!(approx_eq!(f64, rl.end.y, COLOGNE_EPSG_3857.y)); } #[test] @@ -785,10 +718,10 @@ mod tests { let rl = bbox.reproject(&p).unwrap(); - assert!(approx_eq!(f64, rl.lower_left().x, COLOGNE_EPSG_900_913.x)); - assert!(approx_eq!(f64, rl.lower_left().y, MARBURG_EPSG_900_913.y)); - assert!(approx_eq!(f64, rl.upper_right().x, MARBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, rl.upper_right().y, COLOGNE_EPSG_900_913.y)); + assert!(approx_eq!(f64, rl.lower_left().x, COLOGNE_EPSG_3857.x)); + assert!(approx_eq!(f64, rl.lower_left().y, MARBURG_EPSG_3857.y)); + assert!(approx_eq!(f64, rl.upper_right().x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rl.upper_right().y, COLOGNE_EPSG_3857.y)); } #[test] @@ -802,10 +735,10 @@ mod tests { let mp = MultiPoint::new(cs).unwrap(); let rp = mp.reproject(&p).unwrap(); - assert!(approx_eq!(f64, rp.points()[0].x, MARBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, rp.points()[0].y, MARBURG_EPSG_900_913.y)); - assert!(approx_eq!(f64, rp.points()[1].x, COLOGNE_EPSG_900_913.x)); - assert!(approx_eq!(f64, rp.points()[1].y, COLOGNE_EPSG_900_913.y)); + assert!(approx_eq!(f64, rp.points()[0].x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.points()[0].y, MARBURG_EPSG_3857.y)); + assert!(approx_eq!(f64, rp.points()[1].x, COLOGNE_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.points()[1].y, COLOGNE_EPSG_3857.y)); } #[test] @@ -823,12 +756,12 @@ mod tests { let mp = MultiLineString::new(cs).unwrap(); let rp = mp.reproject(&p).unwrap(); - assert!(approx_eq!(f64, rp.lines()[0][0].x, MARBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, rp.lines()[0][0].y, MARBURG_EPSG_900_913.y)); - assert!(approx_eq!(f64, rp.lines()[0][1].x, COLOGNE_EPSG_900_913.x)); - assert!(approx_eq!(f64, rp.lines()[0][1].y, COLOGNE_EPSG_900_913.y)); - assert!(approx_eq!(f64, rp.lines()[0][2].x, HAMBURG_EPSG_900_913.x)); - assert!(approx_eq!(f64, rp.lines()[0][2].y, HAMBURG_EPSG_900_913.y)); + assert!(approx_eq!(f64, rp.lines()[0][0].x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.lines()[0][0].y, MARBURG_EPSG_3857.y)); + assert!(approx_eq!(f64, rp.lines()[0][1].x, COLOGNE_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.lines()[0][1].y, COLOGNE_EPSG_3857.y)); + assert!(approx_eq!(f64, rp.lines()[0][2].x, HAMBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.lines()[0][2].y, HAMBURG_EPSG_3857.y)); } #[test] @@ -850,32 +783,32 @@ mod tests { assert!(approx_eq!( f64, rp.polygons()[0][0][0].x, - MARBURG_EPSG_900_913.x + MARBURG_EPSG_3857.x )); assert!(approx_eq!( f64, rp.polygons()[0][0][0].y, - MARBURG_EPSG_900_913.y + MARBURG_EPSG_3857.y )); assert!(approx_eq!( f64, rp.polygons()[0][0][1].x, - COLOGNE_EPSG_900_913.x + COLOGNE_EPSG_3857.x )); assert!(approx_eq!( f64, rp.polygons()[0][0][1].y, - COLOGNE_EPSG_900_913.y + COLOGNE_EPSG_3857.y )); assert!(approx_eq!( f64, rp.polygons()[0][0][2].x, - HAMBURG_EPSG_900_913.x + HAMBURG_EPSG_3857.x )); assert!(approx_eq!( f64, rp.polygons()[0][0][2].y, - HAMBURG_EPSG_900_913.y + HAMBURG_EPSG_3857.y )); } @@ -1020,7 +953,8 @@ mod tests { fn it_reprojects_and_unifies_bbox() { let bbox = SpatialPartition2D::new_unchecked((-180., 90.).into(), (180., -90.).into()); - let (input, output) = reproject_and_unify_bbox_internal( + /* This is not a good test since math outside the bounds is questionable! + let (input, output) = reproject_and_unify_bbox_internal::<_, CoordinateProjector>( bbox, SpatialReference::epsg_4326(), SpatialReference::new(SpatialReferenceAuthority::Epsg, 3857), @@ -1028,20 +962,24 @@ mod tests { ) .unwrap(); - assert_eq!( + + assert_approx_eq!( + SpatialPartition2D, input.unwrap(), SpatialPartition2D::new_unchecked((-180., 90.).into(), (180., -90.).into()) ); - assert_eq!( + assert_approx_eq!( + SpatialPartition2D, output.unwrap(), SpatialPartition2D::new_unchecked( (-20_037_508.342_789_244, 242_528_680.943_742_72).into(), (20_037_508.342_789_244, -242_528_680.943_742_72).into() ) ); + */ - let (input, output) = reproject_and_unify_bbox_internal( + let (input, output) = reproject_and_unify_bbox_internal::<_, CoordinateProjector>( bbox, SpatialReference::epsg_4326(), SpatialReference::new(SpatialReferenceAuthority::Epsg, 3857), @@ -1049,20 +987,27 @@ mod tests { ) .unwrap(); - assert_eq!( + assert_approx_eq!( + SpatialPartition2D, input.unwrap(), SpatialPartition2D::new_unchecked((-180., 85.06).into(), (180., -85.06).into()) ); - assert_eq!( + assert_approx_eq!( + SpatialPartition2D, output.unwrap(), SpatialPartition2D::new_unchecked( (-20_037_508.342_789_244, 20_048_966.104_014_594).into(), (20_037_508.342_789_244, -20_048_966.104_014_594).into() ) ); + } - let (input, output) = reproject_and_unify_bbox_internal( + /* This part of the tests is problematic since the valid bounds of UTM32 are a lot smaller then the wgs84 bounds and we put in "illegal" values for testing with clip which the static provider can not resolve + + #[test] + fn it_projects_and_unifies_bbox_utm32() { + let (input, output) = reproject_and_unify_bbox_internal::<_, CoordinateProjector>( bbox, SpatialReference::epsg_4326(), SpatialReference::new(SpatialReferenceAuthority::Epsg, 25832), @@ -1086,7 +1031,7 @@ mod tests { ) ); - let (input, output) = reproject_and_unify_bbox_internal( + let (input, output) = reproject_and_unify_bbox_internal::<_, CoordinateProjector>( bbox, SpatialReference::epsg_4326(), SpatialReference::new(SpatialReferenceAuthority::Epsg, 25832), @@ -1110,4 +1055,5 @@ mod tests { ) ); } + */ } diff --git a/geoengine/datatypes/src/primitives/spatial_partition.rs b/geoengine/datatypes/src/primitives/spatial_partition.rs index bea2674849..0160cab084 100644 --- a/geoengine/datatypes/src/primitives/spatial_partition.rs +++ b/geoengine/datatypes/src/primitives/spatial_partition.rs @@ -6,6 +6,7 @@ use crate::raster::GridShape2D; use crate::util::Result; use crate::util::helpers::snap_next; use crate::util::helpers::snap_prev; +use float_cmp::ApproxEq; use postgres_types::FromSql; use postgres_types::ToSql; use serde::{Deserialize, Serialize}; @@ -341,6 +342,22 @@ pub fn partitions_extent>( bboxes.reduce(|s, other| s.extended(&other)) } +impl ApproxEq for SpatialPartition2D { + type Margin = float_cmp::F64Margin; + + fn approx_eq(self, other: Self, margin: M) -> bool + where + M: Into, + { + let m = margin.into(); + self.upper_left_coordinate + .approx_eq(other.upper_left_coordinate, m) + && self + .lower_right_coordinate + .approx_eq(other.lower_right_coordinate, m) + } +} + #[cfg(test)] mod tests { use float_cmp::approx_eq; diff --git a/geoengine/datatypes/src/primitives/time_instance.rs b/geoengine/datatypes/src/primitives/time_instance.rs index 125177cb90..86e1ba4539 100644 --- a/geoengine/datatypes/src/primitives/time_instance.rs +++ b/geoengine/datatypes/src/primitives/time_instance.rs @@ -1,7 +1,9 @@ use super::datetime::DateTimeError; use super::{DateTime, Duration}; -use crate::primitives::error; -use crate::util::Result; +use crate::{ + primitives::error::{self}, + util::Result, +}; use postgres_types::{FromSql, ToSql}; use serde::{Deserialize, Serialize}; use snafu::ensure; @@ -219,7 +221,12 @@ impl<'de> Deserialize<'de> for TimeInstance { } } - deserializer.deserialize_any(IsoStringOrUnixTimestamp) + if deserializer.is_human_readable() { + deserializer.deserialize_any(IsoStringOrUnixTimestamp) + } else { + let millis = i64::deserialize(deserializer)?; + TimeInstance::from_millis(millis).map_err(serde::de::Error::custom) + } } } diff --git a/geoengine/datatypes/src/primitives/time_interval.rs b/geoengine/datatypes/src/primitives/time_interval.rs index 8b02b1cc39..ce45c10071 100755 --- a/geoengine/datatypes/src/primitives/time_interval.rs +++ b/geoengine/datatypes/src/primitives/time_interval.rs @@ -551,6 +551,8 @@ pub fn time_interval_extent>>( #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::primitives::DateTime; use super::*; @@ -793,4 +795,25 @@ mod tests { } } } + + #[test] + fn serialize_time_interval_with_serde() { + let time_interval = TimeInterval::new(1, 2).expect("time interval was created succesfully"); + let hin = serde_json::to_string(&time_interval).expect("serilization works"); + let back: TimeInterval = serde_json::from_str(&hin).expect("deserialization works"); + assert_eq!(time_interval, back); + } + + #[test] + fn serialize_time_interval_as_string_with_serde() { + let time_instance1 = TimeInstance::from_str("2000-01-01T12:00:00Z") + .expect("input should bee a valid date time string"); + let time_instance2 = TimeInstance::from_str("2030-01-01T12:00:00Z") + .expect("input should bee a valid date time string"); + let time_interval = TimeInterval::new(time_instance1, time_instance2) + .expect("time interval was created succesfully"); + let hin = serde_json::to_string(&time_interval).expect("serilization works"); + let back: TimeInterval = serde_json::from_str(&hin).expect("deserialization works"); + assert_eq!(time_interval, back); + } } diff --git a/geoengine/datatypes/src/raster/data_type.rs b/geoengine/datatypes/src/raster/data_type.rs index 5bec0cec11..280115e611 100644 --- a/geoengine/datatypes/src/raster/data_type.rs +++ b/geoengine/datatypes/src/raster/data_type.rs @@ -46,6 +46,7 @@ pub trait Pixel: + TypedRasterConversion + TypedRasterConversion + SaturatingOps + + bytemuck::Pod { } diff --git a/geoengine/datatypes/src/raster/grid_bounds.rs b/geoengine/datatypes/src/raster/grid_bounds.rs index 49eecccee6..04dcb302c0 100644 --- a/geoengine/datatypes/src/raster/grid_bounds.rs +++ b/geoengine/datatypes/src/raster/grid_bounds.rs @@ -5,6 +5,7 @@ use super::{ use crate::{error, raster::GridIdx2D, util::Result}; use serde::{Deserialize, Serialize}; use snafu::ensure; +use std::hash::Hash; use std::ops::Add; #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)] @@ -552,6 +553,13 @@ where GridIdx2DIter::new(bounds) } +impl Hash for GridBoundingBox2D { + fn hash(&self, state: &mut H) { + self.min.hash(state); + self.max.hash(state); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/geoengine/datatypes/src/raster/grid_index.rs b/geoengine/datatypes/src/raster/grid_index.rs index fc5b1da278..94cbeec760 100644 --- a/geoengine/datatypes/src/raster/grid_index.rs +++ b/geoengine/datatypes/src/raster/grid_index.rs @@ -8,7 +8,7 @@ use super::GridShape2D; /// /// The grid index struct. This is a wrapper for arrays with added methods and traits, e.g. Add, Sub... /// -#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize, Hash)] pub struct GridIdx(pub A) where A: AsRef<[isize]>; diff --git a/geoengine/datatypes/src/raster/grid_spatial.rs b/geoengine/datatypes/src/raster/grid_spatial.rs index f07239d364..ed26491017 100644 --- a/geoengine/datatypes/src/raster/grid_spatial.rs +++ b/geoengine/datatypes/src/raster/grid_spatial.rs @@ -3,13 +3,12 @@ use super::{ GridBounds, GridIdx, GridIdx2D, GridIntersection, TilingSpecification, TilingStrategy, }; use crate::{ - operations::reproject::{ - CoordinateProjection, Reproject, ReprojectClipped, suggest_output_spatial_grid_like_gdal, - }, + operations::reproject::{Reproject, ReprojectClipped, suggest_output_spatial_grid_like_gdal}, primitives::{ AxisAlignedRectangle, Coordinate2D, SpatialPartition2D, SpatialPartitioned, SpatialResolution, }, + spatial_reference::CoordinateProjection, util::Result, }; use float_cmp::{ApproxEq, approx_eq}; @@ -401,7 +400,9 @@ mod tests { operations::reproject::suggest_output_spatial_grid_like_gdal_helper, primitives::AxisAlignedRectangle, raster::{BoundedGrid, GridShape}, - spatial_reference::{SpatialReference, SpatialReferenceAuthority}, + spatial_reference::{ + DefaultCoordinateProjector, SpatialReference, SpatialReferenceAuthority, + }, test_data, util::gdal::gdal_open_dataset, }; @@ -518,9 +519,14 @@ mod tests { .bounding_box(), ); + // NOTE: this uses the `DefaultCoordinateProjector` which might change.` let result_res = - suggest_output_spatial_grid_like_gdal_helper(&spatial_grid_3857, epsg_3857, epsg_4326) - .unwrap(); + suggest_output_spatial_grid_like_gdal_helper::( + &spatial_grid_3857, + epsg_3857, + epsg_4326, + ) + .unwrap(); assert!(1. - (result_res.geo_transform().x_pixel_size() / res_4326.x).abs() < 0.02); assert!(1. - (result_res.geo_transform().y_pixel_size() / res_4326.y).abs() < 0.02); diff --git a/geoengine/datatypes/src/raster/mod.rs b/geoengine/datatypes/src/raster/mod.rs index 297d82bb43..ab88e67ebd 100755 --- a/geoengine/datatypes/src/raster/mod.rs +++ b/geoengine/datatypes/src/raster/mod.rs @@ -31,7 +31,6 @@ pub use self::tiling::{TileInformation, TileInformationIter, TilingSpecification pub use self::typed_raster_conversion::TypedRasterConversion; pub use self::typed_raster_tile::{TypedRasterTile2D, TypedRasterTile3D}; pub use self::{grid_traits::ChangeGridBounds, grid_traits::GridShapeAccess}; -pub use arrow_conversion::raster_tile_2d_to_arrow_ipc_file; pub use db_types::GridBoundingBox2DDbType; pub use grid_spatial::{SpatialGridDefinition, TilingSpatialGridDefinition}; pub use masked_grid::{MaskedGrid, MaskedGrid1D, MaskedGrid2D, MaskedGrid3D}; @@ -56,7 +55,7 @@ pub use raster_properties::{ pub use raster_traits::{CoordinatePixelAccess, GeoTransformAccess, Raster}; pub use util::{TileIdxBandCrossProductIter, TileInformationBandCrossProductIter}; -mod arrow_conversion; +pub mod arrow_conversion; mod band_names; mod data_type; mod db_types; diff --git a/geoengine/datatypes/src/raster/raster_properties.rs b/geoengine/datatypes/src/raster/raster_properties.rs index 0c8c963aaa..2286cd49d8 100644 --- a/geoengine/datatypes/src/raster/raster_properties.rs +++ b/geoengine/datatypes/src/raster/raster_properties.rs @@ -16,12 +16,12 @@ use std::fmt::{Display, Formatter}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct RasterProperties { - scale: Option, - offset: Option, - description: Option, + pub scale: Option, + pub offset: Option, + pub description: Option, // serialize as a list of tuples because `RasterPropertiesKey` cannot be used as a key in a JSON dict #[serde_as(as = "Vec<(_, _)>")] - properties_map: HashMap, + pub properties_map: HashMap, } impl ByteSize for RasterProperties { diff --git a/geoengine/datatypes/src/spatial_reference/area_of_use_provider.rs b/geoengine/datatypes/src/spatial_reference/area_of_use_provider.rs new file mode 100644 index 0000000000..527000e771 --- /dev/null +++ b/geoengine/datatypes/src/spatial_reference/area_of_use_provider.rs @@ -0,0 +1,11 @@ +use crate::primitives::AxisAlignedRectangle; +use crate::spatial_reference::SpatialReference; +use crate::util::Result; + +pub trait AreaOfUseProvider { + fn new_known_crs(def: SpatialReference) -> Result + where + Self: Sized; + fn area_of_use(&self) -> Result; + fn area_of_use_projected(&self) -> Result; +} diff --git a/geoengine/datatypes/src/spatial_reference/geodesy_projector.rs b/geoengine/datatypes/src/spatial_reference/geodesy_projector.rs new file mode 100644 index 0000000000..5169d85df2 --- /dev/null +++ b/geoengine/datatypes/src/spatial_reference/geodesy_projector.rs @@ -0,0 +1,289 @@ +use crs_definitions; +use geodesy::{ + coord::{AngularUnits, CoordinateTuple}, + ctx::Context, +}; +use snafu::Snafu; +use strum::IntoStaticStr; + +use crate::{ + primitives::Coordinate2D, + spatial_reference::{CoordinateProjection, SpatialReference, SpatialReferenceAuthority}, +}; + +#[derive(Debug, Snafu, IntoStaticStr)] +pub enum Error { + Geodesy { source: geodesy::Error }, + UnknownCrsCode { code: u32 }, + UnknownCrsAuthority { authority: String }, +} + +impl From for Error { + fn from(source: geodesy::Error) -> Self { + Error::Geodesy { source } + } +} + +#[derive(Debug, Clone)] +pub struct GeodesyCoordinateProjector { + ctx: C, + source_crs: u16, + source: geodesy::ctx::OpHandle, + source_latlon: bool, + target_crs: u16, + target: geodesy::ctx::OpHandle, + target_latlon: bool, +} + +impl GeodesyCoordinateProjector { + pub fn pro4_string_modifications(code: u16, proj4: &str) -> String { + match (code, proj4) { + (3857, _) => proj4.replace("merc", "webmerc"), + _ => proj4.to_string(), + } + } + + pub fn from_spatial_references( + source: SpatialReference, + target: SpatialReference, + ) -> Result { + if !matches!(source.authority(), SpatialReferenceAuthority::Epsg) { + return Err(Error::UnknownCrsAuthority { + authority: source.authority().to_string(), + }); + } + + if !matches!(target.authority(), SpatialReferenceAuthority::Epsg) { + return Err(Error::UnknownCrsAuthority { + authority: target.authority().to_string(), + }); + } + + if source.code() > u32::from(u16::MAX) { + return Err(Error::UnknownCrsCode { + code: source.code(), + }); + } + + if target.code() > u32::from(u16::MAX) { + return Err(Error::UnknownCrsCode { + code: target.code(), + }); + } + + GeodesyCoordinateProjector::from_epsg_codes(source.code() as u16, target.code() as u16) + } + + pub fn from_epsg_codes(source_crs: u16, target_crs: u16) -> Result { + let start_time = std::time::Instant::now(); + + let source_def = crs_definitions::from_code(source_crs).ok_or(Error::UnknownCrsCode { + code: u32::from(source_crs), + })?; + let target_def = crs_definitions::from_code(target_crs).ok_or(Error::UnknownCrsCode { + code: u32::from(target_crs), + })?; + let source_latlon = source_def.proj4.starts_with("+proj=longlat"); + let target_latlon = target_def.proj4.starts_with("+proj=longlat"); + + let source_proj_string = Self::pro4_string_modifications(source_crs, source_def.proj4); + let target_proj_string = Self::pro4_string_modifications(target_crs, target_def.proj4); + + let source_geodesy_string = geodesy::authoring::parse_proj(&source_proj_string)?; + let target_geodesy_string = geodesy::authoring::parse_proj(&target_proj_string)?; + + tracing::trace!( + "Geodesy source / target string: {source_geodesy_string} / {target_geodesy_string}" + ); + + let mut ctx = geodesy_ctx(); + + let source = ctx.op(&source_geodesy_string)?; + let target = ctx.op(&target_geodesy_string)?; + + let transformer = GeodesyCoordinateProjector { + ctx, + source_crs, + source, + source_latlon, + target_crs, + target, + target_latlon, + }; + + tracing::trace!( + "Transformer::from_epsg took {}", + start_time.elapsed().as_nanos() + ); + + Ok(transformer) + } + + pub fn transform_coord(&self, coord: Coordinate2D) -> Result { + let mut geodesy_coord = if self.source_latlon { + [geodesy::coord::Coor2D::gis(coord.x, coord.y)] + } else { + [geodesy::coord::Coor2D::raw(coord.x, coord.y)] + }; + self.ctx + .apply(self.source, geodesy::Direction::Inv, &mut geodesy_coord)?; + self.ctx + .apply(self.target, geodesy::Direction::Fwd, &mut geodesy_coord)?; + + let (x, y) = if self.target_latlon { + geodesy_coord[0].to_degrees().xy() + } else { + geodesy_coord[0].xy() + }; + + Ok(Coordinate2D::new(x, y)) + } +} + +impl GeodesyCoordinateProjector { + pub fn transform_coords(&self, coords: &[Coordinate2D]) -> Result, Error> { + let mut geodesy_coords: Vec = coords + .iter() + .map(|c| geodesy::coord::Coor2D::raw(c.x, c.y)) + .collect(); + + if self.source_latlon { + for c in &mut geodesy_coords { + *c = c.to_radians(); + } + } + + self.ctx + .apply(self.source, geodesy::Direction::Inv, &mut geodesy_coords)?; + self.ctx + .apply(self.target, geodesy::Direction::Fwd, &mut geodesy_coords)?; + + if self.target_latlon { + for c in &mut geodesy_coords { + *c = c.to_degrees(); + } + } + + let transformed_coords: Result, Error> = geodesy_coords + .iter() + .map(|c| Ok(Coordinate2D::new(c.x(), c.y()))) + .collect(); + + transformed_coords + } + + /* + pub async fn transform_coords_parallel( + &self, + coords: &[Coordinate2D], + ) -> Result, Error> + where + geodesy::coord::Coor2D: Sized, + { + const PAR_CHUNK_SIZE: usize = 64; + + let mut geodesy_coords: Vec<(f64, f64)> = coords.iter().map(|c| (c.x, c.y)).collect(); + + if self.source_latlon { + for c in geodesy_coords.as_mut_slice() { + *c = c.to_radians(); + } + } + + geodesy_coords.chunks_mut(PAR_CHUNK_SIZE).for_each(|chunk| { + self.ctx + .apply(self.source, geodesy::Direction::Inv, chunk) + .unwrap(); + + self.ctx + .apply(self.target, geodesy::Direction::Fwd, chunk) + .unwrap(); + }); + + if self.target_latlon { + for c in geodesy_coords.as_mut_slice() { + *c = c.to_degrees(); + } + } + + let transformed_coords: Result, Error> = geodesy_coords + .iter() + .map(|c| Ok(Coordinate2D::new(c.x(), c.y()))) + .collect(); + + Ok(transformed_coords?) + } + */ +} + +pub fn geodesy_ctx() -> geodesy::ctx::Minimal { + geodesy::ctx::Minimal::new() +} + +impl CoordinateProjection for GeodesyCoordinateProjector { + fn from_known_srs(from: SpatialReference, to: SpatialReference) -> super::Result + where + Self: Sized, + { + let start_time: std::time::Instant = std::time::Instant::now(); + + let t = GeodesyCoordinateProjector::from_spatial_references(from, to)?; + + tracing::trace!( + "CoordinateProjection::from_known_srs Geodesy creation took {}", + start_time.elapsed().as_nanos() + ); + + Ok(t) + } + + fn project_coordinate(&self, c: Coordinate2D) -> super::Result { + Ok(self.transform_coord(c)?) + } + + fn project_coordinates>( + &self, + coords: A, + ) -> super::Result> { + let start = std::time::Instant::now(); + + let res = self.transform_coords(coords.as_ref())?; + + tracing::trace!( + "CoordinateProjection::project_coordinates Geodesy convert_array took {}", + start.elapsed().as_nanos() + ); + + Ok(res) + } + + fn source_srs(&self) -> SpatialReference { + SpatialReference::new(SpatialReferenceAuthority::Epsg, u32::from(self.source_crs)) + } + + fn target_srs(&self) -> SpatialReference { + SpatialReference::new(SpatialReferenceAuthority::Epsg, u32::from(self.target_crs)) + } +} + +#[cfg(test)] +mod tests { + use float_cmp::approx_eq; + + use crate::util::well_known_data::{MARBURG_EPSG_3857, MARBURG_EPSG_4326}; + + use super::*; + + #[test] + fn transform_coordinate_4326_3857() -> Result<(), Error> { + let from = SpatialReference::epsg_4326(); + let to = SpatialReference::new(SpatialReferenceAuthority::Epsg, 3857); + let transformer = GeodesyCoordinateProjector::from_spatial_references(from, to)?; + + let rp = transformer.transform_coord(MARBURG_EPSG_4326)?; + + assert!(approx_eq!(f64, rp.x, MARBURG_EPSG_3857.x)); + assert!(approx_eq!(f64, rp.y, MARBURG_EPSG_3857.y)); + Ok(()) + } +} diff --git a/geoengine/datatypes/src/spatial_reference/mixed_area_of_use_provider.rs b/geoengine/datatypes/src/spatial_reference/mixed_area_of_use_provider.rs new file mode 100644 index 0000000000..dcc35d57e2 --- /dev/null +++ b/geoengine/datatypes/src/spatial_reference/mixed_area_of_use_provider.rs @@ -0,0 +1,38 @@ +use super::{ProjAreaOfUseProvider, StaticEpsgAreaProvider}; +use crate::spatial_reference::AreaOfUseProvider; +pub enum MixedAreaOfUseProvider { + Proj(ProjAreaOfUseProvider), + Static(StaticEpsgAreaProvider), +} + +impl AreaOfUseProvider for MixedAreaOfUseProvider { + fn new_known_crs(def: crate::spatial_reference::SpatialReference) -> super::Result + where + Self: Sized, + { + let static_provider_try = StaticEpsgAreaProvider::new_known_crs(def); + if static_provider_try.is_ok() { + tracing::trace!("Using StaticAreaofUseProvider for {def}"); + return static_provider_try.map(MixedAreaOfUseProvider::Static); + } + + tracing::trace!("Using ProjAreaOfUseProvider for {def}"); + ProjAreaOfUseProvider::new_known_crs(def).map(MixedAreaOfUseProvider::Proj) + } + + fn area_of_use(&self) -> super::Result { + match self { + MixedAreaOfUseProvider::Proj(pro) => pro.area_of_use(), + MixedAreaOfUseProvider::Static(sta) => sta.area_of_use(), + } + } + + fn area_of_use_projected( + &self, + ) -> super::Result { + match self { + MixedAreaOfUseProvider::Proj(pro) => pro.area_of_use_projected(), + MixedAreaOfUseProvider::Static(sta) => sta.area_of_use_projected(), + } + } +} diff --git a/geoengine/datatypes/src/spatial_reference/mixed_projector.rs b/geoengine/datatypes/src/spatial_reference/mixed_projector.rs new file mode 100644 index 0000000000..3bba330390 --- /dev/null +++ b/geoengine/datatypes/src/spatial_reference/mixed_projector.rs @@ -0,0 +1,78 @@ +use crate::spatial_reference::CoordinateProjection; + +pub enum MixedCoordinateProjector { + ProjProjector(super::ProjCoordinateProjector), + GeodesyProjector(super::GeodesyCoordinateProjector), +} + +impl MixedCoordinateProjector { + pub fn new( + source: crate::spatial_reference::SpatialReference, + target: crate::spatial_reference::SpatialReference, + ) -> crate::util::Result { + let try_geodesy = + super::geodesy_projector::GeodesyCoordinateProjector::from_known_srs(source, target); + + if try_geodesy.is_ok() { + tracing::debug!( + "Using geodesy for reprojection from {} to {}", + source, + target + ); + try_geodesy.map(Self::GeodesyProjector) + } else { + tracing::debug!("Using proj for reprojection from {} to {}", source, target); + let proj_projector = + super::proj_projector::ProjCoordinateProjector::from_known_srs(source, target)?; + Ok(Self::ProjProjector(proj_projector)) + } + } +} + +impl CoordinateProjection for MixedCoordinateProjector { + fn from_known_srs( + from: crate::spatial_reference::SpatialReference, + to: crate::spatial_reference::SpatialReference, + ) -> super::Result + where + Self: Sized, + { + MixedCoordinateProjector::new(from, to) + } + + fn project_coordinate( + &self, + c: crate::primitives::Coordinate2D, + ) -> super::Result { + match self { + MixedCoordinateProjector::ProjProjector(proj) => proj.project_coordinate(c), + MixedCoordinateProjector::GeodesyProjector(geodesy) => geodesy.project_coordinate(c), + } + } + + fn project_coordinates>( + &self, + coords: A, + ) -> super::Result> { + match self { + MixedCoordinateProjector::ProjProjector(proj) => proj.project_coordinates(coords), + MixedCoordinateProjector::GeodesyProjector(geodesy) => { + geodesy.project_coordinates(coords) + } + } + } + + fn source_srs(&self) -> crate::spatial_reference::SpatialReference { + match self { + MixedCoordinateProjector::ProjProjector(proj) => proj.source_srs(), + MixedCoordinateProjector::GeodesyProjector(geodesy) => geodesy.source_srs(), + } + } + + fn target_srs(&self) -> crate::spatial_reference::SpatialReference { + match self { + MixedCoordinateProjector::ProjProjector(proj) => proj.target_srs(), + MixedCoordinateProjector::GeodesyProjector(geodesy) => geodesy.target_srs(), + } + } +} diff --git a/geoengine/datatypes/src/spatial_reference.rs b/geoengine/datatypes/src/spatial_reference/mod.rs similarity index 93% rename from geoengine/datatypes/src/spatial_reference.rs rename to geoengine/datatypes/src/spatial_reference/mod.rs index 6092d1344e..eab65e3477 100644 --- a/geoengine/datatypes/src/spatial_reference.rs +++ b/geoengine/datatypes/src/spatial_reference/mod.rs @@ -1,15 +1,11 @@ use crate::{ - error, - operations::reproject::{CoordinateProjection, CoordinateProjector, Reproject}, - primitives::AxisAlignedRectangle, - util::Result, + error, operations::reproject::Reproject, primitives::AxisAlignedRectangle, util::Result, }; use gdal::spatial_ref::SpatialRef; use postgres_types::private::BytesMut; use postgres_types::{FromSql, IsNull, ToSql, Type}; -use proj::Proj; use serde::de::Visitor; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -18,6 +14,30 @@ use snafu::ResultExt; use std::str::FromStr; use std::{convert::TryFrom, fmt::Formatter}; +mod area_of_use_provider; +pub use area_of_use_provider::AreaOfUseProvider; + +mod proj_projector; +use proj_projector::{ProjAreaOfUseProvider, ProjCoordinateProjector}; + +mod geodesy_projector; +pub use geodesy_projector::{Error as GeodesyProjectorError, GeodesyCoordinateProjector}; + +mod static_epsg_area_provider; +use static_epsg_area_provider::StaticEpsgAreaProvider; + +mod projection_provider; +pub use projection_provider::CoordinateProjection; + +mod mixed_area_of_use_provider; +use mixed_area_of_use_provider::MixedAreaOfUseProvider; + +mod mixed_projector; +use mixed_projector::MixedCoordinateProjector; + +pub type DefaultCoordinateProjector = MixedCoordinateProjector; +pub type DefaultAreaOfUseProvider = MixedAreaOfUseProvider; + /// A spatial reference authority that is part of a spatial reference definition #[derive( Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, ToSql, FromSql, @@ -73,7 +93,7 @@ impl SpatialReference { pub fn proj_string(self) -> Result { match self.authority { SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 | SpatialReferenceAuthority::Esri => { - Ok(format!("{}:{}", self.authority, self.code)) + Ok(self.srs_string()) } // poor-mans integration of Meteosat Second Generation SpatialReferenceAuthority::SrOrg if self.code == 81 => Ok("+proj=geos +lon_0=0 +h=35785831 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs +type=crs".to_owned()), @@ -86,20 +106,8 @@ impl SpatialReference { /// Return the area of use in EPSG:4326 projection pub fn area_of_use(self) -> Result { - let proj_string = self.proj_string()?; - - let proj = Proj::new(&proj_string).map_err(|_| error::Error::InvalidProjDefinition { - proj_definition: proj_string.clone(), - })?; - let area = proj - .area_of_use() - .context(error::ProjInternal)? - .0 - .ok_or(error::Error::NoAreaOfUseDefined { proj_string })?; - A::from_min_max( - (area.west, area.south).into(), - (area.east, area.north).into(), - ) + let provider = DefaultAreaOfUseProvider::new_known_crs(self)?; + provider.area_of_use() } /// Return the area of use in current projection @@ -107,8 +115,8 @@ impl SpatialReference { if self == Self::epsg_4326() { return self.area_of_use(); } - let p = CoordinateProjector::from_known_srs(Self::epsg_4326(), self)?; - self.area_of_use::()?.reproject(&p) + let provider = DefaultAreaOfUseProvider::new_known_crs(self)?; + provider.area_of_use_projected() } /// Return the srs-string "authority:code" @@ -125,7 +133,7 @@ impl SpatialReference { { // generate a projector which transforms wgs84 into the projection we want to produce. let valid_bounds_proj = - CoordinateProjector::from_known_srs(SpatialReference::epsg_4326(), *self)?; + DefaultCoordinateProjector::from_known_srs(SpatialReference::epsg_4326(), *self)?; // transform the bounds of the input srs (coordinates are in wgs84) into the output projection. // TODO check if there is a better / smarter way to check if the coordinates are valid. diff --git a/geoengine/datatypes/src/spatial_reference/proj_projector.rs b/geoengine/datatypes/src/spatial_reference/proj_projector.rs new file mode 100644 index 0000000000..509de08e58 --- /dev/null +++ b/geoengine/datatypes/src/spatial_reference/proj_projector.rs @@ -0,0 +1,165 @@ +use proj::Proj; +use snafu::ResultExt; +use tracing::instrument; + +use crate::{ + error, + operations::reproject::Reproject, + primitives::{AxisAlignedRectangle, Coordinate2D}, + spatial_reference::{AreaOfUseProvider, CoordinateProjection, SpatialReference}, + util::Result, +}; + +pub struct ProjCoordinateProjector { + pub from: SpatialReference, + pub to: SpatialReference, + p: Proj, +} + +impl CoordinateProjection for ProjCoordinateProjector { + #[instrument] + fn from_known_srs(from: SpatialReference, to: SpatialReference) -> Result { + let p = Proj::new_known_crs(&from.srs_string(), &to.srs_string(), None) + .map_err(|_| error::Error::NoCoordinateProjector { from, to })?; + + Ok(ProjCoordinateProjector { from, to, p }) + } + + #[instrument(skip(self))] + fn project_coordinate(&self, c: Coordinate2D) -> Result { + self.p.convert(c).map_err(Into::into) + } + + #[instrument(skip_all)] + fn project_coordinates>( + &self, + coords: A, + ) -> Result> { + let c_ref = coords.as_ref(); + + let mut cc = Vec::from(c_ref); + self.p.convert_array(&mut cc)?; + + Ok(cc) + } + + fn source_srs(&self) -> SpatialReference { + self.from + } + + fn target_srs(&self) -> SpatialReference { + self.to + } +} + +impl ProjCoordinateProjector { + // TODO: this uses the project bounds method of proj, which might produce other results then our current implementation. + fn _project_bounds(&self, bounds: B) -> Result { + let start_time: std::time::Instant = std::time::Instant::now(); + + let x = self.p.transform_bounds( + bounds.lower_left().x, + bounds.lower_left().y, + bounds.upper_right().x, + bounds.upper_right().y, + 21, + )?; + + tracing::trace!( + "CoordinateProjection::project_coordinates Proj transform_bounds took {}", + start_time.elapsed().as_nanos() + ); + + let new_left = x[0]; + let new_right = x[2]; + + // TODO: how to handle x-axis flip in lat/lon? + let new_right = if self.to.code() == 4326 && new_right < new_left { + new_right + 360. + } else { + new_right + }; + + B::from_min_max( + Coordinate2D { + x: new_left, + y: x[1], + }, + Coordinate2D { + x: new_right, + y: x[3], + }, + ) + } +} + +impl Clone for ProjCoordinateProjector { + #[instrument(skip(self))] + fn clone(&self) -> Self { + let start_time: std::time::Instant = std::time::Instant::now(); + + let p = Proj::new_known_crs(&self.from.to_string(), &self.to.to_string(), None) + .expect("the Proj object creation should work because it already worked in the creation of the `CoordinateProjector`"); + + tracing::trace!( + "CoordinateProjection::clone Proj new_known_crs took {}", + start_time.elapsed().as_nanos() + ); + + ProjCoordinateProjector { + from: self.from, + to: self.to, + p, + } + } +} + +impl AsRef for ProjCoordinateProjector { + fn as_ref(&self) -> &ProjCoordinateProjector { + self + } +} + +pub struct ProjAreaOfUseProvider { + proj: Proj, + def: SpatialReference, +} + +impl AreaOfUseProvider for ProjAreaOfUseProvider { + fn new_known_crs(def: SpatialReference) -> Result + where + Self: Sized, + { + let proj = Proj::new(&def.proj_string()?) + .map_err(|_| error::Error::ProjStringUnresolvable { spatial_ref: def })?; + + Ok(ProjAreaOfUseProvider { proj, def }) + } + + fn area_of_use(&self) -> Result { + let area = + self.proj + .area_of_use() + .context(error::ProjInternal)? + .0 + .ok_or(error::Error::NoAreaOfUseDefined { + proj_string: self.def.proj_string().expect( + "must resolve to a valid proj string or the struct can't be created", + ), + })?; + + A::from_min_max( + (area.west, area.south).into(), + (area.east, area.north).into(), + ) + } + + fn area_of_use_projected(&self) -> Result { + if self.def == SpatialReference::epsg_4326() { + return self.area_of_use(); + } + let p = ProjCoordinateProjector::from_known_srs(SpatialReference::epsg_4326(), self.def)?; + + self.area_of_use::()?.reproject(&p) + } +} diff --git a/geoengine/datatypes/src/spatial_reference/projection_provider.rs b/geoengine/datatypes/src/spatial_reference/projection_provider.rs new file mode 100644 index 0000000000..537eaee796 --- /dev/null +++ b/geoengine/datatypes/src/spatial_reference/projection_provider.rs @@ -0,0 +1,19 @@ +use crate::primitives::Coordinate2D; +use crate::spatial_reference::SpatialReference; +use crate::util::Result; + +pub trait CoordinateProjection { + fn from_known_srs(from: SpatialReference, to: SpatialReference) -> Result + where + Self: Sized; + /// project a single coord + fn project_coordinate(&self, c: Coordinate2D) -> Result; + + /// project a set of coords + fn project_coordinates>(&self, coords: A) + -> Result>; + + fn source_srs(&self) -> SpatialReference; + + fn target_srs(&self) -> SpatialReference; +} diff --git a/geoengine/datatypes/src/spatial_reference/static_epsg_area_provider.rs b/geoengine/datatypes/src/spatial_reference/static_epsg_area_provider.rs new file mode 100644 index 0000000000..cb36d5fc08 --- /dev/null +++ b/geoengine/datatypes/src/spatial_reference/static_epsg_area_provider.rs @@ -0,0 +1,65 @@ +use crate::error::Error; +use crate::primitives::{AxisAlignedRectangle, BoundingBox2D, Coordinate2D}; +use crate::spatial_reference::{AreaOfUseProvider, SpatialReference, SpatialReferenceAuthority}; +use crs_bounds::EpsgBounds; + +pub struct StaticEpsgAreaProvider { + wgs84_bounds: BoundingBox2D, + projected_bounds: Option, +} + +impl AreaOfUseProvider for StaticEpsgAreaProvider { + fn new_known_crs(def: SpatialReference) -> super::Result { + if !matches!(def.authority(), SpatialReferenceAuthority::Epsg) { + return Err(Error::NoAreaOfUseDefined { + proj_string: def.srs_string(), + }); + } + + let code = def.code(); + + // TODO: alias lookup here! e.g. to catch 900931 -> web mercator + + let EpsgBounds { + wgs84_bounds: [x1, y1, x2, y2], + native_bounds, + .. + } = EpsgBounds::from_code(code as u16).ok_or_else(|| Error::NoAreaOfUseDefined { + proj_string: def.srs_string(), + })?; + + Ok(Self { + wgs84_bounds: BoundingBox2D::new( + Coordinate2D { x: *x1, y: *y1 }, + Coordinate2D { x: *x2, y: *y2 }, + )?, + projected_bounds: native_bounds.map(|b| { + BoundingBox2D::new( + Coordinate2D { x: b[0], y: b[1] }, + Coordinate2D { x: b[2], y: b[3] }, + ) + .expect("the native bounds from the epsg registry should always be a valid BoundingBox2D") + }), + }) + } + + fn area_of_use(&self) -> super::Result { + A::from_min_max( + self.wgs84_bounds.lower_left(), + self.wgs84_bounds.upper_right(), + ) + } + + fn area_of_use_projected(&self) -> super::Result { + if let Some(projected_bounds) = self.projected_bounds { + Ok(A::from_min_max( + projected_bounds.lower_left(), + projected_bounds.upper_right(), + )?) + } else { + Err(Error::NoAreaOfUseDefined { + proj_string: "no native bounds defined for this epsg code".to_string(), + }) + } + } +} diff --git a/geoengine/datatypes/src/util/crs_definitions.rs b/geoengine/datatypes/src/util/crs_definitions.rs new file mode 100644 index 0000000000..0fee09c987 --- /dev/null +++ b/geoengine/datatypes/src/util/crs_definitions.rs @@ -0,0 +1,55 @@ +use crs_definitions::{Def, from_code}; + +use crate::{ + primitives::{AxisAlignedRectangle, Coordinate2D}, + spatial_reference::AreaOfUseProvider, +}; + +#[derive(Clone, Debug, Copy)] +pub struct StaticAreaofUseProvider { + def: Def, +} + +impl AreaOfUseProvider for StaticAreaofUseProvider { + fn new_known_crs(spatial_ref: crate::spatial_reference::SpatialReference) -> super::Result + where + Self: Sized, + { + match spatial_ref.authority() { + crate::spatial_reference::SpatialReferenceAuthority::Epsg + if spatial_ref.code() < u32::from(u16::MAX) => + { + let defn = from_code(spatial_ref.code() as u16) + .ok_or(crate::error::Error::ProjStringUnresolvable { spatial_ref })?; + Ok(StaticAreaofUseProvider { def: defn }) + } + _ => Err(crate::error::Error::ProjStringUnresolvable { spatial_ref }), + } + } + + fn area_of_use(&self) -> super::Result { + if let Some(aou) = self.def.area_of_use { + T::from_min_max( + Coordinate2D::new(aou[0], aou[1]), + Coordinate2D::new(aou[2], aou[3]), + ) + } else { + Err(crate::error::Error::NoAreaOfUseDefined { + proj_string: format!("EPSG:{}", self.def.code), + }) + } + } + + fn area_of_use_projected(&self) -> super::Result { + if let Some(aou) = self.def.projected_bounds { + T::from_min_max( + Coordinate2D::new(aou[0], aou[1]), + Coordinate2D::new(aou[2], aou[3]), + ) + } else { + Err(crate::error::Error::NoAreaOfUseDefined { + proj_string: format!("EPSG:{}", self.def.code), + }) + } + } +} diff --git a/geoengine/datatypes/src/util/well_known_data.rs b/geoengine/datatypes/src/util/well_known_data.rs index 09ff220b3f..a103ce49a0 100644 --- a/geoengine/datatypes/src/util/well_known_data.rs +++ b/geoengine/datatypes/src/util/well_known_data.rs @@ -16,7 +16,7 @@ pub const MARBURG_EPSG_4326: Coordinate2D = Coordinate2D { y: 50.802_172_8, }; -pub const MARBURG_EPSG_900_913: Coordinate2D = Coordinate2D { +pub const MARBURG_EPSG_3857: Coordinate2D = Coordinate2D { x: 975_914.966_045_882_4, y: 6_586_374.702_844_66, }; @@ -26,7 +26,7 @@ pub const COLOGNE_EPSG_4326: Coordinate2D = Coordinate2D { y: 50.937_531, }; -pub const COLOGNE_EPSG_900_913: Coordinate2D = Coordinate2D { +pub const COLOGNE_EPSG_3857: Coordinate2D = Coordinate2D { x: 774_814.669_531_319, y: 6_610_251.109_926_419, }; @@ -36,7 +36,7 @@ pub const HAMBURG_EPSG_4326: Coordinate2D = Coordinate2D { y: 53.565_278, }; -pub const HAMBURG_EPSG_900_913: Coordinate2D = Coordinate2D { +pub const HAMBURG_EPSG_3857: Coordinate2D = Coordinate2D { x: 1_113_349.530_705_447_5, y: 7_088_251.296_224_878, }; diff --git a/geoengine/expression/deps-workspace/Cargo.lock b/geoengine/expression/deps-workspace/Cargo.lock index c0e35bf171..45be45d918 100644 --- a/geoengine/expression/deps-workspace/Cargo.lock +++ b/geoengine/expression/deps-workspace/Cargo.lock @@ -29,12 +29,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -61,12 +55,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "earcutr" -version = "0.4.3" +name = "earcut" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01" +checksum = "88459a2a8e3a514b6e6de38cf3aaa9250a894cb098f74a932db77fcc8341b6d0" dependencies = [ - "itertools", "num-traits", ] @@ -84,23 +77,23 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "float_next_after" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" +checksum = "37007738a80ea34f969af54a3390dd72cacdef654974cfd449c9f6f72dbaac10" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "geo" -version = "0.32.0" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3901269ec6d4f6068d3f09e5f02f995bd076398dcd1dfec407cd230b02d11b" +checksum = "30eb1fdc57c1e5cfd11826fe0caec4b9dc7901f3758263bb506228d88c8d9e9a" dependencies = [ - "earcutr", + "earcut", "float_next_after", "geo-types", "geographiclib-rs", @@ -108,6 +101,7 @@ dependencies = [ "log", "num-traits", "rand", + "rand_pcg", "robust", "rstar", "sif-itree", @@ -116,15 +110,16 @@ dependencies = [ [[package]] name = "geo-types" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f8647af4005fa11da47cd56252c6ef030be8fa97bdbf355e7dfb6348f0a82c" +checksum = "94776032c45f950d30a13af6113c2ad5625316c9abfbccee4dd5a6695f8fe0f5" dependencies = [ "approx", "num-traits", "rayon", "rstar", "serde", + "spade", ] [[package]] @@ -144,17 +139,6 @@ dependencies = [ "libm", ] -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hash32" version = "0.3.1" @@ -166,9 +150,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", @@ -187,24 +171,27 @@ dependencies = [ [[package]] name = "i_float" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010025c2c532c8d82e42d0b8bb5184afa449fa6f06c709ea9adcb16c49ae405b" +checksum = "813145bb0ad5b60f55cbbf3c74cdceda1c0a9d253b35c4cc36ae0df7887cb78f" dependencies = [ "libm", ] [[package]] name = "i_key_sort" -version = "0.6.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9190f86706ca38ac8add223b2aed8b1330002b5cdbbce28fb58b10914d38fc27" +checksum = "d73d122b937fca067feb0ad74f62388920272b27c356d4df2d0cfdd59e044cf0" +dependencies = [ + "rayon", +] [[package]] name = "i_overlay" -version = "4.0.7" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413183068e6e0289e18d7d0a1f661b81546e6918d5453a44570b9ab30cbed1b3" +checksum = "934cba666ad1bf60436a190af6eaa3f58f57b5bad28268ce3fb45d9185862232" dependencies = [ "i_float", "i_key_sort", @@ -215,33 +202,18 @@ dependencies = [ [[package]] name = "i_shape" -version = "1.14.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea154b742f7d43dae2897fcd5ead86bc7b5eefcedd305a7ebf9f69d44d61082" +checksum = "bfa9eac533d7509a8ab87672b60ac610c17240f9ea4851d26227689fdfe349c8" dependencies = [ "i_float", ] [[package]] name = "i_tree" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e6d558e6d4c7b82bc51d9c771e7a927862a161a7d87bf2b0541450e0e20915" - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "libc" -version = "0.2.182" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "4804bdc1dc124eb7e1aa9e144ecc04096bcf787a10a15fa44af682b51f0f6cce" [[package]] name = "libm" @@ -265,15 +237,6 @@ dependencies = [ "libm", ] -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -285,48 +248,42 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "libc", - "rand_chacha", "rand_core", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand_core" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] -name = "rand_core" -version = "0.6.4" +name = "rand_pcg" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" dependencies = [ - "getrandom", + "rand_core", ] [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -403,9 +360,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "spade" -version = "2.15.0" +version = "2.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb313e1c8afee5b5647e00ee0fe6855e3d529eb863a0fdae1d60006c4d1e9990" +checksum = "9699399fd9349b00b184f5635b074f9ec93afffef30c853f8c875b32c0f8c7fa" dependencies = [ "hashbrown", "num-traits", @@ -435,29 +392,3 @@ name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "zerocopy" -version = "0.8.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/geoengine/expression/deps-workspace/Cargo.toml b/geoengine/expression/deps-workspace/Cargo.toml index 4b71f321bf..b3b3dcc737 100644 --- a/geoengine/expression/deps-workspace/Cargo.toml +++ b/geoengine/expression/deps-workspace/Cargo.toml @@ -24,5 +24,5 @@ doc = false proc-macro = false [dependencies] -geo = "0.32.0" -geo-types = "0.7.17" # important for compatibility when linking +geo = "0.33.1" +geo-types = "0.7.19" # important for compatibility when linking diff --git a/geoengine/justfile b/geoengine/justfile index 7ccc6eac73..1176881c81 100644 --- a/geoengine/justfile +++ b/geoengine/justfile @@ -38,7 +38,15 @@ lint-sql: common::_clear [arg('mode', pattern='|--release')] [group("build")] build mode="": - cargo build --locked {{ mode }} --verbose + cargo build --bin geoengine-server --locked {{ mode }} --verbose + cargo build --bin geoengine-cli --locked {{ mode }} --verbose + cargo build --bin gdalsource-process --locked {{ mode }} --verbose + +# Build gdalsource-process in dev or release mode. +[arg('mode', pattern='|--release')] +[group("build")] +build-gdalsource-process mode="": + cargo build --bin gdalsource-process --locked {{ mode }} --verbose # Run tests and generate lcov exactly as in CI test action. [group("ci")] @@ -52,9 +60,11 @@ test-coverage output_path="lcov.info": --output-path {{ output_path }} # Run the application. +[arg('mode', pattern='|--release')] [group("run")] -run: common::_clear - cargo run +run mode="": common::_clear + cargo build --bin gdalsource-process {{ mode }} + cargo run {{ mode }} # Run the tests. Optionally, a filter can be provided to run only a subset of the tests. [group("test")] diff --git a/geoengine/macros/src/lib.rs b/geoengine/macros/src/lib.rs index 94b1f85108..c83ded65eb 100644 --- a/geoengine/macros/src/lib.rs +++ b/geoengine/macros/src/lib.rs @@ -22,6 +22,7 @@ type Result = std::result::Result; /// - `oidc_db` - a tuple `(handle, f)` with /// - `handle` being a handle of an OpenID-Connect endpoint, preventing it from dropping too early, and /// - `f` begin function that returns a [`crate::users::OidcManager`] to use for the test +/// - `gdal_process_pool_config`- the config for the gdal process pool /// #[proc_macro_attribute] pub fn test( diff --git a/geoengine/macros/src/testing.rs b/geoengine/macros/src/testing.rs index d8e5bd5718..7d2a68f2ec 100644 --- a/geoengine/macros/src/testing.rs +++ b/geoengine/macros/src/testing.rs @@ -37,6 +37,7 @@ pub fn test(attr: TokenStream, item: &TokenStream) -> Result Result Result, oidc_db: Option, user: UserConfig, + gdal_process_pool_config: Option, } impl TestConfig { @@ -92,6 +96,7 @@ impl TestConfig { quota_config: None, oidc_db: None, user: UserConfig::Anonymous, + gdal_process_pool_config: None, }; if let Some(lit) = args.remove("tiling_spec") { @@ -154,6 +159,10 @@ impl TestConfig { this.oidc_db = Some(quote!(#oidc_db_fn)); } + if let Some(lit) = args.remove("gdal_process_pool_config") { + this.gdal_process_pool_config = Some(literal_to_fn(&lit)?); + } + Ok(this) } @@ -219,6 +228,12 @@ impl TestConfig { }, } } + + pub fn gdal_process_pool_config(&self) -> TokenStream { + self.gdal_process_pool_config.clone().unwrap_or_else(|| { + quote!(crate::config::get_config_element::().unwrap()) + }) + } } enum UserConfig { @@ -412,6 +427,8 @@ mod tests { let quota_config = crate::config::get_config_element::() .unwrap(); let (server, oidc_db) = ((), crate::users::OidcManager::default); + let gdal_process_pool_config = crate::config::get_config_element::() + .unwrap(); (|| {})(); @@ -425,6 +442,7 @@ mod tests { #[warn(clippy::used_underscore_binding)] it_works(app_ctx).await }, + gdal_process_pool_config, ).await } }; @@ -456,6 +474,8 @@ mod tests { let quota_config = crate::config::get_config_element::() .unwrap(); let (server, oidc_db) = ((), crate::users::OidcManager::default); + let gdal_process_pool_config = crate::config::get_config_element::() + .unwrap(); (|| {})(); @@ -475,6 +495,7 @@ mod tests { #[warn(clippy::used_underscore_binding)] it_works(app_ctx, ctx).await }, + gdal_process_pool_config, ).await } }; @@ -508,6 +529,8 @@ mod tests { let quota_config = crate::config::get_config_element::() .unwrap(); let (server, oidc_db) = ((), crate::users::OidcManager::default); + let gdal_process_pool_config = crate::config::get_config_element::() + .unwrap(); (|| {})(); @@ -526,6 +549,7 @@ mod tests { #[warn(clippy::used_underscore_binding)] it_works(db_config, ctx).await }, + gdal_process_pool_config, ).await } }; @@ -550,6 +574,7 @@ mod tests { expect_panic = "panic!!!", quota_config = "baz", oidc_db = "qux", + gdal_process_pool_config = "gwup", }; let expected = quote! { @@ -565,6 +590,7 @@ mod tests { let query_ctx_chunk_size = bar(); let quota_config = baz(); let (server, oidc_db) = qux(); + let gdal_process_pool_config = gwup(); before_fn(); @@ -578,6 +604,7 @@ mod tests { #[warn(clippy::used_underscore_binding)] it_works(app_ctx).await }, + gdal_process_pool_config, ).await } }; diff --git a/geoengine/operators/Cargo.toml b/geoengine/operators/Cargo.toml index 178c0f9719..6f00f2280d 100644 --- a/geoengine/operators/Cargo.toml +++ b/geoengine/operators/Cargo.toml @@ -8,13 +8,22 @@ license-file.workspace = true documentation.workspace = true repository.workspace = true +[features] +default = ["gdalpool_dedup_requests"] +gdalpool_dedup_requests = [] + [lib] bench = false +[[bin]] +name = "gdalsource-process" +path = "src/bin/gdalsource_process_main.rs" + [dependencies] arrow = { workspace = true } async-trait = { workspace = true } bb8-postgres = { workspace = true } +bytemuck = { workspace = true } bytes = { workspace = true } chrono = { workspace = true } csv = { workspace = true } @@ -26,6 +35,7 @@ geo = { workspace = true } geoengine-datatypes = { path = "../datatypes" } geoengine-expression = { path = "../expression" } itertools = { workspace = true } +ipc-channel = { workspace = true } libloading = { workspace = true } tracing = { workspace = true } lru = { workspace = true } @@ -43,7 +53,9 @@ postgres-types = { workspace = true } rayon = { workspace = true } rustc-hash = { workspace = true } serde = { workspace = true } +serde_bytes = { workspace = true } serde_json = { workspace = true } +serial_test = { workspace = true } snafu = { workspace = true } stream-cancel = { workspace = true } tempfile = { workspace = true } @@ -52,18 +64,24 @@ tokio-postgres = { workspace = true } typetag = { workspace = true } uuid = { workspace = true } strum = { workspace = true } +ge = "0.2.0" +lazy_static = "1.5.0" [dev-dependencies] async-stream = { workspace = true } approx = { workspace = true } -criterion = { workspace = true } httptest = { workspace = true } tracing-subscriber = { workspace = true, features = ["json"] } rand = { workspace = true } +criterion = { version = "0.8.2", features = ["html_reports", "plotters", "cargo_bench_support", "async_tokio"], default-features = false } [[bench]] name = "ci_operators" harness = false +[[bench]] +name = "new_raster_cache" +harness = false + [lints] workspace = true diff --git a/geoengine/operators/benches/ci_operators.rs b/geoengine/operators/benches/ci_operators.rs index 2bb3d58efa..ea025db750 100644 --- a/geoengine/operators/benches/ci_operators.rs +++ b/geoengine/operators/benches/ci_operators.rs @@ -61,7 +61,13 @@ async fn raster_vector_join( } fn raster_vector_join_benchmark(c: &mut Criterion) { - let mut execution_context = MockExecutionContext::test_default(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + let _guard = runtime.enter(); + + let mut execution_context = MockExecutionContext::new_with_tiling_spec_and_tokio_handle( + geoengine_datatypes::raster::TilingSpecification::test_default(), + runtime.handle(), + ); let query_context = execution_context.mock_query_context_test_default(); let ndvi_dataset = add_ndvi_dataset(&mut execution_context); @@ -91,9 +97,11 @@ fn raster_vector_join_benchmark(c: &mut Criterion) { .boxed(); c.bench_function("raster_vector_join", |b| { - b.to_async(tokio::runtime::Runtime::new().unwrap()) + b.to_async(&runtime) .iter(|| raster_vector_join(&execution_context, &query_context, operator.clone())); }); + + drop(_guard) } criterion_group!(operators, raster_vector_join_benchmark); diff --git a/geoengine/operators/benches/new_raster_cache.rs b/geoengine/operators/benches/new_raster_cache.rs new file mode 100644 index 0000000000..dd6857aad1 --- /dev/null +++ b/geoengine/operators/benches/new_raster_cache.rs @@ -0,0 +1,304 @@ +#![allow(clippy::unwrap_used, clippy::print_stdout, clippy::print_stderr)] // okay in benchmarks + +use crate::CacheVariant::{NewCache, NoCache, OldCache}; +use criterion::{Criterion, criterion_group, criterion_main}; +use futures::StreamExt; +use geoengine_datatypes::primitives::TimeInstance; +use geoengine_datatypes::{ + primitives::{BandSelection, RasterQueryRectangle, TimeInterval}, + raster::{GridBoundingBox2D, TilesEqualIgnoringCacheHint}, + util::test::TestDefault, +}; +use geoengine_operators::cache::new_raster_cache::{NewRasterCacheEnum, RasterCacheOperator}; +use geoengine_operators::engine::{BoxRasterQueryProcessor, ExecutionContext, MockQueryContext}; +use geoengine_operators::{ + cache::{cache_operator::InitializedCacheOperator, shared_cache::SharedCache}, + engine::{ + ChunkByteSize, InitializedRasterOperator, MockExecutionContext, QueryProcessor, + RasterOperator, SingleRasterSource, WorkflowOperatorPath, + }, + processing::{ + AggregateFunctionParams, NeighborhoodAggregate, NeighborhoodAggregateParams, + NeighborhoodParams, + }, + source::{GdalSource, GdalSourceParameters}, + util::Result, + util::gdal::add_ndvi_dataset, +}; +use lazy_static::lazy_static; +use std::hint::black_box; +use std::str::FromStr; +use std::sync::{Arc, Mutex, OnceLock}; + +/// This benchmarks runs a workflow twice to see the impact of the cache +/// Run it with `cargo bench --bench new_raster_cache` +//#[tokio::main] +async fn main_f() { + let mut exe_ctx = MockExecutionContext::test_default(); + + let ndvi_id = add_ndvi_dataset(&mut exe_ctx); + + let operator = NeighborhoodAggregate { + params: NeighborhoodAggregateParams { + neighborhood: NeighborhoodParams::WeightsMatrix { + weights: vec![vec![1., 2., 3.], vec![4., 5., 6.], vec![7., 8., 9.]], + }, + aggregate_function: AggregateFunctionParams::Sum, + }, + sources: SingleRasterSource { + raster: GdalSource { + params: GdalSourceParameters::new(ndvi_id), + } + .boxed(), + }, + } + .boxed() + .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx) + .await + .unwrap(); + + let cached_op = InitializedCacheOperator::new(operator); + + let processor = cached_op.query_processor().unwrap().get_u8().unwrap(); + + let tile_cache = Arc::new(NewRasterCacheEnum::test_default()); + + let query_ctx = exe_ctx.mock_query_context_with_query_extensions( + ChunkByteSize::test_default(), + None, + Some(tile_cache), + None, + None, + ); + + let start = std::time::Instant::now(); + + let stream = processor + .query( + RasterQueryRectangle::new( + GridBoundingBox2D::new([-900, -1800], [899, 1799]).unwrap(), + TimeInterval::default(), + BandSelection::first(), + ), + &query_ctx, + ) + .await + .unwrap(); + + let tiles = stream.collect::>().await; + + println!("First run: {:?}", start.elapsed()); + + let tiles = tiles.into_iter().collect::>>().unwrap(); + + let start = std::time::Instant::now(); + + let stream_from_cache = processor + .query( + RasterQueryRectangle::new( + GridBoundingBox2D::new([-900, -1800], [899, 1799]).unwrap(), + TimeInterval::default(), + BandSelection::first(), + ), + &query_ctx, + ) + .await + .unwrap(); + + let tiles_from_cache = stream_from_cache.collect::>().await; + + println!("From cache: {:?}", start.elapsed()); + + let tiles_from_cache = tiles_from_cache + .into_iter() + .collect::>>() + .unwrap(); + + assert!(tiles.tiles_equal_ignoring_cache_hint(&tiles_from_cache)); +} + +async fn setup(cache: CacheVariant) -> (Arc>, MockQueryContext) { + let exe_ctx = EXE_CTX.get_or_init(|| { + println!("Initializing OnceLock"); + Mutex::new(MockExecutionContext::test_default()) + }); + let mut exe_ctx = exe_ctx.lock().unwrap(); + + let ndvi_id = add_ndvi_dataset(&mut exe_ctx); + + let operator = NeighborhoodAggregate { + params: NeighborhoodAggregateParams { + neighborhood: NeighborhoodParams::WeightsMatrix { + weights: vec![vec![1., 2., 3.], vec![4., 5., 6.], vec![7., 8., 9.]], + }, + aggregate_function: AggregateFunctionParams::Sum, + }, + sources: SingleRasterSource { + raster: GdalSource { + params: GdalSourceParameters::new(ndvi_id), + } + .boxed(), + }, + } + .boxed() + .initialize(WorkflowOperatorPath::initialize_root(), &*exe_ctx) + .await + .unwrap(); + + match cache { + NewCache => { + let cached_op = RasterCacheOperator::wrap_operator(operator); + + let tile_cache = Arc::new(NewRasterCacheEnum::test_default()); + + let query_ctx = exe_ctx.mock_query_context_with_query_extensions( + ChunkByteSize::test_default(), + None, + Some(tile_cache), + None, + None, + ); + + let processor = cached_op.query_processor().unwrap().get_u8().unwrap(); + + let processor = Arc::new(processor); + + query(processor.clone(), &query_ctx).await; + + return (processor, query_ctx); + } + OldCache => { + let cached_op = InitializedCacheOperator::new(operator); + + let tile_cache = Arc::new(SharedCache::test_default()); + + let query_ctx = exe_ctx.mock_query_context_with_query_extensions( + ChunkByteSize::test_default(), + Some(tile_cache), + None, + None, + None, + ); + + let processor = cached_op.query_processor().unwrap().get_u8().unwrap(); + + let processor = Arc::new(processor); + + query(processor.clone(), &query_ctx).await; + + return (processor, query_ctx); + } + NoCache => { + let processor = operator.query_processor().unwrap().get_u8().unwrap(); + + let query_ctx = exe_ctx.mock_query_context_with_query_extensions( + ChunkByteSize::test_default(), + None, + None, + None, + None, + ); + (Arc::new(processor), query_ctx) + } + } +} + +async fn query(processor: Arc>, query_ctx: &MockQueryContext) { + let stream = processor + .query( + RasterQueryRectangle::new( + GridBoundingBox2D::new([-900, -1800], [899, 1799]).unwrap(), + //TimeInterval::default(), + TimeInterval::new_unchecked( + TimeInstance::from_str("2014-01-01T00:00:00.000Z") + .expect("it should only be used in tests"), + TimeInstance::from_str("2015-01-01T00:00:00.000Z") + .expect("it should only be used in tests"), + ), + BandSelection::first(), + ), + query_ctx, + ) + .await + .unwrap(); + + let tiles = stream.collect::>().await; + + let tiles = tiles.into_iter().collect::>>().unwrap(); + + black_box(tiles); +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("main_f", |b| b.iter(|| main_f())); +} + +static EXE_CTX: OnceLock> = OnceLock::new(); + +enum CacheVariant { + NoCache, + OldCache, + NewCache, +} + +fn benchmark_both(c: &mut Criterion) { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + let mut g = c.benchmark_group("ndvi_neighborhood_aggregate"); + g.sample_size(10); + + // uncached + let (uncached_processor, uncached_query_ctx) = runtime.block_on(async { setup(NoCache).await }); + let uncached_query_ctx = Arc::new(uncached_query_ctx); + g.bench_function("without_cache", |b| { + let proc = uncached_processor.clone(); + let ctx = uncached_query_ctx.clone(); + b.to_async(&runtime).iter(|| { + let proc = proc.clone(); + let ctx = ctx.clone(); + async move { + // `query` erwartet `&MockQueryContext`, daher `&*ctx` + query(proc, &*ctx).await; + } + }) + }); + + // new cache + let (cached_processor, cached_query_ctx) = runtime.block_on(async { setup(NewCache).await }); + let cached_query_ctx = Arc::new(cached_query_ctx); + g.bench_function("with_new_cache", |b| { + let proc = cached_processor.clone(); + let ctx = cached_query_ctx.clone(); + b.to_async(&runtime).iter(|| { + let proc = proc.clone(); + let ctx = ctx.clone(); + async move { + query(proc, &*ctx).await; + } + }) + }); + + // old cache + let (cached_processor, cached_query_ctx) = runtime.block_on(async { setup(OldCache).await }); + let cached_query_ctx = Arc::new(cached_query_ctx); + g.bench_function("with_old_cache", |b| { + let proc = cached_processor.clone(); + let ctx = cached_query_ctx.clone(); + b.to_async(&runtime).iter(|| { + let proc = proc.clone(); + let ctx = ctx.clone(); + async move { + query(proc, &*ctx).await; + } + }) + }); + + g.finish(); +} + +criterion_group!(test_benches, criterion_benchmark); +criterion_group!(benches, benchmark_both); +criterion_main!(benches); diff --git a/geoengine/operators/examples/cache.rs b/geoengine/operators/examples/cache.rs index 31db50b076..b397b19132 100644 --- a/geoengine/operators/examples/cache.rs +++ b/geoengine/operators/examples/cache.rs @@ -60,6 +60,7 @@ async fn main() { Some(tile_cache), None, None, + None, ); let start = std::time::Instant::now(); diff --git a/geoengine/operators/examples/query_chunks.rs b/geoengine/operators/examples/query_chunks.rs index 572f13328b..621f026c59 100644 --- a/geoengine/operators/examples/query_chunks.rs +++ b/geoengine/operators/examples/query_chunks.rs @@ -77,6 +77,7 @@ fn setup_contexts() -> (StatisticsWrappingMockExecutionContext, MockQueryContext let query_ctx = exe_ctx.mock_query_context_with_query_extensions( ChunkByteSize::test_default(), None, + None, Some(QuotaTracking::new( tokio::sync::mpsc::unbounded_channel().0, user, diff --git a/geoengine/operators/examples/sources.rs b/geoengine/operators/examples/sources.rs index 881528e4d4..dde1d8cc4b 100644 --- a/geoengine/operators/examples/sources.rs +++ b/geoengine/operators/examples/sources.rs @@ -13,6 +13,7 @@ use geoengine_datatypes::{ util::test::TestDefault, }; use geoengine_operators::engine::{MockExecutionContext, RasterResultDescriptor, TimeDescriptor}; + use geoengine_operators::{ engine::{ChunkByteSize, RasterQueryProcessor}, mock::MockRasterSourceProcessor, diff --git a/geoengine/operators/src/adapters/raster_subquery/raster_subquery_reprojection.rs b/geoengine/operators/src/adapters/raster_subquery/raster_subquery_reprojection.rs index ac056b6e61..b158ed201a 100644 --- a/geoengine/operators/src/adapters/raster_subquery/raster_subquery_reprojection.rs +++ b/geoengine/operators/src/adapters/raster_subquery/raster_subquery_reprojection.rs @@ -15,21 +15,18 @@ use geoengine_datatypes::raster::{ Grid2D, GridIndexAccess, GridIntersection, GridSize, SpatialGridDefinition, UpdateIndexedElementsParallel, }; -use geoengine_datatypes::{ - operations::reproject::{CoordinateProjection, CoordinateProjector}, - primitives::TimeInterval, - raster::EmptyGrid, - spatial_reference::SpatialReference, -}; use geoengine_datatypes::{ primitives::Coordinate2D, raster::{CoordinatePixelAccess, GridIdx2D, Pixel, RasterTile2D, TileInformation}, }; +use geoengine_datatypes::{ + primitives::TimeInterval, raster::EmptyGrid, spatial_reference::CoordinateProjection, + spatial_reference::DefaultCoordinateProjector, spatial_reference::SpatialReference, +}; use num; use rayon::ThreadPool; use rayon::iter::{IndexedParallelIterator, ParallelIterator}; use rayon::slice::{ParallelSlice, ParallelSliceMut}; -use tracing::debug; use super::{FoldTileAccu, FoldTileAccuMut, SubQueryTileAggregator}; @@ -110,7 +107,7 @@ where }); if let Some(bounds) = valid_spatial_bounds { - let proj = CoordinateProjector::from_known_srs(self.out_srs, self.in_srs)?; + let proj = DefaultCoordinateProjector::from_known_srs(self.out_srs, self.in_srs)?; let projected_bounds = bounds.reproject(&proj); match projected_bounds { @@ -194,13 +191,13 @@ fn projected_coordinate_grid_parallel( let start = std::time::Instant::now(); - let res = pool.install(|| { - // get all pixel idxs and coordinates. - debug!( - "projected_coordinate_grid_parallel {:?}", - &tile_info.global_tile_position - ); + let parallelism = pool.current_num_threads(); + let par_chunk_split = + num::integer::div_ceil(tile_info.tile_size_in_pixels.axis_size_y(), parallelism) + .max(min_rows_in_par_chunk); // don't go below MIN_ROWS_IN_PAR_CHUNK lines per chunk. + let par_chunk_size = tile_info.tile_size_in_pixels.axis_size_x() * par_chunk_split; + let res = pool.install(|| { let mut in_coord_grid: Grid2D> = Grid2D::new_filled(tile_info.tile_size_in_pixels, None); @@ -208,18 +205,6 @@ fn projected_coordinate_grid_parallel( .spatial_grid_definition() .generate_coord_grid_pixel_center(); - let parallelism = pool.current_num_threads(); - let par_chunk_split = - num::integer::div_ceil(tile_info.tile_size_in_pixels.axis_size_y(), parallelism) - .max(min_rows_in_par_chunk); // don't go below MIN_ROWS_IN_PAR_CHUNK lines per chunk. - let par_chunk_size = tile_info.tile_size_in_pixels.axis_size_x() * par_chunk_split; - debug!( - "parallelism: threads={} par_chunk_split={} par_chunk_size={}", - pool.current_num_threads(), - par_chunk_split, - par_chunk_size - ); - in_coord_grid .data .par_chunks_mut(par_chunk_size) @@ -233,24 +218,24 @@ fn projected_coordinate_grid_parallel( let chunk_bounds = BoundingBox2D::from_coord_ref_iter(out_coord_slice.iter()); if chunk_bounds.is_none() { - debug!("reprojection early exit"); + tracing::trace!("reprojection early exit"); return Ok(()); } let chunk_bounds = chunk_bounds.expect("checked above"); let valid_out_area = valid_out_area.as_bbox(); - let proj = CoordinateProjector::from_known_srs(out_srs, in_srs)?; + let proj = DefaultCoordinateProjector::from_known_srs(out_srs, in_srs)?; if valid_out_area.contains_bbox(&chunk_bounds) { - debug!("reproject whole tile chunk"); + tracing::trace!("reproject whole tile chunk"); let in_coords = proj.project_coordinates(out_coord_slice)?; in_coord_slice .iter_mut() .zip(in_coords.into_iter()) .for_each(|(a, b)| *a = Some(b)); } else if valid_out_area.intersects_bbox(&chunk_bounds) { - debug!("reproject part of tile chunk"); + tracing::trace!("reproject part of tile chunk"); in_coord_slice .iter_mut() .zip(out_coord_slice.iter()) @@ -268,8 +253,12 @@ fn projected_coordinate_grid_parallel( })?; Ok(in_coord_grid) }); - debug!( - "projected_coordinate_grid_parallel took {} (ns)", + tracing::trace!( + "projected_coordinate_grid_parallel {:?}, parallelism: threads={} par_chunk_split={} par_chunk_size={} took {} (ns) ", + &tile_info.global_tile_position, + pool.current_num_threads(), + par_chunk_split, + par_chunk_size, start.elapsed().as_nanos() ); res diff --git a/geoengine/operators/src/bin/gdalsource_process_main.rs b/geoengine/operators/src/bin/gdalsource_process_main.rs new file mode 100644 index 0000000000..da7bbda567 --- /dev/null +++ b/geoengine/operators/src/bin/gdalsource_process_main.rs @@ -0,0 +1,176 @@ +use std::{fmt::Display, str::FromStr}; + +use gdal::raster::GdalType; +use geoengine_datatypes::raster::Pixel; +use geoengine_operators::source::gdal_in::process_common::{ + GdalIpcBytePayload, IpcChannelMessage, IpcChannelMessagePayload, IpcProcessError, + IpcProcessRasterResult, +}; +use geoengine_operators::source::gdal_in::process_impl::{ + GdalDatasetHolder, GdalHandling, setup_client, +}; +use ipc_channel::ipc::IpcSender; +use num::FromPrimitive; + +fn exit_with_error(msg: impl Display) -> ! { + tracing::error!("Error: {msg}"); + std::process::exit(1); +} + +/// placeholder for logging implementation +#[allow(clippy::upper_case_acronyms)] +enum Level { + INFO, + DEBUG, +} + +impl FromStr for Level { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "Info" | "info" => Ok(Level::INFO), + "Debug" | "debug" => Ok(Level::DEBUG), + _ => Err("unknown level".to_string()), + } + } +} + +type Token = String; + +fn setup() -> (Token, Level) { + let args: Vec = std::env::args().collect(); + match args.as_slice() { + [_bin, token] => (token.clone(), Level::INFO), + [_bin, token, debug_level] => ( + token.clone(), + Level::from_str(debug_level).unwrap_or(Level::DEBUG), + ), + _ => { + panic!("Usage: gdalprocess-ipc-channel-server ") + } + } +} + +// TODO: when we add a logger to the process, we should re-route gdal logs, too. +/* +/// We install a GDAL error handler that logs all messages with our log macros. +fn reroute_gdal_logging() { + gdal::config::set_error_handler(|error_type, error_num, error_msg| { + const LOG_TARGET: &str = "GDAL"; + match error_type { + gdal::errors::CplErrType::None => { + // should never log anything + tracing::info!(target: LOG_TARGET, "GDAL None {error_num}: {error_msg}"); + } + gdal::errors::CplErrType::Debug => { + tracing::debug!(target: LOG_TARGET, "GDAL Debug {error_num}: {error_msg}"); + } + gdal::errors::CplErrType::Warning => { + tracing::warn!(target: LOG_TARGET, "GDAL Warning {error_num}: {error_msg}"); + } + gdal::errors::CplErrType::Failure => { + tracing::error!(target: LOG_TARGET, "GDAL Failure {error_num}: {error_msg}"); + } + gdal::errors::CplErrType::Fatal => { + tracing::error!(target: LOG_TARGET, "GDAL Fatal {error_num}: {error_msg}"); + } + } + }); +} +*/ + +fn main() { + let (token, _debug_lvl) = setup(); + // TODO: add a logger? + //reroute_gdal_logging(); + run(token); +} + +fn raster_type_dispatch( + payload: IpcChannelMessagePayload, + dataset_cache: &mut GdalDatasetHolder, + sender: &IpcSender, +) -> Result<(), IpcProcessError> { + match payload.data_type { + geoengine_datatypes::raster::RasterDataType::U8 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::U16 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::U32 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::U64 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::I8 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::I16 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::I32 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::I64 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::F32 => { + read_and_send::(payload, dataset_cache, sender)?; + } + geoengine_datatypes::raster::RasterDataType::F64 => { + read_and_send::(payload, dataset_cache, sender)?; + } + } + + Ok(()) +} + +#[allow(clippy::print_stderr)] +fn run(token: Token) { + let (sender, receiver) = match setup_client::(token) + { + Ok(pair) => pair, + Err(err) => exit_with_error(err), + }; + + let mut dataset_cache = GdalDatasetHolder::new(); + + // Loop runs indefinitely, reusing the process and its GDAL dataset cache + while let Ok(message) = receiver.recv() { + let payload = message.0; + + // If helper returns an error, it means the underlying IPC channel is completely broken + if let Err(err) = raster_type_dispatch(payload, &mut dataset_cache, &sender) { + eprintln!("Fatal IPC channel error: {err:?}. Exiting worker thread."); + break; + } + } +} + +fn read_and_send( + IpcChannelMessagePayload { + dataset_params, + read_advise, + data_type: _, + }: IpcChannelMessagePayload, + dataset_cache: &mut GdalDatasetHolder, + sender: &IpcSender, +) -> Result<(), IpcProcessError> { + let gp = GdalHandling::load_tile_data_with_dataset_retry::( + dataset_cache, + &dataset_params, + read_advise, + ); + + let byte_payload = gp.and_then(|p| GdalIpcBytePayload::try_from(p).map_err(Into::into)); + // Propagate channel send errors directly up out of the handler + match byte_payload { + Ok(td) => sender.send(Ok(td)), + Err(err) => sender.send(Err(err)), + }?; + + Ok(()) +} diff --git a/geoengine/operators/src/cache/cache_operator.rs b/geoengine/operators/src/cache/cache_operator.rs index 02233401a0..485a167142 100644 --- a/geoengine/operators/src/cache/cache_operator.rs +++ b/geoengine/operators/src/cache/cache_operator.rs @@ -515,6 +515,7 @@ mod tests { Some(tile_cache), None, None, + None, ); let stream = processor @@ -615,6 +616,7 @@ mod tests { Some(tile_cache), None, None, + None, ); // query the first two bands @@ -714,6 +716,7 @@ mod tests { Some(tile_cache), None, None, + None, ); // query the first two bands diff --git a/geoengine/operators/src/cache/error.rs b/geoengine/operators/src/cache/error.rs index 50785e03a0..9fc34f4277 100644 --- a/geoengine/operators/src/cache/error.rs +++ b/geoengine/operators/src/cache/error.rs @@ -36,4 +36,5 @@ pub enum CacheError { CouldNotReadElementFromBytes { source: arrow::error::ArrowError, }, + Unspecified, } diff --git a/geoengine/operators/src/cache/mod.rs b/geoengine/operators/src/cache/mod.rs index 25f4166999..42902ee678 100644 --- a/geoengine/operators/src/cache/mod.rs +++ b/geoengine/operators/src/cache/mod.rs @@ -3,5 +3,6 @@ pub mod cache_operator; pub mod cache_stream; pub mod cache_tiles; pub mod error; +pub mod new_raster_cache; pub mod shared_cache; pub mod util; diff --git a/geoengine/operators/src/cache/new_raster_cache/mod.rs b/geoengine/operators/src/cache/new_raster_cache/mod.rs new file mode 100644 index 0000000000..4e59ee6e2e --- /dev/null +++ b/geoengine/operators/src/cache/new_raster_cache/mod.rs @@ -0,0 +1,848 @@ +use crate::cache::cache_tiles::CompressedRasterTile2D; +use crate::cache::cache_tiles::CompressedRasterTileExt; +use crate::cache::error::CacheError; +use crate::engine::{ + BoxRasterQueryProcessor, CanonicOperatorName, InitializedRasterOperator, QueryContext, + QueryProcessor, RasterOperator, RasterQueryProcessor, RasterResultDescriptor, + TypedRasterQueryProcessor, WorkflowOperatorPath, +}; +use crate::optimization::OptimizationError; +use crate::util::Result; +use async_trait::async_trait; +use futures::Stream; +use futures::StreamExt; +use futures::stream; +use futures::stream::BoxStream; +use geoengine_datatypes::primitives::{ + BandSelection, QueryRectangle, RasterQueryRectangle, SpatialResolution, TimeInterval, +}; +use geoengine_datatypes::raster::{ + GridBoundingBox2D, GridIdx2D, Pixel, RasterTile2D, TileInformation, +}; +use geoengine_datatypes::util::ByteSize; +use geoengine_datatypes::util::test::TestDefault; +use itertools::FoldWhile::{Continue, Done}; +use itertools::Itertools; +use std::any::Any; +use std::collections::HashMap; +use std::fs::File; +use std::hash::{Hash, Hasher}; +use std::io::{Read, Write}; +use std::iter; +use std::marker::Sync; +use std::pin::Pin; +use std::sync::Arc; +use tokio::sync::RwLock; + +pub struct NewRasterCache { + store: Store, +} + +pub enum NewRasterCacheEnum { + InMemoryCompressedRasterTile2DFifo( + NewRasterCache>, + ), +} + +impl TestDefault for NewRasterCacheEnum { + fn test_default() -> Self { + Self::InMemoryCompressedRasterTile2DFifo(NewRasterCache { + store: InMemoryCacheStore { + cache: RwLock::new(HashMap::new()), + eviction_strategy: RwLock::new(FifoEvictionStrategy::new(8_589_934_592)), + }, + }) + } +} + +impl NewRasterCacheEnum { + async fn get(&self, key: &CacheKey) -> Result> { + match self { + NewRasterCacheEnum::InMemoryCompressedRasterTile2DFifo(cache) => { + cache.store.get(key).await + } + } + } + + async fn insert(&self, key: CacheKey, tile: TypedRasterTile2D) -> Result<()> { + match self { + NewRasterCacheEnum::InMemoryCompressedRasterTile2DFifo(cache) => { + cache.store.insert(key, tile).await + } + } + } +} + +#[async_trait] +trait CacheStore: Send + Sync + 'static { + type SF: StorageFormat; + type ES: EvictionStrategy; + + async fn get(&self, key: &CacheKey) -> Result>; + + async fn insert(&self, key: CacheKey, tile: TypedRasterTile2D) -> Result<()>; +} + +trait EvictionStrategy: Send + Sync + 'static { + fn record_access(&mut self, key: &CacheKey, weight: f64, size: usize); + fn record_hit(&mut self, key: &CacheKey); + fn record_removal(&mut self, key: &CacheKey); + + fn plan_eviction( + &self, + required_space: usize, + max_weight: f64, + is_pinned: F, + ) -> Result + where + F: FnMut(&CacheKey) -> bool; +} + +struct EvictionPlan { + keys_to_remove: Vec, + freed_bytes: usize, +} + +pub struct FifoEvictionStrategy { + queue: Vec, + size: usize, + capacity: usize, +} + +struct EvictionStrategyItem { + key: CacheKey, + weight: f64, + size: usize, +} + +impl FifoEvictionStrategy { + fn new(capacity: usize) -> Self { + FifoEvictionStrategy { + queue: Vec::new(), + size: 0, + capacity, + } + } +} + +impl EvictionStrategy for FifoEvictionStrategy { + fn record_access(&mut self, key: &CacheKey, weight: f64, size: usize) { + self.queue.push(EvictionStrategyItem { + key: key.clone(), + weight, + size, + }); + + self.size += size; + } + + fn record_hit(&mut self, key: &CacheKey) { + // Nothing to do here + } + + fn record_removal(&mut self, key: &CacheKey) { + let item = self.queue.remove( + self.queue + .iter() + .position(|item| item.key.eq(key)) + .expect("Key must exist in eviction strategy"), + ); + + self.size -= item.size; + } + + fn plan_eviction( + &self, + required_space: usize, + _max_weight: f64, + mut is_pinned: F, + ) -> Result + where + F: FnMut(&CacheKey) -> bool, + { + if self.size + required_space <= self.capacity { + return Ok(EvictionPlan { + keys_to_remove: vec![], + freed_bytes: required_space, + }); + } + + Ok(self + .queue + .iter() + .filter(|item| !is_pinned(&item.key)) + .fold_while( + EvictionPlan { + keys_to_remove: vec![], + freed_bytes: 0, + }, + |mut plan, item| { + plan.keys_to_remove.push(item.key.clone()); + plan.freed_bytes += item.size; + + if plan.freed_bytes >= required_space { + Done(plan) + } else { + Continue(plan) + } + }, + ) + .into_inner()) + } +} + +pub struct MockOnDiskCacheStore { + cache: RwLock>>, + eviction_strategy: ES, +} + +pub struct MockOnDiskStorageFormat { + tile: TypedRasterTile2D, +} + +impl StorageFormat for MockOnDiskStorageFormat { + fn store(tile: TypedRasterTile2D) -> Result { + let mut file = File::create("foo.txt")?; + file.write_all(&[67 as u8])?; + Ok(Self { tile }) + } + + fn load(&self) -> Result { + let mut file = File::open("foo.txt")?; + let mut buf = vec![]; + file.read_to_end(&mut buf)?; + Ok(self.tile.clone()) + } + fn byte_size(&self) -> Result { + let mut file = File::open("foo.txt")?; + let mut buf = vec![]; + file.read_to_end(&mut buf) + .map_err(|_| crate::error::Error::Cache { + source: CacheError::Unspecified, + }) + } +} + +#[async_trait] +impl CacheStore for MockOnDiskCacheStore { + type SF = SF; + type ES = ES; + + async fn get(&self, key: &CacheKey) -> Result> { + self.cache + .read() + .await + .get(key) + .map(Arc::clone) + .ok_or(crate::error::Error::Cache { + source: CacheError::Unspecified, + }) + } + + async fn insert(&self, key: CacheKey, tile: TypedRasterTile2D) -> Result<()> { + self.cache + .write() + .await + .insert(key, Arc::new(SF::store(tile)?)); + Ok(()) + } +} + +pub struct InMemoryCacheStore { + cache: RwLock>>, + eviction_strategy: RwLock, +} + +type CacheKey = (CanonicOperatorName, Band, TimeInterval, TileIndex); +type Band = u32; +type TileIndex = GridIdx2D; + +trait StorageFormat: Send + Sync + Sized + 'static { + fn store(tile: TypedRasterTile2D) -> Result; + + fn load(&self) -> Result; + + fn byte_size(&self) -> Result; +} + +#[derive(Clone)] +enum TypedRasterTile2D { + I8(RasterTile2D), + I16(RasterTile2D), + I32(RasterTile2D), + I64(RasterTile2D), + U8(RasterTile2D), + U16(RasterTile2D), + U32(RasterTile2D), + U64(RasterTile2D), + F32(RasterTile2D), + F64(RasterTile2D), +} + +pub enum TypedCompressedRasterTile2D { + I8(CompressedRasterTile2D), + I16(CompressedRasterTile2D), + I32(CompressedRasterTile2D), + I64(CompressedRasterTile2D), + U8(CompressedRasterTile2D), + U16(CompressedRasterTile2D), + U32(CompressedRasterTile2D), + U64(CompressedRasterTile2D), + F32(CompressedRasterTile2D), + F64(CompressedRasterTile2D), +} + +impl StorageFormat for TypedCompressedRasterTile2D { + fn store(tile: TypedRasterTile2D) -> Result { + match tile { + TypedRasterTile2D::I8(tile) => Ok(TypedCompressedRasterTile2D::I8( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::I16(tile) => Ok(TypedCompressedRasterTile2D::I16( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::I32(tile) => Ok(TypedCompressedRasterTile2D::I32( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::I64(tile) => Ok(TypedCompressedRasterTile2D::I64( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::U8(tile) => Ok(TypedCompressedRasterTile2D::U8( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::U16(tile) => Ok(TypedCompressedRasterTile2D::U16( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::U32(tile) => Ok(TypedCompressedRasterTile2D::U32( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::U64(tile) => Ok(TypedCompressedRasterTile2D::U64( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::F32(tile) => Ok(TypedCompressedRasterTile2D::F32( + CompressedRasterTile2D::compress_tile(tile), + )), + TypedRasterTile2D::F64(tile) => Ok(TypedCompressedRasterTile2D::F64( + CompressedRasterTile2D::compress_tile(tile), + )), + } + } + + fn load(&self) -> Result { + match self { + TypedCompressedRasterTile2D::I8(compressed) => Ok(TypedRasterTile2D::I8( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::I16(compressed) => Ok(TypedRasterTile2D::I16( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::I32(compressed) => Ok(TypedRasterTile2D::I32( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::I64(compressed) => Ok(TypedRasterTile2D::I64( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::U8(compressed) => Ok(TypedRasterTile2D::U8( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::U16(compressed) => Ok(TypedRasterTile2D::U16( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::U32(compressed) => Ok(TypedRasterTile2D::U32( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::U64(compressed) => Ok(TypedRasterTile2D::U64( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::F32(compressed) => Ok(TypedRasterTile2D::F32( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + TypedCompressedRasterTile2D::F64(compressed) => Ok(TypedRasterTile2D::F64( + CompressedRasterTile2D::decompress_tile(compressed) + .map_err(|err| CacheError::Unspecified)?, + )), + } + } + + fn byte_size(&self) -> Result { + match self { + TypedCompressedRasterTile2D::I8(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::I16(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::I32(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::I64(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::U8(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::U16(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::U32(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::U64(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::F32(compressed) => Ok(compressed.byte_size()), + TypedCompressedRasterTile2D::F64(compressed) => Ok(compressed.byte_size()), + } + } +} + +#[async_trait] +impl CacheStore for InMemoryCacheStore +where + SF: StorageFormat, + ES: EvictionStrategy, +{ + type SF = SF; + type ES = ES; + + async fn get(&self, key: &CacheKey) -> Result> { + self.cache + .read() + .await + .get(key) + .map(Arc::clone) + .ok_or(crate::error::Error::Cache { + source: CacheError::Unspecified, + }) + } + + async fn insert(&self, key: CacheKey, tile: TypedRasterTile2D) -> Result<()> { + let stored_tile = Arc::new(SF::store(tile)?); + let required_space = stored_tile.byte_size()?; + + let mut cache = self.cache.write().await; + let mut eviction_strategy = self.eviction_strategy.write().await; + let eviction_plan = eviction_strategy.plan_eviction(required_space, f64::MAX, |key| { + cache.get(key).map_or(true, |sf| Arc::strong_count(sf) > 1) + })?; + + if eviction_plan.freed_bytes < required_space { + return Err(crate::error::Error::Cache { + source: CacheError::Unspecified, + }); + } + + for key in eviction_plan.keys_to_remove { + cache.remove(&key); + eviction_strategy.record_removal(&key); + } + + cache.insert(key, stored_tile); + Ok(()) + } +} + +pub struct RasterCacheOperator +where + Source: InitializedRasterOperator, +{ + source: Source, +} + +impl RasterCacheOperator +where + Source: InitializedRasterOperator, +{ + pub fn wrap_operator(source: Source) -> RasterCacheOperator { + Self { source } + } +} + +struct WorkItem { + band: Band, + tile_info: TileInformation, +} + +impl InitializedRasterOperator for RasterCacheOperator +where + Source: InitializedRasterOperator, +{ + fn result_descriptor(&self) -> &RasterResultDescriptor { + self.source.result_descriptor() + } + + fn query_processor(&self) -> Result { + let source_qp = self + .source + .query_processor() + .map_err(|err| CacheError::Unspecified)?; + + Ok(match source_qp { + TypedRasterQueryProcessor::U8(s) => TypedRasterQueryProcessor::U8( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::U16(s) => TypedRasterQueryProcessor::U16( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::U32(s) => TypedRasterQueryProcessor::U32( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::U64(s) => TypedRasterQueryProcessor::U64( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::I8(s) => TypedRasterQueryProcessor::I8( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::I16(s) => TypedRasterQueryProcessor::I16( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::I32(s) => TypedRasterQueryProcessor::I32( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::I64(s) => TypedRasterQueryProcessor::I64( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::F32(s) => TypedRasterQueryProcessor::F32( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + TypedRasterQueryProcessor::F64(s) => TypedRasterQueryProcessor::F64( + RasterCacheQueryProcessor::boxed_new(s, self.canonic_name()), + ), + }) + } + + fn canonic_name(&self) -> CanonicOperatorName { + self.source.canonic_name() + } + + fn name(&self) -> &'static str { + self.source.name() + } + + fn path(&self) -> WorkflowOperatorPath { + self.source.path() + } + + fn optimize( + &self, + resolution: SpatialResolution, + ) -> Result, OptimizationError> { + self.source.optimize(resolution) + } +} + +struct RasterCacheQueryProcessor +where + T: Pixel + SupportedRasterDataType, +{ + source: BoxRasterQueryProcessor, + canonic_operator_name: CanonicOperatorName, +} +impl RasterCacheQueryProcessor +where + T: Pixel + SupportedRasterDataType, +{ + fn boxed_new( + source: BoxRasterQueryProcessor, + canonic_operator_name: CanonicOperatorName, + ) -> BoxRasterQueryProcessor { + Box::new(Self { + source, + canonic_operator_name, + }) + } + + fn result_stream<'a>( + &'a self, + time_intervals: Pin> + Send + 'a>>, + work: Vec, + ctx: &'a dyn QueryContext, + ) -> Pin>> + Send + 'a>> { + let work = Arc::new(work); + + Box::pin(time_intervals.flat_map( + move |time_interval| -> Pin< + Box>> + Send + 'a>, + > { + let work = work.clone(); + + if let Err(_) = time_interval { + return Box::pin(stream::iter( + iter::once(0).chain(1..work.len()).map(|_| Err(crate::error::Error::Cache { source: CacheError::Unspecified })) + )); + } + let time_interval = time_interval.unwrap(); + Box::pin(stream::unfold((work, 0), move |(work, idx)| async move { + if idx >= work.len() { + return None; + } + + let job = &work[idx]; + + let key = ( + self.canonic_operator_name.clone(), + job.band, + time_interval, + job.tile_info.global_upper_left_pixel_idx(), + ); + + let cache_store = ctx + .new_raster_cache() + .expect("Cache should have been created for this operator"); + + /*let mut s = DefaultHasher::new(); + key.0.hash(&mut s); + key.1.hash(&mut s); + key.2.hash(&mut s); + key.3.hash(&mut s); + let hash_key = s.finish(); + + + println!("Cache query for key {hash_key:?}!");*/ + + if let Ok(stored_tile) = cache_store.get(&key).await + { + //println!("Cache hit for key {hash_key:?}!"); + let tile = stored_tile.load(); + + let tile: Result> = match tile { + Ok(tile) => T::map_enum_to_tile(tile), + Err(err) => Err(err) + }; + + Some(( + tile, + (work, idx + 1), + )) + } else { + //println!("Cache miss for key {hash_key:?}!"); + + let source_query = self.source.query( + RasterQueryRectangle::new(job.tile_info.global_pixel_bounds(), time_interval, BandSelection::new_single(job.band)), + ctx, + ).await; + + + if let Ok(mut stream) = source_query { + if let Some(Ok(tile)) = stream.next().await { + // TODO tokio::spawn + let _ = cache_store.insert(key, T::map_tile_to_enum(tile.clone())).await; + + return Some((Ok(tile), (work, idx + 1))); + } + } + + Some(( + Err(crate::error::Error::Cache { source: CacheError::Unspecified }), + (work, idx + 1), + )) + } + })) + }, + )) + } +} +#[async_trait] +impl QueryProcessor for RasterCacheQueryProcessor +where + T: Pixel + SupportedRasterDataType, +{ + type Output = RasterTile2D; + type SpatialBounds = GridBoundingBox2D; + type Selection = BandSelection; + type ResultDescription = RasterResultDescriptor; + + async fn _query<'a>( + &'a self, + query: QueryRectangle, + ctx: &'a dyn QueryContext, + ) -> Result>> { + let tiling_spec = ctx.tiling_specification(); + let result_descriptor = self.result_descriptor(); + + let time_intervals = self.time_query(query.time_interval(), ctx).await?; + let bands = query.attributes().clone(); + + let tile_info_iterator = result_descriptor + .tiling_grid_definition(tiling_spec) + .generate_data_tiling_strategy() + .tile_information_iterator_from_pixel_bounds(query.spatial_bounds()); + + let work: Vec = tile_info_iterator + .flat_map(|tile_info| { + bands + .as_vec() + .into_iter() + .map(move |band| WorkItem { band, tile_info }) + }) + .collect(); + + let res = self.result_stream(time_intervals, work, ctx); + + Ok(res) + } + + fn result_descriptor(&self) -> &Self::ResultDescription { + self.source.result_descriptor() + } +} +#[async_trait] +impl RasterQueryProcessor for RasterCacheQueryProcessor +where + T: Pixel + SupportedRasterDataType, +{ + type RasterType = T; + + async fn _time_query<'a>( + &'a self, + query: TimeInterval, + ctx: &'a dyn QueryContext, + ) -> Result>> { + self.source._time_query(query, ctx).await + } +} + +trait SupportedRasterDataType { + fn map_enum_to_tile(tile: TypedRasterTile2D) -> Result> + where + Self: Sized; + + fn map_tile_to_enum(tile: RasterTile2D) -> TypedRasterTile2D + where + Self: Sized; +} + +macro_rules! supported_raster_data_type_impl { + ( $($ty:ty, $variant:ident),* ) => { + $( + impl SupportedRasterDataType for $ty { + fn map_enum_to_tile(tile: TypedRasterTile2D) -> Result> { + match tile { + TypedRasterTile2D::$variant(tile) => Ok(tile), + _ => Err(crate::error::Error::Cache { + source: CacheError::Unspecified, + }), + } + } + + fn map_tile_to_enum(tile: RasterTile2D) -> TypedRasterTile2D { + return TypedRasterTile2D::$variant(tile); + } + } + )* + }; +} + +supported_raster_data_type_impl!( + i8, I8, i16, I16, i32, I32, i64, I64, u8, U8, u16, U16, u32, U32, u64, U64, f32, F32, f64, F64 +); + +mod test { + use crate::cache::new_raster_cache::{NewRasterCacheEnum, RasterCacheOperator}; + use crate::engine::{ + BoxRasterQueryProcessor, ChunkByteSize, InitializedRasterOperator, MockExecutionContext, + MockQueryContext, RasterOperator, SingleRasterSource, WorkflowOperatorPath, + }; + use crate::processing::{ + AggregateFunctionParams, NeighborhoodAggregate, NeighborhoodAggregateParams, + NeighborhoodParams, + }; + use crate::source::{GdalSource, GdalSourceParameters}; + use crate::util::gdal::add_ndvi_dataset; + use futures::StreamExt; + use geoengine_datatypes::primitives::{ + BandSelection, RasterQueryRectangle, TimeInstance, TimeInterval, + }; + use geoengine_datatypes::raster::GridBoundingBox2D; + use geoengine_datatypes::util::test::TestDefault; + use std::hint::black_box; + use std::str::FromStr; + use std::sync::Arc; + + #[tokio::test] + async fn test() { + let (proc, exe) = setup(true).await; + + query(proc, &exe).await; + } + + async fn setup(cache: bool) -> (Arc>, MockQueryContext) { + let mut exe_ctx = MockExecutionContext::test_default(); + + let ndvi_id = add_ndvi_dataset(&mut exe_ctx); + + let operator = NeighborhoodAggregate { + params: NeighborhoodAggregateParams { + neighborhood: NeighborhoodParams::WeightsMatrix { + weights: vec![vec![1., 2., 3.], vec![4., 5., 6.], vec![7., 8., 9.]], + }, + aggregate_function: AggregateFunctionParams::Sum, + }, + sources: SingleRasterSource { + raster: GdalSource { + params: GdalSourceParameters::new(ndvi_id), + } + .boxed(), + }, + } + .boxed() + .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx) + .await + .unwrap(); + + if cache { + let cached_op = RasterCacheOperator::wrap_operator(operator); + + let tile_cache = Arc::new(NewRasterCacheEnum::test_default()); + + let query_ctx = exe_ctx.mock_query_context_with_query_extensions( + ChunkByteSize::test_default(), + None, + Some(tile_cache), + None, + None, + ); + + let processor = cached_op.query_processor().unwrap().get_u8().unwrap(); + + let processor = Arc::new(processor); + + query(processor.clone(), &query_ctx).await; + + return (processor, query_ctx); + } + + let processor = operator.query_processor().unwrap().get_u8().unwrap(); + + let query_ctx = exe_ctx.mock_query_context_with_query_extensions( + ChunkByteSize::test_default(), + None, + None, + None, + None, + ); + (Arc::new(processor), query_ctx) + } + + async fn query(processor: Arc>, query_ctx: &MockQueryContext) { + let stream = processor + .query( + RasterQueryRectangle::new( + GridBoundingBox2D::new([-900, -1800], [899, 1799]).unwrap(), + //TimeInterval::default(), + TimeInterval::new_unchecked( + TimeInstance::from_str("2014-01-01T00:00:00.000Z") + .expect("it should only be used in tests"), + TimeInstance::from_str("2015-01-01T00:00:00.000Z") + .expect("it should only be used in tests"), + ), + BandSelection::first(), + ), + query_ctx, + ) + .await + .unwrap(); + + let tiles = stream.collect::>().await; + + let tiles = tiles + .into_iter() + .collect::>>() + .unwrap(); + + black_box(tiles); + } +} diff --git a/geoengine/operators/src/engine/execution_context.rs b/geoengine/operators/src/engine/execution_context.rs index c2821e2418..74fc2d8df4 100644 --- a/geoengine/operators/src/engine/execution_context.rs +++ b/geoengine/operators/src/engine/execution_context.rs @@ -2,6 +2,7 @@ use super::{ CreateSpan, InitializedPlotOperator, InitializedRasterOperator, InitializedVectorOperator, MockQueryContext, }; +use crate::cache::new_raster_cache::NewRasterCacheEnum; use crate::cache::shared_cache::SharedCache; use crate::engine::{ ChunkByteSize, RasterResultDescriptor, ResultDescriptor, VectorResultDescriptor, @@ -11,6 +12,7 @@ use crate::machine_learning::MlModelLoadingInfo; use crate::meta::quota::{QuotaChecker, QuotaTracking}; use crate::meta::wrapper::InitializedOperatorWrapper; use crate::mock::MockDatasetDataSourceLoadingInfo; +use crate::source::gdal_in::{GdalProcessPool, GdalProcessPoolAccess}; use crate::source::{ GdalLoadingInfo, MultiBandGdalLoadingInfo, MultiBandGdalLoadingInfoQueryRectangle, OgrSourceDataset, @@ -41,7 +43,7 @@ pub trait ExecutionContext: Send MultiBandGdalLoadingInfo, RasterResultDescriptor, MultiBandGdalLoadingInfoQueryRectangle, - > + > + GdalProcessPoolAccess { fn thread_pool(&self) -> &Arc; fn tiling_specification(&self) -> TilingSpecification; @@ -67,6 +69,10 @@ pub trait ExecutionContext: Send async fn resolve_named_data(&self, data: &NamedData) -> Result; async fn ml_model_loading_info(&self, name: &MlModelName) -> Result; + + fn gdal_process_pool(&self) -> &Arc { + self.get_gdal_pool() + } } #[async_trait] @@ -103,6 +109,7 @@ pub struct MockExecutionContext { pub named_data: HashMap, pub ml_models: HashMap, pub tiling_specification: TilingSpecification, + pub gdal_process_pool: Arc, } impl TestDefault for MockExecutionContext { @@ -113,11 +120,26 @@ impl TestDefault for MockExecutionContext { named_data: HashMap::default(), ml_models: HashMap::default(), tiling_specification: TilingSpecification::test_default(), + gdal_process_pool: GdalProcessPool::new(2, 2, 2), } } } impl MockExecutionContext { + pub fn new_with_tiling_spec_and_tokio_handle( + tiling_specification: TilingSpecification, + handle: &tokio::runtime::Handle, + ) -> Self { + Self { + thread_pool: create_rayon_thread_pool(0), + meta_data: HashMap::default(), + named_data: HashMap::default(), + ml_models: HashMap::default(), + tiling_specification, + gdal_process_pool: GdalProcessPool::new_with_tokio_handle(handle, 2, 2, 2), + } + } + pub fn new_with_tiling_spec(tiling_specification: TilingSpecification) -> Self { Self { thread_pool: create_rayon_thread_pool(0), @@ -125,6 +147,7 @@ impl MockExecutionContext { named_data: HashMap::default(), ml_models: HashMap::default(), tiling_specification, + gdal_process_pool: GdalProcessPool::new(2, 2, 2), // TODO: GdalProcessPool defaults! } } @@ -138,6 +161,7 @@ impl MockExecutionContext { named_data: HashMap::default(), ml_models: HashMap::default(), tiling_specification, + gdal_process_pool: GdalProcessPool::new(2, 2, 2), } } @@ -167,24 +191,35 @@ impl MockExecutionContext { } pub fn mock_query_context_test_default(&self) -> MockQueryContext { - MockQueryContext::new(ChunkByteSize::test_default(), self.tiling_specification) + MockQueryContext::new( + ChunkByteSize::test_default(), + self.tiling_specification, + self.gdal_process_pool.clone(), + ) } pub fn mock_query_context(&self, chunk_byte_size: ChunkByteSize) -> MockQueryContext { - MockQueryContext::new(chunk_byte_size, self.tiling_specification) + MockQueryContext::new( + chunk_byte_size, + self.tiling_specification, + self.gdal_process_pool.clone(), + ) } pub fn mock_query_context_with_query_extensions( &self, chunk_byte_size: ChunkByteSize, cache: Option>, + new_raster_cache: Option>, quota_tracking: Option, quota_checker: Option, ) -> MockQueryContext { MockQueryContext::new_with_query_extensions( chunk_byte_size, self.tiling_specification, + self.gdal_process_pool.clone(), cache, + new_raster_cache, quota_tracking, quota_checker, ) @@ -199,10 +234,17 @@ impl MockExecutionContext { chunk_byte_size, self.tiling_specification, num_threads, + self.gdal_process_pool.clone(), ) } } +impl GdalProcessPoolAccess for MockExecutionContext { + fn get_gdal_pool(&self) -> &Arc { + &self.gdal_process_pool + } +} + #[async_trait::async_trait] impl ExecutionContext for MockExecutionContext { fn thread_pool(&self) -> &Arc { @@ -400,12 +442,14 @@ impl StatisticsWrappingMockExecutionContext { &self, chunk_byte_size: ChunkByteSize, cache: Option>, + new_raster_cache: Option>, quota_tracking: Option, quota_checker: Option, ) -> MockQueryContext { self.inner.mock_query_context_with_query_extensions( chunk_byte_size, cache, + new_raster_cache, quota_tracking, quota_checker, ) @@ -455,6 +499,12 @@ impl ExecutionContext for StatisticsWrappingMockExecutionContext { } } +impl GdalProcessPoolAccess for StatisticsWrappingMockExecutionContext { + fn get_gdal_pool(&self) -> &Arc { + self.inner.get_gdal_pool() + } +} + #[async_trait] impl MetaDataProvider for StatisticsWrappingMockExecutionContext where diff --git a/geoengine/operators/src/engine/query.rs b/geoengine/operators/src/engine/query.rs index 66cfa0e895..d8b3ad9e15 100644 --- a/geoengine/operators/src/engine/query.rs +++ b/geoengine/operators/src/engine/query.rs @@ -1,10 +1,15 @@ use std::{ + pin::Pin, + sync::Arc, task::{Context, Poll}, - {pin::Pin, sync::Arc}, }; +use crate::cache::new_raster_cache::NewRasterCacheEnum; use crate::{ - cache::shared_cache::SharedCache, error, meta::quota::QuotaChecker, + cache::shared_cache::SharedCache, + error, + meta::quota::QuotaChecker, + source::gdal_in::{GdalProcessPool, GdalProcessPoolAccess}, util::create_rayon_thread_pool, }; use crate::{meta::quota::QuotaTracking, util::Result}; @@ -50,7 +55,7 @@ impl TestDefault for ChunkByteSize { } } -pub trait QueryContext: Send + Sync { +pub trait QueryContext: Send + Sync + GdalProcessPoolAccess { fn chunk_byte_size(&self) -> ChunkByteSize; fn tiling_specification(&self) -> TilingSpecification; fn thread_pool(&self) -> &Arc; @@ -60,9 +65,14 @@ pub trait QueryContext: Send + Sync { fn quota_checker(&self) -> Option<&QuotaChecker>; fn cache(&self) -> Option>; + fn new_raster_cache(&self) -> Option>; fn abort_registration(&self) -> &QueryAbortRegistration; fn abort_trigger(&mut self) -> Result; + + fn gdal_process_pool(&self) -> &Arc { + self.get_gdal_pool() + } } /// This type allow wrapping multiple streams with `QueryAbortWrapper`s that @@ -122,17 +132,21 @@ pub struct MockQueryContext { pub thread_pool: Arc, pub cache: Option>, + pub new_raster_cache: Option>, pub quota_tracking: Option, pub quota_checker: Option, pub abort_registration: QueryAbortRegistration, pub abort_trigger: Option, + + gdal_process_pool: Arc, } impl MockQueryContext { pub(super) fn new( chunk_byte_size: ChunkByteSize, tiling_specification: TilingSpecification, + gdal_process_pool: Arc, ) -> Self { let (abort_registration, abort_trigger) = QueryAbortRegistration::new(); Self { @@ -140,17 +154,21 @@ impl MockQueryContext { tiling_specification, thread_pool: create_rayon_thread_pool(0), cache: None, + new_raster_cache: None, quota_checker: None, quota_tracking: None, abort_registration, abort_trigger: Some(abort_trigger), + gdal_process_pool, } } pub(super) fn new_with_query_extensions( chunk_byte_size: ChunkByteSize, tiling_specification: TilingSpecification, + gdal_process_pool: Arc, cache: Option>, + new_raster_cache: Option>, quota_tracking: Option, quota_checker: Option, ) -> Self { @@ -160,10 +178,12 @@ impl MockQueryContext { tiling_specification, thread_pool: create_rayon_thread_pool(0), cache, + new_raster_cache, quota_checker, quota_tracking, abort_registration, abort_trigger: Some(abort_trigger), + gdal_process_pool, } } @@ -171,13 +191,16 @@ impl MockQueryContext { chunk_byte_size: ChunkByteSize, tiling_specification: TilingSpecification, num_threads: usize, + gdal_process_pool: Arc, ) -> Self { let (abort_registration, abort_trigger) = QueryAbortRegistration::new(); Self { chunk_byte_size, tiling_specification, thread_pool: create_rayon_thread_pool(num_threads), + gdal_process_pool, cache: None, + new_raster_cache: None, quota_checker: None, quota_tracking: None, abort_registration, @@ -220,4 +243,14 @@ impl QueryContext for MockQueryContext { fn cache(&self) -> Option> { self.cache.clone() } + + fn new_raster_cache(&self) -> Option> { + self.new_raster_cache.clone() + } +} + +impl GdalProcessPoolAccess for MockQueryContext { + fn get_gdal_pool(&self) -> &Arc { + &self.gdal_process_pool + } } diff --git a/geoengine/operators/src/engine/query_processor.rs b/geoengine/operators/src/engine/query_processor.rs index 2475e9afdd..b1a4fda670 100644 --- a/geoengine/operators/src/engine/query_processor.rs +++ b/geoengine/operators/src/engine/query_processor.rs @@ -137,20 +137,11 @@ pub trait RasterQueryProcessor: ) -> Result>> { let rdt = self.raster_result_descriptor().time; if let Some(regular_time) = rdt.dimension.unwrap_regular() { - debug!( - "Using time query shortcut for regular time dimension: {regular_time:?} and query: {query:?}" - ); - let iter = regular_time - .intersecting_intervals(query)? - .inspect(|t| debug!("time_query regular shortcut yielded {t:?}")) - .map(Result::Ok); + let iter = regular_time.intersecting_intervals(query)?.map(Result::Ok); return Ok(futures::StreamExt::boxed(futures::stream::iter(iter))); } - debug!( - "Delegating time query to the {} query processors implementation", - std::any::type_name::() - ); + debug!("Delegating time query to the query processors implementation",); // TODO: get operator name in debug output #[allow(clippy::used_underscore_items)] // TODO: maybe rename? self._time_query(query, ctx).await } diff --git a/geoengine/operators/src/engine/result_descriptor.rs b/geoengine/operators/src/engine/result_descriptor.rs index ceafad909b..88ae309988 100644 --- a/geoengine/operators/src/engine/result_descriptor.rs +++ b/geoengine/operators/src/engine/result_descriptor.rs @@ -2,7 +2,7 @@ use crate::error::{ Error, RasterBandNameMustNotBeEmpty, RasterBandNameTooLong, RasterBandNamesMustBeUnique, }; use crate::util::Result; -use geoengine_datatypes::operations::reproject::{CoordinateProjection, ReprojectClipped}; +use geoengine_datatypes::operations::reproject::ReprojectClipped; use geoengine_datatypes::primitives::{ AxisAlignedRectangle, BandSelection, BoundingBox2D, ColumnSelection, Coordinate2D, FeatureDataType, Measurement, PlotSeriesSelection, QueryAttributeSelection, QueryRectangle, @@ -13,6 +13,7 @@ use geoengine_datatypes::raster::{ GeoTransform, GeoTransformAccess, Grid, GridBoundingBox2D, GridShape2D, GridShapeAccess, SpatialGridDefinition, TilingSpatialGridDefinition, TilingSpecification, }; +use geoengine_datatypes::spatial_reference::CoordinateProjection; use geoengine_datatypes::util::ByteSize; use geoengine_datatypes::{ collections::VectorDataType, raster::RasterDataType, spatial_reference::SpatialReferenceOption, diff --git a/geoengine/operators/src/error.rs b/geoengine/operators/src/error.rs index 192694b0cf..a14fd66aa0 100644 --- a/geoengine/operators/src/error.rs +++ b/geoengine/operators/src/error.rs @@ -1,4 +1,5 @@ use crate::adapters::SimpleRasterStackerError; +use crate::cache::error::CacheError; use crate::engine::SpatialGridDescriptor; use crate::optimization::OptimizationError; use crate::processing::BandNeighborhoodAggregateError; @@ -535,6 +536,10 @@ pub enum Error { Optimization { source: OptimizationError, }, + #[snafu(display("CacheError: {source}"))] + Cache { + source: CacheError, + }, #[snafu(display("Error in the SimpleRasterStacker: {source}"))] SimpleRasterStacker { @@ -602,3 +607,9 @@ impl From for Error { Error::InvalidNotNanFloatKey { source } } } + +impl From for Error { + fn from(source: CacheError) -> Self { + Error::Cache { source } + } +} diff --git a/geoengine/operators/src/meta/wrapper.rs b/geoengine/operators/src/meta/wrapper.rs index ab4681ac74..3844af3e27 100644 --- a/geoengine/operators/src/meta/wrapper.rs +++ b/geoengine/operators/src/meta/wrapper.rs @@ -31,7 +31,7 @@ impl InitializedOperatorWrapper { impl InitializedRasterOperator for InitializedOperatorWrapper> { fn result_descriptor(&self) -> &RasterResultDescriptor { - tracing::debug!( + tracing::trace!( event = "raster result descriptor", path = self.source.path().to_string() ); @@ -138,7 +138,7 @@ impl InitializedRasterOperator for InitializedOperatorWrapper { diff --git a/geoengine/operators/src/processing/meteosat/mod.rs b/geoengine/operators/src/processing/meteosat/mod.rs index a5f5aee30b..22caf9ed23 100644 --- a/geoengine/operators/src/processing/meteosat/mod.rs +++ b/geoengine/operators/src/processing/meteosat/mod.rs @@ -66,10 +66,13 @@ mod test_util { use crate::processing::meteosat::{ new_channel_key, new_offset_key, new_satellite_key, new_slope_key, }; + use crate::source::gdal_source::GdalMetaDataRegular; use crate::source::{ - FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetaDataRegular, - GdalMetadataMapping, GdalSource, GdalSourceParameters, GdalSourceTimePlaceholder, - TimeReference, + GdalSource, GdalSourceParameters, + gdal_in::{ + FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, + GdalMetadataMapping, GdalSourceTimePlaceholder, TimeReference, + }, }; use crate::test_data; use crate::util::Result; diff --git a/geoengine/operators/src/processing/neighborhood_aggregate/tile_sub_query.rs b/geoengine/operators/src/processing/neighborhood_aggregate/tile_sub_query.rs index 36cbdd04e8..52e12bded8 100644 --- a/geoengine/operators/src/processing/neighborhood_aggregate/tile_sub_query.rs +++ b/geoengine/operators/src/processing/neighborhood_aggregate/tile_sub_query.rs @@ -359,9 +359,9 @@ mod tests { util::test::TestDefault, }; - #[test] + #[tokio::test] #[allow(clippy::float_cmp)] - fn test_create_enlarged_tile() { + async fn test_create_enlarged_tile() { let execution_context = MockExecutionContext::new_with_tiling_spec(TilingSpecification::test_default()); diff --git a/geoengine/operators/src/processing/reprojection.rs b/geoengine/operators/src/processing/reprojection.rs index 956403d9a8..a3f7366731 100644 --- a/geoengine/operators/src/processing/reprojection.rs +++ b/geoengine/operators/src/processing/reprojection.rs @@ -28,16 +28,13 @@ use futures::{StreamExt, stream}; use geoengine_datatypes::{ collections::FeatureCollection, error::BoxedResultExt, - operations::reproject::{ - CoordinateProjection, CoordinateProjector, Reproject, ReprojectClipped, - reproject_spatial_query, - }, + operations::reproject::{Reproject, ReprojectClipped, reproject_spatial_query}, primitives::{ BandSelection, BoundingBox2D, ColumnSelection, Geometry, RasterQueryRectangle, SpatialPartition2D, SpatialResolution, VectorQueryRectangle, }, raster::{GridBoundingBox2D, Pixel, RasterTile2D, TilingSpecification}, - spatial_reference::SpatialReference, + spatial_reference::{CoordinateProjection, DefaultCoordinateProjector, SpatialReference}, util::arrow::ArrowTyped, }; use serde::{Deserialize, Serialize}; @@ -108,8 +105,10 @@ impl InitializedVectorReprojection { .ok_or(Error::AllSourcesMustHaveSameSpatialReference)?; let bbox = if let Some(bbox) = in_desc.bbox { - let projector = - CoordinateProjector::from_known_srs(in_srs, params.target_spatial_reference)?; + let projector = DefaultCoordinateProjector::from_known_srs( + in_srs, + params.target_spatial_reference, + )?; bbox.reproject_clipped(&projector)? // TODO: if this is none then we could skip the whole reprojection similar to raster? } else { @@ -150,7 +149,7 @@ impl InitializedRasterReprojection { // calculate the intersection of input and output srs in both coordinate systems let proj_from_to = - CoordinateProjector::from_known_srs(in_srs, params.target_spatial_reference)?; + DefaultCoordinateProjector::from_known_srs(in_srs, params.target_spatial_reference)?; let out_spatial_grid = match params.derive_out_spec { DeriveOutRasterSpecsSource::DataBounds => in_desc @@ -212,7 +211,7 @@ fn compute_output_spatial_grid( params: ReprojectionParams, ) -> Result { let proj_from_to = - CoordinateProjector::from_known_srs(in_srs, params.target_spatial_reference)?; + DefaultCoordinateProjector::from_known_srs(in_srs, params.target_spatial_reference)?; let out_spatial_grid = match params.derive_out_spec { DeriveOutRasterSpecsSource::DataBounds => { in_spatial_grid_descriptor.reproject_clipped(&proj_from_to)? @@ -279,17 +278,18 @@ impl InitializedVectorOperator for InitializedVectorReprojection { match self.source.query_processor()? { TypedVectorQueryProcessor::Data(source) => Ok(TypedVectorQueryProcessor::Data( MapQueryProcessor::new(source, move |query: VectorQueryRectangle| { - reproject_spatial_query(query.spatial_bounds(), source_srs, target_srs, false) - .map(|sqr| { - sqr.map(|x| { - VectorQueryRectangle::new( - x, - query.time_interval(), - *query.attributes(), - ) - }) + reproject_spatial_query::<_, DefaultCoordinateProjector>( + query.spatial_bounds(), + source_srs, + target_srs, + false, + ) // NOTE: this uses the `DefaultCoordinateProjector`, which may change. + .map(|sqr| { + sqr.map(|x| { + VectorQueryRectangle::new(x, query.time_interval(), *query.attributes()) }) - .map_err(From::from) + }) + .map_err(From::from) }) .boxed(), )), @@ -396,7 +396,7 @@ where Selection = ColumnSelection, ResultDescription = VectorResultDescriptor, >, - FeatureCollection: Reproject>, + FeatureCollection: Reproject>, // NOTE: this uses the `DefaultCoordinateProjector`, which may change. G: Geometry + ArrowTyped, { type Output = FeatureCollection; @@ -409,8 +409,12 @@ where query: VectorQueryRectangle, ctx: &'a dyn QueryContext, ) -> Result>> { - let rewritten_spatial_query = - reproject_spatial_query(query.spatial_bounds(), self.from, self.to, false)?; + let rewritten_spatial_query = reproject_spatial_query::<_, DefaultCoordinateProjector>( + query.spatial_bounds(), + self.from, + self.to, + false, + )?; let rewritten_query = rewritten_spatial_query.map(|rwq| query.select_spatial_bounds(rwq)); @@ -421,8 +425,8 @@ where .await? .map(move |collection_result| { collection_result.and_then(|collection| { - CoordinateProjector::from_known_srs(self.from, self.to) - .and_then(|projector| collection.reproject(projector.as_ref())) + DefaultCoordinateProjector::from_known_srs(self.from, self.to) + .and_then(|projector| collection.reproject(&projector)) .map_err(Into::into) }) }) @@ -868,8 +872,8 @@ mod tests { Identifier, test::TestDefault, well_known_data::{ - COLOGNE_EPSG_900_913, COLOGNE_EPSG_4326, HAMBURG_EPSG_900_913, HAMBURG_EPSG_4326, - MARBURG_EPSG_900_913, MARBURG_EPSG_4326, + COLOGNE_EPSG_3857, COLOGNE_EPSG_4326, HAMBURG_EPSG_3857, HAMBURG_EPSG_4326, + MARBURG_EPSG_3857, MARBURG_EPSG_4326, }, }, }; @@ -892,9 +896,9 @@ mod tests { )?; let expected = MultiPoint::many(vec![ - MARBURG_EPSG_900_913, - COLOGNE_EPSG_900_913, - HAMBURG_EPSG_900_913, + MARBURG_EPSG_3857, + COLOGNE_EPSG_3857, + HAMBURG_EPSG_3857, ]) .unwrap(); @@ -968,9 +972,9 @@ mod tests { )?; let expected = [MultiLineString::new(vec![vec![ - MARBURG_EPSG_900_913, - COLOGNE_EPSG_900_913, - HAMBURG_EPSG_900_913, + MARBURG_EPSG_3857, + COLOGNE_EPSG_3857, + HAMBURG_EPSG_3857, ]]) .unwrap()]; @@ -1050,10 +1054,10 @@ mod tests { )?; let expected = [MultiPolygon::new(vec![vec![vec![ - MARBURG_EPSG_900_913, - COLOGNE_EPSG_900_913, - HAMBURG_EPSG_900_913, - MARBURG_EPSG_900_913, + MARBURG_EPSG_3857, + COLOGNE_EPSG_3857, + HAMBURG_EPSG_3857, + MARBURG_EPSG_3857, ]]]) .unwrap()]; @@ -1399,7 +1403,8 @@ mod tests { (20_037_508.342_789_244, 20_048_966.104_014_594).into(), ); - let reprojected = reproject_spatial_query( + // NOTE: this uses the `DefaultCoordinateProjector`, which may change. + let reprojected = reproject_spatial_query::<_, DefaultCoordinateProjector>( query.spatial_bounds(), SpatialReference::new(SpatialReferenceAuthority::Epsg, 3857), SpatialReference::epsg_4326(), @@ -1887,16 +1892,19 @@ mod tests { let grid_bounds = GridBoundingBox2D::new_min_max(-850, 849, -1800, 1799).unwrap(); let spatial_grid = SpatialGridDefinition::new(geo_transform, grid_bounds); - let projector = CoordinateProjector::from_known_srs(in_proj, out_proj).unwrap(); + // NOTE: this uses the `DefaultCoordinateProjector`, which may change. + let projector = DefaultCoordinateProjector::from_known_srs(in_proj, out_proj).unwrap(); let out_spatial_grid = spatial_grid.reproject(&projector).unwrap(); - assert_eq!( + assert_approx_eq!( + Coordinate2D, out_spatial_grid.geo_transform.origin_coordinate(), Coordinate2D::new(0., 0.) ); - assert_eq!( + assert_approx_eq!( + SpatialResolution, out_spatial_grid.geo_transform.spatial_resolution(), SpatialResolution::new_unchecked(14_212.246_793_017_477, 14_212.246_793_017_477) ); diff --git a/geoengine/operators/src/source/gdal_in/db_types.rs b/geoengine/operators/src/source/gdal_in/db_types.rs new file mode 100644 index 0000000000..3b68885d2b --- /dev/null +++ b/geoengine/operators/src/source/gdal_in/db_types.rs @@ -0,0 +1,97 @@ +use geoengine_datatypes::{delegate_from_to_sql, util::StringPair}; +use postgres_types::{FromSql, ToSql}; + +use super::{ + FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetadataMapping, + GdalRetryOptions, +}; +use crate::error::Error; + +#[derive(Debug, ToSql, FromSql)] +#[postgres(name = "GdalDatasetParameters")] +pub struct GdalDatasetParametersDbType { + pub file_path: String, + pub rasterband_channel: i64, + pub geo_transform: GdalDatasetGeoTransform, + pub width: i64, + pub height: i64, + pub file_not_found_handling: FileNotFoundHandling, + pub no_data_value: Option, + pub properties_mapping: Option>, + pub gdal_open_options: Option>, + pub gdal_config_options: Option>, + pub allow_alphaband_as_mask: bool, + pub retry: Option, +} + +impl From<&GdalDatasetParameters> for GdalDatasetParametersDbType { + fn from(other: &GdalDatasetParameters) -> Self { + Self { + file_path: other.file_path.to_string_lossy().to_string(), + rasterband_channel: other.rasterband_channel as i64, + geo_transform: other.geo_transform, + width: other.width as i64, + height: other.height as i64, + file_not_found_handling: other.file_not_found_handling, + no_data_value: other.no_data_value, + properties_mapping: other.properties_mapping.clone(), + gdal_open_options: other.gdal_open_options.clone(), + gdal_config_options: other + .gdal_config_options + .clone() + .map(|v| v.into_iter().map(Into::into).collect()), + allow_alphaband_as_mask: other.allow_alphaband_as_mask, + retry: other.retry, + } + } +} + +impl TryFrom for GdalDatasetParameters { + type Error = Error; + + fn try_from(other: GdalDatasetParametersDbType) -> Result { + Ok(Self { + file_path: other.file_path.into(), + rasterband_channel: other.rasterband_channel as usize, + geo_transform: other.geo_transform, + width: other.width as usize, + height: other.height as usize, + file_not_found_handling: other.file_not_found_handling, + no_data_value: other.no_data_value, + properties_mapping: other.properties_mapping, + gdal_open_options: other.gdal_open_options, + gdal_config_options: other + .gdal_config_options + .map(|v| v.into_iter().map(Into::into).collect()), + allow_alphaband_as_mask: other.allow_alphaband_as_mask, + retry: other.retry, + }) + } +} + +#[derive(Debug, FromSql, ToSql)] +#[postgres(name = "GdalRetryOptions")] +pub struct GdalRetryOptionsDbType { + pub max_retries: i64, +} + +impl From<&GdalRetryOptions> for GdalRetryOptionsDbType { + fn from(other: &GdalRetryOptions) -> Self { + Self { + max_retries: other.max_retries as i64, + } + } +} + +impl TryFrom for GdalRetryOptions { + type Error = Error; + + fn try_from(other: GdalRetryOptionsDbType) -> Result { + Ok(Self { + max_retries: other.max_retries as usize, + }) + } +} + +delegate_from_to_sql!(GdalDatasetParameters, GdalDatasetParametersDbType); +delegate_from_to_sql!(GdalRetryOptions, GdalRetryOptionsDbType); diff --git a/geoengine/operators/src/source/gdal_in/gdal_dataset_params.rs b/geoengine/operators/src/source/gdal_in/gdal_dataset_params.rs new file mode 100644 index 0000000000..3b58b039b1 --- /dev/null +++ b/geoengine/operators/src/source/gdal_in/gdal_dataset_params.rs @@ -0,0 +1,293 @@ +use crate::error::Error; // TODO: figure out more scoped errors! +use crate::util::Result; // TODO: use Result with more scoped errors. +use crate::util::input::float_option_with_nan; +use float_cmp::ApproxEq; +use geoengine_datatypes::{ + primitives::{Coordinate2D, DateTimeParseFormat, TimeInterval}, + raster::{ + GeoTransform, GridBoundingBox2D, GridShapeAccess, RasterPropertiesEntryType, + RasterPropertiesKey, SpatialGridDefinition, + }, + util::test::TestDefault, +}; +use postgres_types::{FromSql, ToSql}; +use serde::{Deserialize, Serialize}; +use snafu::ensure; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, + path::PathBuf, +}; + +/// Parameters for loading data using Gdal +#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct GdalDatasetParameters { + pub file_path: PathBuf, + pub rasterband_channel: usize, + pub geo_transform: GdalDatasetGeoTransform, // TODO: discuss if we need this at all + pub width: usize, + pub height: usize, + pub file_not_found_handling: FileNotFoundHandling, + #[serde(default)] + #[serde(with = "float_option_with_nan")] + pub no_data_value: Option, + pub properties_mapping: Option>, + // Dataset open option as strings, e.g. `vec!["UserPwd=geoengine:pwd".to_owned(), "HttpAuth=BASIC".to_owned()]` + pub gdal_open_options: Option>, + // Configs as key, value pairs that will be set as thread local config options, e.g. + // `vec!["AWS_REGION".to_owned(), "eu-central-1".to_owned()]` and unset afterwards + // TODO: validate the config options: only allow specific keys and specific values + pub gdal_config_options: Option>, + #[serde(default)] + pub allow_alphaband_as_mask: bool, + pub retry: Option, +} + +impl GdalDatasetParameters { + pub fn dataset_bounds(&self) -> GridBoundingBox2D { + GridBoundingBox2D::new_unchecked( + [0, 0], + [self.height as isize - 1, self.width as isize - 1], + ) + } + + pub fn gdal_geo_transform(&self) -> GdalDatasetGeoTransform { + self.geo_transform + } + + /// Returns the `SpatialGridDefinition` of the Gdal dataset. + /// + /// Note: This allows upside down datasets (where `GeoTransform` `y_pixel_size` is positive)! + /// + /// # Panics + /// Panics if the `GdalDatasetParameters` are faulty. + pub fn spatial_grid_definition(&self) -> SpatialGridDefinition { + let gdal_geo_transform = GeoTransform::new( + self.gdal_geo_transform().origin_coordinate, + self.gdal_geo_transform().x_pixel_size, + self.gdal_geo_transform().y_pixel_size, + ); + + SpatialGridDefinition::new(gdal_geo_transform, self.dataset_bounds()) + } + + pub fn gdal_config_options_vsi_curl() -> &'static [(&'static str, &'static str)] { + &[ + ("GDAL_DISABLE_READDIR_ON_OPEN", "EMPTY_DIR"), + ("CPL_VSIL_CURL_ALLOWED_EXTENSIONS", ".tif,.tiff,.jp2,.ovr"), + ("CPL_VSIL_CURL_CHUNK_SIZE", "2097152"), // 2mb TODO: need to tune this! + ("VSI_CACHE", "TRUE"), + ("VSI_CACHE_SIZE", "134217728"), // 128mb per worker! TODO: need to tune this! + ] + } + + pub fn is_vis_curl(&self) -> bool { + self.file_path.starts_with("/vsicurl/") || self.file_path.starts_with("/vsis3/") + } + + pub fn gdal_config_options_with_defaults(&self) -> Option> { + if !self.is_vis_curl() { + return self.gdal_config_options.clone(); + } + + let mut opts = self.gdal_config_options.clone().unwrap_or_default(); + let opt_keys: Vec<&str> = opts.iter().map(|(k, _)| k.as_ref()).collect(); + + let mut use_defaults: Vec<(String, String)> = Self::gdal_config_options_vsi_curl() + .iter() + .filter(|(k, _)| !opt_keys.contains(k)) + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + + opts.append(&mut use_defaults); + + if opts.is_empty() { None } else { Some(opts) } + } + + pub fn max_retries(&self) -> Option { + self.retry.map(|r| r.max_retries) + } + + pub fn partial_hash(&self, state: &mut H) { + // 1. Hash the file path + self.file_path.hash(state); + + // 2. Hash gdal_open_options + self.gdal_open_options.hash(state); + + // 3. Hash gdal_config_options + self.gdal_config_options.hash(state); + } + + pub fn full_hash(&self, state: &mut H) { + self.partial_hash(state); + + self.rasterband_channel.hash(state); + self.width.hash(state); + self.height.hash(state); + //self.file_not_found_handling.hash(state); // TODO: is this required? + self.allow_alphaband_as_mask.hash(state); + + // Hash f64 safely using bits + if let Some(nd) = self.no_data_value { + nd.to_bits().hash(state); + } + } +} + +#[derive(PartialEq, Serialize, Deserialize, Debug, Clone, Copy)] +#[serde(rename_all = "camelCase")] +pub struct GdalRetryOptions { + pub max_retries: usize, +} + +/// A user friendly representation of Gdal's geo transform. In contrast to [`GeoTransform`] this +/// geo transform allows arbitrary pixel sizes and can thus also represent rasters where the origin is not located +/// in the upper left corner. It should only be used for loading rasters with Gdal and not internally. +/// The GDAL pixel space is usually anchored at the "top-left" corner of the data spatial bounds. Therefore the raster data is stored with spatial coordinate y-values decreasing with the rasters rows. This is represented by a negative pixel size. +/// However, there are datasets where the data is stored "upside-down". If this is the case, the pixel size is positive. +#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, FromSql, ToSql)] +#[serde(rename_all = "camelCase")] +pub struct GdalDatasetGeoTransform { + pub origin_coordinate: Coordinate2D, + pub x_pixel_size: f64, + pub y_pixel_size: f64, +} + +/// Default implementation for testing purposes where geo transform doesn't matter +impl TestDefault for GdalDatasetGeoTransform { + fn test_default() -> Self { + Self { + origin_coordinate: (0.0, 0.0).into(), + x_pixel_size: 1.0, + y_pixel_size: -1.0, + } + } +} + +impl ApproxEq for GdalDatasetGeoTransform { + type Margin = float_cmp::F64Margin; + + fn approx_eq(self, other: Self, margin: M) -> bool + where + M: Into, + { + let m = margin.into(); + self.origin_coordinate.approx_eq(other.origin_coordinate, m) + && self.x_pixel_size.approx_eq(other.x_pixel_size, m) + && self.y_pixel_size.approx_eq(other.y_pixel_size, m) + } +} + +/// Direct conversion from `GdalDatasetGeoTransform` to [`GeoTransform`] only works if origin is located in the upper left corner. +impl TryFrom for GeoTransform { + type Error = Error; + + fn try_from(dataset_geo_transform: GdalDatasetGeoTransform) -> Result { + ensure!( + dataset_geo_transform.x_pixel_size != 0.0 && dataset_geo_transform.y_pixel_size != 0.0, + crate::error::GeoTransformOrigin // TODO new name? + ); + + Ok(GeoTransform::new( + dataset_geo_transform.origin_coordinate, + dataset_geo_transform.x_pixel_size, + dataset_geo_transform.y_pixel_size, + )) + } +} + +impl From for GdalDatasetGeoTransform { + fn from(gdal_geo_transform: gdal::GeoTransform) -> Self { + Self { + origin_coordinate: (gdal_geo_transform[0], gdal_geo_transform[3]).into(), + x_pixel_size: gdal_geo_transform[1], + y_pixel_size: gdal_geo_transform[5], + } + } +} + +impl GridShapeAccess for GdalDatasetParameters { + type ShapeArray = [usize; 2]; + + fn grid_shape_array(&self) -> Self::ShapeArray { + [self.height, self.width] + } +} + +/// How to handle file not found errors +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, FromSql, ToSql)] +pub enum FileNotFoundHandling { + NoData, // output tiles filled with nodata + Error, // return error tile +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, FromSql, ToSql)] +#[serde(rename_all = "camelCase")] +pub enum TimeReference { + Start, + End, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, FromSql, ToSql)] +#[serde(rename_all = "camelCase")] +pub struct GdalSourceTimePlaceholder { + pub format: DateTimeParseFormat, + pub reference: TimeReference, +} + +impl GdalDatasetParameters { + /// Placeholders are replaced by formatted time value. + /// E.g. `%my_placeholder%` could be replaced by `2014-04-01` depending on the format and time input. + pub fn replace_time_placeholders( + &self, + placeholders: &HashMap, + time: TimeInterval, + ) -> Result { + let mut file_path: String = self.file_path.to_string_lossy().into(); + + for (placeholder, time_placeholder) in placeholders { + let time = match time_placeholder.reference { + TimeReference::Start => time.start(), + TimeReference::End => time.end(), + }; + let time_string = time + .as_date_time() + .ok_or(Error::TimeInstanceNotDisplayable)? + .format(&time_placeholder.format); + + // TODO: use more efficient algorithm for replacing multiple placeholders, e.g. aho-corasick + file_path = file_path.replace(placeholder, &time_string); + } + + Ok(Self { + file_not_found_handling: self.file_not_found_handling, + file_path: file_path.into(), + properties_mapping: self.properties_mapping.clone(), + gdal_open_options: self.gdal_open_options.clone(), + gdal_config_options: self.gdal_config_options.clone(), + ..*self + }) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, FromSql, ToSql)] +pub struct GdalMetadataMapping { + pub source_key: RasterPropertiesKey, + pub target_key: RasterPropertiesKey, + pub target_type: RasterPropertiesEntryType, +} + +impl GdalMetadataMapping { + pub fn identity( + key: RasterPropertiesKey, + target_type: RasterPropertiesEntryType, + ) -> GdalMetadataMapping { + GdalMetadataMapping { + source_key: key.clone(), + target_key: key, + target_type, + } + } +} diff --git a/geoengine/operators/src/source/gdal_in/grid_and_properties.rs b/geoengine/operators/src/source/gdal_in/grid_and_properties.rs new file mode 100644 index 0000000000..be0ad9588f --- /dev/null +++ b/geoengine/operators/src/source/gdal_in/grid_and_properties.rs @@ -0,0 +1,20 @@ +use geoengine_datatypes::raster::{ + ChangeGridBounds, GridBoundingBox2D, GridOrEmpty, GridShape2D, RasterProperties, +}; + +pub struct GridAndProperties { + pub grid: GridOrEmpty, + pub properties: RasterProperties, +} + +impl From> for GridAndProperties +where + T: Copy, +{ + fn from(value: GridAndProperties) -> Self { + GridAndProperties { + grid: value.grid.unbounded(), + properties: value.properties, + } + } +} diff --git a/geoengine/operators/src/source/gdal_in/mod.rs b/geoengine/operators/src/source/gdal_in/mod.rs new file mode 100644 index 0000000000..37eb737d04 --- /dev/null +++ b/geoengine/operators/src/source/gdal_in/mod.rs @@ -0,0 +1,20 @@ +mod db_types; +mod gdal_dataset_params; +mod grid_and_properties; +pub mod process_common; +pub mod process_impl; +mod process_pool; + +pub use gdal_dataset_params::{ + FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetadataMapping, + GdalRetryOptions, GdalSourceTimePlaceholder, TimeReference, +}; +pub use grid_and_properties::GridAndProperties; +pub use process_pool::{GdalPoolWorkerInstance, GdalProcessPool, GdalProcessPoolError}; + +pub trait GdalProcessPoolAccess { + fn get_gdal_pool(&self) -> &std::sync::Arc; + fn get_gdal_worker(&self) -> GdalPoolWorkerInstance { + GdalPoolWorkerInstance::new(self.get_gdal_pool().clone()) + } +} diff --git a/geoengine/operators/src/source/gdal_in/process_common.rs b/geoengine/operators/src/source/gdal_in/process_common.rs new file mode 100644 index 0000000000..42fddc1334 --- /dev/null +++ b/geoengine/operators/src/source/gdal_in/process_common.rs @@ -0,0 +1,546 @@ +use bytemuck::{AnyBitPattern, NoUninit}; +use geoengine_datatypes::raster::{ + Grid, GridBoundingBox2D, GridIdx2D, GridOrEmpty, GridShape2D, GridSize, MaskedGrid, + NoDataValueGrid, Pixel, RasterDataType, RasterProperties, RasterPropertiesEntry, + RasterPropertiesKey, +}; +use ipc_channel::IpcError; +use serde::{Deserialize, Serialize}; +use snafu::Snafu; +use std::{ + borrow::Cow, + hash::{Hash, Hasher}, +}; + +use super::{GdalDatasetParameters, GridAndProperties}; + +/// This struct is used to advise the GDAL reader how to read the data from the dataset. +/// The Workflow is as follows: +/// 1. The `gdal_read_window` is the window in the pixel space of the dataset that should be read. +/// 2. The `read_window_bounds` is the area in the target pixel space where the data should be placed. +/// 2.1 The data read in step one is read to the width and height of the `read_window_bounds`. +/// 2.2 if `flip_y` is true the data is flipped in the y direction. And should be unflipped after reading. +/// 3. The `bounds_of_target` is the area in the target pixel space where the data should be placed. +/// 3.1 The `read_window_bounds` might be offset from the `bounds_of_target` or might have a different size. +/// Then, the data needs to be placed in the target pixel space accordingly. Other parts of the target pixel space should be filled with nodata. +#[allow(dead_code)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Hash)] +pub struct GdalReadAdvise { + pub gdal_read_widow: GdalReadWindow, + pub read_window_bounds: GridBoundingBox2D, + pub bounds_of_target: GridBoundingBox2D, + pub flip_y: bool, +} + +impl GdalReadAdvise { + pub fn direct_read(&self) -> bool { + self.read_window_bounds == self.bounds_of_target + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] +pub struct GdalReadWindow { + pub(crate) start_x: isize, // pixelspace origin + pub(crate) start_y: isize, + pub(crate) size_x: usize, // pixelspace size + pub(crate) size_y: usize, +} + +impl GdalReadWindow { + pub fn new(start: GridIdx2D, size: GridShape2D) -> Self { + Self { + start_x: start.x(), + start_y: start.y(), + size_x: size.axis_size_x(), + size_y: size.axis_size_y(), + } + } + + pub fn gdal_window_start(&self) -> (isize, isize) { + (self.start_x, self.start_y) + } + + pub fn gdal_window_size(&self) -> (usize, usize) { + (self.size_x, self.size_y) + } +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)] +pub struct IpcChannelMessagePayload { + pub dataset_params: GdalDatasetParameters, + pub read_advise: GdalReadAdvise, + /// We use this to know what type we serialize + pub data_type: RasterDataType, +} + +pub type IpcProcessRasterResult = Result; + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct GdalIpcBytePayload { + pub dimensions: GridBoundingBox2D, + pub properties: GdalIpcRasterProperties, + pub data_variant: GdalDataGridByteVariant, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum GdalIpcRasterPropertiesEntry { + Number(f64), + String(String), +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct GdalIpcRasterProperties { + pub offset: Option, + pub scale: Option, + pub description: Option, + pub properties_map: Vec<(RasterPropertiesKey, GdalIpcRasterPropertiesEntry)>, +} + +impl From for GdalIpcRasterProperties { + fn from( + RasterProperties { + offset, + scale, + description, + properties_map, + }: RasterProperties, + ) -> Self { + Self { + offset, + scale, + description, + properties_map: properties_map + .into_iter() + .map(|(k, v)| { + let v_converted = match v { + RasterPropertiesEntry::Number(n) => GdalIpcRasterPropertiesEntry::Number(n), + RasterPropertiesEntry::String(s) => GdalIpcRasterPropertiesEntry::String(s), + }; + (k, v_converted) + }) + .collect(), + } + } +} + +impl From for RasterProperties { + fn from( + GdalIpcRasterProperties { + offset, + scale, + description, + properties_map, + }: GdalIpcRasterProperties, + ) -> Self { + Self { + offset, + scale, + description, + properties_map: properties_map + .into_iter() + .map(|(k, v)| { + let v_converted = match v { + GdalIpcRasterPropertiesEntry::Number(n) => RasterPropertiesEntry::Number(n), + GdalIpcRasterPropertiesEntry::String(s) => RasterPropertiesEntry::String(s), + }; + (k, v_converted) + }) + .collect(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct GdalIpcRasterPropertiesKey { + pub domain: Option, + pub key: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub enum GdalDataGridByteVariant { + /// Represents an Empty Grid (zero allocation for pixels or masks) + Empty, + + /// GDAL Native: Every single pixel in the buffer is valid. + /// ZERO mask data is written to or read from the IPC pipe. + AllValid { + #[serde(with = "serde_bytes")] + raw_bytes: Vec, + }, + + /// GDAL Native: Invalid pixels are defined by a specific scalar value. + /// The mask vector is completely omitted from the wire and calculated by the caller. + WithNoData { + #[serde(with = "serde_bytes")] + raw_bytes: Vec, + no_data_value: f64, // Stored as f64 to easily cover all primitive type limits + }, + + /// Fallback: Used only if an irregular mask band is present + WithExplicitMask { + #[serde(with = "serde_bytes")] + raw_bytes: Vec, + #[serde(with = "serde_bytes")] + validity_mask: Vec, // 1 byte per pixel: 1 = valid, 0 = invalid + }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GdalIpcPayload { + pub dimensions: GridBoundingBox2D, + pub properties: RasterProperties, + pub data_variant: GdalDataGridVariant, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum GdalDataGridVariant { + /// Represents an Empty Grid (zero allocation for pixels or masks) + Empty, + AllValid { + data: Vec, + }, + + WithNoData { + data: Vec, + no_data_value: f64, // Stored as f64 to easily cover all primitive type limits + }, + + WithExplicitMask { + data: Vec, + validity_mask: Vec, // 1 byte per pixel: 1 = valid, 0 = invalid + }, +} + +impl TryFrom> for GdalIpcBytePayload +where + T: bytemuck::NoUninit, +{ + type Error = bytemuck::PodCastError; + + fn try_from(value: GdalIpcPayload) -> Result { + Ok(Self { + dimensions: value.dimensions, + properties: value.properties.into(), + data_variant: value.data_variant.try_into()?, + }) + } +} + +impl TryFrom> for GdalDataGridByteVariant +where + T: NoUninit, +{ + type Error = bytemuck::PodCastError; + + fn try_from(value: GdalDataGridVariant) -> Result { + match value { + GdalDataGridVariant::Empty => Ok(GdalDataGridByteVariant::Empty), + GdalDataGridVariant::AllValid { data } => { + bytemuck::try_cast_slice::(&data).map(|s| { + GdalDataGridByteVariant::AllValid { + raw_bytes: s.to_vec(), + } + }) + } + GdalDataGridVariant::WithNoData { + data, + no_data_value, + } => bytemuck::try_cast_slice::(&data).map(|s| { + GdalDataGridByteVariant::WithNoData { + raw_bytes: s.to_vec(), + no_data_value, + } + }), + GdalDataGridVariant::WithExplicitMask { + data, + validity_mask, + } => bytemuck::try_cast_slice::(&data).map(|s| { + GdalDataGridByteVariant::WithExplicitMask { + raw_bytes: s.to_vec(), + validity_mask, + } + }), + } + } +} + +impl TryFrom<&GdalDataGridByteVariant> for GdalDataGridVariant +where + T: AnyBitPattern, +{ + type Error = bytemuck::PodCastError; + + fn try_from(value: &GdalDataGridByteVariant) -> Result { + match value { + GdalDataGridByteVariant::Empty => Ok(GdalDataGridVariant::Empty), + GdalDataGridByteVariant::AllValid { raw_bytes } => { + // We borrow the well-aligned bytes from the Arc, cast them, and allocate Vec + let data = bytemuck::try_cast_slice::(raw_bytes)?.to_vec(); + Ok(GdalDataGridVariant::AllValid { data }) + } + GdalDataGridByteVariant::WithNoData { + raw_bytes, + no_data_value, + } => { + let data = bytemuck::try_cast_slice::(raw_bytes)?.to_vec(); + Ok(GdalDataGridVariant::WithNoData { + data, + no_data_value: *no_data_value, + }) + } + GdalDataGridByteVariant::WithExplicitMask { + raw_bytes, + validity_mask, + } => { + let data = bytemuck::try_cast_slice::(raw_bytes)?.to_vec(); + Ok(GdalDataGridVariant::WithExplicitMask { + data, + validity_mask: validity_mask.clone(), // Mask is u8, so clone is cheap and safe + }) + } + } + } +} + +impl TryFrom<&GdalIpcBytePayload> for GdalIpcPayload +where + T: bytemuck::AnyBitPattern, +{ + type Error = bytemuck::PodCastError; + + fn try_from(value: &GdalIpcBytePayload) -> Result { + Ok(Self { + dimensions: value.dimensions, + properties: value.properties.clone().into(), + data_variant: (&value.data_variant).try_into()?, + }) + } +} + +impl From> for GridAndProperties +where + T: Pixel, +{ + fn from(value: GdalIpcPayload) -> Self { + match value.data_variant { + GdalDataGridVariant::Empty => GridAndProperties { + grid: GridOrEmpty::new_empty_shape(value.dimensions), + properties: value.properties, + }, + GdalDataGridVariant::AllValid { data } => GridAndProperties { + grid: GridOrEmpty::new_grid(MaskedGrid::new_with_data( + Grid::new(value.dimensions, data).expect("shape and data match"), + )), + properties: value.properties, + }, + GdalDataGridVariant::WithNoData { + data, + no_data_value, + } => { + let no_data_t = T::from_(no_data_value); + GridAndProperties { + grid: GridOrEmpty::new_grid( + NoDataValueGrid::new( + Grid::new(value.dimensions, data).expect("shape and data match"), + Some(no_data_t), + ) + .into(), + ), + properties: value.properties, + } + } + GdalDataGridVariant::WithExplicitMask { + data, + validity_mask, + } => GridAndProperties { + grid: GridOrEmpty::new_grid( + MaskedGrid::new( + Grid::new(value.dimensions, data).expect("shape and data match"), + Grid::new( + value.dimensions, + validity_mask.iter().map(|&m| m > 0).collect(), + ) + .expect("shape and data match"), + ) + .expect("dimensions must match"), + ), + properties: value.properties, + }, + } + } +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq)] +pub enum IpcProcessGdalErrorKind { + FileNotFound, + AccessDenied, + InvalidDataset, + ConnectionTimeout, + Unknown, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq)] +pub enum IpcProcessIoErrorKind { + NotFound, + PermissionDenied, + ConnectionRefused, + ConnectionAborted, + UnexpectedEof, + Other, +} + +#[derive(Debug, Snafu, Serialize, Deserialize, Clone, PartialEq)] +#[snafu(visibility(pub))] +pub enum IpcProcessError { + #[snafu(display("Serialization error: {msg}"))] + SerializationError { + // Cow allows zero-allocation compilation-string sharing on creation. + // On the receiving side of ipc-channel, serde automatically hydrates this into Cow::Owned. + msg: String, + }, + + #[snafu(display("IO error ({kind:?}): {context}"))] + Io { + kind: IpcProcessIoErrorKind, + context: String, + }, + + #[snafu(display("IPC channel disconnected"))] + Disconnected, + + #[snafu(display("Unsupported or invalid data type: {datatype}"))] + DataType { datatype: Cow<'static, str> }, + + #[snafu(display("GDAL operational error ({kind:?}): {details}"))] + GdalError { + kind: IpcProcessGdalErrorKind, + details: String, + }, + + #[snafu(display("Raster Arrow conversion failure: {msg}"))] + RasterArrowConversion { msg: String }, + + #[snafu(display("Unhandled worker exception: {msg}"))] + IpcOther { msg: String }, + + #[snafu(display("Gdal buffer len {buffer_len} does not match grid shape {shape:?}"))] + ShapeBufferMissmatch { + shape: GridShape2D, + buffer_len: usize, + }, + + #[snafu(display("Error casting data to bytes for IPC transmission: {error}"))] + PocCastError { error: String }, + + #[snafu(display("The dataset uses alpha mask but its not allowed in dataset params"))] + AlphaBandAsMaskNotAllowed, +} + +impl IpcProcessError { + /// Convenience checker for your parent-side retry loop and fallback handlers + pub fn is_file_not_found(&self) -> bool { + matches!( + self, + Self::GdalError { + kind: IpcProcessGdalErrorKind::FileNotFound, + .. + } | Self::Io { + kind: IpcProcessIoErrorKind::NotFound, + .. + } + ) + } +} + +impl From for IpcProcessError { + fn from(err: std::io::Error) -> Self { + let kind = match err.kind() { + std::io::ErrorKind::NotFound => IpcProcessIoErrorKind::NotFound, + std::io::ErrorKind::PermissionDenied => IpcProcessIoErrorKind::PermissionDenied, + std::io::ErrorKind::ConnectionRefused => IpcProcessIoErrorKind::ConnectionRefused, + std::io::ErrorKind::ConnectionAborted => IpcProcessIoErrorKind::ConnectionAborted, + std::io::ErrorKind::UnexpectedEof => IpcProcessIoErrorKind::UnexpectedEof, + _ => IpcProcessIoErrorKind::Other, + }; + IpcProcessError::Io { + kind, + context: err.to_string(), + } + } +} + +impl From for IpcProcessError { + /// Maps a raw GDAL library error into a strongly-typed structural variant + /// before serialization across the IPC channel. + fn from(err: gdal::errors::GdalError) -> Self { + let err_string = err.to_string(); + + // Parse the error structural context cleanly + let kind = match &err { + gdal::errors::GdalError::NullPointer { method_name, msg } + if *method_name == "GDALOpenEx" + && (*msg == "HTTP response code: 404" + || msg.ends_with("No such file or directory")) => + { + IpcProcessGdalErrorKind::FileNotFound + } + gdal::errors::GdalError::NullPointer { .. } + | gdal::errors::GdalError::OgrError { .. } => IpcProcessGdalErrorKind::InvalidDataset, + _ if err_string.contains("No such file or directory") || err_string.contains("404") => { + IpcProcessGdalErrorKind::FileNotFound + } + _ if err_string.contains("Permission denied") || err_string.contains("403") => { + IpcProcessGdalErrorKind::AccessDenied + } + _ if err_string.contains("timeout") || err_string.contains("Connection timed out") => { + IpcProcessGdalErrorKind::ConnectionTimeout + } + _ => IpcProcessGdalErrorKind::Unknown, + }; + + IpcProcessError::GdalError { + kind, + details: err_string, + } + } +} + +impl From for IpcProcessError { + fn from(err: IpcError) -> Self { + match err { + IpcError::SerializationError(serde_err) => IpcProcessError::SerializationError { + msg: serde_err.to_string(), + }, + IpcError::Io(io_err) => { + // Automatically delegates to our optimized From implementation + IpcProcessError::from(io_err) + } + IpcError::Disconnected => IpcProcessError::Disconnected, + } + } +} + +impl From for IpcProcessError { + fn from(error: bytemuck::PodCastError) -> Self { + IpcProcessError::PocCastError { + error: error.to_string(), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct IpcChannelMessage(pub IpcChannelMessagePayload); + +impl IpcChannelMessage { + pub fn new_request_tile_message(data: IpcChannelMessagePayload) -> Self { + Self(data) + } + + pub fn full_hash(&self, state: &mut H) { + self.0.dataset_params.full_hash(state); + + // Hash read advise + self.0.read_advise.hash(state); + self.0.data_type.hash(state); + } +} diff --git a/geoengine/operators/src/source/gdal_in/process_impl.rs b/geoengine/operators/src/source/gdal_in/process_impl.rs new file mode 100644 index 0000000000..49753c519c --- /dev/null +++ b/geoengine/operators/src/source/gdal_in/process_impl.rs @@ -0,0 +1,1461 @@ +use super::{ + GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetadataMapping, + process_common::{ + GdalDataGridVariant, GdalIpcPayload, GdalReadAdvise, GdalReadWindow, IpcProcessError, + }, +}; +use crate::{ + source::gdal_in::process_common::IpcProcessGdalErrorKind, + util::{GdalConfigOptions, gdal::gdal_open_ex_gdal_error, retry::retry_sync}, +}; +use float_cmp::approx_eq; +use gdal::{ + Dataset as GdalDataset, DatasetOptions, GdalOpenFlags, Metadata as GdalMetadata, + errors::GdalError, + raster::{GdalType, RasterBand as GdalRasterBand}, +}; +use gdal_sys::VSICurlPartialClearCache; +use geoengine_datatypes::{ + primitives::Coordinate2D, + raster::{GridSize, Pixel, RasterProperties, RasterPropertiesEntry, RasterPropertiesEntryType}, +}; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use num::FromPrimitive; +use std::{ + env, + ffi::CString, + path::{Path, PathBuf}, + process::Command, + sync::OnceLock, + time::Instant, +}; +use tracing::debug; + +/// Global storage for the detected path, initialized on first access. +static GDALSOURCE_PROCESS_PATH: OnceLock = OnceLock::new(); + +static GDAL_RETRY_INITIAL_BACKOFF_MS: u64 = 1000; +static GDAL_RETRY_MAX_BACKOFF_MS: u64 = 60 * 60 * 1000; +static GDAL_RETRY_EXPONENTIAL_BACKOFF_FACTOR: f64 = 2.; + +/// Returns a reference to the cached `gdalsource-process` path. +/// The detection logic runs exactly once on the very first call. +fn get_gdalsource_path() -> &'static Path { + GDALSOURCE_PROCESS_PATH.get_or_init(|| { + // 1. Try the environment variable path first + if let Ok(env_path) = env::var("GDALSOURCE_PROCESS_PATH") + && !env_path.is_empty() + { + let path = PathBuf::from(env_path); + if path.is_file() { + tracing::debug!( + "Using gdalsource-process path from environment variable: {}", + path.display() + ); + return path; + } + } + + // 2. Fallback detection logic + let mut exe_path = env::current_exe() + .unwrap_or_else(|_| env::current_dir().unwrap_or_else(|_| PathBuf::from("."))); + + if exe_path.is_file() { + exe_path.pop(); + } + + if exe_path.file_name().is_some_and(|name| name == "deps") { + exe_path.pop(); + } + + let binary_name = if cfg!(windows) { + "gdalsource-process.exe" + } else { + "gdalsource-process" + }; + exe_path.push(binary_name); + + tracing::debug!("Detected gdalsource-process path: {}", exe_path.display()); + + exe_path + }) +} + +pub struct ChildProcessGuard { + child: std::process::Child, +} + +impl Drop for ChildProcessGuard { + fn drop(&mut self) { + // Forcefully kill the process when the guard is dropped + let _ = self.child.kill(); + let _ = self.child.wait(); // Prevent zombie processes + } +} + +/// Spawns the IPC server process and establishes the communication channels. +/// +/// Returns a guard for the child process (which will automatically clean up on drop), +/// as well as the sender and receiver for communication with the server. +/// Assumes that the server executable is located at the path specified by the `GDAL_SOURCE_PROCESS_PATH` environment variable, +/// or defaults to a sibling executable named `gdalsource-process` in the same directory as the current executable if the environment variable is not set. +/// +/// # Errors +/// Returns an `IpcProcessError` if the server process fails to start, or if the IPC channels fail to establish communication. +/// +/// # Panics +/// Panics if the current executable path cannot be determined, or if the executable has no parent directory, which should not happen under normal circumstances. +pub fn spawn_ipc_server_process() +-> Result<(ChildProcessGuard, IpcSender, IpcReceiver), IpcProcessError> { + let (server, token) = ipc::IpcOneShotServer::<(IpcSender, IpcReceiver)>::new() + .map_err(IpcProcessError::from)?; + + tracing::debug!("spawn_ipc_server token: {}", token); + + let exe_path = get_gdalsource_path(); + + let mut cmd = Command::new(exe_path); + + cmd.arg(token) + .arg("debug") // FIXME: paste log level here! + .stderr(std::process::Stdio::inherit()); // This sends child logs to the parent's stderr; + + if std::cfg!(debug_assertions) { + // llcov inserts these env params. We need to remove them from the gdal-processor processes. Otherwise the processes overwrite the main process data and the files become corrupt. + cmd.env_remove("LLVM_PROFILE_FILE") + .env_remove("LLVM_PROFILE_FILE_NAME"); + } + + let child = cmd.spawn().map_err(IpcProcessError::from)?; + + let (_rx, channels) = server.accept().map_err(IpcProcessError::from)?; + Ok((ChildProcessGuard { child }, channels.0, channels.1)) +} + +/// Creates channels and connects to the IPC server with the given token, +/// sending the channels to the server, so that communication can be established. +/// +/// Assumes that the server is already running and listening for connections. +pub fn setup_client( + token: String, +) -> crate::util::Result<(IpcSender, IpcReceiver), IpcProcessError> +where + S: for<'de> serde::Deserialize<'de> + serde::Serialize, + C: for<'de> serde::Deserialize<'de> + serde::Serialize, +{ + let (server_sender, client_reciever) = ipc::channel::()?; + let (client_sender, server_reciever) = ipc::channel::()?; + + let sender = ipc::IpcSender::<(IpcSender, IpcReceiver)>::connect(token)?; + + sender.send((server_sender, server_reciever))?; + Ok((client_sender, client_reciever)) +} + +/// A simple, single-entry cache for an open GDAL dataset tile. +#[derive(Default)] +pub struct GdalDatasetHolder { + params: Option, + dataset: Option, + thread_local: Option, +} + +struct PrevDatasetOpt { + _prev_dataset: Option, +} + +impl GdalDatasetHolder { + pub fn new() -> Self { + Self { + params: None, + dataset: None, + thread_local: None, + } + } + + fn open_ex( + dataset_params: &GdalDatasetParameters, + ) -> Result<(GdalDataset, Option), GdalError> { + let options = dataset_params + .gdal_open_options + .as_ref() + .map(|o| o.iter().map(String::as_str).collect::>()); + + // reverts the thread local configs on drop + let thread_local_configs: Option = dataset_params + .gdal_config_options_with_defaults() + .as_ref() + .map(|config_options| GdalConfigOptions::new(config_options)) + .transpose() + .expect("GdalConfigOptions must not fail"); + + let ds = gdal_open_ex_gdal_error( + &dataset_params.file_path, + DatasetOptions { + open_flags: GdalOpenFlags::GDAL_OF_RASTER, + open_options: options.as_deref(), + ..DatasetOptions::default() + }, + )?; + + Ok((ds, thread_local_configs)) + } + + pub fn get(&mut self, params: &GdalDatasetParameters) -> Option<&mut GdalDataset> { + if Self::is_hit(self.params.as_ref(), params) { + self.dataset.as_mut() + } else { + None + } + } + + /// Retrieves the open dataset if the parameters match, otherwise opens a new dataset with the given parameters, updates the stored dataset, and returns it. + /// The current open dataset is kept alive until the new one is successfully opened to maintain GDAL's internal caching benefits. + /// This method ensures that only one dataset is open at a time, and that the state is updated atomically to prevent stale data. + /// The caller is responsible for ensuring that the returned dataset is not used concurrently across threads, as GDAL datasets are generally not thread-safe. + /// # Errors + /// Returns a `GdalError` if opening the new dataset fails. In this case, the stored dataset remains unchanged and the previous dataset (if any) is still valid. + /// + /// # Panics + /// Panics if the dataset is unexpectedly missing after a it was detected as open, which should never happen. + /// Panics if the configuration options fail to apply, which should also not happen under normal circumstances. + /// Panics if the caller attempts to use the returned dataset concurrently across threads, which is not allowed due to GDAL's thread safety constraints. + pub fn get_or_open( + &mut self, + params: &GdalDatasetParameters, + ) -> Result<&mut GdalDataset, GdalError> { + let hit = Self::is_hit(self.params.as_ref(), params); + + if hit { + Ok(self.dataset.as_mut().expect("Dataset must be set")) + } else { + let _prev_dataset = self.clear(); // keep old dataset alive untill new one to keep Gdal internal cache alive + + let (ds, tlo) = Self::open_ex(params)?; + + self.params = Some(params.clone()); + self.dataset = Some(ds); + self.thread_local = tlo; + + Ok(self.dataset.as_mut().expect("Dataset must be set")) + } + } + + fn is_hit(params: Option<&GdalDatasetParameters>, other: &GdalDatasetParameters) -> bool { + // TODO: we could optimize this by hashing the parameters and comparing the hash for a quick check before doing the full equality check, if it turns out to be a bottleneck. + if let Some(current_ds_params) = params { + current_ds_params.file_path == other.file_path + && current_ds_params.gdal_open_options == other.gdal_open_options + && current_ds_params.gdal_config_options == other.gdal_config_options + } else { + false + } + } + + fn clear(&mut self) -> PrevDatasetOpt { + let prev = PrevDatasetOpt { + _prev_dataset: self.dataset.take(), + }; + self.params = None; + self.thread_local = None; + prev + } + + pub fn contains(&self, params: &GdalDatasetParameters) -> bool { + Self::is_hit(self.params.as_ref(), params) + } +} + +pub struct GdalHandling; + +impl GdalHandling { + pub fn error_is_gdal_file_not_found(error: &GdalError) -> bool { + matches!( + error, + GdalError::NullPointer { + method_name, + msg + } + if *method_name == "GDALOpenEx" && (*msg == "HTTP response code: 404" || msg.ends_with("No such file or directory")) + ) + } + + fn clear_gdal_vsi_cache_for_path(file_path: &Path) { + unsafe { + if let Some(Some(c_string)) = file_path.to_str().map(|s| CString::new(s).ok()) { + VSICurlPartialClearCache(c_string.as_ptr()); + } + } + } + + /// + /// A method to load single tiles from a GDAL dataset. + /// + pub fn load_tile_data_with_dataset_retry( + cache: &mut GdalDatasetHolder, + dataset_params: &GdalDatasetParameters, + read_advise: GdalReadAdvise, + ) -> Result, IpcProcessError> { + let is_vsi_curl = dataset_params.is_vis_curl(); + let max_retries = dataset_params.max_retries().unwrap_or(0); + let dp = &dataset_params; + + // Wrap both OPEN and READ actions inside the retry loop + + retry_sync( + max_retries, + GDAL_RETRY_INITIAL_BACKOFF_MS, + GDAL_RETRY_EXPONENTIAL_BACKOFF_FACTOR, + Some(GDAL_RETRY_MAX_BACKOFF_MS), + || { + let ds = match cache.get_or_open(dp) { + Ok(dataset) => dataset, + Err(gdal_error) => { + if is_vsi_curl { + Self::clear_gdal_vsi_cache_for_path(dp.file_path.as_path()); + } + return Err(IpcProcessError::from(gdal_error)); + } + }; + + Self::load_tile_data(ds, dataset_params, read_advise).inspect_err(|_e| { + if is_vsi_curl { + Self::clear_gdal_vsi_cache_for_path(dp.file_path.as_path()); + } + }) + }, + // If the file explicitly does not exist, do not waste time retrying + |e| matches!(e, IpcProcessError::GdalError { kind, details: _ } if *kind == IpcProcessGdalErrorKind::FileNotFound), + ) + } + + /// This method reads the data for a single grid with a specified size from the GDAL dataset. + /// It fails if the tile is not within the dataset. + #[allow(clippy::float_cmp)] + pub fn read_grid_from_raster( + rasterband: &GdalRasterBand, + read_window: &GdalReadWindow, + out_shape: &D, + dataset_params: &GdalDatasetParameters, + ) -> Result, IpcProcessError> + where + T: Pixel + GdalType + Default + FromPrimitive, + D: Clone + GridSize + PartialEq, + { + let gdal_out_shape = (out_shape.axis_size_x(), out_shape.axis_size_y()); + + let buffer = rasterband.read_as::( + read_window.gdal_window_start(), // pixelspace origin + read_window.gdal_window_size(), // pixelspace size + gdal_out_shape, // requested raster size + None, // sampling mode + )?; + let (_, buffer_data) = buffer.into_shape_and_vec(); + + let dataset_mask_flags = rasterband.mask_flags()?; + + if dataset_mask_flags.is_all_valid() { + debug!("all pixels are valid --> skip no-data and mask handling."); + return Ok(GdalDataGridVariant::AllValid { data: buffer_data }); + } + + if dataset_mask_flags.is_nodata() { + debug!("raster uses a no-data value --> use no-data handling."); + let no_data_value = dataset_params + .no_data_value + .or_else(|| rasterband.no_data_value()); + + if let Some(no_data_value) = no_data_value { + return Ok(GdalDataGridVariant::WithNoData { + data: buffer_data, + no_data_value, + }); + } + return Ok(GdalDataGridVariant::AllValid { data: buffer_data }); + } + + if dataset_mask_flags.is_alpha() { + debug!("raster uses alpha band to mask pixels."); + if !dataset_params.allow_alphaband_as_mask { + return Err(IpcProcessError::AlphaBandAsMaskNotAllowed); + } + } + + debug!("use mask based no-data handling."); + + let mask_band = rasterband.open_mask_band()?; + let mask_buffer = mask_band.read_as::( + read_window.gdal_window_start(), // pixelspace origin + read_window.gdal_window_size(), // pixelspace size + gdal_out_shape, // requested raster size + None, // sampling mode + )?; + let (_, mask_buffer_data) = mask_buffer.into_shape_and_vec(); + Ok(GdalDataGridVariant::WithExplicitMask { + data: buffer_data, + validity_mask: mask_buffer_data, + }) + } + + pub fn properties_from_gdal_metadata<'a, I, M>( + properties: &mut RasterProperties, + gdal_dataset: &M, + properties_mapping: I, + ) where + I: IntoIterator, + M: GdalMetadata, + { + let mapping_iter = properties_mapping.into_iter(); + + for m in mapping_iter { + let data = if let Some(domain) = &m.source_key.domain { + gdal_dataset.metadata_item(&m.source_key.key, domain) + } else { + gdal_dataset.metadata_item(&m.source_key.key, "") + }; + + if let Some(d) = data { + let entry = match m.target_type { + RasterPropertiesEntryType::Number => d.parse::().map_or_else( + |_| RasterPropertiesEntry::String(d), + RasterPropertiesEntry::Number, + ), + RasterPropertiesEntryType::String => RasterPropertiesEntry::String(d), + }; + + debug!( + "gdal properties key \"{:?}\" => target key \"{:?}\". Value: {:?} ", + &m.source_key, &m.target_key, &entry + ); + + properties.insert_property(m.target_key.clone(), entry); + } + } + } + + pub fn properties_from_band(properties: &mut RasterProperties, gdal_dataset: &GdalRasterBand) { + if let Some(scale) = gdal_dataset.scale() { + properties.set_scale(scale); + } + if let Some(offset) = gdal_dataset.offset() { + properties.set_offset(offset); + } + + // ignore if there is no description + if let Ok(description) = gdal_dataset.description() { + properties.set_description(description); + } + } + + /// This method reads the data for a single tile with a specified size from the GDAL dataset and adds the requested metadata as properties to the tile. + pub fn read_raster_properties( + dataset: &GdalDataset, + dataset_params: &GdalDatasetParameters, + rasterband: &GdalRasterBand, + ) -> RasterProperties { + let mut properties = RasterProperties::default(); + + // always read the scale and offset values from the rasterband + Self::properties_from_band(&mut properties, rasterband); + + // read the properties from the dataset and rasterband metadata + if let Some(properties_mapping) = dataset_params.properties_mapping.as_ref() { + Self::properties_from_gdal_metadata(&mut properties, dataset, properties_mapping); + Self::properties_from_gdal_metadata(&mut properties, rasterband, properties_mapping); + } + + properties + } + + /// + /// A method to load single tiles from a GDAL dataset. + /// + fn load_tile_data( + dataset: &mut GdalDataset, + dataset_params: &GdalDatasetParameters, + read_advise: GdalReadAdvise, + ) -> Result, IpcProcessError> { + let start = Instant::now(); + + debug!( + "GridOrEmpty2D<{:?}> requested for {:?}.", + T::TYPE, + &read_advise.bounds_of_target, + ); + + let gdal_dataset_geotransform = GdalDatasetGeoTransform::from(dataset.geo_transform()?); + // check that the dataset geo transform is the same as the one we get from GDAL + debug_assert!( + approx_eq!( + Coordinate2D, + gdal_dataset_geotransform.origin_coordinate, + dataset_params.geo_transform.origin_coordinate + ), + "expected dataset geo transform origin coordinate {:?} to be approximately equal to GDAL dataset geo transform origin coordinate {:?}", + dataset_params.geo_transform.origin_coordinate, + gdal_dataset_geotransform.origin_coordinate + ); + + debug_assert!( + approx_eq!( + f64, + gdal_dataset_geotransform.x_pixel_size, + dataset_params.geo_transform.x_pixel_size + ), + "expected dataset geo transform x pixel size {:?} to be approximately equal to GDAL dataset geo transform x pixel size {:?}", + dataset_params.geo_transform.x_pixel_size, + gdal_dataset_geotransform.x_pixel_size + ); + + debug_assert!( + approx_eq!( + f64, + gdal_dataset_geotransform.y_pixel_size, + dataset_params.geo_transform.y_pixel_size + ), + "expected dataset geo transform y pixel size {:?} to be approximately equal to GDAL dataset geo transform y pixel size {:?}", + dataset_params.geo_transform.y_pixel_size, + gdal_dataset_geotransform.y_pixel_size + ); + + let (gdal_dataset_pixels_x, gdal_dataset_pixels_y) = dataset.raster_size(); + // check that the dataset pixel size is the same as the one we get from GDAL + debug_assert_eq!(gdal_dataset_pixels_x, dataset_params.width); + debug_assert_eq!(gdal_dataset_pixels_y, dataset_params.height); + + let rasterband = dataset.rasterband(dataset_params.rasterband_channel)?; + + let result_gdal_raster = Self::read_grid_from_raster( + &rasterband, + &read_advise.gdal_read_widow, + &read_advise.read_window_bounds, + dataset_params, + )?; + + let properties = Self::read_raster_properties(dataset, dataset_params, &rasterband); + + let elapsed = start.elapsed(); + debug!("data loaded -> returning data grid, took {elapsed:?}"); + + Ok(GdalIpcPayload { + dimensions: read_advise.read_window_bounds, + properties, + data_variant: result_gdal_raster, + }) + } +} + +#[cfg(test)] +mod tests { + + use super::super::{ + FileNotFoundHandling, GridAndProperties, + process_common::{ + IpcChannelMessage, IpcChannelMessagePayload, IpcProcessGdalErrorKind, + IpcProcessRasterResult, + }, + }; + use super::*; + use float_cmp::assert_approx_eq; + use geoengine_datatypes::{ + primitives::{AxisAlignedRectangle, SpatialPartition2D, TimeInstance, TimeInterval}, + raster::{ + GeoTransform, GridBoundingBox2D, GridShape2D, RasterDataType, RasterPropertiesKey, + SpatialGridDefinition, TileInformation, + }, + test_data, + util::{gdal::hide_gdal_errors, test::TestDefault}, + }; + use httptest::{Expectation, Server, matchers::request, responders}; + + fn get_params() -> GdalDatasetParameters { + GdalDatasetParameters { + file_path: test_data!("raster/modis_ndvi/MOD13A2_M_NDVI_2014-01-01.TIFF").into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (-180., 90.).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, + }, + width: 3600, + height: 1800, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: Some(0.), + properties_mapping: Some(vec![ + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + }, + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE".to_string()), + key: "COMPRESSION".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE_INFO".to_string()), + key: "COMPRESSION".to_string(), + }, + }, + ]), + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + } + } + + fn tile_information_with_partition_and_shape( + partition: SpatialPartition2D, + shape: GridShape2D, + ) -> TileInformation { + let real_geotransform = GeoTransform::new( + partition.upper_left(), + partition.size_x() / shape.axis_size_x() as f64, + -partition.size_y() / shape.axis_size_y() as f64, + ); + + TileInformation { + tile_size_in_pixels: shape, + global_tile_position: [0, 0].into(), + global_geo_transform: real_geotransform, + } + } + + #[test] + #[serial_test::serial] + fn ipc_process_error_ipc_channel_roundtrip() { + let error = IpcProcessError::GdalError { + kind: IpcProcessGdalErrorKind::FileNotFound, + details: "not found".into(), + }; + + let (sender, receiver) = ipc::channel::().unwrap(); + sender.send(Err(error.clone())).unwrap(); + let decoded = receiver.recv().unwrap(); + + assert_eq!(decoded, Err(error)); + } + + #[test] + #[serial_test::serial] + fn test_sending_gdal_dataset_parameters_via_string() { + let msg = get_params(); + + let (sender, receiver) = ipc_channel::ipc::channel::().unwrap(); + + sender.send(serde_json::to_string(&msg).unwrap()).unwrap(); + let recv = receiver.recv().unwrap(); + let recv = serde_json::from_str::(&recv).unwrap(); + assert_eq!(msg.properties_mapping, recv.properties_mapping); + assert_eq!(msg, recv); + } + + #[test] + #[serial_test::serial] + fn test_sending_gdal_dataset_parameters() { + let msg = get_params(); + + let (sender, receiver) = ipc_channel::ipc::channel().unwrap(); + + sender.send(msg.clone()).unwrap(); + let recv = receiver.recv().unwrap(); + assert_eq!(msg.properties_mapping, recv.properties_mapping); + + assert_eq!(msg, recv); + } + + #[test] + #[serial_test::serial] + fn test_sending_tile_information() { + let output_shape: GridShape2D = [8, 8].into(); + let output_bounds = + SpatialPartition2D::new_unchecked((-180., 90.).into(), (180., -90.).into()); + + let msg = tile_information_with_partition_and_shape(output_bounds, output_shape); + + let (sender, receiver) = ipc_channel::ipc::channel().unwrap(); + + sender.send(msg).unwrap(); + let recv = receiver.recv().unwrap(); + assert_eq!(msg, recv); + } + + #[test] + #[serial_test::serial] + fn test_sending_time() { + let msg = TimeInstance::from_millis(10).unwrap(); + + let (sender, receiver) = ipc_channel::ipc::channel().unwrap(); + + sender.send(msg.clone()).unwrap(); + let recv = receiver.recv().unwrap(); + assert_eq!(msg, recv); + } + + #[test] + #[serial_test::serial] + fn test_sending_time_interval() { + let msg = TimeInterval::default(); + + let (sender, receiver) = ipc_channel::ipc::channel().unwrap(); + + sender.send(msg.clone()).unwrap(); + let recv = receiver.recv().unwrap(); + assert_eq!(msg, recv); + } + + #[test] + #[serial_test::serial] + fn test_sending_request_tile_data() { + let output_shape: GridShape2D = [8, 8].into(); + + let read_advise = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), output_shape), + read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + flip_y: false, + }; + + let payload = IpcChannelMessagePayload { + data_type: RasterDataType::U8, + dataset_params: GdalDatasetParameters { + file_path: test_data!("raster/modis_ndvi/MOD13A2_M_NDVI_2014-01-01.TIFF").into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (-180., 90.).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, + }, + width: 3600, + height: 1800, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: Some(0.), + properties_mapping: Some(vec![ + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + }, + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE".to_string()), + key: "COMPRESSION".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE_INFO".to_string()), + key: "COMPRESSION".to_string(), + }, + }, + ]), + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + }, + read_advise, + }; + + let msg = IpcChannelMessage::new_request_tile_message(payload); + + let (sender, receiver) = ipc_channel::ipc::channel().unwrap(); + + sender.send(msg.clone()).unwrap(); + let recv = receiver.recv().unwrap(); + + assert_eq!(msg, recv); + } + + #[test] + #[serial_test::serial] + fn test_ipc_channel_roundtrip_tile() { + let output_shape: GridShape2D = [8, 8].into(); + + let read_advise = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), output_shape), + read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + flip_y: false, + }; + + let payload = IpcChannelMessagePayload { + data_type: RasterDataType::U8, + dataset_params: GdalDatasetParameters { + file_path: test_data!("raster/modis_ndvi/MOD13A2_M_NDVI_2014-01-01.TIFF").into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (-180., 90.).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, + }, + width: 3600, + height: 1800, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: Some(0.), + properties_mapping: Some(vec![ + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + }, + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE".to_string()), + key: "COMPRESSION".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE_INFO".to_string()), + key: "COMPRESSION".to_string(), + }, + }, + ]), + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + }, + read_advise, + }; + + let msg = IpcChannelMessage::new_request_tile_message(payload); + + let (_child_guard, sender, receiver) = + spawn_ipc_server_process::().unwrap(); + + sender.send(msg).unwrap(); + let rx_result = receiver + .recv() + .inspect_err(|e| panic!("IPC receive: {:?}", e)) + .unwrap(); + + let payload = match rx_result { + Ok(r) => r, + Err(e) => panic!("Error receiving from IPC process: {:?}", e), + }; + + let result_2: GdalIpcPayload = (&payload).try_into().unwrap(); + + let grid_and_props: GridAndProperties = result_2.into(); + + assert!(!grid_and_props.grid.is_empty()); + + let grid = grid_and_props.grid.into_materialized_masked_grid(); + + assert_eq!(grid.inner_grid.data.len(), 64); + assert_eq!(grid.validity_mask.data.len(), 64); + } + + // This method loads raster data from a cropped MODIS NDVI raster. + // To inspect the byte values first convert the file to XYZ with GDAL: + // 'gdal_translate -of xyz MOD13A2_M_NDVI_2014-04-01_30X30.tif MOD13A2_M_NDVI_2014-04-01_30x30.xyz' + // Then you can convert them to gruped bytes: + // 'cut -d ' ' -f 1,2 --complement MOD13A2_M_NDVI_2014-04-01_30x30.xyz | xargs -n 30 > MOD13A2_M_NDVI_2014-04-01_30x30_bytes.txt'. + fn load_ndvi_apr_2014_cropped( + gdal_read_advice: GdalReadAdvise, + ) -> Result, IpcProcessError> { + let dataset_params = GdalDatasetParameters { + file_path: test_data!("raster/modis_ndvi/cropped/MOD13A2_M_NDVI_2014-04-01_30x30.tif") + .into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (8.0, 57.4).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, + }, + width: 30, + height: 30, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: Some(255.), + properties_mapping: Some(vec![GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + }]), + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + }; + + let mut gdc = GdalDatasetHolder::new(); + let dataset = gdc.get_or_open(&dataset_params).unwrap(); + + let reader_payload = + GdalHandling::load_tile_data::(dataset, &dataset_params, gdal_read_advice) + .map_err(IpcProcessError::from)?; + + let grid_and_props: GridAndProperties = reader_payload.into(); + Ok(grid_and_props.into()) + } + + #[tokio::test] + #[serial_test::serial] + async fn test_load_tile_data_top_left() { + let gdal_read_advice = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), + read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + flip_y: false, + }; + + let GridAndProperties { grid, properties } = + load_ndvi_apr_2014_cropped(gdal_read_advice).unwrap(); + + assert!(!grid.is_empty()); + + let grid = grid.into_materialized_masked_grid(); + + assert_eq!(grid.inner_grid.data.len(), 64); + // pixel value are the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt + assert_eq!( + grid.inner_grid.data, + &[ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 127, 107, 255, 255, 255, 255, 255, 164, 185, 182, + 255, 255, 255, 175, 186, 190, 167, 140, 255, 255, 161, 175, 184, 173, 170, 188, + 255, 255, 128, 177, 165, 145, 191, 174, 255, 117, 100, 174, 159, 147, 99, 135 + ] + ); + + assert_eq!(grid.validity_mask.data.len(), 64); + // pixel mask is pixel > 0 from the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt + assert_eq!( + grid.validity_mask.data, + &[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, true, true, + false, false, false, false, false, true, true, true, false, false, false, true, + true, true, true, true, false, false, true, true, true, true, true, true, false, + false, true, true, true, true, true, true, false, true, true, true, true, true, + true, true + ] + ); + + assert_eq!( + properties.get_property(&RasterPropertiesKey { + key: "AREA_OR_POINT".to_string(), + domain: None, + }), + Some(&RasterPropertiesEntry::String("Area".to_string())) + ); + } + + #[test] + #[serial_test::serial] + fn test_load_tile_data_overlaps_dataset_bounds_top_left_out1() { + // shift world bbox one pixel up and to the left + let gdal_read_advice = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), [7, 7].into()), // this is the data we can read + read_window_bounds: GridBoundingBox2D::new([1, 1], [7, 7]).unwrap(), // this is the area we can fill in target + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), // this is the area of the target + flip_y: false, + }; + + let GridAndProperties { + grid, + properties: _properties, + } = load_ndvi_apr_2014_cropped(gdal_read_advice).unwrap(); + + assert!(!grid.is_empty()); + + let x = grid.into_materialized_masked_grid(); + + assert_eq!(x.inner_grid.data.len(), 49); + assert_eq!( + x.inner_grid.data, + &[ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 164, 185, 255, 255, 255, 175, + 186, 190, 167, 255, 255, 161, 175, 184, 173, 170, 255, 255, 128, 177, 165, 145, + 191, + ] + ); + + assert_eq!(x.validity_mask.data.len(), 49); + // pixel mask is pixel == 255 from the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt + assert_eq!( + x.validity_mask.data, + &[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, true, false, false, false, + false, false, true, true, false, false, false, true, true, true, true, false, + false, true, true, true, true, true, false, false, true, true, true, true, true, + ] + ); + } + + #[test] + #[serial_test::serial] + fn it_creates_no_data_only_for_missing_files() { + hide_gdal_errors(); + + let ds = GdalDatasetParameters { + file_path: "nonexisting_file.tif".into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (-180., 90.).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, + }, + width: 3600, + height: 1800, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: None, + properties_mapping: None, + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + }; + + let gdal_read_advice = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), + read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + flip_y: false, + }; + + let mut gdc = GdalDatasetHolder::new(); + + let result = + GdalHandling::load_tile_data_with_dataset_retry::(&mut gdc, &ds, gdal_read_advice); + + // file not found => specific error + match result { + Err(IpcProcessError::GdalError { kind, .. }) + if kind == IpcProcessGdalErrorKind::FileNotFound => {} + _ => panic!("expected FileNotFound error"), + } + + let ds = GdalDatasetParameters { + file_path: test_data!("raster/modis_ndvi/MOD13A2_M_NDVI_2014-01-01.TIFF").into(), + rasterband_channel: 100, // invalid channel + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (-180., 90.).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, + }, + width: 3600, + height: 1800, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: None, + properties_mapping: None, + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + }; + + // invalid channel => error + let result = + GdalHandling::load_tile_data_with_dataset_retry::(&mut gdc, &ds, gdal_read_advice); + match result { + Err(IpcProcessError::GdalError { .. }) => {} + _ => panic!("expected GdalError error"), + } + } + + #[test] + #[serial_test::serial] + fn it_creates_no_data_only_for_http_404() { + let server = Server::run(); + + server.expect( + Expectation::matching(request::method_path("HEAD", "/non_existing.tif")) + .times(1) + .respond_with(responders::cycle![responders::status_code(404),]), + ); + + server.expect( + Expectation::matching(request::method_path("HEAD", "/internal_error.tif")) + .times(1) + .respond_with(responders::cycle![responders::status_code(500),]), + ); + + let ds = GdalDatasetParameters { + file_path: format!("/vsicurl/{}", server.url_str("/non_existing.tif")).into(), + rasterband_channel: 1, + geo_transform: TestDefault::test_default(), + width: 100, + height: 100, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: None, + properties_mapping: None, + gdal_open_options: None, + gdal_config_options: Some(vec![ + ( + "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), + ".tif".to_owned(), + ), + ( + "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), + "EMPTY_DIR".to_owned(), + ), + ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), + ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), + ]), + allow_alphaband_as_mask: true, + retry: None, + }; + + // 404 => no data + let gdal_read_advice = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), + read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + flip_y: false, + }; + + let mut gdc = GdalDatasetHolder::new(); + + let result = + GdalHandling::load_tile_data_with_dataset_retry::(&mut gdc, &ds, gdal_read_advice); + + // file not found => specific error + match result { + Err(IpcProcessError::GdalError { kind, .. }) + if kind == IpcProcessGdalErrorKind::FileNotFound => {} + _ => panic!("expected FileNotFound error"), + } + + let ds = GdalDatasetParameters { + file_path: format!("/vsicurl/{}", server.url_str("/internal_error.tif")).into(), + rasterband_channel: 1, + geo_transform: TestDefault::test_default(), + width: 100, + height: 100, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: None, + properties_mapping: None, + gdal_open_options: None, + gdal_config_options: Some(vec![ + ( + "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), + ".tif".to_owned(), + ), + ( + "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), + "EMPTY_DIR".to_owned(), + ), + ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), + ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), + ]), + allow_alphaband_as_mask: true, + retry: None, + }; + + // 500 => error + let result = + GdalHandling::load_tile_data_with_dataset_retry::(&mut gdc, &ds, gdal_read_advice); + match result { + Err(IpcProcessError::GdalError { .. }) => {} + _ => panic!("expected GdalError error"), + } + } + + #[test] + #[serial_test::serial] + fn it_retries_only_after_clearing_vsi_cache() { + hide_gdal_errors(); + + let server = Server::run(); + + server.expect( + Expectation::matching(request::method_path("HEAD", "/foo.tif")) + .times(2) + .respond_with(responders::cycle![ + // first generic error + responders::status_code(500), + // then 404 file not found + responders::status_code(404) + ]), + ); + + let file_path: PathBuf = format!("/vsicurl/{}", server.url_str("/foo.tif")).into(); + + let options = Some(vec![ + ( + "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), + ".tif".to_owned(), + ), + ( + "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), + "EMPTY_DIR".to_owned(), + ), + ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), + ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), + ]); + + let _thread_local_configs = options + .as_ref() + .map(|config_options| GdalConfigOptions::new(config_options)); + + // first fail + let result = gdal_open_ex_gdal_error( + file_path.as_path(), + DatasetOptions { + open_flags: GdalOpenFlags::GDAL_OF_RASTER, + ..DatasetOptions::default() + }, + ); + + // it failed, but not with file not found + assert!(result.is_err()); + if let Err(error) = result { + assert!(!GdalHandling::error_is_gdal_file_not_found(&error)); + } + + // second fail doesn't even try, so still not "file not found", even though it should be now + let result = gdal_open_ex_gdal_error( + file_path.as_path(), + DatasetOptions { + open_flags: GdalOpenFlags::GDAL_OF_RASTER, + ..DatasetOptions::default() + }, + ); + + assert!(result.is_err()); + if let Err(error) = result { + assert!(!GdalHandling::error_is_gdal_file_not_found(&error)); + } + + GdalHandling::clear_gdal_vsi_cache_for_path(file_path.as_path()); + + // after clearing the cache, it tries again + let result = gdal_open_ex_gdal_error( + file_path.as_path(), + DatasetOptions { + open_flags: GdalOpenFlags::GDAL_OF_RASTER, + ..DatasetOptions::default() + }, + ); + + // now we get the file not found error + assert!(result.is_err()); + if let Err(error) = result { + assert!(GdalHandling::error_is_gdal_file_not_found(&error)); + } + } + + #[test] + #[serial_test::serial] + #[allow(clippy::too_many_lines)] + fn read_up_side_down_raster() { + let up_side_down_params = GdalDatasetParameters { + file_path: test_data!( + "raster/modis_ndvi/flipped_axis_y/MOD13A2_M_NDVI_2014-01-01_flipped_y.tiff" + ) + .into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (-180., -90.).into(), + x_pixel_size: 0.1, + y_pixel_size: 0.1, + }, + width: 3600, + height: 1800, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: Some(0.), + properties_mapping: Some(vec![ + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + }, + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE".to_string()), + key: "COMPRESSION".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE_INFO".to_string()), + key: "COMPRESSION".to_string(), + }, + }, + ]), + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + }; + + let ge_global_dataset_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., 90.), 0.1, -0.1), + GridBoundingBox2D::new_min_max(0, 1799, 0, 3599).unwrap(), + ); + + let gdal_dataset_grid = ge_global_dataset_grid.flip_axis_y(); // first, flip axis + assert_approx_eq!( + Coordinate2D, + gdal_dataset_grid.geo_transform().origin_coordinate, + Coordinate2D::new(-180., 90.) + ); + assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.y_pixel_size(), 0.1); + assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.x_pixel_size(), 0.1); + assert_eq!( + gdal_dataset_grid.grid_bounds, + GridBoundingBox2D::new_min_max(-1800, -1, 0, 3599).unwrap() + ); + + let gdal_dataset_grid = gdal_dataset_grid + .with_moved_origin_exact_grid(Coordinate2D::new(-180., -90.)) + .unwrap(); // second, move origin (to other side of axis) + assert_approx_eq!( + Coordinate2D, + gdal_dataset_grid.geo_transform().origin_coordinate, + Coordinate2D::new(-180., -90.) + ); + assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.y_pixel_size(), 0.1); + assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.x_pixel_size(), 0.1); + assert_eq!( + gdal_dataset_grid.grid_bounds, + GridBoundingBox2D::new_min_max(0, 1799, 0, 3599).unwrap() + ); + + let gdal_read_advice = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([1466, 1880].into(), [8, 8].into()), + read_window_bounds: GridBoundingBox2D::new([326, 1880], [326 + 7, 1880 + 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([326, 1880], [326 + 7, 1880 + 7]).unwrap(), + flip_y: true, + }; + + let mut gdc = GdalDatasetHolder::new(); + let dataset = gdc.get_or_open(&up_side_down_params).unwrap(); + + let reader_payload = + GdalHandling::load_tile_data::(dataset, &up_side_down_params, gdal_read_advice) + .unwrap(); + + let GridAndProperties { grid, properties } = reader_payload.into(); + assert!(!grid.is_empty()); + let grid = grid.into_materialized_masked_grid(); + + assert_eq!(grid.inner_grid.data.len(), 64); + + assert_eq!( + grid.inner_grid.data, + &[ + // this is not yet flipped! + 255, 47, 42, 82, 81, 76, 73, 98, 255, 255, 59, 95, 85, 66, 105, 104, 255, 255, 91, + 97, 100, 86, 78, 106, 255, 255, 255, 97, 102, 91, 73, 72, 255, 255, 255, 255, 255, + 68, 81, 93, 255, 255, 255, 255, 255, 255, 53, 47, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + ] + ); + + assert_eq!(grid.validity_mask.data.len(), 64); + assert_eq!(grid.validity_mask.data, &[true; 64]); + + assert!(properties.offset_option().is_none()); + assert!(properties.scale_option().is_none()); + } + + #[test] + #[serial_test::serial] + fn read_raster_and_offset_scale() { + let up_side_down_params = GdalDatasetParameters { + file_path: test_data!("raster/modis_ndvi/cropped/MOD13A2_M_NDVI_2014-04-01_30x30.tif") + .into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (8.0, 57.4).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, + }, + width: 30, + height: 30, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: Some(255.), + properties_mapping: None, + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, + }; + + let gdal_read_advice = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), + read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + flip_y: false, + }; + + let mut gdc = GdalDatasetHolder::new(); + let dataset = gdc.get_or_open(&up_side_down_params).unwrap(); + + let GridAndProperties { grid, properties } = + GdalHandling::load_tile_data::(dataset, &up_side_down_params, gdal_read_advice) + .unwrap() + .into(); + + assert!(!grid.is_empty()); + + let grid = grid.into_materialized_masked_grid(); + + assert_eq!(grid.inner_grid.data.len(), 64); + // pixel value are the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt + assert_eq!( + grid.inner_grid.data, + &[ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 127, 107, 255, 255, 255, 255, 255, 164, 185, 182, + 255, 255, 255, 175, 186, 190, 167, 140, 255, 255, 161, 175, 184, 173, 170, 188, + 255, 255, 128, 177, 165, 145, 191, 174, 255, 117, 100, 174, 159, 147, 99, 135 + ] + ); + + assert_eq!(grid.validity_mask.data.len(), 64); + // pixel mask is pixel > 0 from the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt + assert_eq!( + grid.validity_mask.data, + &[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, true, true, + false, false, false, false, false, true, true, true, false, false, false, true, + true, true, true, true, false, false, true, true, true, true, true, true, false, + false, true, true, true, true, true, true, false, true, true, true, true, true, + true, true + ] + ); + + assert_eq!(properties.offset_option(), Some(1.)); + assert_eq!(properties.scale_option(), Some(2.)); + + assert!(approx_eq!(f64, properties.offset(), 1.)); + assert!(approx_eq!(f64, properties.scale(), 2.)); + } +} diff --git a/geoengine/operators/src/source/gdal_in/process_pool.rs b/geoengine/operators/src/source/gdal_in/process_pool.rs new file mode 100644 index 0000000000..b959f70b30 --- /dev/null +++ b/geoengine/operators/src/source/gdal_in/process_pool.rs @@ -0,0 +1,940 @@ +use snafu::Snafu; +use std::collections::VecDeque; +use std::hash::{BuildHasherDefault, Hasher}; +use std::sync::Arc; +#[cfg(feature = "gdalpool_dedup_requests")] +use std::sync::Mutex; +use std::time::Instant; +use tokio::sync::{mpsc, oneshot}; +use tokio::time::Duration; + +use geoengine_datatypes::raster::{GridBoundingBox2D, GridBounds, Pixel}; +use ipc_channel::ipc::{IpcReceiver, IpcSender}; +use rustc_hash::FxHasher; + +#[cfg(feature = "gdalpool_dedup_requests")] +use crate::source::gdal_in::process_common::GdalIpcBytePayload; + +use super::{ + GdalProcessPoolAccess, + process_common::{GdalIpcPayload, IpcChannelMessage, IpcProcessError, IpcProcessRasterResult}, + process_impl::{ChildProcessGuard, spawn_ipc_server_process}, +}; + +// --- Core Structural Parameters & Tuning Constants --- + +/// Capacity for the broker incoming request multi-producer mpsc channel. +const BROKER_QUEUE_CAPACITY: usize = 8192; + +/// Maximum number of commands to read reactively from the mpsc channel per loop iteration. +/// Prevents an incoming flood of requests from starving the dispatch execution logic. +const BATCH_RECEIVE_LIMIT: usize = 256; + +/// Staggers process forks in milliseconds so the kernel handles initial +/// page-table allocations cleanly without thrashing. +const INITIAL_SPAWN_DELAY_MS: u64 = 15; + +/// Latency budget warning threshold in milliseconds. If an update cycle inside the broker loop +/// blocks the execution context past this point, a performance warning trace is issued. +const THROTTLING_THRESHOLD_MS: u64 = 2; +const THROTTLING_THRESHOLD_DURATION: Duration = Duration::from_millis(THROTTLING_THRESHOLD_MS); + +/// Cache Time-To-Live (TTL) threshold for affinity routing data tracking. +/// Set to 30 minutes (1800s) to prioritize heavy S3 / cloud storage network connection caches. +const CACHE_TTL_SECS: f64 = 1800.0; + +/// Baseline numeric floor used when initiating the maximum-score matrix search. +const SCORE_INITIAL_FLOOR: f64 = -1.0; + +/// Base affinity score bonus awarded when a worker already has the requested dataset open. +const SCORE_DATASET_MATCH: f64 = 10000.0; + +/// Additional score bonus awarded when both the dataset AND the specific raster band match. +const SCORE_BAND_MATCH: f64 = 1000.0; + +/// Additional score bonus when the exact spatial window requested matches the worker's last task. +const SCORE_EXACT_WINDOW_MATCH: f64 = 100.0; + +/// Maximum base bonus for nearby spatial windows to reward spatial locality. +const SCORE_NEARBY_WINDOW_MAX: f64 = 50.0; + +/// Default baseline score given to completely fresh workers. +const SCORE_FRESH_WORKER_DEFAULT: f64 = 0.5; + +/// High-affinity immediate dispatch cutoff threshold. If a candidate worker's affinity score +/// matches or exceeds this, we short-circuit the matrix evaluation instantly. +const IMMEDIATE_DISPATCH_THRESHOLD: f64 = SCORE_DATASET_MATCH + SCORE_BAND_MATCH; + +/// Max lookahead horizontal threshold for sweeps over unique datasets. +const MAX_ELIGIBLE_SCAN_DEPTH: usize = 16; + +type FastHashMap = std::collections::HashMap>; + +#[derive(Debug, Snafu, Clone)] +#[snafu(visibility(pub))] +pub enum GdalProcessPoolError { + IpcProcessError { + source: IpcProcessError, + }, + + #[snafu(display("IpcError caused by: {error_str}"))] + IpcError { + error_str: String, + }, + + WorkerPanic, +} + +impl From for GdalProcessPoolError { + fn from(source: IpcProcessError) -> Self { + GdalProcessPoolError::IpcProcessError { source } + } +} + +impl From for GdalProcessPoolError { + fn from(source: ipc_channel::IpcError) -> Self { + GdalProcessPoolError::IpcError { + error_str: source.to_string(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SchedulingStrategy { + FifoGreedy, + GlobalMatrix, +} + +struct WorkerJob { + request: IpcChannelMessage, + respond_to: oneshot::Sender>, + dataset_hash: u64, +} + +#[derive(Debug, Clone)] +struct WorkerAffinity { + dataset_hash: u64, + band: usize, + spatial_window: GridBoundingBox2D, + timestamp: Instant, +} + +impl WorkerAffinity { + /// Computes the affinity score, decaying the reward linearly over time. + /// `now` is passed down to bypass the expensive vDSO clock lookup bottleneck inside hot loops. + #[inline] + pub fn calculate_score( + &self, + dataset_hash: u64, + band: usize, + window: &GridBoundingBox2D, + now: Instant, + ) -> f64 { + let idle_duration = now.saturating_duration_since(self.timestamp).as_secs_f64(); + + if idle_duration > CACHE_TTL_SECS { + return 0.0; // Cache expired, connections likely closed or dead + } + + let decay = 1.0 - (idle_duration / CACHE_TTL_SECS); + let mut score = 0.0; + + if self.dataset_hash == dataset_hash { + score += SCORE_DATASET_MATCH * decay; + + if self.band == band { + score += SCORE_BAND_MATCH * decay; + + let dist = calculate_grid_distance(&self.spatial_window, window); + if dist == 0.0 { + score += SCORE_EXACT_WINDOW_MATCH * decay; + } else { + score += (SCORE_NEARBY_WINDOW_MAX / (1.0 + dist)) * decay; + } + } + } + + score + } +} + +struct WorkerProcess { + _id: usize, + job_tx: mpsc::UnboundedSender, + child_guard: ChildProcessGuard, + // A single, unified state representing what the GDAL process last worked on. + affinity: Option, +} + +impl WorkerProcess { + #[inline] + fn score_for_job( + &self, + dataset_hash: u64, + band: usize, + window: &GridBoundingBox2D, + now: Instant, + ) -> f64 { + match &self.affinity { + Some(affinity) => affinity.calculate_score(dataset_hash, band, window, now), + None => SCORE_FRESH_WORKER_DEFAULT, + } + } +} + +struct DatasetSlot { + active_count: usize, + queue: VecDeque, +} + +impl DatasetSlot { + #[inline] + pub fn clean_canceled_front(&mut self) { + while let Some(req) = self.queue.front() { + if req.respond_to.is_closed() { + self.queue.pop_front(); + } else { + break; + } + } + } + + #[inline] + pub fn peek_first_valid(&self) -> Option<&QueuedRequest> { + self.queue.iter().find(|req| !req.respond_to.is_closed()) + } +} + +struct QueuedRequest { + dataset_hash: u64, + request: IpcChannelMessage, + respond_to: oneshot::Sender>, +} + +enum BrokerCommand { + Read(Box), + ReturnWorker { + worker_id: usize, + dataset_hash: u64, + band: usize, + window: GridBoundingBox2D, + }, + WorkerDied { + worker_id: usize, + dataset_hash: u64, + }, + WorkerReplaced { + worker_id: usize, + child_guard: ChildProcessGuard, + job_tx: mpsc::UnboundedSender, + }, +} + +#[cfg(feature = "gdalpool_dedup_requests")] +type SharedResult = Option>>; + +pub struct GdalProcessPool { + broker_tx: mpsc::Sender, + + #[cfg(feature = "gdalpool_dedup_requests")] + in_flight_tiles: Mutex>>, +} + +impl GdalProcessPool { + const STRATEGY: SchedulingStrategy = SchedulingStrategy::GlobalMatrix; + + /// Initializes the GDAL process pool and spawns the broker loop in a dedicated Tokio task. + /// The broker is responsible for all scheduling decisions and routing of requests to worker processes. + /// The worker processes themselves are spawned in a blocking thread to avoid stalling the async runtime during fork and initialization. + /// The number of worker processes is determined by `max_total`, while `max_active_global` and `max_parallel_per_dataset` control the scheduling constraints for concurrent active requests. + /// Returns an `Arc` to the initialized `GdalProcessPool`, which can be cloned and shared across the application for submitting read requests. + /// # Parameters + /// - `max_total`: The total number of GDAL worker processes to spawn and maintain in the pool. + /// - `max_active_global`: The maximum number of active (in-flight) requests allowed across all datasets at any given time. + /// - `max_parallel_per_dataset`: The maximum number of active requests allowed concurrently for the same dataset, enforcing per-dataset concurrency limits. + /// # Panics + /// This function will panic if any of the worker processes fail to spawn successfully. + pub fn new_with_tokio_handle( + handle: &tokio::runtime::Handle, + number_of_processes: usize, + max_active_processes: usize, + max_dataset_processes: usize, + ) -> Arc { + let (broker_tx, broker_rx) = mpsc::channel(BROKER_QUEUE_CAPACITY); + let b_tx_clone = broker_tx.clone(); + + handle.spawn(async move { + let b_tx_clone_2 = b_tx_clone.clone(); + let workers = tokio::task::spawn_blocking(move || { + let mut w = Vec::with_capacity(number_of_processes); + for id in 0..number_of_processes { + let (child_guard, _join_handle, job_tx) = + Self::create_worker_with_id(id, b_tx_clone_2.clone()) + .expect("Failed to create worker"); + + w.push(WorkerProcess { + _id: id, + job_tx, + child_guard, + affinity: None, + }); + std::thread::sleep(Duration::from_millis(INITIAL_SPAWN_DELAY_MS)); + } + w + }) + .await + .expect("Failed to initialize static GDAL worker processes"); + + Self::broker_loop( + broker_rx, + workers, + max_active_processes, + max_dataset_processes, + b_tx_clone, + ) + .await; + }); + + Arc::new(Self { + broker_tx, + #[cfg(feature = "gdalpool_dedup_requests")] + in_flight_tiles: Mutex::new(FastHashMap::default()), + }) + } + + /// Initializes the GDAL process pool and spawns the broker loop in a dedicated Tokio task. + /// The broker is responsible for all scheduling decisions and routing of requests to worker processes. + /// The worker processes themselves are spawned in a blocking thread to avoid stalling the async runtime during fork and initialization. + /// The number of worker processes is determined by `max_total`, while `max_active_global` and `max_parallel_per_dataset` control the scheduling constraints for concurrent active requests. + /// Returns an `Arc` to the initialized `GdalProcessPool`, which can be cloned and shared across the application for submitting read requests. + /// + /// # Parameters + /// - `number_of_processes`: The total number of GDAL worker processes to spawn and maintain in the pool. + /// - `max_active_processes`: The maximum number of active (in-flight) requests allowed across all datasets at any given time. + /// - `max_dataset_processes`: The maximum number of active requests allowed concurrently for the same dataset, enforcing per-dataset concurrency limits. + /// + /// # Panics + /// This function will panic if any of the worker processes fail to spawn successfully. + /// It is designed to be called during application initialization, and assumes that the system has sufficient resources to spawn the specified number of worker processes. + /// + pub fn new( + number_of_processes: usize, + max_active_processes: usize, + max_dataset_processes: usize, + ) -> Arc { + let (broker_tx, broker_rx) = mpsc::channel(BROKER_QUEUE_CAPACITY); + let b_tx_clone = broker_tx.clone(); + + tokio::spawn(async move { + let b_tx_clone_2 = b_tx_clone.clone(); + let workers = tokio::task::spawn_blocking(move || { + let mut w = Vec::with_capacity(number_of_processes); + for id in 0..number_of_processes { + let (child_guard, _join_handle, job_tx) = + Self::create_worker_with_id(id, b_tx_clone_2.clone()) + .expect("Failed to create worker"); + + w.push(WorkerProcess { + _id: id, + job_tx, + child_guard, + affinity: None, + }); + std::thread::sleep(Duration::from_millis(INITIAL_SPAWN_DELAY_MS)); + } + w + }) + .await + .expect("Failed to initialize static GDAL worker processes"); + + Self::broker_loop( + broker_rx, + workers, + max_active_processes, + max_dataset_processes, + b_tx_clone, + ) + .await; + }); + + Arc::new(Self { + broker_tx, + #[cfg(feature = "gdalpool_dedup_requests")] + in_flight_tiles: Mutex::new(FastHashMap::default()), + }) + } + + fn create_worker_with_id( + worker_id: usize, + broker_tx: mpsc::Sender, + ) -> Result< + ( + ChildProcessGuard, + std::thread::JoinHandle<()>, + mpsc::UnboundedSender, + ), + GdalProcessPoolError, + > { + let (guard, tx, rx) = + spawn_ipc_server_process::()?; + + let (job_tx, mut job_rx) = mpsc::unbounded_channel(); + + let handle = std::thread::Builder::new() + .name(format!("gdal-worker-companion-{worker_id}")) + .spawn(move || { + Self::worker_companion_loop(worker_id, &tx, &rx, &mut job_rx, &broker_tx); + }) + .map_err(|_| GdalProcessPoolError::WorkerPanic)?; + + Ok((guard, handle, job_tx)) + } + + fn worker_companion_loop( + worker_id: usize, + sender: &IpcSender, + receiver: &IpcReceiver, + job_rx: &mut mpsc::UnboundedReceiver, + broker_tx: &mpsc::Sender, + ) { + while let Some(job) = job_rx.blocking_recv() { + let window = job.request.0.read_advise.read_window_bounds; + let band = job.request.0.dataset_params.rasterband_channel; + let dataset_hash = job.dataset_hash; + + let res = sender + .send(job.request) + .map_err(GdalProcessPoolError::from) + .and_then(|()| receiver.recv().map_err(GdalProcessPoolError::from)); + + let is_dead = res.is_err(); + let _ = job.respond_to.send(res); + + if is_dead { + let _ = broker_tx.blocking_send(BrokerCommand::WorkerDied { + worker_id, + dataset_hash, + }); + break; + } + let _ = broker_tx.blocking_send(BrokerCommand::ReturnWorker { + worker_id, + dataset_hash, + band, + window, + }); + } + } + + async fn broker_loop( + mut rx: mpsc::Receiver, + workers: Vec, + max_active_processes: usize, + max_dataset_processes: usize, + broker_tx: mpsc::Sender, + ) { + let mut state = BrokerState::new( + workers, + max_dataset_processes, + max_active_processes, + Self::STRATEGY, + ); + + while let Some(first_cmd) = rx.recv().await { + let start_tick = Instant::now(); + let mut batch = vec![first_cmd]; + + while let Ok(cmd) = rx.try_recv() { + batch.push(cmd); + if batch.len() >= BATCH_RECEIVE_LIMIT { + break; + } + } + + for cmd in batch { + match cmd { + BrokerCommand::Read(req) => { + state.enqueue_request(req); + } + BrokerCommand::ReturnWorker { + worker_id, + dataset_hash, + band, + window, + } => { + state.release_worker(worker_id, dataset_hash, band, window); + } + BrokerCommand::WorkerDied { + worker_id, + dataset_hash, + } => { + tracing::error!( + "GDAL Worker {} died unexpectedly! Initiating respawn...", + worker_id + ); + if let Some(slot) = state.dataset_registry.get_mut(&dataset_hash) { + slot.active_count = slot.active_count.saturating_sub(1); + } + state.global_active_count = state.global_active_count.saturating_sub(1); + + let b_tx = broker_tx.clone(); + tokio::task::spawn_blocking(move || { + std::thread::sleep(std::time::Duration::from_millis( + INITIAL_SPAWN_DELAY_MS, + )); + + match Self::create_worker_with_id(worker_id, b_tx.clone()) { + Ok((child_guard, _join_handle, job_tx)) => { + tracing::info!( + "Successfully respawned GDAL Worker {}", + worker_id + ); + let _ = b_tx.blocking_send(BrokerCommand::WorkerReplaced { + worker_id, + child_guard, + job_tx, + }); + } + Err(e) => { + // Prevent silent failures if the respawn itself crashes + tracing::error!( + "FATAL: Failed to respawn GDAL Worker {}: {:?}", + worker_id, + e + ); + } + } + }); + } + BrokerCommand::WorkerReplaced { + worker_id, + child_guard, + job_tx, + } => { + if let Some(worker) = state.workers.get_mut(worker_id) { + worker.child_guard = child_guard; + worker.job_tx = job_tx; + worker.affinity = None; + } + state.idle_workers.push(worker_id); + } + } + } + + state.try_dispatch(); + + let elapsed = start_tick.elapsed(); + if elapsed > THROTTLING_THRESHOLD_DURATION { + let total_pending: usize = + state.dataset_registry.values().map(|s| s.queue.len()).sum(); + tracing::warn!( + duration_ms = elapsed.as_secs_f64() * 1000.0, + queue_len = total_pending, + "GDAL broker loop iteration safety warning!" + ); + } + } + } +} + +// Encapsulates all state variables and synchronous routing logic inside the broker. +struct BrokerState { + idle_workers: Vec, + workers: Vec, + dataset_registry: FastHashMap, + active_datasets: VecDeque, + global_active_count: usize, + max_dataset_processes: usize, + max_active_processes: usize, + strategy: SchedulingStrategy, +} + +impl BrokerState { + pub fn new( + workers: Vec, + max_dataset_processes: usize, + max_active_processes: usize, + strategy: SchedulingStrategy, + ) -> Self { + let idle_workers = (0..workers.len()).collect(); + Self { + idle_workers, + workers, + dataset_registry: FastHashMap::default(), + active_datasets: VecDeque::new(), + global_active_count: 0, + max_dataset_processes, + max_active_processes, + strategy, + } + } + + #[inline] + // on BrokerCommand::Read + pub fn enqueue_request(&mut self, req: Box) { + if req.respond_to.is_closed() { + return; + } + let hash = req.dataset_hash; + let slot = self + .dataset_registry + .entry(hash) + .or_insert_with(|| DatasetSlot { + active_count: 0, + queue: VecDeque::new(), + }); + + if slot.queue.is_empty() { + self.active_datasets.push_back(hash); + } + slot.queue.push_back(*req); + } + + #[inline] + pub fn release_worker( + &mut self, + worker_id: usize, + dataset_hash: u64, + band: usize, + window: GridBoundingBox2D, + ) { + if let Some(slot) = self.dataset_registry.get_mut(&dataset_hash) { + slot.active_count = slot.active_count.saturating_sub(1); + } + self.global_active_count = self.global_active_count.saturating_sub(1); + + if let Some(worker) = self.workers.get_mut(worker_id) { + worker.affinity = Some(WorkerAffinity { + dataset_hash, + band, + spatial_window: window, + timestamp: Instant::now(), + }); + } + + self.idle_workers.push(worker_id); + } + + /// Single-pass scheduler execution pass. Inlines scheduling decisions to prevent matrix re-scanning. + #[allow(clippy::too_many_lines)] + #[inline] + pub fn try_dispatch(&mut self) { + while let Some(&front_hash) = self.active_datasets.front() { + let should_pop = match self.dataset_registry.get_mut(&front_hash) { + Some(slot) => { + slot.clean_canceled_front(); + slot.queue.is_empty() + } + None => true, + }; + if should_pop { + self.active_datasets.pop_front(); + } else { + break; + } + } + + // Call the clock precisely ONCE per batch processing window + let now = Instant::now(); + + while !self.idle_workers.is_empty() && !self.active_datasets.is_empty() { + if self.global_active_count >= self.max_active_processes { + break; + } + + let mut best_score = SCORE_INITIAL_FLOOR; + let mut best_w_matrix_idx = 0; + let mut best_dataset_hash = 0; + let mut best_dataset_active_idx = 0; + let mut datasets_scanned = 0; + + for (idx, &hash) in self.active_datasets.iter().enumerate() { + let slot = self + .dataset_registry + .get(&hash) + .expect("Active dataset missing from registry - invariant broken"); + if slot.active_count >= self.max_dataset_processes { + continue; + } + + let Some(req) = slot.peek_first_valid() else { + continue; + }; + + datasets_scanned += 1; + let window = &req.request.0.read_advise.read_window_bounds; + let band = req.request.0.dataset_params.rasterband_channel; + + let mut current_best_w_idx = 0; + let mut current_best_score = SCORE_INITIAL_FLOOR; + + for (w_idx, &w_id) in self.idle_workers.iter().enumerate() { + let w = self + .workers + .get(w_id) + .expect("Idle worker ID missing from registry - invariant broken"); + let score = w.score_for_job(hash, band, window, now); + + if score > current_best_score { + current_best_score = score; + current_best_w_idx = w_idx; + } + if score >= IMMEDIATE_DISPATCH_THRESHOLD { + break; + } + } + + if current_best_score > best_score { + best_score = current_best_score; + best_w_matrix_idx = current_best_w_idx; + best_dataset_hash = hash; + best_dataset_active_idx = idx; + } + + if best_score >= IMMEDIATE_DISPATCH_THRESHOLD { + break; + } + + if self.strategy == SchedulingStrategy::FifoGreedy && best_score >= 0.0 { + break; + } + + if datasets_scanned >= MAX_ELIGIBLE_SCAN_DEPTH { + break; + } + } + + if best_score == SCORE_INITIAL_FLOOR { + break; + } + + // Perform atomic dispatch operation + let worker_id = self.idle_workers.swap_remove(best_w_matrix_idx); + let worker = self + .workers + .get_mut(worker_id) + .expect("Selected worker ID missing from registry - invariant broken"); + let slot = self + .dataset_registry + .get_mut(&best_dataset_hash) + .expect("Selected dataset missing from registry - invariant broken"); + + slot.clean_canceled_front(); + + let req = slot + .queue + .pop_front() + .expect("Selected request missing from dataset slot - invariant broken"); + + self.global_active_count += 1; + slot.active_count += 1; + + let _ = worker.job_tx.send(WorkerJob { + request: req.request, + respond_to: req.respond_to, + dataset_hash: req.dataset_hash, + }); + + slot.clean_canceled_front(); + + // Maintain FIFO/Round-Robin dataset cycling fairness + let tracked_hash = self + .active_datasets + .remove(best_dataset_active_idx) + .expect("Best dataset hash missing from active queue - invariant broken"); + if !slot.queue.is_empty() { + self.active_datasets.push_back(tracked_hash); + } + } + } +} + +#[inline] +fn calculate_grid_distance(a: &GridBoundingBox2D, b: &GridBoundingBox2D) -> f64 { + let a_min = a.min_index(); + let b_min = b.min_index(); + let dy = (a_min.y() - b_min.y()).abs() as f64; + let dx = (a_min.x() - b_min.x()).abs() as f64; + dy.max(dx) +} + +#[derive(Clone)] +pub struct GdalPoolWorkerInstance { + pool: Arc, +} + +#[cfg(feature = "gdalpool_dedup_requests")] +struct LeaderCleanupGuard { + pool: std::sync::Arc, + tile_key: u64, +} + +#[cfg(feature = "gdalpool_dedup_requests")] +impl Drop for LeaderCleanupGuard { + fn drop(&mut self) { + // This is guaranteed to run whether the function succeeds, errors, + // panics, or is abruptly cancelled by Tokio. + if let Ok(mut lock) = self.pool.in_flight_tiles.lock() { + lock.remove(&self.tile_key); + } + } +} + +impl GdalPoolWorkerInstance { + pub fn new(pool: Arc) -> Self { + Self { pool } + } + + #[cfg(not(feature = "gdalpool_dedup_requests"))] + pub async fn read_data( + &self, + request: IpcChannelMessage, + ) -> Result, GdalProcessPoolError> { + let mut s = rustc_hash::FxHasher::default(); + request.0.dataset_params.partial_hash(&mut s); + let hash = s.finish(); + + let (tx, rx) = oneshot::channel(); + + self.pool + .broker_tx + .send(BrokerCommand::Read(Box::new(QueuedRequest { + dataset_hash: hash, + request, + respond_to: tx, + }))) + .await + .map_err(|_| GdalProcessPoolError::WorkerPanic)?; + + let res = rx + .await + .map_err(|_| GdalProcessPoolError::WorkerPanic)?? + .map_err(|e| GdalProcessPoolError::from(e)) + .inspect_err(|e| tracing::error!("Ipc response error: {e}"))?; + + let payload: GdalIpcPayload

= + (&res).try_into().map_err(|e: bytemuck::PodCastError| { + GdalProcessPoolError::IpcProcessError { + source: IpcProcessError::PocCastError { + error: e.to_string(), + }, + } + })?; + Ok(payload) + } + + #[cfg(feature = "gdalpool_dedup_requests")] + /// Read gdal tiles with leader & follower feature. + /// + /// # Panics + /// This function will panic if watch channel fails + pub async fn read_data( + &self, + request: IpcChannelMessage, + ) -> Result, GdalProcessPoolError> { + let mut s = rustc_hash::FxHasher::default(); + request.full_hash(&mut s); + let tile_key = s.finish(); + + let mut rx = { + let mut lock = self + .pool + .in_flight_tiles + .lock() + .map_err(|_| GdalProcessPoolError::WorkerPanic)?; + + // test if data is already read + if let Some(rx) = lock.get(&tile_key) { + tracing::trace!("Follower attached to existing fetch: {tile_key}"); + rx.clone() + } else { + tracing::trace!("Spawning background fetch task for: {tile_key}"); + let (tx, rx) = tokio::sync::watch::channel(None); + lock.insert(tile_key, rx.clone()); + + let pool_clone = self.pool.clone(); + let request_clone = request.clone(); + + // DETACHED LEADER: Guaranteed to run to completion even if the parent stream is suspended or dropped! + tokio::spawn(async move { + let _cleanup_guard = LeaderCleanupGuard { + pool: pool_clone.clone(), + tile_key, + }; + + let mut s_routing = rustc_hash::FxHasher::default(); + request_clone.0.dataset_params.partial_hash(&mut s_routing); + let routing_hash = s_routing.finish(); + + let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); + + let dispatched = pool_clone + .broker_tx + .send(BrokerCommand::Read(Box::new(QueuedRequest { + dataset_hash: routing_hash, + request: request_clone, + respond_to: oneshot_tx, + }))) + .await; + + let leader_res = if dispatched.is_ok() { + oneshot_rx + .await + .map_err(|_| GdalProcessPoolError::WorkerPanic) + .flatten() + .and_then(|ipc_res| ipc_res.map_err(GdalProcessPoolError::from)) + } else { + Err(GdalProcessPoolError::WorkerPanic) + }; + + // Broadcast to all waiting tasks. (Arc preserves perfect memory alignment) + let _ = tx.send(Some(std::sync::Arc::new(leader_res))); + }); + + rx + } + }; + + // ALL requests (including the one that spawned the background task) waits for the task. + while rx.borrow().is_none() { + if rx.changed().await.is_err() { + // Background task panicked before sending + return Err(GdalProcessPoolError::WorkerPanic); + } + } + + let shared_res = rx + .borrow() + .as_ref() + .expect("Watch channel must contain a value after changed() yields") + .clone(); + + let byte_payload_ref = + shared_res + .as_ref() + .as_ref() + .map_err(|e| GdalProcessPoolError::IpcProcessError { + source: IpcProcessError::IpcOther { msg: e.to_string() }, + })?; + + let payload: GdalIpcPayload

= + byte_payload_ref + .try_into() + .map_err(|e: bytemuck::PodCastError| { + tracing::error!("DEBUG: Failed to cast payload: {:?}", e); + GdalProcessPoolError::IpcProcessError { + source: IpcProcessError::PocCastError { + error: e.to_string(), + }, + } + })?; + + Ok(payload) + } +} + +impl GdalProcessPoolAccess for Arc { + fn get_gdal_pool(&self) -> &std::sync::Arc { + self + } +} diff --git a/geoengine/operators/src/source/gdal_source/db_types.rs b/geoengine/operators/src/source/gdal_source/db_types.rs index b0caa96d40..7a49e8f53f 100644 --- a/geoengine/operators/src/source/gdal_source/db_types.rs +++ b/geoengine/operators/src/source/gdal_source/db_types.rs @@ -1,101 +1,17 @@ -use super::{ - FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetaDataRegular, - GdalMetadataMapping, GdalMetadataNetCdfCf, GdalRetryOptions, GdalSourceTimePlaceholder, +use crate::{ + engine::RasterResultDescriptor, + error::Error, + source::{ + gdal_in::{GdalDatasetParameters, GdalSourceTimePlaceholder}, + gdal_source::loading_info::{GdalMetaDataRegular, GdalMetadataNetCdfCf}, + }, }; -use crate::{engine::RasterResultDescriptor, error::Error}; use geoengine_datatypes::{ delegate_from_to_sql, primitives::{CacheTtlSeconds, TimeInstance, TimeInterval, TimeStep}, - util::StringPair, }; use postgres_types::{FromSql, ToSql}; -#[derive(Debug, ToSql, FromSql)] -#[postgres(name = "GdalDatasetParameters")] -pub struct GdalDatasetParametersDbType { - pub file_path: String, - pub rasterband_channel: i64, - pub geo_transform: GdalDatasetGeoTransform, - pub width: i64, - pub height: i64, - pub file_not_found_handling: FileNotFoundHandling, - pub no_data_value: Option, - pub properties_mapping: Option>, - pub gdal_open_options: Option>, - pub gdal_config_options: Option>, - pub allow_alphaband_as_mask: bool, - pub retry: Option, -} - -impl From<&GdalDatasetParameters> for GdalDatasetParametersDbType { - fn from(other: &GdalDatasetParameters) -> Self { - Self { - file_path: other.file_path.to_string_lossy().to_string(), - rasterband_channel: other.rasterband_channel as i64, - geo_transform: other.geo_transform, - width: other.width as i64, - height: other.height as i64, - file_not_found_handling: other.file_not_found_handling, - no_data_value: other.no_data_value, - properties_mapping: other.properties_mapping.clone(), - gdal_open_options: other.gdal_open_options.clone(), - gdal_config_options: other - .gdal_config_options - .clone() - .map(|v| v.into_iter().map(Into::into).collect()), - allow_alphaband_as_mask: other.allow_alphaband_as_mask, - retry: other.retry, - } - } -} - -impl TryFrom for GdalDatasetParameters { - type Error = Error; - - fn try_from(other: GdalDatasetParametersDbType) -> Result { - Ok(Self { - file_path: other.file_path.into(), - rasterband_channel: other.rasterband_channel as usize, - geo_transform: other.geo_transform, - width: other.width as usize, - height: other.height as usize, - file_not_found_handling: other.file_not_found_handling, - no_data_value: other.no_data_value, - properties_mapping: other.properties_mapping, - gdal_open_options: other.gdal_open_options, - gdal_config_options: other - .gdal_config_options - .map(|v| v.into_iter().map(Into::into).collect()), - allow_alphaband_as_mask: other.allow_alphaband_as_mask, - retry: other.retry, - }) - } -} - -#[derive(Debug, FromSql, ToSql)] -#[postgres(name = "GdalRetryOptions")] -pub struct GdalRetryOptionsDbType { - pub max_retries: i64, -} - -impl From<&GdalRetryOptions> for GdalRetryOptionsDbType { - fn from(other: &GdalRetryOptions) -> Self { - Self { - max_retries: other.max_retries as i64, - } - } -} - -impl TryFrom for GdalRetryOptions { - type Error = Error; - - fn try_from(other: GdalRetryOptionsDbType) -> Result { - Ok(Self { - max_retries: other.max_retries as usize, - }) - } -} - #[derive(Debug, ToSql, FromSql, PartialEq)] pub struct TextGdalSourceTimePlaceholderKeyValue { pub key: String, @@ -194,7 +110,5 @@ impl TryFrom for GdalMetadataNetCdfCf { } } -delegate_from_to_sql!(GdalDatasetParameters, GdalDatasetParametersDbType); delegate_from_to_sql!(GdalMetadataNetCdfCf, GdalMetadataNetCdfCfDbType); delegate_from_to_sql!(GdalMetaDataRegular, GdalMetaDataRegularDbType); -delegate_from_to_sql!(GdalRetryOptions, GdalRetryOptionsDbType); diff --git a/geoengine/operators/src/source/gdal_source/error.rs b/geoengine/operators/src/source/gdal_source/error.rs index ca9411e619..e3bf97ff08 100644 --- a/geoengine/operators/src/source/gdal_source/error.rs +++ b/geoengine/operators/src/source/gdal_source/error.rs @@ -1,13 +1,66 @@ +use std::io; + use geoengine_datatypes::raster::{GridBoundingBox2D, RasterDataType}; +use ipc_channel::IpcError; use snafu::Snafu; +use crate::source::gdal_in::{GdalProcessPoolError, process_common::IpcProcessError}; + #[derive(Debug, Snafu)] #[snafu(visibility(pub(crate)))] #[snafu(context(suffix(false)))] // disables default `Snafu` suffix pub enum GdalSourceError { #[snafu(display("Unsupported raster type: {raster_type:?}"))] - UnsupportedRasterType { raster_type: RasterDataType }, + UnsupportedRasterType { + raster_type: RasterDataType, + }, #[snafu(display("Unsupported spatial query: {spatial_query:?}"))] - IncompatibleSpatialQuery { spatial_query: GridBoundingBox2D }, + IncompatibleSpatialQuery { + spatial_query: GridBoundingBox2D, + }, + + #[snafu(display("GdalProcess with an unknown error: {error}"))] + UnknownErrorHappenedWhileReading { + error: String, + }, + + #[snafu(display("Encountered a poisoned ProcessData Mutex"))] + ProcessLockPoisoned { + error: String, + }, + + #[snafu(display("Error while sending data through ipc_channel: {error}"))] + IpcSendError { + error: IpcError, + }, + + #[snafu(display("Error while receiving data through ipc_channel: {error}"))] + IpcReceiveError { + error: IpcError, + }, + + #[snafu(display("Error while setting up the GdalSource reading process: {reason}"))] + ProcessSetupFailed { + reason: io::Error, + }, + + #[snafu(display("Error in the GdalSource reading process: {source}"))] + IpcProcessError { + source: IpcProcessError, + }, + + PoolInitializationError, + + WorkerPanic, + + GdalProcessPoolError { + source: GdalProcessPoolError, + }, +} + +impl From for GdalSourceError { + fn from(source: GdalProcessPoolError) -> Self { + GdalSourceError::GdalProcessPoolError { source } + } } diff --git a/geoengine/operators/src/source/gdal_source/loading_info.rs b/geoengine/operators/src/source/gdal_source/loading_info.rs index 8ae2cf528f..5d11d210c5 100644 --- a/geoengine/operators/src/source/gdal_source/loading_info.rs +++ b/geoengine/operators/src/source/gdal_source/loading_info.rs @@ -1,4 +1,4 @@ -use super::{GdalDatasetParameters, GdalSourceTimePlaceholder}; +use super::super::gdal_in::{GdalDatasetParameters, GdalSourceTimePlaceholder}; use crate::{ engine::{MetaData, RasterResultDescriptor}, error::Error, @@ -665,7 +665,7 @@ mod tests { use crate::{ engine::{RasterBandDescriptors, SpatialGridDescriptor, TimeDescriptor}, - source::{FileNotFoundHandling, GdalDatasetGeoTransform, TimeReference}, + source::gdal_in::{FileNotFoundHandling, GdalDatasetGeoTransform, TimeReference}, }; use super::*; diff --git a/geoengine/operators/src/source/gdal_source/mod.rs b/geoengine/operators/src/source/gdal_source/mod.rs index 57f604ac4c..ad54fd57fa 100644 --- a/geoengine/operators/src/source/gdal_source/mod.rs +++ b/geoengine/operators/src/source/gdal_source/mod.rs @@ -1,78 +1,57 @@ +use super::gdal_in::{GdalDatasetParameters, GdalPoolWorkerInstance, GridAndProperties}; use crate::engine::{ CanonicOperatorName, MetaData, OperatorData, OperatorName, QueryProcessor, SpatialGridDescriptor, WorkflowOperatorPath, }; use crate::optimization::{OptimizableOperator, OptimizationError, SourcesMustNotUseOverviews}; -use crate::source::gdal_source::reader::ReaderState; -use crate::util::TemporaryGdalThreadLocalConfigOptions; -use crate::util::gdal::gdal_open_dataset_ex; -use crate::util::input::float_option_with_nan; -use crate::util::retry::retry; +use crate::source::gdal_source::reader::GdalPoolReader; use crate::{ engine::{ InitializedRasterOperator, RasterOperator, RasterQueryProcessor, RasterResultDescriptor, SourceOperator, TypedRasterQueryProcessor, }, - error::Error, util::Result, }; use async_trait::async_trait; pub use error::GdalSourceError; -use float_cmp::{ApproxEq, approx_eq}; -use futures::{Future, TryStreamExt}; +use futures::{Future, TryFutureExt, TryStreamExt}; use futures::{ Stream, stream::{self, BoxStream, StreamExt}, }; -use gdal::errors::GdalError; -use gdal::raster::{GdalType, RasterBand as GdalRasterBand}; -use gdal::{Dataset as GdalDataset, DatasetOptions, GdalOpenFlags, Metadata as GdalMetadata}; -use gdal_sys::VSICurlPartialClearCache; -use geoengine_datatypes::primitives::{SpatialResolution, find_next_best_overview_level}; +use gdal::raster::GdalType; +use geoengine_datatypes::raster::ChangeGridBounds; use geoengine_datatypes::{ dataset::NamedData, primitives::{ - BandSelection, CacheHint, Coordinate2D, DateTimeParseFormat, RasterQueryRectangle, - TimeInterval, TryIrregularTimeFillIterExt, TryRegularTimeFillIterExt, + BandSelection, CacheHint, RasterQueryRectangle, SpatialResolution, TimeInterval, + TryIrregularTimeFillIterExt, TryRegularTimeFillIterExt, find_next_best_overview_level, }, raster::{ - ChangeGridBounds, EmptyGrid, GeoTransform, Grid, GridBlit, GridBoundingBox2D, GridOrEmpty, - GridOrEmpty2D, GridShapeAccess, GridSize, MapElements, MaskedGrid, NoDataValueGrid, Pixel, - RasterDataType, RasterProperties, RasterPropertiesEntry, RasterPropertiesEntryType, - RasterPropertiesKey, RasterTile2D, SpatialGridDefinition, TileInformation, - TilingSpecification, TilingStrategy, + EmptyGrid, GeoTransform, GridBoundingBox2D, Pixel, RasterDataType, RasterProperties, + RasterTile2D, SpatialGridDefinition, TileInformation, TilingSpecification, TilingStrategy, }, - util::test::TestDefault, }; use itertools::Itertools; pub use loading_info::{ GdalLoadingInfo, GdalLoadingInfoTemporalSlice, GdalLoadingInfoTemporalSliceIterator, GdalMetaDataList, GdalMetaDataRegular, GdalMetaDataStatic, GdalMetadataNetCdfCf, }; -use num::FromPrimitive; -use num::integer::{div_ceil, div_floor}; -use postgres_types::{FromSql, ToSql}; -use reader::{ - GdalReadAdvise, GdalReadWindow, GdalReaderMode, GridAndProperties, OverviewReaderState, +use num::{ + FromPrimitive, + integer::{div_ceil, div_floor}, }; +use reader_mode::{GdalReaderMode, OverviewReaderState, ReaderState}; use serde::{Deserialize, Serialize}; -use snafu::{ResultExt, ensure}; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::ffi::CString; +use snafu::ensure; use std::marker::PhantomData; -use std::path::{Path, PathBuf}; -use std::time::Instant; use tracing::{debug, warn}; mod db_types; -mod error; -mod loading_info; +pub mod error; +pub mod loading_info; mod reader; - -static GDAL_RETRY_INITIAL_BACKOFF_MS: u64 = 1000; -static GDAL_RETRY_MAX_BACKOFF_MS: u64 = 60 * 60 * 1000; -static GDAL_RETRY_EXPONENTIAL_BACKOFF_FACTOR: f64 = 2.; +mod reader_mode; /// Parameters for the GDAL Source Operator #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -116,307 +95,81 @@ impl OperatorData for GdalSourceParameters { type GdalMetaData = Box>; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, FromSql, ToSql)] -#[serde(rename_all = "camelCase")] -pub struct GdalSourceTimePlaceholder { - pub format: DateTimeParseFormat, - pub reference: TimeReference, -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, FromSql, ToSql)] -#[serde(rename_all = "camelCase")] -pub enum TimeReference { - Start, - End, -} - -/// Parameters for loading data using Gdal -#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct GdalDatasetParameters { - pub file_path: PathBuf, - pub rasterband_channel: usize, - pub geo_transform: GdalDatasetGeoTransform, // TODO: discuss if we need this at all - pub width: usize, - pub height: usize, - pub file_not_found_handling: FileNotFoundHandling, - #[serde(default)] - #[serde(with = "float_option_with_nan")] - pub no_data_value: Option, - pub properties_mapping: Option>, - // Dataset open option as strings, e.g. `vec!["UserPwd=geoengine:pwd".to_owned(), "HttpAuth=BASIC".to_owned()]` - pub gdal_open_options: Option>, - // Configs as key, value pairs that will be set as thread local config options, e.g. - // `vec!["AWS_REGION".to_owned(), "eu-central-1".to_owned()]` and unset afterwards - // TODO: validate the config options: only allow specific keys and specific values - pub gdal_config_options: Option>, - #[serde(default)] - pub allow_alphaband_as_mask: bool, - pub retry: Option, -} - -impl GdalDatasetParameters { - pub fn dataset_bounds(&self) -> GridBoundingBox2D { - GridBoundingBox2D::new_unchecked( - [0, 0], - [self.height as isize - 1, self.width as isize - 1], - ) - } - - pub fn gdal_geo_transform(&self) -> GdalDatasetGeoTransform { - self.geo_transform - } - - /// Returns the `SpatialGridDefinition` of the Gdal dataset. - /// - /// Note: This allows upside down datasets (where `GeoTransform` `y_pixel_size` is positive)! - /// - /// # Panics - /// Panics if the `GdalDatasetParameters` are faulty. - pub fn spatial_grid_definition(&self) -> SpatialGridDefinition { - let gdal_geo_transform = GeoTransform::new( - self.gdal_geo_transform().origin_coordinate, - self.gdal_geo_transform().x_pixel_size, - self.gdal_geo_transform().y_pixel_size, - ); - - SpatialGridDefinition::new(gdal_geo_transform, self.dataset_bounds()) - } -} - -#[derive(PartialEq, Serialize, Deserialize, Debug, Clone, Copy)] -#[serde(rename_all = "camelCase")] -pub struct GdalRetryOptions { - pub max_retries: usize, -} - -/// A user friendly representation of Gdal's geo transform. In contrast to [`GeoTransform`] this -/// geo transform allows arbitrary pixel sizes and can thus also represent rasters where the origin is not located -/// in the upper left corner. It should only be used for loading rasters with Gdal and not internally. -/// The GDAL pixel space is usually anchored at the "top-left" corner of the data spatial bounds. Therefore the raster data is stored with spatial coordinate y-values decreasing with the rasters rows. This is represented by a negative pixel size. -/// However, there are datasets where the data is stored "upside-down". If this is the case, the pixel size is positive. -#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, FromSql, ToSql)] -#[serde(rename_all = "camelCase")] -pub struct GdalDatasetGeoTransform { - pub origin_coordinate: Coordinate2D, - pub x_pixel_size: f64, - pub y_pixel_size: f64, -} - -/// Default implementation for testing purposes where geo transform doesn't matter -impl TestDefault for GdalDatasetGeoTransform { - fn test_default() -> Self { - Self { - origin_coordinate: (0.0, 0.0).into(), - x_pixel_size: 1.0, - y_pixel_size: -1.0, - } - } -} - -impl ApproxEq for GdalDatasetGeoTransform { - type Margin = float_cmp::F64Margin; - - fn approx_eq(self, other: Self, margin: M) -> bool - where - M: Into, - { - let m = margin.into(); - self.origin_coordinate.approx_eq(other.origin_coordinate, m) - && self.x_pixel_size.approx_eq(other.x_pixel_size, m) - && self.y_pixel_size.approx_eq(other.y_pixel_size, m) - } -} +pub struct GdalRasterLoader; -/// Direct conversion from `GdalDatasetGeoTransform` to [`GeoTransform`] only works if origin is located in the upper left corner. -impl TryFrom for GeoTransform { - type Error = Error; - - fn try_from(dataset_geo_transform: GdalDatasetGeoTransform) -> Result { - ensure!( - dataset_geo_transform.x_pixel_size != 0.0 && dataset_geo_transform.y_pixel_size != 0.0, - crate::error::GeoTransformOrigin // TODO new name? +impl GdalRasterLoader { + pub async fn load_tile_grid_props( + dataset_params: GdalDatasetParameters, + reader_mode: GdalReaderMode, + tile_information: TileInformation, + gdal_worker: GdalPoolWorkerInstance, + ) -> Result>, GdalSourceError> { + tracing::trace!( + "Loading tile {:?}, from {}, band: {}", + &tile_information, + dataset_params.file_path.display(), + dataset_params.rasterband_channel ); - Ok(GeoTransform::new( - dataset_geo_transform.origin_coordinate, - dataset_geo_transform.x_pixel_size, - dataset_geo_transform.y_pixel_size, - )) - } -} - -impl From for GdalDatasetGeoTransform { - fn from(gdal_geo_transform: gdal::GeoTransform) -> Self { - Self { - origin_coordinate: (gdal_geo_transform[0], gdal_geo_transform[3]).into(), - x_pixel_size: gdal_geo_transform[1], - y_pixel_size: gdal_geo_transform[5], - } - } -} - -impl GridShapeAccess for GdalDatasetParameters { - type ShapeArray = [usize; 2]; - - fn grid_shape_array(&self) -> Self::ShapeArray { - [self.height, self.width] - } -} - -/// How to handle file not found errors -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, FromSql, ToSql)] -pub enum FileNotFoundHandling { - NoData, // output tiles filled with nodata - Error, // return error tile -} - -impl GdalDatasetParameters { - /// Placeholders are replaced by formatted time value. - /// E.g. `%my_placeholder%` could be replaced by `2014-04-01` depending on the format and time input. - pub fn replace_time_placeholders( - &self, - placeholders: &HashMap, - time: TimeInterval, - ) -> Result { - let mut file_path: String = self.file_path.to_string_lossy().into(); - - for (placeholder, time_placeholder) in placeholders { - let time = match time_placeholder.reference { - TimeReference::Start => time.start(), - TimeReference::End => time.end(), - }; - let time_string = time - .as_date_time() - .ok_or(Error::TimeInstanceNotDisplayable)? - .format(&time_placeholder.format); - - // TODO: use more efficient algorithm for replacing multiple placeholders, e.g. aho-corasick - file_path = file_path.replace(placeholder, &time_string); - } - - Ok(Self { - file_not_found_handling: self.file_not_found_handling, - file_path: file_path.into(), - properties_mapping: self.properties_mapping.clone(), - gdal_open_options: self.gdal_open_options.clone(), - gdal_config_options: self.gdal_config_options.clone(), - ..*self - }) - } -} - -pub struct GdalSourceProcessor -where - T: Pixel, -{ - pub produced_result_descriptor: RasterResultDescriptor, - pub tiling_specification: TilingSpecification, - pub meta_data: GdalMetaData, - pub overview_level: u32, - pub original_resolution_spatial_grid: Option, - pub _phantom_data: PhantomData, -} - -struct GdalRasterLoader {} + let ds_spatial_grid = dataset_params.spatial_grid_definition(); + let tile_spatial_grid = tile_information.spatial_grid_definition(); + tracing::trace!( + "ds_spatial_grid: {:?}, tile_spatial_grid {:?}", + ds_spatial_grid, + tile_spatial_grid + ); + let gdal_read_advise = + reader_mode.tiling_to_dataset_read_advise(&ds_spatial_grid, &tile_spatial_grid); + + let Some(gdal_read_advise) = gdal_read_advise else { + tracing::trace!( + "Tile {:?} not intersecting dataset grid or gdal grid {:?}", + &tile_information, + dataset_params.file_path + ); + return Ok(None); + }; -impl GdalRasterLoader { - /// - /// A method to async load single tiles from a GDAL dataset. - /// - async fn load_tile_data_async( - dataset_params: GdalDatasetParameters, - read_advise: GdalReadAdvise, - ) -> Result>> { - // TODO: detect usage of vsi curl properly, e.g. also check for `/vsicurl_streaming` and combinations with `/vsizip` - let is_vsi_curl = dataset_params.file_path.starts_with("/vsicurl/"); - - retry( - dataset_params - .retry - .map(|r| r.max_retries) - .unwrap_or_default(), - GDAL_RETRY_INITIAL_BACKOFF_MS, - GDAL_RETRY_EXPONENTIAL_BACKOFF_FACTOR, - Some(GDAL_RETRY_MAX_BACKOFF_MS), - move || { - let ds = dataset_params.clone(); - let file_path = ds.file_path.clone(); - - async move { - let load_tile_result = - crate::util::spawn_blocking(move || Self::load_tile_data(&ds, read_advise)) - .await - .context(crate::error::TokioJoin); - - match load_tile_result { - Ok(Ok(r)) => Ok(r), - Ok(Err(e)) | Err(e) => { - if is_vsi_curl { - // clear the VSICurl cache, to force GDAL to try to re-download the file - // otherwise it will assume any observed error will happen again - clear_gdal_vsi_cache_for_path(file_path.as_path()); - } + let res = GdalPoolReader::from(gdal_worker) + .load_tile_data_process(dataset_params, gdal_read_advise) + .await?; - Err(e) - } - } - } - }, - ) - .await + Ok(Some(res)) } + /// Load a single tile using the process manager. async fn load_tile_async( dataset_params: Option, reader_mode: GdalReaderMode, tile_information: TileInformation, tile_time: TimeInterval, cache_hint: CacheHint, - ) -> Result> { - let tile_spatial_grid = tile_information.spatial_grid_definition(); - + gdal_worker: GdalPoolWorkerInstance, + ) -> Result, GdalSourceError> { match dataset_params { // TODO: discuss if we need this check here. The metadata provider should only pass on loading infos if the query intersects the datasets bounds! And the tiling strategy should only generate tiles that intersect the querys bbox. Some(ds) => { - debug!( - "Loading tile {:?}, from {}, band: {}", - &tile_information, - ds.file_path.display(), - ds.rasterband_channel - ); - let ds_spatial_grid = ds.spatial_grid_definition(); - tracing::trace!( - "ds_spatial_grid: {:?}, tile_spatial_grid {:?}", - &ds_spatial_grid, - &tile_spatial_grid - ); - - let gdal_read_advise: Option = - reader_mode.tiling_to_dataset_read_advise(&ds_spatial_grid, &tile_spatial_grid); - - let Some(gdal_read_advise) = gdal_read_advise else { - debug!( - "Tile {:?} not intersecting dataset grid or gdal grid {:?}", - &tile_information, ds.file_path - ); - return Ok(create_no_data_tile(tile_information, tile_time, cache_hint)); - }; - - let grid = Self::load_tile_data_async(ds, gdal_read_advise).await?; - - match grid { - Some(grid) => Ok(RasterTile2D::new_with_properties( + let grid_and_props = + Self::load_tile_grid_props(ds, reader_mode, tile_information, gdal_worker) + .await?; + let tile = match grid_and_props { + Some(gp) => RasterTile2D::new_with_tile_info_and_properties( tile_time, - tile_information.global_tile_position, + tile_information, 0, - tile_information.global_geo_transform, - grid.grid, - grid.properties, + gp.grid.unbounded(), + gp.properties, cache_hint, - )), - None => Ok(create_no_data_tile(tile_information, tile_time, cache_hint)), - } + ), + None => { + debug!( + "Tile {:?} not intersecting dataset grid or gdal grid", + &tile_information.global_tile_position + ); + Self::create_no_data_tile(tile_information, tile_time, cache_hint) + } + }; + Ok(tile) } _ => { debug!( @@ -424,193 +177,102 @@ impl GdalRasterLoader { &tile_information ); - Ok(create_no_data_tile(tile_information, tile_time, cache_hint)) + Ok(Self::create_no_data_tile( + tile_information, + tile_time, + cache_hint, + )) } } } - /// - /// A method to load single tiles from a GDAL dataset. - /// - fn load_tile_data( - dataset_params: &GdalDatasetParameters, - read_advise: GdalReadAdvise, - ) -> Result>> { - let start = Instant::now(); - - debug!( - "GridOrEmpty2D<{:?}> requested for {:?}.", - T::TYPE, - &read_advise.bounds_of_target, - ); - - let options = dataset_params - .gdal_open_options - .as_ref() - .map(|o| o.iter().map(String::as_str).collect::>()); - - // reverts the thread local configs on drop - let _thread_local_configs = dataset_params - .gdal_config_options - .as_ref() - .map(|config_options| TemporaryGdalThreadLocalConfigOptions::new(config_options)); - - let dataset_result = gdal_open_dataset_ex( - &dataset_params.file_path, - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - open_options: options.as_deref(), - ..DatasetOptions::default() - }, - ); - - if let Err(error) = &dataset_result { - let is_file_not_found = error_is_gdal_file_not_found(error); - - let err_result = match dataset_params.file_not_found_handling { - FileNotFoundHandling::NoData if is_file_not_found => Ok(None), - _ => Err(crate::error::Error::CouldNotOpenGdalDataset { - file_path: dataset_params.file_path.to_string_lossy().to_string(), - }), - }; - let elapsed = start.elapsed(); - debug!( - "error opening dataset: {:?} -> returning error = {}, took: {:?}, file: {}", - error, - err_result.is_err(), - elapsed, - dataset_params.file_path.display() - ); - return err_result; - } - - let dataset = dataset_result.expect("checked"); - - let rasterband = dataset.rasterband(dataset_params.rasterband_channel)?; - - let gdal_dataset_geotransform = GdalDatasetGeoTransform::from(dataset.geo_transform()?); - // check that the dataset geo transform is the same as the one we get from GDAL - debug_assert!( - approx_eq!( - Coordinate2D, - gdal_dataset_geotransform.origin_coordinate, - dataset_params.geo_transform.origin_coordinate - ), - "expected dataset geo transform origin coordinate {:?} to be approximately equal to GDAL dataset geo transform origin coordinate {:?}", - dataset_params.geo_transform.origin_coordinate, - gdal_dataset_geotransform.origin_coordinate - ); - - debug_assert!( - approx_eq!( - f64, - gdal_dataset_geotransform.x_pixel_size, - dataset_params.geo_transform.x_pixel_size - ), - "expected dataset geo transform x pixel size {:?} to be approximately equal to GDAL dataset geo transform x pixel size {:?}", - dataset_params.geo_transform.x_pixel_size, - gdal_dataset_geotransform.x_pixel_size - ); - - debug_assert!( - approx_eq!( - f64, - gdal_dataset_geotransform.y_pixel_size, - dataset_params.geo_transform.y_pixel_size - ), - "expected dataset geo transform y pixel size {:?} to be approximately equal to GDAL dataset geo transform y pixel size {:?}", - dataset_params.geo_transform.y_pixel_size, - gdal_dataset_geotransform.y_pixel_size - ); - - let (gdal_dataset_pixels_x, gdal_dataset_pixels_y) = dataset.raster_size(); - // check that the dataset pixel size is the same as the one we get from GDAL - debug_assert_eq!(gdal_dataset_pixels_x, dataset_params.width); - debug_assert_eq!(gdal_dataset_pixels_y, dataset_params.height); - - let result_grid = - read_grid_and_handle_edges(&dataset, &rasterband, dataset_params, read_advise)?; - - let properties = read_raster_properties(&dataset, dataset_params, &rasterband); - - let elapsed = start.elapsed(); - debug!("data loaded -> returning data grid, took {elapsed:?}"); - - Ok(Some(GridAndProperties { - grid: result_grid, - properties, - })) - } - - /// - /// A stream of futures producing `RasterTile2D` for a single slice in time - /// - fn temporal_slice_tile_future_stream( - spatial_query: GridBoundingBox2D, - info: GdalLoadingInfoTemporalSlice, - tiling_strategy: TilingStrategy, - reader_mode: GdalReaderMode, - ) -> impl Stream>>> + use { - stream::iter( - tiling_strategy - .tile_information_iterator_from_pixel_bounds(spatial_query) - .map(move |tile| { - GdalRasterLoader::load_tile_async( - info.params.clone(), - reader_mode, - tile, - info.time, - info.cache_ttl.into(), - ) - }), + fn create_no_data_tile( + tile_info: TileInformation, + tile_time: TimeInterval, + cache_hint: CacheHint, + ) -> RasterTile2D { + RasterTile2D::new_with_tile_info_and_properties( + tile_time, + tile_info, + 0, + EmptyGrid::new(tile_info.tile_size_in_pixels).into(), + RasterProperties::default(), + cache_hint, ) } - - fn loading_info_to_tile_stream< - T: Pixel + GdalType + FromPrimitive, - S: Stream>, - >( - loading_info_stream: S, - spatial_query: GridBoundingBox2D, - tiling_strategy: TilingStrategy, - reader_mode: GdalReaderMode, - ) -> impl Stream>> + use { - loading_info_stream - .map_ok(move |info| { - GdalRasterLoader::temporal_slice_tile_future_stream( - spatial_query, - info, - tiling_strategy, - reader_mode, - ) - .map(Result::Ok) - }) - .try_flatten() - .try_buffered(16) // TODO: make this configurable - } } -fn error_is_gdal_file_not_found(error: &Error) -> bool { - matches!( - error, - Error::Gdal { - source: GdalError::NullPointer { - method_name, - msg - }, - } if *method_name == "GDALOpenEx" && (*msg == "HTTP response code: 404" || msg.ends_with("No such file or directory")) +// This is where the source attaches! +/// A stream of futures producing `RasterTile2D` for a single slice in time +fn temporal_slice_tile_future_stream( + spatial_bounds: GridBoundingBox2D, + info: GdalLoadingInfoTemporalSlice, + tiling_strategy: TilingStrategy, + reader_mode: GdalReaderMode, + gdal_worker: GdalPoolWorkerInstance, +) -> impl Stream>>> + use { + stream::iter(tiling_strategy.tile_information_iterator_from_pixel_bounds(spatial_bounds)).map( + move |tile| { + GdalRasterLoader::load_tile_async( + info.params.clone(), + reader_mode, + tile, + info.time, + info.cache_ttl.into(), + gdal_worker.clone(), + ) + .map_err(Into::into) + }, ) } -fn clear_gdal_vsi_cache_for_path(file_path: &Path) { - unsafe { - if let Some(Some(c_string)) = file_path.to_str().map(|s| CString::new(s).ok()) { - VSICurlPartialClearCache(c_string.as_ptr()); +pub struct GdalSourceProcessor +where + T: Pixel, +{ + pub produced_result_descriptor: RasterResultDescriptor, + pub tiling_specification: TilingSpecification, + pub meta_data: GdalMetaData, + pub overview_level: u32, + pub original_resolution_spatial_grid: Option, + pub _phantom_data: PhantomData, +} + +impl GdalSourceProcessor +where + T: gdal::raster::GdalType + Pixel, +{ + pub fn new( + produced_result_descriptor: RasterResultDescriptor, + tiling_specification: TilingSpecification, + meta_data: GdalMetaData, + overview_level: u32, + original_resolution_spatial_grid: Option, + ) -> Self { + Self { + produced_result_descriptor, + tiling_specification, + meta_data, + overview_level, + original_resolution_spatial_grid, + _phantom_data: PhantomData, } } -} -impl GdalSourceProcessor where T: gdal::raster::GdalType + Pixel {} + pub fn new_no_overview( + produced_result_descriptor: RasterResultDescriptor, + tiling_specification: TilingSpecification, + meta_data: GdalMetaData, + ) -> Self { + Self::new( + produced_result_descriptor, + tiling_specification, + meta_data, + 0, + None, + ) + } +} #[async_trait] impl

QueryProcessor for GdalSourceProcessor

@@ -625,13 +287,13 @@ where async fn _query<'a>( &'a self, query: RasterQueryRectangle, - _ctx: &'a dyn crate::engine::QueryContext, + ctx: &'a dyn crate::engine::QueryContext, ) -> Result>> { ensure!( query.attributes().as_slice() == [0], crate::error::GdalSourceDoesNotSupportQueryingOtherBandsThanTheFirstOneYet ); - tracing::debug!( + tracing::trace!( "Querying GdalSourceProcessor<{:?}> with: {:?}.", P::TYPE, &query @@ -672,18 +334,15 @@ where ), }; - tracing::debug!( - "Creating reader mode: {:?}. Original grid: {:?} and target grid: {:?}", - reader_mode, - self.original_resolution_spatial_grid, - produced_tiling_grid - ); - let loading_info = self.meta_data.loading_info(query.clone()).await?; - debug!( - "GdalSource _query loading info: start_time_of_output_stream: {:?}, end_time_of_output_stream: {:?}", - loading_info.start_time_of_output_stream, loading_info.end_time_of_output_stream + tracing::trace!( + "Reader mode: {:?}. Original grid: {:?} and target grid: {:?}. Loadinginfo start_time_of_output_stream: {:?}, end_time_of_output_stream: {:?}", + reader_mode, + self.original_resolution_spatial_grid, + produced_tiling_grid, + loading_info.start_time_of_output_stream, + loading_info.end_time_of_output_stream ); debug_assert!( @@ -719,23 +378,26 @@ where } }; - let source_stream = GdalRasterLoader::loading_info_to_tile_stream( - filled_loading_info_stream - .inspect_ok(move |r| { - debug!("GdalSource _query now producing time slice: {:?}", r.time); - if !r.time.intersects(&query_time) { - warn!( - "GdalSource _query producing time slice that does not intersect query time: {:?} vs {:?}", - r.time, query_time - ); - } - }), + let stream = filled_loading_info_stream + .inspect_ok(move |r| { + tracing::trace!("GdalSource _query now producing time slice: {:?}", r.time); + if !r.time.intersects(&query_time) { + warn!( + "GdalSource _query producing time slice that does not intersect query time: {:?} vs {:?}", + r.time, query_time + ); + } + }); + + let loaded_source_stream = load_source_stream::( + stream, query.spatial_bounds(), tiling_strategy, reader_mode, + ctx.get_gdal_worker(), ); - Ok(source_stream.boxed()) + Ok(loaded_source_stream.boxed()) } fn result_descriptor(&self) -> &RasterResultDescriptor { @@ -791,7 +453,9 @@ where }; Ok(res_stream - .inspect_ok(|ti| debug!("GdalSource time_query producing time interval: {:?}", ti)) + .inspect_ok(|ti| { + tracing::trace!("GdalSource time_query producing time interval: {:?}", ti); + }) .boxed()) } } @@ -802,12 +466,38 @@ impl OperatorName for GdalSource { const TYPE_NAME: &'static str = "GdalSource"; } +fn load_source_stream( + source_stream: S, + spatial_query: GridBoundingBox2D, + tiling_strategy: TilingStrategy, + reader_mode: GdalReaderMode, + gdal_worker: GdalPoolWorkerInstance, +) -> impl Stream>> + use +where + P: Pixel + GdalType + FromPrimitive, + S: Stream>, +{ + source_stream + .map_ok(move |info| { + temporal_slice_tile_future_stream( + spatial_query, + info, + tiling_strategy, + reader_mode, + gdal_worker.clone(), + ) + .map(Result::Ok) + }) + .try_flatten() + .try_buffered(8) // TODO: make this configurable +} + fn overview_level_spatial_grid( source_spatial_grid: SpatialGridDefinition, overview_level: u32, ) -> Option { if overview_level > 0 { - debug!("Using overview level {overview_level}"); + tracing::trace!("Using overview level {overview_level}"); let geo_transform = GeoTransform::new( source_spatial_grid.geo_transform.origin_coordinate, source_spatial_grid.geo_transform.x_pixel_size() * f64::from(overview_level), @@ -835,7 +525,7 @@ fn overview_level_spatial_grid( Some(SpatialGridDefinition::new(geo_transform, grid_bounds)) } else { - debug!("Using original resolution (ov = 0)"); + tracing::trace!("Using original resolution (ov = 0)"); None } } @@ -851,8 +541,11 @@ impl RasterOperator for GdalSource { let data_id = context.resolve_named_data(&self.params.data).await?; let meta_data: GdalMetaData = context.meta_data(&data_id).await?; - debug!("Initializing GdalSource for {:?}.", &self.params.data); - debug!("GdalSource path: {:?}", path); + tracing::trace!( + "Initializing GdalSource for {:?}. GdalSource path: {:?}", + &self.params.data, + path + ); let meta_data_result_descriptor = meta_data.result_descriptor().await?; @@ -919,6 +612,12 @@ impl InitializedGdalSourceOperator { } } + /// Initializes the operator with a result descriptor that incorporates the overview level. The original resolution spatial grid is stored in the operator for later use in the query processor when reading the data. + /// If the overview level is 0, this method behaves the same as `initialize_original_resolution`. + /// + /// # Panics + /// Panics if the result descriptor does not contain a source spatial grid definition or if the overview level is greater than 0 but the overview level spatial grid could not be created. + /// The latter can only happen if the overview level is greater than 0 but the source spatial grid is smaller than the overview level, which should not happen in practice. pub fn initialize_with_overview_level( name: CanonicOperatorName, path: WorkflowOperatorPath, @@ -963,6 +662,7 @@ impl InitializedRasterOperator for InitializedGdalSourceOperator { &self.produced_result_descriptor } + #[allow(clippy::too_many_lines)] fn query_processor(&self) -> Result { Ok(match self.result_descriptor().data_type { RasterDataType::U8 => TypedRasterQueryProcessor::U8( @@ -1114,345 +814,67 @@ impl InitializedRasterOperator for InitializedGdalSourceOperator { } } -/// This method reads the data for a single grid with a specified size from the GDAL dataset. -/// It fails if the tile is not within the dataset. -#[allow(clippy::float_cmp)] -fn read_grid_from_raster( - rasterband: &GdalRasterBand, - read_window: &GdalReadWindow, - out_shape: D, - dataset_params: &GdalDatasetParameters, - flip_y_axis: bool, -) -> Result> -where - T: Pixel + GdalType + Default + FromPrimitive, - D: Clone + GridSize + PartialEq, -{ - let gdal_out_shape = (out_shape.axis_size_x(), out_shape.axis_size_y()); - - let buffer = rasterband.read_as::( - read_window.gdal_window_start(), // pixelspace origin - read_window.gdal_window_size(), // pixelspace size - gdal_out_shape, // requested raster size - None, // sampling mode - )?; - let (_, buffer_data) = buffer.into_shape_and_vec(); - let data_grid = Grid::new(out_shape.clone(), buffer_data)?; - - let data_grid = if flip_y_axis { - data_grid.reversed_y_axis_grid() - } else { - data_grid +#[cfg(test)] +mod tests { + use super::*; + use crate::engine::{MockExecutionContext, MockQueryContext}; + use crate::source::gdal_in::{ + FileNotFoundHandling, GdalDatasetGeoTransform, GdalMetadataMapping, GdalProcessPool, + GdalProcessPoolAccess, GdalSourceTimePlaceholder, TimeReference, + }; + use crate::util::{Result, gdal::add_ndvi_dataset}; + use geoengine_datatypes::primitives::AxisAlignedRectangle; + use geoengine_datatypes::{ + hashmap, + primitives::{DateTimeParseFormat, SpatialPartition2D, TimeInstance}, + raster::{ + BoundedGrid, EmptyGrid2D, GridBounds, GridIdx2D, GridShape2D, GridSize, + RasterPropertiesEntryType, RasterPropertiesKey, SpatialGridDefinition, TileInformation, + TilesEqualIgnoringCacheHint, TilingStrategy, + }, + util::{gdal::hide_gdal_errors, test::TestDefault}, }; - let dataset_mask_flags = rasterband.mask_flags()?; + async fn query_gdal_source( + exe_ctx: &MockExecutionContext, + query_ctx: &MockQueryContext, + name: NamedData, + spatial_query: GridBoundingBox2D, + time_interval: TimeInterval, + ) -> Vec>> { + let op = GdalSource { + params: GdalSourceParameters::new(name.clone()), + } + .boxed(); - if dataset_mask_flags.is_all_valid() { - debug!("all pixels are valid --> skip no-data and mask handling."); - return Ok(MaskedGrid::new_with_data(data_grid).into()); - } + let o = op + .initialize(WorkflowOperatorPath::initialize_root(), exe_ctx) + .await + .unwrap(); - if dataset_mask_flags.is_nodata() { - debug!("raster uses a no-data value --> use no-data handling."); - let no_data_value = dataset_params - .no_data_value - .or_else(|| rasterband.no_data_value()) - .and_then(FromPrimitive::from_f64); - let no_data_value_grid = NoDataValueGrid::new(data_grid, no_data_value); - let grid_or_empty = GridOrEmpty::from(no_data_value_grid); - return Ok(grid_or_empty); + o.query_processor() + .unwrap() + .get_u8() + .unwrap() + .raster_query( + RasterQueryRectangle::new(spatial_query, time_interval, BandSelection::first()), + query_ctx, + ) + .await + .unwrap() + .collect() + .await } - if dataset_mask_flags.is_alpha() { - debug!("raster uses alpha band to mask pixels."); - if !dataset_params.allow_alphaband_as_mask { - return Err(Error::AlphaBandAsMaskNotAllowed); - } - } - - debug!("use mask based no-data handling."); - - let mask_band = rasterband.open_mask_band()?; - let mask_buffer = mask_band.read_as::( - read_window.gdal_window_start(), // pixelspace origin - read_window.gdal_window_size(), // pixelspace size - gdal_out_shape, // requested raster size - None, // sampling mode - )?; - let (_, mask_buffer_data) = mask_buffer.into_shape_and_vec(); - let mask_grid = Grid::new(out_shape, mask_buffer_data)?.map_elements(|p: u8| p > 0); - - let mask_grid = if flip_y_axis { - mask_grid.reversed_y_axis_grid() - } else { - mask_grid - }; - - let masked_grid = MaskedGrid::new(data_grid, mask_grid)?; - Ok(GridOrEmpty::from(masked_grid)) -} - -/// This method reads the data for a single tile with a specified size from the GDAL dataset. -/// It handles conversion to grid coordinates. -/// If the tile is inside the dataset it uses the `read_grid_from_raster` method. -/// If the tile overlaps the borders of the dataset it uses the `read_partial_grid_from_raster` method. -fn read_grid_and_handle_edges( - _dataset: &GdalDataset, - rasterband: &GdalRasterBand, - dataset_params: &GdalDatasetParameters, - gdal_read_advice: GdalReadAdvise, -) -> Result> -where - T: Pixel + GdalType + Default + FromPrimitive, -{ - let result_grid = if gdal_read_advice.direct_read() { - read_grid_from_raster( - rasterband, - &gdal_read_advice.gdal_read_widow, - gdal_read_advice.read_window_bounds.grid_shape(), - dataset_params, - gdal_read_advice.flip_y, - )? - } else { - let r: GridOrEmpty = read_grid_from_raster( - rasterband, - &gdal_read_advice.gdal_read_widow, - gdal_read_advice.read_window_bounds, - dataset_params, - gdal_read_advice.flip_y, - )?; - let mut tile_raster = GridOrEmpty::from(EmptyGrid::new(gdal_read_advice.bounds_of_target)); - tile_raster.grid_blit_from(&r); - tile_raster.unbounded() - }; - - Ok(result_grid) -} - -/// This method reads the data for a single tile with a specified size from the GDAL dataset and adds the requested metadata as properties to the tile. -fn read_raster_properties( - dataset: &GdalDataset, - dataset_params: &GdalDatasetParameters, - rasterband: &GdalRasterBand, -) -> RasterProperties { - let mut properties = RasterProperties::default(); - - // always read the scale and offset values from the rasterband - properties_from_band(&mut properties, rasterband); - - // read the properties from the dataset and rasterband metadata - if let Some(properties_mapping) = dataset_params.properties_mapping.as_ref() { - properties_from_gdal_metadata(&mut properties, dataset, properties_mapping); - properties_from_gdal_metadata(&mut properties, rasterband, properties_mapping); - } - - properties -} - -fn create_no_data_tile( - tile_info: TileInformation, - tile_time: TimeInterval, - cache_hint: CacheHint, -) -> RasterTile2D { - // TODO: add cache_hint - RasterTile2D::new_with_tile_info_and_properties( - tile_time, - tile_info, - 0, - EmptyGrid::new(tile_info.tile_size_in_pixels).into(), - RasterProperties::default(), - cache_hint, - ) -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, FromSql, ToSql)] -pub struct GdalMetadataMapping { - pub source_key: RasterPropertiesKey, - pub target_key: RasterPropertiesKey, - pub target_type: RasterPropertiesEntryType, -} - -impl GdalMetadataMapping { - pub fn identity( - key: RasterPropertiesKey, - target_type: RasterPropertiesEntryType, - ) -> GdalMetadataMapping { - GdalMetadataMapping { - source_key: key.clone(), - target_key: key, - target_type, - } - } -} - -fn properties_from_gdal_metadata<'a, I, M>( - properties: &mut RasterProperties, - gdal_dataset: &M, - properties_mapping: I, -) where - I: IntoIterator, - M: GdalMetadata, -{ - let mapping_iter = properties_mapping.into_iter(); - - for m in mapping_iter { - let data = if let Some(domain) = &m.source_key.domain { - gdal_dataset.metadata_item(&m.source_key.key, domain) - } else { - gdal_dataset.metadata_item(&m.source_key.key, "") - }; - - if let Some(d) = data { - let entry = match m.target_type { - RasterPropertiesEntryType::Number => d.parse::().map_or_else( - |_| RasterPropertiesEntry::String(d), - RasterPropertiesEntry::Number, - ), - RasterPropertiesEntryType::String => RasterPropertiesEntry::String(d), - }; - - debug!( - "gdal properties key \"{:?}\" => target key \"{:?}\". Value: {:?} ", - &m.source_key, &m.target_key, &entry - ); - - properties.insert_property(m.target_key.clone(), entry); - } - } -} - -fn properties_from_band(properties: &mut RasterProperties, gdal_dataset: &GdalRasterBand) { - if let Some(scale) = gdal_dataset.scale() { - properties.set_scale(scale); - } - if let Some(offset) = gdal_dataset.offset() { - properties.set_offset(offset); - } - - // ignore if there is no description - if let Ok(description) = gdal_dataset.description() { - properties.set_description(description); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::engine::{MockExecutionContext, MockQueryContext}; - use crate::test_data; - use crate::util::Result; - use crate::util::gdal::add_ndvi_dataset; - use float_cmp::assert_approx_eq; - use geoengine_datatypes::hashmap; - use geoengine_datatypes::primitives::{AxisAlignedRectangle, SpatialPartition2D, TimeInstance}; - use geoengine_datatypes::raster::{BoundedGrid, GridShape2D, SpatialGridDefinition}; - use geoengine_datatypes::raster::{ - EmptyGrid2D, GridBounds, GridIdx2D, TilesEqualIgnoringCacheHint, - }; - use geoengine_datatypes::raster::{TileInformation, TilingStrategy}; - use geoengine_datatypes::util::gdal::hide_gdal_errors; - use httptest::matchers::request; - use httptest::{Expectation, Server, responders}; - use reader::{GdalReadAdvise, GdalReadWindow}; - - fn tile_information_with_partition_and_shape( - partition: SpatialPartition2D, - shape: GridShape2D, - ) -> TileInformation { - let real_geotransform = GeoTransform::new( - partition.upper_left(), - partition.size_x() / shape.axis_size_x() as f64, - -partition.size_y() / shape.axis_size_y() as f64, - ); - - TileInformation { - tile_size_in_pixels: shape, - global_tile_position: [0, 0].into(), - global_geo_transform: real_geotransform, - } - } - - async fn query_gdal_source( - exe_ctx: &MockExecutionContext, - query_ctx: &MockQueryContext, - name: NamedData, - spatial_query: GridBoundingBox2D, - time_interval: TimeInterval, - ) -> Vec>> { - let op = GdalSource { - params: GdalSourceParameters::new(name.clone()), - } - .boxed(); - - let o = op - .initialize(WorkflowOperatorPath::initialize_root(), exe_ctx) - .await - .unwrap(); - - o.query_processor() - .unwrap() - .get_u8() - .unwrap() - .raster_query( - RasterQueryRectangle::new(spatial_query, time_interval, BandSelection::first()), - query_ctx, - ) - .await - .unwrap() - .collect() - .await - } - - // This method loads raster data from a cropped MODIS NDVI raster. - // To inspect the byte values first convert the file to XYZ with GDAL: - // 'gdal_translate -of xyz MOD13A2_M_NDVI_2014-04-01_30X30.tif MOD13A2_M_NDVI_2014-04-01_30x30.xyz' - // Then you can convert them to gruped bytes: - // 'cut -d ' ' -f 1,2 --complement MOD13A2_M_NDVI_2014-04-01_30x30.xyz | xargs -n 30 > MOD13A2_M_NDVI_2014-04-01_30x30_bytes.txt'. - fn load_ndvi_apr_2014_cropped( - gdal_read_advice: GdalReadAdvise, - ) -> Result>> { - let dataset_params = GdalDatasetParameters { - file_path: test_data!("raster/modis_ndvi/cropped/MOD13A2_M_NDVI_2014-04-01_30x30.tif") - .into(), - rasterband_channel: 1, - geo_transform: GdalDatasetGeoTransform { - origin_coordinate: (8.0, 57.4).into(), - x_pixel_size: 0.1, - y_pixel_size: -0.1, - }, - width: 30, - height: 30, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: Some(255.), - properties_mapping: Some(vec![GdalMetadataMapping { - source_key: RasterPropertiesKey { - domain: None, - key: "AREA_OR_POINT".to_string(), - }, - target_type: RasterPropertiesEntryType::String, - target_key: RasterPropertiesKey { - domain: None, - key: "AREA_OR_POINT".to_string(), - }, - }]), - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - GdalRasterLoader::load_tile_data::(&dataset_params, gdal_read_advice) - } - - #[test] - fn it_deserializes() { - let json_string = r#" - { - "type": "GdalSource", - "params": { - "data": "ns:dataset" - } - }"#; + #[test] + fn it_deserializes() { + let json_string = r#" + { + "type": "GdalSource", + "params": { + "data": "ns:dataset" + } + }"#; let operator: GdalSource = serde_json::from_str(json_string).unwrap(); @@ -1668,105 +1090,6 @@ mod tests { ); } - #[test] - fn test_load_tile_data_top_left() { - let gdal_read_advice = GdalReadAdvise { - gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), - read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - flip_y: false, - }; - - let GridAndProperties { grid, properties } = load_ndvi_apr_2014_cropped(gdal_read_advice) - .unwrap() - .unwrap(); - - assert!(!grid.is_empty()); - - let grid = grid.into_materialized_masked_grid(); - - assert_eq!(grid.inner_grid.data.len(), 64); - // pixel value are the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt - assert_eq!( - grid.inner_grid.data, - &[ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 127, 107, 255, 255, 255, 255, 255, 164, 185, 182, - 255, 255, 255, 175, 186, 190, 167, 140, 255, 255, 161, 175, 184, 173, 170, 188, - 255, 255, 128, 177, 165, 145, 191, 174, 255, 117, 100, 174, 159, 147, 99, 135 - ] - ); - - assert_eq!(grid.validity_mask.data.len(), 64); - // pixel mask is pixel > 0 from the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt - assert_eq!( - grid.validity_mask.data, - &[ - false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, true, true, - false, false, false, false, false, true, true, true, false, false, false, true, - true, true, true, true, false, false, true, true, true, true, true, true, false, - false, true, true, true, true, true, true, false, true, true, true, true, true, - true, true - ] - ); - - assert_eq!( - properties.get_property(&RasterPropertiesKey { - key: "AREA_OR_POINT".to_string(), - domain: None, - }), - Some(&RasterPropertiesEntry::String("Area".to_string())) - ); - } - - #[test] - fn test_load_tile_data_overlaps_dataset_bounds_top_left_out1() { - // shift world bbox one pixel up and to the left - let gdal_read_advice = GdalReadAdvise { - gdal_read_widow: GdalReadWindow::new([0, 0].into(), [7, 7].into()), // this is the data we can read - read_window_bounds: GridBoundingBox2D::new([1, 1], [7, 7]).unwrap(), // this is the area we can fill in target - bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), // this is the area of the target - flip_y: false, - }; - - let GridAndProperties { - grid, - properties: _properties, - } = load_ndvi_apr_2014_cropped(gdal_read_advice) - .unwrap() - .unwrap(); - - assert!(!grid.is_empty()); - - let x = grid.into_materialized_masked_grid(); - - assert_eq!(x.inner_grid.data.len(), 64); - assert_eq!( - x.inner_grid.data, - &[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, - 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 127, 0, 255, 255, 255, 255, - 255, 164, 185, 0, 255, 255, 255, 175, 186, 190, 167, 0, 255, 255, 161, 175, 184, - 173, 170, 0, 255, 255, 128, 177, 165, 145, 191, - ] - ); - - assert_eq!(x.validity_mask.data.len(), 64); - // pixel mask is pixel == 255 from the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt - assert_eq!( - x.validity_mask.data, - &[ - false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, true, false, false, false, false, - false, false, true, true, false, false, false, false, true, true, true, true, - false, false, false, true, true, true, true, true, false, false, false, true, true, - true, true, true, - ] - ); - } - #[tokio::test] async fn test_query_single_time_slice() { let mut exe_ctx = MockExecutionContext::test_default(); @@ -1899,6 +1222,23 @@ mod tests { assert!(tile_1.is_empty()); } + fn tile_information_with_partition_and_shape( + partition: SpatialPartition2D, + shape: GridShape2D, + ) -> TileInformation { + let real_geotransform = GeoTransform::new( + partition.upper_left(), + partition.size_x() / shape.axis_size_x() as f64, + -partition.size_y() / shape.axis_size_y() as f64, + ); + + TileInformation { + tile_size_in_pixels: shape, + global_tile_position: [0, 0].into(), + global_geo_transform: real_geotransform, + } + } + #[tokio::test] async fn timestep_without_params() { let output_bounds = @@ -1915,12 +1255,16 @@ mod tests { ), }); + let gpp = GdalProcessPool::new(2, 2, 2); + let gw: GdalPoolWorkerInstance = gpp.get_gdal_worker(); + let tile = GdalRasterLoader::load_tile_async::( params, reader_mode, tile_info, time_interval, CacheHint::default(), + gw, ) .await; @@ -1937,26 +1281,6 @@ mod tests { assert!(tile.unwrap().tiles_equal_ignoring_cache_hint(&expected)); } - #[test] - fn it_reverts_config_options() { - let config_options = vec![("foo".to_owned(), "bar".to_owned())]; - - { - let _config = - TemporaryGdalThreadLocalConfigOptions::new(config_options.as_slice()).unwrap(); - - assert_eq!( - gdal::config::get_config_option("foo", "default").unwrap(), - "bar".to_owned() - ); - } - - assert_eq!( - gdal::config::get_config_option("foo", "").unwrap(), - String::new() - ); - } - #[test] #[allow(clippy::too_many_lines)] fn deserialize_dataset_parameters() { @@ -2092,428 +1416,6 @@ mod tests { ); } - #[test] - #[allow(clippy::too_many_lines)] - fn read_up_side_down_raster() { - let up_side_down_params = GdalDatasetParameters { - file_path: test_data!( - "raster/modis_ndvi/flipped_axis_y/MOD13A2_M_NDVI_2014-01-01_flipped_y.tiff" - ) - .into(), - rasterband_channel: 1, - geo_transform: GdalDatasetGeoTransform { - origin_coordinate: (-180., -90.).into(), - x_pixel_size: 0.1, - y_pixel_size: 0.1, - }, - width: 3600, - height: 1800, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: Some(0.), - properties_mapping: Some(vec![ - GdalMetadataMapping { - source_key: RasterPropertiesKey { - domain: None, - key: "AREA_OR_POINT".to_string(), - }, - target_type: RasterPropertiesEntryType::String, - target_key: RasterPropertiesKey { - domain: None, - key: "AREA_OR_POINT".to_string(), - }, - }, - GdalMetadataMapping { - source_key: RasterPropertiesKey { - domain: Some("IMAGE_STRUCTURE".to_string()), - key: "COMPRESSION".to_string(), - }, - target_type: RasterPropertiesEntryType::String, - target_key: RasterPropertiesKey { - domain: Some("IMAGE_STRUCTURE_INFO".to_string()), - key: "COMPRESSION".to_string(), - }, - }, - ]), - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - let ge_global_dataset_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., 90.), 0.1, -0.1), - GridBoundingBox2D::new_min_max(0, 1799, 0, 3599).unwrap(), - ); - - let gdal_dataset_grid = ge_global_dataset_grid.flip_axis_y(); // first, flip axis - assert_approx_eq!( - Coordinate2D, - gdal_dataset_grid.geo_transform().origin_coordinate, - Coordinate2D::new(-180., 90.) - ); - assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.y_pixel_size(), 0.1); - assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.x_pixel_size(), 0.1); - assert_eq!( - gdal_dataset_grid.grid_bounds, - GridBoundingBox2D::new_min_max(-1800, -1, 0, 3599).unwrap() - ); - - let gdal_dataset_grid = gdal_dataset_grid - .with_moved_origin_exact_grid(Coordinate2D::new(-180., -90.)) - .unwrap(); // second, move origin (to other side of axis) - assert_approx_eq!( - Coordinate2D, - gdal_dataset_grid.geo_transform().origin_coordinate, - Coordinate2D::new(-180., -90.) - ); - assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.y_pixel_size(), 0.1); - assert_approx_eq!(f64, gdal_dataset_grid.geo_transform.x_pixel_size(), 0.1); - assert_eq!( - gdal_dataset_grid.grid_bounds, - GridBoundingBox2D::new_min_max(0, 1799, 0, 3599).unwrap() - ); - - let ovr = OverviewReaderState::new(ge_global_dataset_grid); - - let tile = SpatialGridDefinition::new( - ge_global_dataset_grid.geo_transform, - GridBoundingBox2D::new_min_max(326, 326 + 7, 1880, 1880 + 7).unwrap(), - ); - - let gdal_read_advice = ovr - .tiling_to_dataset_read_advise(&gdal_dataset_grid, &tile) - .unwrap(); - - let exp_gdal_read_advice = GdalReadAdvise { - gdal_read_widow: GdalReadWindow::new([1466, 1880].into(), [8, 8].into()), - read_window_bounds: GridBoundingBox2D::new([326, 1880], [326 + 7, 1880 + 7]).unwrap(), - bounds_of_target: GridBoundingBox2D::new([326, 1880], [326 + 7, 1880 + 7]).unwrap(), - flip_y: true, - }; - - assert_eq!(gdal_read_advice, exp_gdal_read_advice); - - let GridAndProperties { grid, properties } = - GdalRasterLoader::load_tile_data::(&up_side_down_params, gdal_read_advice) - .unwrap() - .unwrap(); - - assert!(!grid.is_empty()); - let grid = grid.into_materialized_masked_grid(); - - assert_eq!(grid.inner_grid.data.len(), 64); - assert_eq!( - grid.inner_grid.data, - &[ - // TODO: check in tiff! - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 53, 47, 255, 255, 255, 255, 255, 68, 81, 93, 255, 255, - 255, 97, 102, 91, 73, 72, 255, 255, 91, 97, 100, 86, 78, 106, 255, 255, 59, 95, 85, - 66, 105, 104, 255, 47, 42, 82, 81, 76, 73, 98 - ] - ); - - assert_eq!(grid.validity_mask.data.len(), 64); - assert_eq!(grid.validity_mask.data, &[true; 64]); - - assert!(properties.offset_option().is_none()); - assert!(properties.scale_option().is_none()); - } - - #[test] - fn read_raster_and_offset_scale() { - let up_side_down_params = GdalDatasetParameters { - file_path: test_data!("raster/modis_ndvi/cropped/MOD13A2_M_NDVI_2014-04-01_30x30.tif") - .into(), - rasterband_channel: 1, - geo_transform: GdalDatasetGeoTransform { - origin_coordinate: (8.0, 57.4).into(), - x_pixel_size: 0.1, - y_pixel_size: -0.1, - }, - width: 30, - height: 30, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: Some(255.), - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - let gdal_read_advice = GdalReadAdvise { - gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), - read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - flip_y: false, - }; - - let GridAndProperties { grid, properties } = - GdalRasterLoader::load_tile_data::(&up_side_down_params, gdal_read_advice) - .unwrap() - .unwrap(); - - assert!(!grid.is_empty()); - - let grid = grid.into_materialized_masked_grid(); - - assert_eq!(grid.inner_grid.data.len(), 64); - // pixel value are the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt - assert_eq!( - grid.inner_grid.data, - &[ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 127, 107, 255, 255, 255, 255, 255, 164, 185, 182, - 255, 255, 255, 175, 186, 190, 167, 140, 255, 255, 161, 175, 184, 173, 170, 188, - 255, 255, 128, 177, 165, 145, 191, 174, 255, 117, 100, 174, 159, 147, 99, 135 - ] - ); - - assert_eq!(grid.validity_mask.data.len(), 64); - // pixel mask is pixel > 0 from the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt - assert_eq!( - grid.validity_mask.data, - &[ - false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, true, true, - false, false, false, false, false, true, true, true, false, false, false, true, - true, true, true, true, false, false, true, true, true, true, true, true, false, - false, true, true, true, true, true, true, false, true, true, true, true, true, - true, true - ] - ); - - assert_eq!(properties.offset_option(), Some(1.)); - assert_eq!(properties.scale_option(), Some(2.)); - - assert!(approx_eq!(f64, properties.offset(), 1.)); - assert!(approx_eq!(f64, properties.scale(), 2.)); - } - - #[test] - fn it_creates_no_data_only_for_missing_files() { - hide_gdal_errors(); - - let ds = GdalDatasetParameters { - file_path: "nonexisting_file.tif".into(), - rasterband_channel: 1, - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - let gdal_read_advice = GdalReadAdvise { - gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), - read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - flip_y: false, - }; - - let res = GdalRasterLoader::load_tile_data::(&ds, gdal_read_advice); - - assert!(res.is_ok()); - - let res = res.unwrap(); - - assert!(res.is_none()); - - let ds = GdalDatasetParameters { - file_path: test_data!("raster/modis_ndvi/MOD13A2_M_NDVI_2014-01-01.TIFF").into(), - rasterband_channel: 100, // invalid channel - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - // invalid channel => error - let result = GdalRasterLoader::load_tile_data::(&ds, gdal_read_advice); - assert!(result.is_err()); - } - - #[test] - fn it_creates_no_data_only_for_http_404() { - let server = Server::run(); - - server.expect( - Expectation::matching(request::method_path("HEAD", "/non_existing.tif")) - .times(1) - .respond_with(responders::cycle![responders::status_code(404),]), - ); - - server.expect( - Expectation::matching(request::method_path("HEAD", "/internal_error.tif")) - .times(1) - .respond_with(responders::cycle![responders::status_code(500),]), - ); - - let ds = GdalDatasetParameters { - file_path: format!("/vsicurl/{}", server.url_str("/non_existing.tif")).into(), - rasterband_channel: 1, - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: Some(vec![ - ( - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), - ".tif".to_owned(), - ), - ( - "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), - "EMPTY_DIR".to_owned(), - ), - ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), - ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), - ]), - allow_alphaband_as_mask: true, - retry: None, - }; - - // 404 => no data - let gdal_read_advice = GdalReadAdvise { - gdal_read_widow: GdalReadWindow::new([0, 0].into(), [8, 8].into()), - read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), - flip_y: false, - }; - - let res = GdalRasterLoader::load_tile_data::(&ds, gdal_read_advice); - assert!(res.is_ok()); - let res = res.unwrap(); - assert!(res.is_none()); - - let ds = GdalDatasetParameters { - file_path: format!("/vsicurl/{}", server.url_str("/internal_error.tif")).into(), - rasterband_channel: 1, - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: Some(vec![ - ( - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), - ".tif".to_owned(), - ), - ( - "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), - "EMPTY_DIR".to_owned(), - ), - ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), - ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), - ]), - allow_alphaband_as_mask: true, - retry: None, - }; - - // 500 => error - let res = GdalRasterLoader::load_tile_data::(&ds, gdal_read_advice); - assert!(res.is_err()); - } - - #[test] - fn it_retries_only_after_clearing_vsi_cache() { - hide_gdal_errors(); - - let server = Server::run(); - - server.expect( - Expectation::matching(request::method_path("HEAD", "/foo.tif")) - .times(2) - .respond_with(responders::cycle![ - // first generic error - responders::status_code(500), - // then 404 file not found - responders::status_code(404) - ]), - ); - - let file_path: PathBuf = format!("/vsicurl/{}", server.url_str("/foo.tif")).into(); - - let options = Some(vec![ - ( - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), - ".tif".to_owned(), - ), - ( - "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), - "EMPTY_DIR".to_owned(), - ), - ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), - ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), - ]); - - let _thread_local_configs = options - .as_ref() - .map(|config_options| TemporaryGdalThreadLocalConfigOptions::new(config_options)); - - // first fail - let result = gdal_open_dataset_ex( - file_path.as_path(), - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - ..DatasetOptions::default() - }, - ); - - // it failed, but not with file not found - assert!(result.is_err()); - if let Err(error) = result { - assert!(!error_is_gdal_file_not_found(&error)); - } - - // second fail doesn't even try, so still not "file not found", even though it should be now - let result = gdal_open_dataset_ex( - file_path.as_path(), - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - ..DatasetOptions::default() - }, - ); - - assert!(result.is_err()); - if let Err(error) = result { - assert!(!error_is_gdal_file_not_found(&error)); - } - - clear_gdal_vsi_cache_for_path(file_path.as_path()); - - // after clearing the cache, it tries again - let result = gdal_open_dataset_ex( - file_path.as_path(), - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - ..DatasetOptions::default() - }, - ); - - // now we get the file not found error - assert!(result.is_err()); - if let Err(error) = result { - assert!(error_is_gdal_file_not_found(&error)); - } - } - #[tokio::test] async fn it_attaches_cache_hint() { let output_bounds = @@ -2524,6 +1426,9 @@ mod tests { let time_interval = TimeInterval::new_unchecked(1_388_534_400_000, 1_391_212_800_000); // 2014-01-01 - 2014-01-15 let params = None; + let gpp = GdalProcessPool::new(2, 2, 2); + let gw: GdalPoolWorkerInstance = gpp.get_gdal_worker(); + let tile = GdalRasterLoader::load_tile_async::( params, GdalReaderMode::OriginalResolution(ReaderState { @@ -2535,6 +1440,7 @@ mod tests { tile_info, time_interval, CacheHint::seconds(1234), + gw, ) .await; diff --git a/geoengine/operators/src/source/gdal_source/reader.rs b/geoengine/operators/src/source/gdal_source/reader.rs index dd05f269d0..19ba6c1245 100644 --- a/geoengine/operators/src/source/gdal_source/reader.rs +++ b/geoengine/operators/src/source/gdal_source/reader.rs @@ -1,1209 +1,274 @@ +use gdal::raster::GdalType; use geoengine_datatypes::raster::{ - GridBoundingBox2D, GridBounds, GridContains, GridIdx2D, GridOrEmpty2D, GridShape2D, - GridShapeAccess, GridSize, RasterProperties, SpatialGridDefinition, + EmptyGrid, GridBlit, GridBoundingBox2D, GridOrEmpty, MaskedGrid, Pixel, RasterProperties, +}; +use num::FromPrimitive; + +use crate::source::gdal_in::{ + FileNotFoundHandling, GdalDatasetParameters, GdalPoolWorkerInstance, GdalProcessPoolError, + GridAndProperties, + process_common::{ + GdalReadAdvise, IpcChannelMessage, IpcChannelMessagePayload, IpcProcessError, + IpcProcessGdalErrorKind, + }, }; -use num::Zero; - -/// This struct is used to advise the GDAL reader how to read the data from the dataset. -/// The Workflow is as follows: -/// 1. The `gdal_read_window` is the window in the pixel space of the dataset that should be read. -/// 2. The `read_window_bounds` is the area in the target pixel space where the data should be placed. -/// 2.1 The data read in step one is read to the width and height of the `read_window_bounds`. -/// 2.2 if `flip_y` is true the data is flipped in the y direction. And should be unflipped after reading. -/// 3. The `bounds_of_target` is the area in the target pixel space where the data should be placed. -/// 3.1 The `read_window_bounds` might be offset from the `bounds_of_target` or might have a different size. -/// Then, the data needs to be placed in the target pixel space accordingly. Other parts of the target pixel space should be filled with nodata. -#[allow(dead_code)] -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct GdalReadAdvise { - pub gdal_read_widow: GdalReadWindow, - pub read_window_bounds: GridBoundingBox2D, - pub bounds_of_target: GridBoundingBox2D, - pub flip_y: bool, -} - -impl GdalReadAdvise { - pub fn direct_read(&self) -> bool { - self.read_window_bounds == self.bounds_of_target - } -} - -#[allow(dead_code)] -#[derive(Copy, Clone, Debug)] -pub enum GdalReaderMode { - // read the original resolution - OriginalResolution(ReaderState), - // read an overview level of the dataset - OverviewLevel(OverviewReaderState), -} - -impl GdalReaderMode { - /// Returns the read advise for the tiling based bounds - pub fn tiling_to_dataset_read_advise( - &self, - actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, - tile: &SpatialGridDefinition, - ) -> Option { - match self { - GdalReaderMode::OriginalResolution(re) => { - re.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) - } - GdalReaderMode::OverviewLevel(rs) => { - rs.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) - } - } - } -} - -#[derive(Copy, Clone, Debug)] -pub struct ReaderState { - pub dataset_spatial_grid: SpatialGridDefinition, -} - -impl ReaderState { - pub fn tiling_to_dataset_read_advise( - &self, - actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, - tile: &SpatialGridDefinition, - ) -> Option { - // Check if the y_axis is fliped. - let (actual_gdal_dataset_spatial_grid_definition, flip_y) = - if actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .y_axis_is_neg() - == self.dataset_spatial_grid.geo_transform().y_axis_is_neg() - { - (*actual_gdal_dataset_spatial_grid_definition, false) - } else { - ( - actual_gdal_dataset_spatial_grid_definition - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )), - true, - ) - }; - - // Now we can work with a matching dataset. However, we need to reverse the read window later! - - // let's only look at data in the geo engine dataset definition! The intersection is relative to the first elements origin coordinate. - let dataset_gdal_data_intersection = - actual_gdal_dataset_spatial_grid_definition.intersection(&self.dataset_spatial_grid)?; - - // Now, we need the tile in the gdal dataset bounds to identify readable areas - let tile_in_gdal_dataset_bounds = tile.with_moved_origin_exact_grid( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .origin_coordinate, - )?; // TODO: raise error if this fails! - - // Then, calculate the intersection between the datataset and the tile. Again, the intersection is relative to the first elements orrigin coordinate. - let tile_gdal_dataset_intersection = - dataset_gdal_data_intersection.intersection(&tile_in_gdal_dataset_bounds)?; - - // if we need to unflip the dataset grid now is the time to do this. - let tile_intersection_for_read_window = if flip_y { - tile_gdal_dataset_intersection - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )) - } else { - tile_gdal_dataset_intersection - }; - - // generate the read window for GDAL - - let gdal_read_window = GdalReadWindow::new( - tile_intersection_for_read_window.grid_bounds.min_index(), - tile_intersection_for_read_window.grid_bounds.grid_shape(), - ); - - // if the read window has the same shape as the tiling based bounds we can fill that completely - if tile_in_gdal_dataset_bounds == tile_gdal_dataset_intersection { - return Some(GdalReadAdvise { - gdal_read_widow: gdal_read_window, - read_window_bounds: tile.grid_bounds, - bounds_of_target: tile.grid_bounds, - flip_y, - }); - } - - // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds - let crop_tl = - tile_gdal_dataset_intersection.min_index() - tile_in_gdal_dataset_bounds.min_index(); - let crop_lr = - tile_gdal_dataset_intersection.max_index() - tile_in_gdal_dataset_bounds.max_index(); - - let shifted_tl = tile.grid_bounds.min_index() + crop_tl; - let shifted_lr = tile.grid_bounds.max_index() + crop_lr; - // now we need to adapt the target pixel space read window to the clipped dataset intersection area - let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); - debug_assert!( - tile.grid_bounds().contains(&shifted_readable_bounds), - "readable bounds must be contained in tile bounds" - ); +pub struct GdalPoolReader(GdalPoolWorkerInstance); - Some(GdalReadAdvise { - gdal_read_widow: gdal_read_window, - read_window_bounds: shifted_readable_bounds, - bounds_of_target: tile.grid_bounds, - flip_y, - }) +impl From for GdalPoolReader { + fn from(value: GdalPoolWorkerInstance) -> Self { + GdalPoolReader(value) } } -#[derive(Copy, Clone, Debug)] -pub struct OverviewReaderState { - pub original_dataset_grid: SpatialGridDefinition, -} - -impl OverviewReaderState { - pub fn new(original_dataset_grid: SpatialGridDefinition) -> Self { - OverviewReaderState { - original_dataset_grid, - } +impl GdalPoolReader { + #[inline] + fn worker_instance(&self) -> &GdalPoolWorkerInstance { + &self.0 } - #[allow(clippy::too_many_lines)] - pub fn tiling_to_dataset_read_advise( + /// Loads a tile using the separate process and the provided parameters and read advise. + /// This is the method where the source operator attaches to the process pool. + /// The method sends a message to the process pool and waits for the response. The response is then converted to a `RasterTile2D` and returned. + /// The method also handles the case where the file is not found and the `file_not_found_handling` is set to `NoData` by returning a tile filled with nodata values. + /// # Errors + /// Returns a `GdalSourceError` if the process returns an error, or if the response cannot be converted to a `RasterTile2D`. + /// + /// # Panics + /// Panics if the response from the process is not in the expected format, or if the grid blitting fails (which should not happen if the bounds are correct). + pub async fn load_tile_data_process( &self, - actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, // This is the spatial grid of an actual gdal file - tile: &SpatialGridDefinition, // This is a tile inside the grid we use for the global dataset consisting of potentially many gdal files... - ) -> Option { - tracing::trace!( - "OverviewReader, global grid: {:?}, gdal file grid {:?}, tile {:?}", - self.original_dataset_grid, - actual_gdal_dataset_spatial_grid_definition, - tile - ); - - // Check if the y_axis is fliped. - let (actual_gdal_dataset_spatial_grid_definition, flip_y) = - if actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .y_axis_is_neg() - == self.original_dataset_grid.geo_transform().y_axis_is_neg() - { - (*actual_gdal_dataset_spatial_grid_definition, false) - } else { - ( - actual_gdal_dataset_spatial_grid_definition - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )), - true, - ) - }; - - // This is the intersection of grid of the gdal file and the global grid we use. Usually the dataset is inside the global dataset grid. - // IF the intersection is empty the we return early and load nothing - // The intersection uses the geo_transform of the gdal dataset which enables us to adress gdal pixels starting at 0,0 - let actual_bounds_to_use_original_resolution = actual_gdal_dataset_spatial_grid_definition - .intersection(&self.original_dataset_grid)?; - - // now we map the tile we want to fill to the original grid. First, we set the tile to use the same origin coordinate as the gdal file/dataset - /* - let tile_with_overview_resolution_in_actual_space = tile.with_moved_origin_exact_grid( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .origin_coordinate(), - ); - */ - - let (tile_with_overview_resolution_in_actual_space, distance) = tile - .with_moved_origin_to_nearest_grid_edge_with_distance( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .origin_coordinate(), - ); - - let needs_shift = !distance.x.is_zero() || !distance.y.is_zero(); - - let tile_with_original_resolution_in_actual_space = - tile_with_overview_resolution_in_actual_space.with_changed_resolution( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .spatial_resolution(), - ); - - // Now we need to intersect the tile and the actual bounds to use to identify what we can really read - let tile_intersection_original_resolution_actual_space = - &tile_with_original_resolution_in_actual_space - .intersection(&actual_bounds_to_use_original_resolution)?; - - if needs_shift { - tracing::trace!( - "The overview grid does not match the gdal file. This happens when files cover different areas!, Tile (OVR): {:?}, Tile (ORIG): {:?}, Distance: {:?}, Readable: {:?}", - tile_with_overview_resolution_in_actual_space, - tile_with_original_resolution_in_actual_space, - distance, - tile_intersection_original_resolution_actual_space - ); - } - - // if we need to unflip the dataset grid now is the time to do this. - let tile_intersection_for_read_window = if flip_y { - tile_intersection_original_resolution_actual_space - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )) - } else { - *tile_intersection_original_resolution_actual_space - }; - - let tile_intersection_for_read_window = if needs_shift { - let shift_x = distance.x - / actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .x_pixel_size(); - let shift_y = distance.y - / actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .y_pixel_size(); - - let offset = GridIdx2D::new_y_x(-(shift_y as isize), -(shift_x as isize)); - - tracing::trace!( - "Shifting read bounds to match gdal pixels. ({shift_y}, {shift_x}) -> {offset:?} " - ); - - tile_intersection_for_read_window.shift_bounds_relative_by_pixel_offset(offset) - } else { - tile_intersection_for_read_window - }; - - // generate the read window for GDAL --> This is what we can read in any case. - let read_window: GdalReadWindow = GdalReadWindow::new( - tile_intersection_for_read_window.min_index(), - tile_intersection_for_read_window.grid_bounds().grid_shape(), - ); - - let is_tile_contained = tile_intersection_for_read_window.grid_bounds() - == tile_with_original_resolution_in_actual_space.grid_bounds(); - - if is_tile_contained { - return Some(GdalReadAdvise { - gdal_read_widow: read_window, - read_window_bounds: tile.grid_bounds, - bounds_of_target: tile.grid_bounds, - flip_y, - }); - } - - // IF we can't read the whole tile, we have to find out which area of the tile we can fill. - let readble_area_in_overview_res = tile_intersection_original_resolution_actual_space - .with_changed_resolution(tile.geo_transform().spatial_resolution()); - - // Calculate the intersection of the readable area and the tile, result is in geotransform of the tile! - let readable_tile_area = tile.intersection(&readble_area_in_overview_res).expect( - "Since there was an intersection earlyer, there must be a part of data to read.", - ); - - // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds - let crop_tl = readable_tile_area.min_index() - tile.min_index(); - let crop_lr = readable_tile_area.max_index() - tile.max_index(); - - let shifted_tl = tile.grid_bounds.min_index() + crop_tl; - let shifted_lr = tile.grid_bounds.max_index() + crop_lr; - - // now we need to adapt the target pixel space read window to the clipped dataset intersection area - let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); - debug_assert!( - tile.grid_bounds().contains(&shifted_readable_bounds), - "readable bounds must be contained in tile bounds" - ); - - Some(GdalReadAdvise { - gdal_read_widow: read_window, - read_window_bounds: shifted_readable_bounds, - bounds_of_target: tile.grid_bounds, - flip_y, - }) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct GdalReadWindow { - start_x: isize, // pixelspace origin - start_y: isize, - size_x: usize, // pixelspace size - size_y: usize, -} - -impl GdalReadWindow { - pub fn new(start: GridIdx2D, size: GridShape2D) -> Self { - Self { - start_x: start.x(), - start_y: start.y(), - size_x: size.axis_size_x(), - size_y: size.axis_size_y(), - } - } - - pub fn gdal_window_start(&self) -> (isize, isize) { - (self.start_x, self.start_y) - } - - pub fn gdal_window_size(&self) -> (usize, usize) { - (self.size_x, self.size_y) + dataset_params: GdalDatasetParameters, + read_advise: GdalReadAdvise, + ) -> Result, GdalProcessPoolError> { + let file_not_found_as_no_data = + dataset_params.file_not_found_handling == FileNotFoundHandling::NoData; + + let message = IpcChannelMessage::new_request_tile_message(IpcChannelMessagePayload { + dataset_params, + read_advise, + data_type: T::TYPE, + }); + + let res: Result<_, _> = self.worker_instance().read_data(message).await; + + let res = match res { + Ok(t) => { + // Here we need to handle edges! + // First, convert response to GridAndProperties + let GridAndProperties { grid, properties }: GridAndProperties< + T, + GridBoundingBox2D, + > = t.into(); + // Second, flip y-axis if necessary + let grid = if read_advise.flip_y { + match grid { + GridOrEmpty::Grid(MaskedGrid { + inner_grid, + validity_mask, + }) => GridOrEmpty::new_grid( + MaskedGrid::new( + inner_grid.reversed_y_axis_grid(), + validity_mask.reversed_y_axis_grid(), + ) + .expect("The bounds of the input grid should be the same after reversing the y axis, so this should never fail"), + ), + GridOrEmpty::Empty(e) => GridOrEmpty::new_empty(e), + } + } else { + grid + }; + // Third, blit the grid to the tile bounds if necessary + let grid = if read_advise.direct_read() { + grid + } else { + let mut tile_raster = + GridOrEmpty::from(EmptyGrid::new(read_advise.bounds_of_target)); + tile_raster.grid_blit_from(&grid); + tile_raster + }; + Ok(GridAndProperties { grid, properties }) + } + Err(GdalProcessPoolError::IpcProcessError { + source: + IpcProcessError::GdalError { + kind: IpcProcessGdalErrorKind::FileNotFound, + details: _details, + }, + }) if file_not_found_as_no_data => Ok(GridAndProperties { + grid: GridOrEmpty::new_empty_shape(read_advise.bounds_of_target), + properties: RasterProperties::default(), + }), + Err(other_err) => Err(other_err), + }?; + + Ok(res) } } -pub struct GridAndProperties { - pub grid: GridOrEmpty2D, - pub properties: RasterProperties, -} - #[cfg(test)] mod tests { use geoengine_datatypes::{ - primitives::Coordinate2D, + primitives::{AxisAlignedRectangle, CacheHint, SpatialPartition2D, TimeInterval}, raster::{ - BoundedGrid, GeoTransform, GridBoundingBox2D, GridIdx2D, GridShape2D, - SpatialGridDefinition, + ChangeGridBounds, GeoTransform, GridShape2D, GridSize, RasterPropertiesEntry, + RasterPropertiesEntryType, RasterPropertiesKey, RasterTile2D, TileInformation, }, + test_data, }; - use crate::source::gdal_source::reader::{GdalReadWindow, OverviewReaderState, ReaderState}; - - #[test] - fn reader_state_dataset_geo_transform() { - let reader_state = ReaderState { - dataset_spatial_grid: SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([1024, 1024]).bounding_box(), - ), - }; - - assert_eq!( - reader_state.dataset_spatial_grid.geo_transform(), - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.) - ); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_no_change() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([1024, 1024]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 512, - size_y: 512, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_shifted() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 180, - start_y: 90, - size_x: 180, - size_y: 90, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_shifted_and_clipped() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])) - .unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 190, - start_y: 100, - size_x: 170, - size_y: 80, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_shifted_flipy() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let spatial_grid_flipy = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., -90.), 1., 1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid_flipy, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + use crate::source::gdal_in::{ + GdalDatasetGeoTransform, GdalMetadataMapping, GdalProcessPool, GdalProcessPoolAccess, + process_common::GdalReadWindow, + }; - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 180, - start_y: 0, - size_x: 180, - size_y: 90, + use super::*; + + // TODO (low): name / test + async fn load_ndvi_jan_2014_by_process( + gdal_read_advice: GdalReadAdvise, + tile_information: TileInformation, + gdal_worker: GdalPoolWorkerInstance, + ) -> Result, GdalProcessPoolError> { + let dataset_params = GdalDatasetParameters { + file_path: test_data!("raster/modis_ndvi/MOD13A2_M_NDVI_2014-01-01.TIFF").into(), + rasterband_channel: 1, + geo_transform: GdalDatasetGeoTransform { + origin_coordinate: (-180., 90.).into(), + x_pixel_size: 0.1, + y_pixel_size: -0.1, }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert!(tiling_to_dataset_read_advise.flip_y); - } - - /* - #[test] - fn gdal_geotransform_to_read_bounds() { - let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { - origin_coordinate: Coordinate2D::new(0., 0.), - x_pixel_size: 1., - y_pixel_size: -1., - }; - - let gdal_data_size = GridShape2D::new([1024, 1024]); - - let ti: TileInformation = TileInformation::new( - GridIdx([1, 1]), - GridShape2D::new([512, 512]), - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - ); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - assert_eq!( - read_window, - GdalReadWindow { - size_x: 512, - size_y: 512, - start_x: 512, - start_y: 512, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([512, 512]), GridIdx([1023, 1023])).unwrap() - ); - } - - #[test] - fn gdal_geotransform_to_read_bounds_half_res() { - let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { - origin_coordinate: Coordinate2D::new(0., 0.), - x_pixel_size: 1., - y_pixel_size: -1., + width: 3600, + height: 1800, + file_not_found_handling: FileNotFoundHandling::NoData, + no_data_value: Some(0.), + properties_mapping: Some(vec![ + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }, + }, + GdalMetadataMapping { + source_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE".to_string()), + key: "COMPRESSION".to_string(), + }, + target_type: RasterPropertiesEntryType::String, + target_key: RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE_INFO".to_string()), + key: "COMPRESSION".to_string(), + }, + }, + ]), + gdal_open_options: None, + gdal_config_options: None, + allow_alphaband_as_mask: true, + retry: None, }; - let gdal_data_size = GridShape2D::new([1024, 1024]); - - let ti: TileInformation = TileInformation::new( - GridIdx([0, 0]), - GridShape2D::new([512, 512]), - GeoTransform::new(Coordinate2D::new(0., 0.), 2., -2.), - ); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - assert_eq!( - read_window, - GdalReadWindow { - size_x: 1024, - size_y: 1024, - start_x: 0, - start_y: 0, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([511, 511])).unwrap() - ); - } - - #[test] - fn gdal_geotransform_to_read_bounds_2x_res() { - let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { - origin_coordinate: Coordinate2D::new(0., 0.), - x_pixel_size: 1., - y_pixel_size: -1., - }; - - let gdal_data_size = GridShape2D::new([1024, 1024]); - - let ti: TileInformation = TileInformation::new( - GridIdx([0, 0]), - GridShape2D::new([512, 512]), - GeoTransform::new(Coordinate2D::new(0., 0.), 0.5, -0.5), - ); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - assert_eq!( - read_window, - GdalReadWindow { - size_x: 256, - size_y: 256, - start_x: 0, - start_y: 0, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([511, 511])).unwrap() - ); - } - - #[test] - fn gdal_geotransform_to_read_bounds_ul_out() { - let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { - origin_coordinate: Coordinate2D::new(-3., 3.), - x_pixel_size: 1., - y_pixel_size: -1., - }; - - let gdal_data_size = GridShape2D::new([1024, 1024]); - let tile_grid_shape = GridShape2D::new([512, 512]); - let tiling_global_geo_transfom = GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.); - - let ti: TileInformation = - TileInformation::new(GridIdx([0, 0]), tile_grid_shape, tiling_global_geo_transfom); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - // since the origin of the tile is at -3,3 and the "coordinate nearest to zero" is 0,0 the tile at tile position 0,0 maps to the read window starting at 3,3 with 512x512 pixels - assert_eq!( - read_window, - GdalReadWindow { - size_x: 512, - size_y: 512, - start_x: 3, - start_y: 3, - } - ); - - // the data maps to the complete tile - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([511, 511])).unwrap() - ); - - let ti: TileInformation = - TileInformation::new(GridIdx([1, 1]), tile_grid_shape, tiling_global_geo_transfom); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - // since the origin of the tile is at -3,3 and the "coordinate nearest to zero" is 0,0 the tile at tile position 1,1 maps to the read window starting at 515,515 (512+3, 512+3) with 512x512 pixels - assert_eq!( - read_window, - GdalReadWindow { - size_x: 509, - size_y: 509, - start_x: 515, - start_y: 515, - } - ); - - // the data maps only to a part of the tile since the data is only 1024x1024 pixels in size. So the tile at tile position 1,1 maps to the data starting at 515,515 (512+3, 512+3) with 509x509 pixels left. - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([512, 512]), GridIdx([1020, 1020])).unwrap() - ); + GdalPoolReader::from(gdal_worker) + .load_tile_data_process::(dataset_params, gdal_read_advice) + .await + .map(|r| { + RasterTile2D::new_with_tile_info_and_properties( + TimeInterval::default(), + tile_information, + 0, + r.grid.unbounded(), + r.properties, + CacheHint::default(), + ) + }) + .map_err(Into::into) } - #[test] - fn gdal_geotransform_to_read_bounds_ul_in() { - let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { - origin_coordinate: Coordinate2D::new(3., -3.), - x_pixel_size: 1., - y_pixel_size: -1., - }; - - let gdal_data_size = GridShape2D::new([1024, 1024]); - let tile_grid_shape = GridShape2D::new([512, 512]); - let tiling_global_geo_transfom = GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.); - - let ti: TileInformation = - TileInformation::new(GridIdx([0, 0]), tile_grid_shape, tiling_global_geo_transfom); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - // in this case the data origin is at 3,-3 which is inside the tile at tile position 0,0. Since the tile starts at the "coordinate nearest to zero, which is 0.0,0.0" we need to read the data starting at data 0,0 with 509x509 pixels (512-3, 512-3). - assert_eq!( - read_window, - GdalReadWindow { - size_x: 509, - size_y: 509, - start_x: 0, - start_y: 0, - } - ); - - // in this case, the data only maps to the last 509x509 pixels of the tile. So the data we read does not fill a whole tile. - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([3, 3]), GridIdx([511, 511])).unwrap() + fn tile_information_with_partition_and_shape( + partition: SpatialPartition2D, + shape: GridShape2D, + ) -> TileInformation { + let real_geotransform = GeoTransform::new( + partition.upper_left(), + partition.size_x() / shape.axis_size_x() as f64, + -partition.size_y() / shape.axis_size_y() as f64, ); - let ti: TileInformation = - TileInformation::new(GridIdx([1, 1]), tile_grid_shape, tiling_global_geo_transfom); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - assert_eq!( - read_window, - GdalReadWindow { - size_x: 512, - size_y: 512, - start_x: 509, - start_y: 509, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([512, 512]), GridIdx([1023, 1023])).unwrap() - ); - - let ti: TileInformation = - TileInformation::new(GridIdx([2, 2]), tile_grid_shape, tiling_global_geo_transfom); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - assert_eq!( - read_window, - GdalReadWindow { - size_x: 3, - size_y: 3, - start_x: 1021, - start_y: 1021, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([1024, 1024]), GridIdx([1026, 1026])).unwrap() - ); + TileInformation { + tile_size_in_pixels: shape, + global_tile_position: [0, 0].into(), + global_geo_transform: real_geotransform, + } } - #[test] - fn gdal_geotransform_to_read_bounds_ul_out_frac_res() { - let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { - origin_coordinate: Coordinate2D::new(-9., 9.), - x_pixel_size: 9., - y_pixel_size: -9., + #[tokio::test] + async fn test_load_tile_data_process() { + let output_shape: GridShape2D = [8, 8].into(); + let output_bounds = + SpatialPartition2D::new_unchecked((-180., 90.).into(), (-179.2, 89.2).into()); + + let gdal_read_advice = GdalReadAdvise { + gdal_read_widow: GdalReadWindow::new([0, 0].into(), output_shape), + read_window_bounds: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + bounds_of_target: GridBoundingBox2D::new([0, 0], [7, 7]).unwrap(), + flip_y: false, }; - let gdal_data_size = GridShape2D::new([1024, 1024]); - let tile_grid_shape = GridShape2D::new([512, 512]); - let tiling_global_geo_transfom = GeoTransform::new(Coordinate2D::new(-0., 0.), 3., -3.); - - let ti: TileInformation = - TileInformation::new(GridIdx([0, 0]), tile_grid_shape, tiling_global_geo_transfom); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - assert_eq!( - read_window, - GdalReadWindow { - size_x: 170, // - size_y: 170, - start_x: 1, - start_y: 1, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([512, 512])).unwrap() - ); // we need to read 683 pixels but we only want 682.6666666666666 pixels. - - let ti: TileInformation = - TileInformation::new(GridIdx([1, 1]), tile_grid_shape, tiling_global_geo_transfom); - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + let tile_information = + tile_information_with_partition_and_shape(output_bounds, output_shape); + + let gpp = GdalProcessPool::new(2, 2, 2); + let gw = gpp.get_gdal_worker(); + + let RasterTile2D { + global_geo_transform: _, + grid_array: grid, + tile_position: _, + band: _, + time: _, + properties, + cache_hint: _, + } = load_ndvi_jan_2014_by_process(gdal_read_advice, tile_information, gw) + .await .unwrap(); - assert_eq!( - read_window, - GdalReadWindow { - size_x: 171, - size_y: 171, - start_x: 171, - start_y: 171, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([510, 510]), GridIdx([1025, 1025])).unwrap() - ); - - let ti: TileInformation = - TileInformation::new(GridIdx([2, 2]), tile_grid_shape, tiling_global_geo_transfom); - - let (read_window, target_bounds) = gdal_geo_transform - .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) - .unwrap(); - - assert_eq!( - read_window, - GdalReadWindow { - size_x: 171, - size_y: 171, - start_x: 342, - start_y: 342, - } - ); - - assert_eq!( - target_bounds, - GridBoundingBox2D::new(GridIdx([1023, 1023]), GridIdx([1535, 1535])).unwrap() - ); - } - */ - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_2() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([1024, 1024]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 2., -2.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + assert!(!grid.is_empty()); - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 1024, - size_y: 1024, - }, - ); + let grid = grid.into_materialized_masked_grid(); + assert_eq!(grid.inner_grid.data.len(), 64); assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([2048, 2048]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), + grid.inner_grid.data, + &[ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 + ] ); - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + assert_eq!(grid.validity_mask.data.len(), 64); + assert_eq!(grid.validity_mask.data, &[true; 64]); + assert!((properties.scale_option()).is_none()); + assert!(properties.offset_option().is_none()); assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 2048, - size_y: 2048, - }, + properties.get_property(&RasterPropertiesKey { + domain: Some("IMAGE_STRUCTURE_INFO".to_string()), + key: "COMPRESSION".to_string(), + }), + Some(&RasterPropertiesEntry::String("LZW".to_string())) ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + properties.get_property(&RasterPropertiesKey { + domain: None, + key: "AREA_OR_POINT".to_string(), + }), + Some(&RasterPropertiesEntry::String("Area".to_string())) ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([4096, 4096]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 2048, - start_y: 2048, - size_x: 2048, - size_y: 2048, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_lrcrop() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 2048, - start_y: 2048, - size_x: 2048 - 16, - size_y: 2048 - 16, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new( - GridIdx2D::new([512, 512]), - GridIdx2D::new([1023 - 4, 1023 - 4]) - ) - .unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(16., -16.), 1., -1.), - GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 2048 - 16, - size_y: 2048 - 16, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop_numbers() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(17.123_456, -17.123_456), 1., -1.), - GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(1.123_456, -1.123_456), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 2048 - 16, - size_y: 2048 - 16, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); } } diff --git a/geoengine/operators/src/source/gdal_source/reader_mode.rs b/geoengine/operators/src/source/gdal_source/reader_mode.rs new file mode 100644 index 0000000000..5ce5002873 --- /dev/null +++ b/geoengine/operators/src/source/gdal_source/reader_mode.rs @@ -0,0 +1,1168 @@ +use geoengine_datatypes::raster::{ + GridBoundingBox2D, GridBounds, GridContains, GridShapeAccess, GridSize, +}; +use geoengine_datatypes::raster::{GridIdx2D, SpatialGridDefinition}; +use num::Zero; + +use crate::source::gdal_in::process_common::{GdalReadAdvise, GdalReadWindow}; + +#[allow(dead_code)] +#[derive(Copy, Clone, Debug)] +pub enum GdalReaderMode { + // read the original resolution + OriginalResolution(ReaderState), + // read an overview level of the dataset + OverviewLevel(OverviewReaderState), +} + +impl GdalReaderMode { + /// Returns the read advise for the tiling based bounds + pub fn tiling_to_dataset_read_advise( + &self, + actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, + tile: &SpatialGridDefinition, + ) -> Option { + match self { + GdalReaderMode::OriginalResolution(re) => { + re.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) + } + GdalReaderMode::OverviewLevel(rs) => { + rs.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) + } + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct ReaderState { + pub dataset_spatial_grid: SpatialGridDefinition, +} + +impl ReaderState { + pub fn tiling_to_dataset_read_advise( + &self, + actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, + tile: &SpatialGridDefinition, + ) -> Option { + // Check if the y_axis is fliped. + let (actual_gdal_dataset_spatial_grid_definition, flip_y) = + if actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .y_axis_is_neg() + == self.dataset_spatial_grid.geo_transform().y_axis_is_neg() + { + (*actual_gdal_dataset_spatial_grid_definition, false) + } else { + ( + actual_gdal_dataset_spatial_grid_definition + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )), + true, + ) + }; + + // Now we can work with a matching dataset. However, we need to reverse the read window later! + + // let's only look at data in the geo engine dataset definition! The intersection is relative to the first elements origin coordinate. + let dataset_gdal_data_intersection = + actual_gdal_dataset_spatial_grid_definition.intersection(&self.dataset_spatial_grid)?; + + // Now, we need the tile in the gdal dataset bounds to identify readable areas + let tile_in_gdal_dataset_bounds = tile.with_moved_origin_exact_grid( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .origin_coordinate, + )?; // TODO: raise error if this fails! + + // Then, calculate the intersection between the datataset and the tile. Again, the intersection is relative to the first elements orrigin coordinate. + let tile_gdal_dataset_intersection = + dataset_gdal_data_intersection.intersection(&tile_in_gdal_dataset_bounds)?; + + // if we need to unflip the dataset grid now is the time to do this. + let tile_intersection_for_read_window = if flip_y { + tile_gdal_dataset_intersection + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )) + } else { + tile_gdal_dataset_intersection + }; + + // generate the read window for GDAL + + let gdal_read_window = GdalReadWindow::new( + tile_intersection_for_read_window.grid_bounds.min_index(), + tile_intersection_for_read_window.grid_bounds.grid_shape(), + ); + + // if the read window has the same shape as the tiling based bounds we can fill that completely + if tile_in_gdal_dataset_bounds == tile_gdal_dataset_intersection { + return Some(GdalReadAdvise { + gdal_read_widow: gdal_read_window, + read_window_bounds: tile.grid_bounds, + bounds_of_target: tile.grid_bounds, + flip_y, + }); + } + + // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds + let crop_tl = + tile_gdal_dataset_intersection.min_index() - tile_in_gdal_dataset_bounds.min_index(); + let crop_lr = + tile_gdal_dataset_intersection.max_index() - tile_in_gdal_dataset_bounds.max_index(); + + let shifted_tl = tile.grid_bounds.min_index() + crop_tl; + let shifted_lr = tile.grid_bounds.max_index() + crop_lr; + + // now we need to adapt the target pixel space read window to the clipped dataset intersection area + let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); + debug_assert!( + tile.grid_bounds().contains(&shifted_readable_bounds), + "readable bounds must be contained in tile bounds" + ); + + Some(GdalReadAdvise { + gdal_read_widow: gdal_read_window, + read_window_bounds: shifted_readable_bounds, + bounds_of_target: tile.grid_bounds, + flip_y, + }) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct OverviewReaderState { + pub original_dataset_grid: SpatialGridDefinition, +} + +impl OverviewReaderState { + pub fn new(original_dataset_grid: SpatialGridDefinition) -> Self { + OverviewReaderState { + original_dataset_grid, + } + } + + /// Returns the read advise for the tiling based bounds + /// This is a bit more complex than for the original resolution reader, because we need to consider that the overview grid might not match the gdal dataset grid. This happens when files cover different areas, then the overview grid is based on the largest area covered by all files. In this case, we need to shift the tile to match the gdal dataset grid and calculate the read window accordingly. + /// The workflow is as follows: + /// 1. We first check if the y-axis is flipped between the overview grid and the gdal dataset grid. If it is, we flip the gdal dataset grid to match the overview grid. This simplifies the calculations later on, because we can work with matching grids. We just need to remember to unflip the data after reading. + /// 2. We calculate the intersection of the gdal dataset grid and the overview grid. This is the area where we can read data from the gdal dataset. If there is no intersection, we can return early and load nothing. + /// 3. We map the tile we want to fill to the original grid. First, we set the tile to use the same origin coordinate as the gdal dataset grid. Then, we calculate the distance between the tile and the gdal dataset grid. If there is a distance, we need to shift the tile to match the gdal dataset grid. + /// 4. We calculate the intersection of the tile and the actual bounds to use to identify what we can really read. This is the area where we can read data from the gdal dataset to fill the tile. + /// 5. If we need to unflip the dataset grid, we unflip the read window to match the original grid. + /// 6. If we needed to shift the tile, we need to shift the read window accordingly to match the gdal dataset grid. + /// 7. Finally, we generate the read window for GDAL and return the read advise. + /// + /// # Panics + /// Panics if the tile cannot be mapped to the gdal dataset grid, which should never happen, because the overview grid is based on the largest area covered by all files. But if it does, it means that there is a bug in the code or a mismatch between the overview grid and the gdal dataset grid. + #[allow(clippy::too_many_lines)] + pub fn tiling_to_dataset_read_advise( + &self, + actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, // This is the spatial grid of an actual gdal file + tile: &SpatialGridDefinition, // This is a tile inside the grid we use for the global dataset consisting of potentially many gdal files... + ) -> Option { + tracing::trace!( + "OverviewReader, global grid: {:?}, gdal file grid {:?}, tile {:?}", + self.original_dataset_grid, + actual_gdal_dataset_spatial_grid_definition, + tile + ); + + // Check if the y_axis is fliped. + let (actual_gdal_dataset_spatial_grid_definition, flip_y) = + if actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .y_axis_is_neg() + == self.original_dataset_grid.geo_transform().y_axis_is_neg() + { + (*actual_gdal_dataset_spatial_grid_definition, false) + } else { + ( + actual_gdal_dataset_spatial_grid_definition + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )), + true, + ) + }; + + // This is the intersection of grid of the gdal file and the global grid we use. Usually the dataset is inside the global dataset grid. + // IF the intersection is empty the we return early and load nothing + // The intersection uses the geo_transform of the gdal dataset which enables us to adress gdal pixels starting at 0,0 + let actual_bounds_to_use_original_resolution = actual_gdal_dataset_spatial_grid_definition + .intersection(&self.original_dataset_grid)?; + + // now we map the tile we want to fill to the original grid. First, we set the tile to use the same origin coordinate as the gdal file/dataset + /* + let tile_with_overview_resolution_in_actual_space = tile.with_moved_origin_exact_grid( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .origin_coordinate(), + ); + */ + + let (tile_with_overview_resolution_in_actual_space, distance) = tile + .with_moved_origin_to_nearest_grid_edge_with_distance( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .origin_coordinate(), + ); + + let needs_shift = !distance.x.is_zero() || !distance.y.is_zero(); + + let tile_with_original_resolution_in_actual_space = + tile_with_overview_resolution_in_actual_space.with_changed_resolution( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .spatial_resolution(), + ); + + // Now we need to intersect the tile and the actual bounds to use to identify what we can really read + let tile_intersection_original_resolution_actual_space = + &tile_with_original_resolution_in_actual_space + .intersection(&actual_bounds_to_use_original_resolution)?; + + if needs_shift { + tracing::trace!( + "The overview grid does not match the gdal file. This happens when files cover different areas!, Tile (OVR): {:?}, Tile (ORIG): {:?}, Distance: {:?}, Readable: {:?}", + tile_with_overview_resolution_in_actual_space, + tile_with_original_resolution_in_actual_space, + distance, + tile_intersection_original_resolution_actual_space + ); + } + + // if we need to unflip the dataset grid now is the time to do this. + let tile_intersection_for_read_window = if flip_y { + tile_intersection_original_resolution_actual_space + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )) + } else { + *tile_intersection_original_resolution_actual_space + }; + + let tile_intersection_for_read_window = if needs_shift { + let shift_x = distance.x + / actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .x_pixel_size(); + let shift_y = distance.y + / actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .y_pixel_size(); + + let offset = GridIdx2D::new_y_x(-(shift_y as isize), -(shift_x as isize)); + + tracing::trace!( + "Shifting read bounds to match gdal pixels. ({shift_y}, {shift_x}) -> {offset:?} " + ); + + tile_intersection_for_read_window.shift_bounds_relative_by_pixel_offset(offset) + } else { + tile_intersection_for_read_window + }; + + // generate the read window for GDAL --> This is what we can read in any case. + let read_window: GdalReadWindow = GdalReadWindow::new( + tile_intersection_for_read_window.min_index(), + tile_intersection_for_read_window.grid_bounds().grid_shape(), + ); + + let is_tile_contained = tile_intersection_for_read_window.grid_bounds() + == tile_with_original_resolution_in_actual_space.grid_bounds(); + + if is_tile_contained { + return Some(GdalReadAdvise { + gdal_read_widow: read_window, + read_window_bounds: tile.grid_bounds, + bounds_of_target: tile.grid_bounds, + flip_y, + }); + } + + // IF we can't read the whole tile, we have to find out which area of the tile we can fill. + let readble_area_in_overview_res = tile_intersection_original_resolution_actual_space + .with_changed_resolution(tile.geo_transform().spatial_resolution()); + + // Calculate the intersection of the readable area and the tile, result is in geotransform of the tile! + let readable_tile_area = tile.intersection(&readble_area_in_overview_res).expect( + "Since there was an intersection earlyer, there must be a part of data to read.", + ); + + // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds + let crop_tl = readable_tile_area.min_index() - tile.min_index(); + let crop_lr = readable_tile_area.max_index() - tile.max_index(); + + let shifted_tl = tile.grid_bounds.min_index() + crop_tl; + let shifted_lr = tile.grid_bounds.max_index() + crop_lr; + + // now we need to adapt the target pixel space read window to the clipped dataset intersection area + let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); + debug_assert!( + tile.grid_bounds().contains(&shifted_readable_bounds), + "readable bounds must be contained in tile bounds" + ); + + Some(GdalReadAdvise { + gdal_read_widow: read_window, + read_window_bounds: shifted_readable_bounds, + bounds_of_target: tile.grid_bounds, + flip_y, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use geoengine_datatypes::{ + primitives::Coordinate2D, + raster::{ + BoundedGrid, GeoTransform, GridBoundingBox2D, GridIdx2D, GridShape2D, + SpatialGridDefinition, + }, + }; + + #[test] + fn reader_state_dataset_geo_transform() { + let reader_state = ReaderState { + dataset_spatial_grid: SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([1024, 1024]).bounding_box(), + ), + }; + + assert_eq!( + reader_state.dataset_spatial_grid.geo_transform(), + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.) + ); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_no_change() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([1024, 1024]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 512, + size_y: 512, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_shifted() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 180, + start_y: 90, + size_x: 180, + size_y: 90, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_shifted_and_clipped() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])) + .unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 190, + start_y: 100, + size_x: 170, + size_y: 80, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_shifted_flipy() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let spatial_grid_flipy = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., -90.), 1., 1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid_flipy, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 180, + start_y: 0, + size_x: 180, + size_y: 90, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert!(tiling_to_dataset_read_advise.flip_y); + } + + /* + #[test] + fn gdal_geotransform_to_read_bounds() { + let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { + origin_coordinate: Coordinate2D::new(0., 0.), + x_pixel_size: 1., + y_pixel_size: -1., + }; + + let gdal_data_size = GridShape2D::new([1024, 1024]); + + let ti: TileInformation = TileInformation::new( + GridIdx([1, 1]), + GridShape2D::new([512, 512]), + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + ); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 512, + size_y: 512, + start_x: 512, + start_y: 512, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([512, 512]), GridIdx([1023, 1023])).unwrap() + ); + } + + #[test] + fn gdal_geotransform_to_read_bounds_half_res() { + let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { + origin_coordinate: Coordinate2D::new(0., 0.), + x_pixel_size: 1., + y_pixel_size: -1., + }; + + let gdal_data_size = GridShape2D::new([1024, 1024]); + + let ti: TileInformation = TileInformation::new( + GridIdx([0, 0]), + GridShape2D::new([512, 512]), + GeoTransform::new(Coordinate2D::new(0., 0.), 2., -2.), + ); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 1024, + size_y: 1024, + start_x: 0, + start_y: 0, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([511, 511])).unwrap() + ); + } + + #[test] + fn gdal_geotransform_to_read_bounds_2x_res() { + let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { + origin_coordinate: Coordinate2D::new(0., 0.), + x_pixel_size: 1., + y_pixel_size: -1., + }; + + let gdal_data_size = GridShape2D::new([1024, 1024]); + + let ti: TileInformation = TileInformation::new( + GridIdx([0, 0]), + GridShape2D::new([512, 512]), + GeoTransform::new(Coordinate2D::new(0., 0.), 0.5, -0.5), + ); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 256, + size_y: 256, + start_x: 0, + start_y: 0, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([511, 511])).unwrap() + ); + } + + #[test] + fn gdal_geotransform_to_read_bounds_ul_out() { + let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { + origin_coordinate: Coordinate2D::new(-3., 3.), + x_pixel_size: 1., + y_pixel_size: -1., + }; + + let gdal_data_size = GridShape2D::new([1024, 1024]); + let tile_grid_shape = GridShape2D::new([512, 512]); + let tiling_global_geo_transfom = GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.); + + let ti: TileInformation = + TileInformation::new(GridIdx([0, 0]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + // since the origin of the tile is at -3,3 and the "coordinate nearest to zero" is 0,0 the tile at tile position 0,0 maps to the read window starting at 3,3 with 512x512 pixels + assert_eq!( + read_window, + GdalReadWindow { + size_x: 512, + size_y: 512, + start_x: 3, + start_y: 3, + } + ); + + // the data maps to the complete tile + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([511, 511])).unwrap() + ); + + let ti: TileInformation = + TileInformation::new(GridIdx([1, 1]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + // since the origin of the tile is at -3,3 and the "coordinate nearest to zero" is 0,0 the tile at tile position 1,1 maps to the read window starting at 515,515 (512+3, 512+3) with 512x512 pixels + assert_eq!( + read_window, + GdalReadWindow { + size_x: 509, + size_y: 509, + start_x: 515, + start_y: 515, + } + ); + + // the data maps only to a part of the tile since the data is only 1024x1024 pixels in size. So the tile at tile position 1,1 maps to the data starting at 515,515 (512+3, 512+3) with 509x509 pixels left. + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([512, 512]), GridIdx([1020, 1020])).unwrap() + ); + } + + #[test] + fn gdal_geotransform_to_read_bounds_ul_in() { + let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { + origin_coordinate: Coordinate2D::new(3., -3.), + x_pixel_size: 1., + y_pixel_size: -1., + }; + + let gdal_data_size = GridShape2D::new([1024, 1024]); + let tile_grid_shape = GridShape2D::new([512, 512]); + let tiling_global_geo_transfom = GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.); + + let ti: TileInformation = + TileInformation::new(GridIdx([0, 0]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + // in this case the data origin is at 3,-3 which is inside the tile at tile position 0,0. Since the tile starts at the "coordinate nearest to zero, which is 0.0,0.0" we need to read the data starting at data 0,0 with 509x509 pixels (512-3, 512-3). + assert_eq!( + read_window, + GdalReadWindow { + size_x: 509, + size_y: 509, + start_x: 0, + start_y: 0, + } + ); + + // in this case, the data only maps to the last 509x509 pixels of the tile. So the data we read does not fill a whole tile. + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([3, 3]), GridIdx([511, 511])).unwrap() + ); + + let ti: TileInformation = + TileInformation::new(GridIdx([1, 1]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 512, + size_y: 512, + start_x: 509, + start_y: 509, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([512, 512]), GridIdx([1023, 1023])).unwrap() + ); + + let ti: TileInformation = + TileInformation::new(GridIdx([2, 2]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 3, + size_y: 3, + start_x: 1021, + start_y: 1021, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([1024, 1024]), GridIdx([1026, 1026])).unwrap() + ); + } + + #[test] + fn gdal_geotransform_to_read_bounds_ul_out_frac_res() { + let gdal_geo_transform: GdalDatasetGeoTransform = GdalDatasetGeoTransform { + origin_coordinate: Coordinate2D::new(-9., 9.), + x_pixel_size: 9., + y_pixel_size: -9., + }; + let gdal_data_size = GridShape2D::new([1024, 1024]); + let tile_grid_shape = GridShape2D::new([512, 512]); + let tiling_global_geo_transfom = GeoTransform::new(Coordinate2D::new(-0., 0.), 3., -3.); + + let ti: TileInformation = + TileInformation::new(GridIdx([0, 0]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 170, // + size_y: 170, + start_x: 1, + start_y: 1, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([0, 0]), GridIdx([512, 512])).unwrap() + ); // we need to read 683 pixels but we only want 682.6666666666666 pixels. + + let ti: TileInformation = + TileInformation::new(GridIdx([1, 1]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 171, + size_y: 171, + start_x: 171, + start_y: 171, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([510, 510]), GridIdx([1025, 1025])).unwrap() + ); + + let ti: TileInformation = + TileInformation::new(GridIdx([2, 2]), tile_grid_shape, tiling_global_geo_transfom); + + let (read_window, target_bounds) = gdal_geo_transform + .grid_bounds_resolution_to_read_window_and_target_grid(gdal_data_size, &ti) + .unwrap(); + + assert_eq!( + read_window, + GdalReadWindow { + size_x: 171, + size_y: 171, + start_x: 342, + start_y: 342, + } + ); + + assert_eq!( + target_bounds, + GridBoundingBox2D::new(GridIdx([1023, 1023]), GridIdx([1535, 1535])).unwrap() + ); + } + */ + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_2() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([1024, 1024]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 2., -2.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 1024, + size_y: 1024, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([2048, 2048]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 2048, + size_y: 2048, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([4096, 4096]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 2048, + start_y: 2048, + size_x: 2048, + size_y: 2048, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_lrcrop() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 2048, + start_y: 2048, + size_x: 2048 - 16, + size_y: 2048 - 16, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new( + GridIdx2D::new([512, 512]), + GridIdx2D::new([1023 - 4, 1023 - 4]) + ) + .unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(16., -16.), 1., -1.), + GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 2048 - 16, + size_y: 2048 - 16, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop_numbers() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(17.123_456, -17.123_456), 1., -1.), + GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(1.123_456, -1.123_456), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 2048 - 16, + size_y: 2048 - 16, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } +} diff --git a/geoengine/operators/src/source/mod.rs b/geoengine/operators/src/source/mod.rs index 696879eb72..5ba7ff73a2 100755 --- a/geoengine/operators/src/source/mod.rs +++ b/geoengine/operators/src/source/mod.rs @@ -1,18 +1,22 @@ mod csv; -mod gdal_source; +pub mod gdal_in; +pub mod gdal_source; mod multi_band_gdal_source; mod ogr_source; pub use self::csv::{ CsvGeometrySpecification, CsvSource, CsvSourceParameters, CsvSourceStream, CsvTimeSpecification, }; -pub use self::gdal_source::{ - FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, GdalLoadingInfo, - GdalLoadingInfoTemporalSlice, GdalLoadingInfoTemporalSliceIterator, GdalMetaDataList, - GdalMetaDataRegular, GdalMetaDataStatic, GdalMetadataMapping, GdalMetadataNetCdfCf, - GdalRetryOptions, GdalSource, GdalSourceError, GdalSourceParameters, GdalSourceProcessor, +pub use self::gdal_in::{ + FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetadataMapping, + GdalProcessPool, GdalProcessPoolAccess, GdalProcessPoolError, GdalRetryOptions, GdalSourceTimePlaceholder, TimeReference, }; +pub use self::gdal_source::{ + GdalLoadingInfo, GdalLoadingInfoTemporalSlice, GdalLoadingInfoTemporalSliceIterator, + GdalMetaDataList, GdalMetaDataRegular, GdalMetaDataStatic, GdalMetadataNetCdfCf, GdalSource, + GdalSourceError, GdalSourceParameters, GdalSourceProcessor, +}; pub use self::multi_band_gdal_source::{ GdalMultiBand, GdalSourceError as MultiBandGdalSourceError, GdalSourceParameters as MultiBandGdalSourceParameters, MultiBandGdalLoadingInfo, diff --git a/geoengine/operators/src/source/multi_band_gdal_source/error.rs b/geoengine/operators/src/source/multi_band_gdal_source/error.rs index ca9411e619..450f638854 100644 --- a/geoengine/operators/src/source/multi_band_gdal_source/error.rs +++ b/geoengine/operators/src/source/multi_band_gdal_source/error.rs @@ -1,3 +1,4 @@ +use crate::source::gdal_in::{GdalProcessPoolError, process_common::IpcProcessError}; use geoengine_datatypes::raster::{GridBoundingBox2D, RasterDataType}; use snafu::Snafu; @@ -6,8 +7,27 @@ use snafu::Snafu; #[snafu(context(suffix(false)))] // disables default `Snafu` suffix pub enum GdalSourceError { #[snafu(display("Unsupported raster type: {raster_type:?}"))] - UnsupportedRasterType { raster_type: RasterDataType }, + UnsupportedRasterType { + raster_type: RasterDataType, + }, #[snafu(display("Unsupported spatial query: {spatial_query:?}"))] - IncompatibleSpatialQuery { spatial_query: GridBoundingBox2D }, + IncompatibleSpatialQuery { + spatial_query: GridBoundingBox2D, + }, + + #[snafu(display("Error in the GdalSource reading process: {source}"))] + IpcProcessError { + source: IpcProcessError, + }, + + GdalProcessPoolError { + source: GdalProcessPoolError, + }, +} + +impl From for GdalSourceError { + fn from(source: GdalProcessPoolError) -> Self { + GdalSourceError::GdalProcessPoolError { source } + } } diff --git a/geoengine/operators/src/source/multi_band_gdal_source/mod.rs b/geoengine/operators/src/source/multi_band_gdal_source/mod.rs index 7573c0756f..53ddf5936f 100644 --- a/geoengine/operators/src/source/multi_band_gdal_source/mod.rs +++ b/geoengine/operators/src/source/multi_band_gdal_source/mod.rs @@ -2,64 +2,45 @@ use crate::engine::{ CanonicOperatorName, MetaData, OperatorData, OperatorName, QueryContext, QueryProcessor, SpatialGridDescriptor, WorkflowOperatorPath, }; +use crate::error::Error; use crate::optimization::{OptimizableOperator, OptimizationError, SourcesMustNotUseOverviews}; -use crate::source::{ - FileNotFoundHandling, GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetadataMapping, -}; -use crate::util::TemporaryGdalThreadLocalConfigOptions; -use crate::util::gdal::gdal_open_dataset_ex; -use crate::util::retry::retry; +use crate::source::GdalDatasetParameters; +use crate::source::multi_band_gdal_source::reader::GdalPoolReader; use crate::{ engine::{ InitializedRasterOperator, RasterOperator, RasterQueryProcessor, RasterResultDescriptor, SourceOperator, TypedRasterQueryProcessor, }, - error::Error, util::Result, }; use async_trait::async_trait; pub use error::GdalSourceError; -use float_cmp::approx_eq; +use futures::TryFutureExt; use futures::stream::{self, BoxStream, StreamExt}; -use gdal::errors::GdalError; -use gdal::raster::{GdalType, RasterBand as GdalRasterBand}; -use gdal::{Dataset as GdalDataset, DatasetOptions, GdalOpenFlags, Metadata as GdalMetadata}; -use gdal_sys::VSICurlPartialClearCache; -use geoengine_datatypes::primitives::{ - QueryRectangle, SpatialPartition2D, SpatialResolution, find_next_best_overview_level, -}; -use geoengine_datatypes::raster::TilingSpatialGridDefinition; use geoengine_datatypes::{ dataset::NamedData, - primitives::{BandSelection, Coordinate2D, RasterQueryRectangle, TimeInterval}, + primitives::{ + BandSelection, QueryRectangle, RasterQueryRectangle, SpatialPartition2D, SpatialResolution, + TimeInterval, find_next_best_overview_level, + }, raster::{ - ChangeGridBounds, EmptyGrid, GeoTransform, Grid, GridBlit, GridBoundingBox2D, GridOrEmpty, - GridSize, MapElements, MaskedGrid, NoDataValueGrid, Pixel, RasterDataType, - RasterProperties, RasterPropertiesEntry, RasterPropertiesEntryType, RasterTile2D, - SpatialGridDefinition, TileInformation, TilingSpecification, + GeoTransform, GridBoundingBox2D, Pixel, RasterDataType, RasterTile2D, + SpatialGridDefinition, TilingSpatialGridDefinition, TilingSpecification, }, }; pub use loading_info::{GdalMultiBand, MultiBandGdalLoadingInfo, TileFile}; use num::{FromPrimitive, integer::div_ceil, integer::div_floor}; -use reader::{ - GdalReadAdvise, GdalReadWindow, GdalReaderMode, GridAndProperties, OverviewReaderState, - ReaderState, -}; +use reader_mode::{GdalReaderMode, OverviewReaderState, ReaderState}; use serde::{Deserialize, Serialize}; -use snafu::{ResultExt, ensure}; -use std::ffi::CString; +use snafu::ensure; use std::marker::PhantomData; -use std::path::Path; use std::time::Instant; use tracing::{debug, trace}; mod error; mod loading_info; mod reader; - -static GDAL_RETRY_INITIAL_BACKOFF_MS: u64 = 1000; -static GDAL_RETRY_MAX_BACKOFF_MS: u64 = 60 * 60 * 1000; -static GDAL_RETRY_EXPONENTIAL_BACKOFF_FACTOR: f64 = 2.; +mod reader_mode; /// Parameters for the GDAL Source Operator #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -168,244 +149,6 @@ where pub _phantom_data: PhantomData, } -struct GdalRasterLoader {} - -impl GdalRasterLoader { - async fn load_tile_from_files_async( - loading_info: MultiBandGdalLoadingInfo, - reader_mode: GdalReaderMode, - tile_information: TileInformation, - time: TimeInterval, - band: u32, - ) -> Result> { - debug!( - "loading tile {:?} for time: {}, band: {band}", - tile_information.global_tile_position.inner(), - time.to_string() - ); - let tile_files = loading_info.tile_files(time, tile_information, band); - - debug!( - "tile_files: {}", - tile_files - .iter() - .map(|tf| tf.file_path.display().to_string()) - .collect::>() - .join(", ") - ); - - let mut tile_raster: GridOrEmpty = - GridOrEmpty::from(EmptyGrid::new(tile_information.global_pixel_bounds())); - - let mut properties = RasterProperties::default(); - let cache_hint = loading_info.cache_hint(); - - for dataset_params in tile_files { - let Some(file_tile) = Self::retrying_load_raster_tile_from_file::( - &dataset_params, - reader_mode, - tile_information, - ) - .await? - else { - debug!("didn't load from file: {:?}", dataset_params.file_path); - continue; - }; - tile_raster.grid_blit_from(&file_tile.grid); - - properties = file_tile.properties; - } - - Ok(RasterTile2D::new_with_properties( - time, - tile_information.global_tile_position, - band, - tile_information.global_geo_transform, - tile_raster.unbounded(), - properties, - cache_hint, - )) - } - - async fn retrying_load_raster_tile_from_file( - dataset_params: &GdalDatasetParameters, - reader_mode: GdalReaderMode, - tile_information: TileInformation, - ) -> Result>> { - // TODO: detect usage of vsi curl properly, e.g. also check for `/vsicurl_streaming` and combinations with `/vsizip` - let is_vsi_curl = dataset_params.file_path.starts_with("/vsicurl/"); - - retry( - dataset_params - .retry - .map(|r| r.max_retries) - .unwrap_or_default(), - GDAL_RETRY_INITIAL_BACKOFF_MS, - GDAL_RETRY_EXPONENTIAL_BACKOFF_FACTOR, - Some(GDAL_RETRY_MAX_BACKOFF_MS), - move || { - let ds = dataset_params.clone(); - let file_path = ds.file_path.clone(); - - async move { - let load_tile_result = crate::util::spawn_blocking(move || { - Self::load_raster_tile_from_file(&ds, reader_mode, tile_information) - }) - .await - .context(crate::error::TokioJoin); - - match load_tile_result { - Ok(Ok(r)) => Ok(r), - Ok(Err(e)) | Err(e) => { - if is_vsi_curl { - // clear the VSICurl cache, to force GDAL to try to re-download the file - // otherwise it will assume any observed error will happen again - clear_gdal_vsi_cache_for_path(file_path.as_path()); - } - - Err(e) - } - } - } - }, - ) - .await - } - - fn load_raster_tile_from_file( - dataset_params: &GdalDatasetParameters, - reader_mode: GdalReaderMode, - tile_information: TileInformation, - ) -> Result>> { - debug!( - "Loading raster tile from file: {:?}", - dataset_params.file_path - ); - let gdal_read_advise: Option = reader_mode.tiling_to_dataset_read_advise( - &dataset_params.spatial_grid_definition(), - &tile_information.spatial_grid_definition(), - ); - - let Some(gdal_read_advise) = gdal_read_advise else { - trace!( - "no read advise returned for tile {:?}, skipping file.", - tile_information.global_tile_position.inner(), - ); - return Ok(None); - }; - - let options = dataset_params - .gdal_open_options - .as_ref() - .map(|o| o.iter().map(String::as_str).collect::>()); - - let config_options = if dataset_params.file_path.extension() == Some("jp2".as_ref()) { - // ensure that GDAL only uses a single thread for JP2 files because otherwise the thread local configs may not be used by all GDAL threads - // TODO: remove this workaround once raster loading is done via separate subprocesses - if let Some(config_options) = &dataset_params.gdal_config_options { - let mut options = config_options.clone(); - options.push(("GDAL_NUM_THREADS".to_string(), "1".to_string())); - Some(options) - } else { - Some(vec![("GDAL_NUM_THREADS".to_string(), "1".to_string())]) - } - } else { - dataset_params.gdal_config_options.clone() - }; - - // reverts the thread local configs on drop - let _thread_local_configs = config_options - .as_ref() - .map(|config_options| TemporaryGdalThreadLocalConfigOptions::new(config_options)); - - let dataset_result = gdal_open_dataset_ex( - &dataset_params.file_path, - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - open_options: options.as_deref(), - ..DatasetOptions::default() - }, - ); - - let dataset = match dataset_result { - Ok(dataset) => dataset, - Err(error) => { - let is_file_not_found = error_is_gdal_file_not_found(&error); - - match dataset_params.file_not_found_handling { - FileNotFoundHandling::NoData if is_file_not_found => return Ok(None), - _ => { - return Err(crate::error::Error::CouldNotOpenGdalDataset { - file_path: dataset_params.file_path.to_string_lossy().to_string(), - }); - } - }; - } - }; - - let rasterband = dataset.rasterband(dataset_params.rasterband_channel)?; - - // overwrite old properties with the properties of the current dataset (z-index) - let properties = read_raster_properties(&dataset, dataset_params, &rasterband); - - let gdal_dataset_geotransform = GdalDatasetGeoTransform::from(dataset.geo_transform()?); - // check that the dataset geo transform is the same as the one we get from GDAL - debug_assert!(approx_eq!( - Coordinate2D, - gdal_dataset_geotransform.origin_coordinate, - dataset_params.geo_transform.origin_coordinate - )); - - debug_assert!(approx_eq!( - f64, - gdal_dataset_geotransform.x_pixel_size, - dataset_params.geo_transform.x_pixel_size - )); - - debug_assert!(approx_eq!( - f64, - gdal_dataset_geotransform.y_pixel_size, - dataset_params.geo_transform.y_pixel_size - )); - - let (gdal_dataset_pixels_x, gdal_dataset_pixels_y) = dataset.raster_size(); - // check that the dataset pixel size is the same as the one we get from GDAL - debug_assert_eq!(gdal_dataset_pixels_x, dataset_params.width); - debug_assert_eq!(gdal_dataset_pixels_y, dataset_params.height); - - Ok(Some(GridAndProperties { - grid: read_grid_from_raster::( - &rasterband, - &gdal_read_advise.gdal_read_widow, - gdal_read_advise.read_window_bounds, - dataset_params, - gdal_read_advise.flip_y, - )?, - properties, - })) - } -} - -fn error_is_gdal_file_not_found(error: &Error) -> bool { - matches!( - error, - Error::Gdal { - source: GdalError::NullPointer { - method_name, - msg - }, - } if *method_name == "GDALOpenEx" && (*msg == "HTTP response code: 404" || msg.ends_with("No such file or directory")) - ) -} - -fn clear_gdal_vsi_cache_for_path(file_path: &Path) { - unsafe { - if let Some(Some(c_string)) = file_path.to_str().map(|s| CString::new(s).ok()) { - VSICurlPartialClearCache(c_string.as_ptr()); - } - } -} - impl GdalSourceProcessor where T: gdal::raster::GdalType + Pixel {} #[async_trait] @@ -421,7 +164,7 @@ where async fn _query<'a>( &'a self, query: RasterQueryRectangle, - _ctx: &'a dyn crate::engine::QueryContext, + ctx: &'a dyn crate::engine::QueryContext, ) -> Result>> { // TODO: check all bands exist @@ -483,6 +226,7 @@ where trace!(loading_info_ms, "loading_info timing"); } + let gdal_worker = ctx.get_gdal_worker(); let time_steps = loading_info.time_steps().to_vec(); let bands = query.attributes().clone().as_vec(); let spatial_tiles = tiling_strategy @@ -505,13 +249,15 @@ where let stream = stream::iter(time_tile_band_iter) .map(move |(time_interval, tile_info, band_idx)| { - GdalRasterLoader::load_tile_from_files_async::

( + GdalPoolReader::load_tile_from_files_async::

( loading_info.clone(), reader_mode, tile_info, time_interval, band_idx, + gdal_worker.clone(), ) + .map_err(Error::from) }) .buffered(16) // TODO: make configurable .boxed(); @@ -885,171 +631,8 @@ impl InitializedRasterOperator for InitializedGdalSourceOperator { } } -/// This method reads the data for a single grid with a specified size from the GDAL dataset. -/// It fails if the tile is not within the dataset. -#[allow(clippy::float_cmp)] -fn read_grid_from_raster( - rasterband: &GdalRasterBand, - read_window: &GdalReadWindow, - out_shape: D, - dataset_params: &GdalDatasetParameters, - flip_y_axis: bool, -) -> Result> -where - T: Pixel + GdalType + Default + FromPrimitive, - D: Clone + GridSize + PartialEq, -{ - let gdal_out_shape = (out_shape.axis_size_x(), out_shape.axis_size_y()); - - let raster_read_start = tracing::enabled!(tracing::Level::TRACE).then(Instant::now); - let buffer = rasterband.read_as::( - read_window.gdal_window_start(), // pixelspace origin - read_window.gdal_window_size(), // pixelspace size - gdal_out_shape, // requested raster size - None, // sampling mode - )?; - if let Some(raster_read_start) = raster_read_start { - let raster_band_read_s = raster_read_start.elapsed().as_secs_f64(); - trace!(raster_band_read_s, "read raster band timing"); - } - - let (_, buffer_data) = buffer.into_shape_and_vec(); - let data_grid = Grid::new(out_shape.clone(), buffer_data)?; - - let data_grid = if flip_y_axis { - data_grid.reversed_y_axis_grid() - } else { - data_grid - }; - - let dataset_mask_flags = rasterband.mask_flags()?; - - if dataset_mask_flags.is_all_valid() { - // debug!("all pixels are valid --> skip no-data and mask handling."); - return Ok(MaskedGrid::new_with_data(data_grid).into()); - } - - if dataset_mask_flags.is_nodata() { - // debug!("raster uses a no-data value --> use no-data handling."); - let no_data_value = dataset_params - .no_data_value - .or_else(|| rasterband.no_data_value()) - .and_then(FromPrimitive::from_f64); - let no_data_value_grid = NoDataValueGrid::new(data_grid, no_data_value); - let grid_or_empty = GridOrEmpty::from(no_data_value_grid); - return Ok(grid_or_empty); - } - - if dataset_mask_flags.is_alpha() { - // debug!("raster uses alpha band to mask pixels."); - if !dataset_params.allow_alphaband_as_mask { - return Err(Error::AlphaBandAsMaskNotAllowed); - } - } - - // debug!("use mask based no-data handling."); - - let mask_band = rasterband.open_mask_band()?; - - let mask_read_start = tracing::enabled!(tracing::Level::TRACE).then(Instant::now); - let mask_buffer = mask_band.read_as::( - read_window.gdal_window_start(), // pixelspace origin - read_window.gdal_window_size(), // pixelspace size - gdal_out_shape, // requested raster size - None, // sampling mode - )?; - - if let Some(mask_read_start) = mask_read_start { - let mask_band_read_s = mask_read_start.elapsed().as_secs_f64(); - trace!(mask_band_read_s, "read mask band timing"); - } - - let (_, mask_buffer_data) = mask_buffer.into_shape_and_vec(); - let mask_grid = Grid::new(out_shape, mask_buffer_data)?.map_elements(|p: u8| p > 0); - - let mask_grid = if flip_y_axis { - mask_grid.reversed_y_axis_grid() - } else { - mask_grid - }; - - let masked_grid = MaskedGrid::new(data_grid, mask_grid)?; - Ok(GridOrEmpty::from(masked_grid)) -} - -/// This method reads the data for a single tile with a specified size from the GDAL dataset and adds the requested metadata as properties to the tile. -fn read_raster_properties( - dataset: &GdalDataset, - dataset_params: &GdalDatasetParameters, - rasterband: &GdalRasterBand, -) -> RasterProperties { - let mut properties = RasterProperties::default(); - - // always read the scale and offset values from the rasterband - properties_from_band(&mut properties, rasterband); - - // read the properties from the dataset and rasterband metadata - if let Some(properties_mapping) = dataset_params.properties_mapping.as_ref() { - properties_from_gdal_metadata(&mut properties, dataset, properties_mapping); - properties_from_gdal_metadata(&mut properties, rasterband, properties_mapping); - } - - properties -} - -fn properties_from_gdal_metadata<'a, I, M>( - properties: &mut RasterProperties, - gdal_dataset: &M, - properties_mapping: I, -) where - I: IntoIterator, - M: GdalMetadata, -{ - let mapping_iter = properties_mapping.into_iter(); - - for m in mapping_iter { - let data = if let Some(domain) = &m.source_key.domain { - gdal_dataset.metadata_item(&m.source_key.key, domain) - } else { - gdal_dataset.metadata_item(&m.source_key.key, "") - }; - - if let Some(d) = data { - let entry = match m.target_type { - RasterPropertiesEntryType::Number => d.parse::().map_or_else( - |_| RasterPropertiesEntry::String(d), - RasterPropertiesEntry::Number, - ), - RasterPropertiesEntryType::String => RasterPropertiesEntry::String(d), - }; - - // debug!( - // "gdal properties key \"{:?}\" => target key \"{:?}\". Value: {:?} ", - // &m.source_key, &m.target_key, &entry - // ); - - properties.insert_property(m.target_key.clone(), entry); - } - } -} - -fn properties_from_band(properties: &mut RasterProperties, gdal_dataset: &GdalRasterBand) { - if let Some(scale) = gdal_dataset.scale() { - properties.set_scale(scale); - } - if let Some(offset) = gdal_dataset.offset() { - properties.set_offset(offset); - } - - // ignore if there is no description - if let Ok(description) = gdal_dataset.description() { - properties.set_description(description); - } -} - #[cfg(test)] mod tests { - use std::path::PathBuf; use std::str::FromStr; use super::*; @@ -1057,6 +640,7 @@ mod tests { ExecutionContext, MockExecutionContext, RasterBandDescriptor, StaticMetaData, TimeDescriptor, }; + use crate::source::{FileNotFoundHandling, GdalDatasetGeoTransform, GdalMetadataMapping}; use crate::test_data; use crate::util::test::raster_tile_from_file; use futures::TryStreamExt; @@ -1064,15 +648,14 @@ mod tests { use geoengine_datatypes::primitives::{ CacheHint, Measurement, SpatialPartition2D, TimeInstance, }; - use geoengine_datatypes::raster::{GridBounds, GridIdx2D}; - use geoengine_datatypes::raster::{RasterPropertiesKey, SpatialGridDefinition}; + use geoengine_datatypes::raster::{ + GridBoundingBox2D, GridBounds, GridIdx2D, GridSize, RasterDataType, + }; + use geoengine_datatypes::raster::{RasterPropertiesEntryType, RasterPropertiesKey}; use geoengine_datatypes::raster::{TileInformation, TilingStrategy}; use geoengine_datatypes::spatial_reference::SpatialReference; use geoengine_datatypes::util::Identifier; - use geoengine_datatypes::util::gdal::hide_gdal_errors; use geoengine_datatypes::util::test::{TestDefault, assert_eq_two_list_of_tiles}; - use httptest::matchers::request; - use httptest::{Expectation, Server, responders}; #[test] fn it_deserializes() { @@ -1392,323 +975,6 @@ mod tests { ); } - #[test] - fn read_raster_and_offset_scale() { - let up_side_down_params = GdalDatasetParameters { - file_path: test_data!("raster/modis_ndvi/cropped/MOD13A2_M_NDVI_2014-04-01_30x30.tif") - .into(), - rasterband_channel: 1, - geo_transform: GdalDatasetGeoTransform { - origin_coordinate: (8.0, 57.4).into(), - x_pixel_size: 0.1, - y_pixel_size: -0.1, - }, - width: 30, - height: 30, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: Some(255.), - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - let tile_information = TileInformation::new( - [0, 0].into(), - [8, 8].into(), - up_side_down_params.geo_transform.try_into().unwrap(), - ); - let reader_mode = GdalReaderMode::OriginalResolution(ReaderState { - dataset_spatial_grid: SpatialGridDefinition::new( - up_side_down_params.geo_transform.try_into().unwrap(), - GridBoundingBox2D::new([0, 0], [29, 29]).unwrap(), - ), - }); - - let GridAndProperties { grid, properties } = - GdalRasterLoader::load_raster_tile_from_file::( - &up_side_down_params, - reader_mode, - tile_information, - ) - .unwrap() - .unwrap(); - - assert!(!grid.is_empty()); - - let grid = grid.into_materialized_masked_grid(); - - assert_eq!(grid.inner_grid.data.len(), 64); - // pixel value are the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt - assert_eq!( - grid.inner_grid.data, - &[ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 127, 107, 255, 255, 255, 255, 255, 164, 185, 182, - 255, 255, 255, 175, 186, 190, 167, 140, 255, 255, 161, 175, 184, 173, 170, 188, - 255, 255, 128, 177, 165, 145, 191, 174, 255, 117, 100, 174, 159, 147, 99, 135 - ] - ); - - assert_eq!(grid.validity_mask.data.len(), 64); - // pixel mask is pixel > 0 from the top left 8x8 block from MOD13A2_M_NDVI_2014-04-01_27x27_bytes.txt - assert_eq!( - grid.validity_mask.data, - &[ - false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, true, true, - false, false, false, false, false, true, true, true, false, false, false, true, - true, true, true, true, false, false, true, true, true, true, true, true, false, - false, true, true, true, true, true, true, false, true, true, true, true, true, - true, true - ] - ); - - assert_eq!(properties.offset_option(), Some(1.)); - assert_eq!(properties.scale_option(), Some(2.)); - - assert!(approx_eq!(f64, properties.offset(), 1.)); - assert!(approx_eq!(f64, properties.scale(), 2.)); - } - - #[test] - fn it_creates_no_data_only_for_missing_files() { - hide_gdal_errors(); - - let ds = GdalDatasetParameters { - file_path: "nonexisting_file.tif".into(), - rasterband_channel: 1, - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - let tile_information = TileInformation::new( - [0, 0].into(), - [8, 8].into(), - ds.geo_transform.try_into().unwrap(), - ); - let reader_mode = GdalReaderMode::OriginalResolution(ReaderState { - dataset_spatial_grid: SpatialGridDefinition::new( - ds.geo_transform.try_into().unwrap(), - GridBoundingBox2D::new([0, 0], [29, 29]).unwrap(), - ), - }); - - let res = - GdalRasterLoader::load_raster_tile_from_file::(&ds, reader_mode, tile_information); - - assert!(res.is_ok()); - - let res = res.unwrap(); - - assert!(res.is_none()); - - let ds = GdalDatasetParameters { - file_path: test_data!("raster/modis_ndvi/MOD13A2_M_NDVI_2014-01-01.TIFF").into(), - rasterband_channel: 100, // invalid channel - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: None, - allow_alphaband_as_mask: true, - retry: None, - }; - - // invalid channel => error - let result = - GdalRasterLoader::load_raster_tile_from_file::(&ds, reader_mode, tile_information); - assert!(result.is_err()); - } - - #[test] - fn it_creates_no_data_only_for_http_404() { - let server = Server::run(); - - server.expect( - Expectation::matching(request::method_path("HEAD", "/non_existing.tif")) - .times(1) - .respond_with(responders::cycle![responders::status_code(404),]), - ); - - server.expect( - Expectation::matching(request::method_path("HEAD", "/internal_error.tif")) - .times(1) - .respond_with(responders::cycle![responders::status_code(500),]), - ); - - let ds = GdalDatasetParameters { - file_path: format!("/vsicurl/{}", server.url_str("/non_existing.tif")).into(), - rasterband_channel: 1, - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: Some(vec![ - ( - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), - ".tif".to_owned(), - ), - ( - "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), - "EMPTY_DIR".to_owned(), - ), - ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), - ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), - ]), - allow_alphaband_as_mask: true, - retry: None, - }; - - // 404 => no data - let tile_information = TileInformation::new( - [0, 0].into(), - [8, 8].into(), - ds.geo_transform.try_into().unwrap(), - ); - let reader_mode = GdalReaderMode::OriginalResolution(ReaderState { - dataset_spatial_grid: SpatialGridDefinition::new( - ds.geo_transform.try_into().unwrap(), - GridBoundingBox2D::new([0, 0], [29, 29]).unwrap(), - ), - }); - - let res = - GdalRasterLoader::load_raster_tile_from_file::(&ds, reader_mode, tile_information); - assert!(res.is_ok()); - let res = res.unwrap(); - assert!(res.is_none()); - - let ds = GdalDatasetParameters { - file_path: format!("/vsicurl/{}", server.url_str("/internal_error.tif")).into(), - rasterband_channel: 1, - geo_transform: TestDefault::test_default(), - width: 100, - height: 100, - file_not_found_handling: FileNotFoundHandling::NoData, - no_data_value: None, - properties_mapping: None, - gdal_open_options: None, - gdal_config_options: Some(vec![ - ( - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), - ".tif".to_owned(), - ), - ( - "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), - "EMPTY_DIR".to_owned(), - ), - ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), - ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), - ]), - allow_alphaband_as_mask: true, - retry: None, - }; - - // 500 => error - let res = - GdalRasterLoader::load_raster_tile_from_file::(&ds, reader_mode, tile_information); - assert!(res.is_err()); - } - - #[test] - fn it_retries_only_after_clearing_vsi_cache() { - hide_gdal_errors(); - - let server = Server::run(); - - server.expect( - Expectation::matching(request::method_path("HEAD", "/foo.tif")) - .times(2) - .respond_with(responders::cycle![ - // first generic error - responders::status_code(500), - // then 404 file not found - responders::status_code(404) - ]), - ); - - let file_path: PathBuf = format!("/vsicurl/{}", server.url_str("/foo.tif")).into(); - - let options = Some(vec![ - ( - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS".to_owned(), - ".tif".to_owned(), - ), - ( - "GDAL_DISABLE_READDIR_ON_OPEN".to_owned(), - "EMPTY_DIR".to_owned(), - ), - ("GDAL_HTTP_NETRC".to_owned(), "NO".to_owned()), - ("GDAL_HTTP_MAX_RETRY".to_owned(), "0".to_string()), - ]); - - let _thread_local_configs = options - .as_ref() - .map(|config_options| TemporaryGdalThreadLocalConfigOptions::new(config_options)); - - // first fail - let result = gdal_open_dataset_ex( - file_path.as_path(), - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - ..DatasetOptions::default() - }, - ); - - // it failed, but not with file not found - assert!(result.is_err()); - if let Err(error) = result { - assert!(!error_is_gdal_file_not_found(&error)); - } - - // second fail doesn't even try, so still not "file not found", even though it should be now - let result = gdal_open_dataset_ex( - file_path.as_path(), - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - ..DatasetOptions::default() - }, - ); - - assert!(result.is_err()); - if let Err(error) = result { - assert!(!error_is_gdal_file_not_found(&error)); - } - - clear_gdal_vsi_cache_for_path(file_path.as_path()); - - // after clearing the cache, it tries again - let result = gdal_open_dataset_ex( - file_path.as_path(), - DatasetOptions { - open_flags: GdalOpenFlags::GDAL_OF_RASTER, - ..DatasetOptions::default() - }, - ); - - // now we get the file not found error - assert!(result.is_err()); - if let Err(error) = result { - assert!(error_is_gdal_file_not_found(&error)); - } - } - fn add_multi_tile_dataset( ctx: &mut MockExecutionContext, mut files: Vec, diff --git a/geoengine/operators/src/source/multi_band_gdal_source/reader.rs b/geoengine/operators/src/source/multi_band_gdal_source/reader.rs index 9b9192a0ed..68a742918b 100644 --- a/geoengine/operators/src/source/multi_band_gdal_source/reader.rs +++ b/geoengine/operators/src/source/multi_band_gdal_source/reader.rs @@ -1,1099 +1,180 @@ +use gdal::raster::GdalType; use geoengine_datatypes::{ - primitives::AxisAlignedRectangle, + primitives::TimeInterval, raster::{ - GridBoundingBox2D, GridBounds, GridContains, GridIdx2D, GridOrEmpty, GridShape2D, - GridShapeAccess, GridSize, RasterProperties, SpatialGridDefinition, + ChangeGridBounds, EmptyGrid, GridBlit, GridBoundingBox2D, GridOrEmpty, MaskedGrid, Pixel, + RasterProperties, RasterTile2D, TileInformation, }, }; -use tracing::{trace, warn}; - -/// This struct is used to advise the GDAL reader how to read the data from the dataset. -/// The Workflow is as follows: -/// 1. The `gdal_read_window` is the window in the pixel space of the dataset that should be read. -/// 2. The `read_window_bounds` is the area in the target pixel space where the data should be placed. -/// 2.1 The data read in step one is read to the width and height of the `read_window_bounds`. -/// 2.2 if `flip_y` is true the data is flipped in the y direction. And should be unflipped after reading. -/// 3. The `bounds_of_target` is the area in the target pixel space where the data should be placed. -/// 3.1 The `read_window_bounds` might be offset from the `bounds_of_target` or might have a different size. -/// Then, the data needs to be placed in the target pixel space accordingly. Other parts of the target pixel space should be filled with nodata. -#[allow(dead_code)] -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct GdalReadAdvise { - pub gdal_read_widow: GdalReadWindow, - pub read_window_bounds: GridBoundingBox2D, - pub bounds_of_target: GridBoundingBox2D, - pub flip_y: bool, -} - -// TODO: re-implement direct read(?) -// impl GdalReadAdvise { -// pub fn direct_read(&self) -> bool { -// self.read_window_bounds == self.bounds_of_target -// } -// } +use num::FromPrimitive; +use tracing::{debug, trace}; + +use crate::source::{ + MultiBandGdalLoadingInfo, MultiBandGdalSourceError, + gdal_in::{ + FileNotFoundHandling, GdalDatasetParameters, GdalPoolWorkerInstance, GdalProcessPoolError, + GridAndProperties, + process_common::{ + GdalReadAdvise, IpcChannelMessage, IpcChannelMessagePayload, IpcProcessError, + IpcProcessGdalErrorKind, + }, + }, + multi_band_gdal_source::reader_mode::GdalReaderMode, +}; -#[allow(dead_code)] -#[derive(Copy, Clone, Debug)] -pub enum GdalReaderMode { - // read the original resolution - OriginalResolution(ReaderState), - // read an overview level of the dataset - OverviewLevel(OverviewReaderState), -} +pub(crate) struct GdalPoolReader(GdalPoolWorkerInstance); -impl GdalReaderMode { - /// Returns the read advise for the tiling based bounds - pub fn tiling_to_dataset_read_advise( - &self, - actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, - tile: &SpatialGridDefinition, - ) -> Option { - match self { - GdalReaderMode::OriginalResolution(re) => { - re.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) - } - GdalReaderMode::OverviewLevel(rs) => { - rs.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) - } - } +impl From for GdalPoolReader { + fn from(value: GdalPoolWorkerInstance) -> Self { + GdalPoolReader(value) } } -#[derive(Copy, Clone, Debug)] -pub struct ReaderState { - pub dataset_spatial_grid: SpatialGridDefinition, -} - -impl ReaderState { - pub fn tiling_to_dataset_read_advise( - &self, - actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, - tile: &SpatialGridDefinition, - ) -> Option { - // Check if the y_axis is fliped. - let (actual_gdal_dataset_spatial_grid_definition, flip_y) = - if actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .y_axis_is_neg() - == self.dataset_spatial_grid.geo_transform().y_axis_is_neg() - { - (*actual_gdal_dataset_spatial_grid_definition, false) - } else { - ( - actual_gdal_dataset_spatial_grid_definition - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )), - true, - ) - }; - - // Now we can work with a matching dataset. However, we need to reverse the read window later! - - // let's only look at data in the geo engine dataset definition! The intersection is relative to the first elements origin coordinate. - let dataset_gdal_data_intersection = - actual_gdal_dataset_spatial_grid_definition.intersection(&self.dataset_spatial_grid)?; - - // Now, we need the tile in the gdal dataset bounds to identify readable areas - let tile_in_gdal_dataset_bounds = tile.with_moved_origin_exact_grid( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .origin_coordinate, - ); // TODO: raise error if this fails! - - let Some(tile_in_gdal_dataset_bounds) = tile_in_gdal_dataset_bounds else { - warn!("BUG: Tile is not in the dataset bounds, skipping read advise generation"); - return None; - }; - - // Then, calculate the intersection between the datataset and the tile. Again, the intersection is relative to the first elements orrigin coordinate. - let tile_gdal_dataset_intersection = - dataset_gdal_data_intersection.intersection(&tile_in_gdal_dataset_bounds)?; - - // if we need to unflip the dataset grid now is the time to do this. - let tile_intersection_for_read_window = if flip_y { - tile_gdal_dataset_intersection - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )) - } else { - tile_gdal_dataset_intersection - }; - - // generate the read window for GDAL - - let gdal_read_window = GdalReadWindow::new( - tile_intersection_for_read_window.grid_bounds.min_index(), - tile_intersection_for_read_window.grid_bounds.grid_shape(), - ); - - // if the read window has the same shape as the tiling based bounds we can fill that completely - if tile_in_gdal_dataset_bounds == tile_gdal_dataset_intersection { - return Some(GdalReadAdvise { - gdal_read_widow: gdal_read_window, - read_window_bounds: tile.grid_bounds, - bounds_of_target: tile.grid_bounds, - flip_y, - }); - } - - // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds - let crop_tl = - tile_gdal_dataset_intersection.min_index() - tile_in_gdal_dataset_bounds.min_index(); - let crop_lr = - tile_gdal_dataset_intersection.max_index() - tile_in_gdal_dataset_bounds.max_index(); - - let shifted_tl = tile.grid_bounds.min_index() + crop_tl; - let shifted_lr = tile.grid_bounds.max_index() + crop_lr; - - // now we need to adapt the target pixel space read window to the clipped dataset intersection area - let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); - debug_assert!( - tile.grid_bounds().contains(&shifted_readable_bounds), - "readable bounds must be contained in tile bounds" - ); - - Some(GdalReadAdvise { - gdal_read_widow: gdal_read_window, - read_window_bounds: shifted_readable_bounds, - bounds_of_target: tile.grid_bounds, - flip_y, - }) +impl GdalPoolReader { + #[inline] + fn worker_instance(&self) -> &GdalPoolWorkerInstance { + &self.0 } -} - -#[derive(Copy, Clone, Debug)] -pub struct OverviewReaderState { - pub original_dataset_grid: SpatialGridDefinition, - pub overview_level: u32, -} -impl OverviewReaderState { - /// Compute the `ReadAdvise` for a given input gdal dataset (file) and output tile, i.e., which pixels we need to read and where to put them in the output tile. + /// Loads a tile using the separate process and the provided parameters and read advise. + /// This is the method where the source operator attaches to the process pool. + /// The method sends a message to the process pool and waits for the response. The response is then converted to a `RasterTile2D` and returned. + /// The method also handles the case where the file is not found and the `file_not_found_handling` is set to `NoData` by returning a tile filled with nodata values. + /// # Errors + /// Returns a `GdalSourceError` if the process returns an error, or if the response cannot be converted to a `RasterTile2D`. /// - /// There are some special cases here, because while the grid of the input file must be aligned with the output tile, - /// the overviews of (a) the geo engine raster and (b) the indiviual file at hand, must not necessarily be aligned. - /// This is, e.g., the case if the file's origin distance from the geo engine datasets's global origin is not a multiple of 2. - /// In that case, for building a pixel in the overview we would need to take one pixel from one file, and another pixel from a different file. - /// Currently, we do not do this, however we need to correctly calculate the read window for the overview that - /// (1) reads from the file s.t. the existing overviews (pyramid levels) can be used - /// (2) all pixels in the output tile that can be filled, are filled - pub fn tiling_to_dataset_read_advise( + /// # Panics + /// Panics if the response from the process is not in the expected format, or if the grid blitting fails (which should not happen if the bounds are correct). + pub async fn load_tile_data_process( &self, - actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, // This is the spatial grid of an actual gdal file - tile: &SpatialGridDefinition, // This is a tile inside the grid we use for the global dataset consisting of potentially many gdal files... - ) -> Option { - // Check if the y_axis is fliped. - let (actual_gdal_dataset_spatial_grid_definition, flip_y) = - self.normalize_y_axis(actual_gdal_dataset_spatial_grid_definition); - - let Some(( - tile_with_original_resolution_in_actual_space, - tile_intersection_original_resolution_actual_space, - tile_intersection_for_read_window, - tile_intersection_for_read_window_snapped_to_overview_level, - )) = self.intersect_tile_and_dataset( - tile, - actual_gdal_dataset_spatial_grid_definition, - flip_y, - ) - else { - trace!( - "no intersection between tile {:?} and dataset grid {:?}, skipping read advise generation, grid: {:?}, overview level: {}", - tile, - actual_gdal_dataset_spatial_grid_definition, - self.original_dataset_grid, - self.overview_level - ); - return None; - }; - - // generate the read window for GDAL --> This is what we can read in any case. - let read_window = GdalReadWindow::new( - tile_intersection_for_read_window_snapped_to_overview_level.min_index(), - tile_intersection_for_read_window_snapped_to_overview_level - .grid_bounds() - .grid_shape(), - ); - - // ensure that start and size of the read window are aligned with the overview level - // must start from overview level aligned pixel - debug_assert_eq!(read_window.start_x % self.overview_level as isize, 0); - debug_assert_eq!(read_window.start_y % self.overview_level as isize, 0); - - // must be multiple of overview level in size, unless we are at the edge of the dataset - debug_assert!( - read_window - .size_x - .is_multiple_of(self.overview_level as usize) - || read_window.start_x + read_window.size_x as isize - == actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_x() as isize - ); - debug_assert!( - read_window - .size_y - .is_multiple_of(self.overview_level as usize) - || read_window.start_y + read_window.size_y as isize - == actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize - ); - - let can_fill_whole_tile = tile_intersection_for_read_window.grid_bounds() - == tile_with_original_resolution_in_actual_space.grid_bounds(); - - let read_window_bounds = if can_fill_whole_tile { - // ensure that the read window is `overview_level` times larger than the tile - debug_assert_eq!( - read_window.size_x / tile.grid_bounds().grid_shape().x(), - self.overview_level as usize - ); - debug_assert_eq!( - read_window.size_y / tile.grid_bounds().grid_shape().y(), - self.overview_level as usize - ); - - trace!("Tile is fully contained"); - - tile.grid_bounds - } else { - // IF we can't fill the whole tile, we have to find out which area of the tile we can fill. - read_window_bounds_for_partially_tile( - tile, - actual_gdal_dataset_spatial_grid_definition, - tile_intersection_original_resolution_actual_space, - ) - }; + dataset_params: GdalDatasetParameters, + local_read_advise: GdalReadAdvise, + ) -> Result>, GdalProcessPoolError> { + let file_not_found_as_no_data = + dataset_params.file_not_found_handling == FileNotFoundHandling::NoData; + + let message = IpcChannelMessage::new_request_tile_message(IpcChannelMessagePayload { + dataset_params, + read_advise: local_read_advise, + data_type: T::TYPE, + }); + + let res: Result<_, _> = self.worker_instance().read_data(message).await; + + let res = match res { + Ok(t) => { + // Here we need to handle edges! + // First, convert response to GridAndProperties + let GridAndProperties { grid, properties } = t.into(); + // Second, flip y-axis if necessary + let grid = if local_read_advise.flip_y { + match grid { + GridOrEmpty::Grid(MaskedGrid { + inner_grid, + validity_mask, + }) => GridOrEmpty::new_grid( + MaskedGrid::new( + inner_grid.reversed_y_axis_grid(), + validity_mask.reversed_y_axis_grid(), + ) + .expect("The bounds of the input grid should be the same after reversing the y axis, so this should never fail"), + ), + GridOrEmpty::Empty(e) => GridOrEmpty::new_empty(e), + } + } else { + grid + }; + + Ok(Some(GridAndProperties { grid, properties })) + } + Err(GdalProcessPoolError::IpcProcessError { + source: + IpcProcessError::GdalError { + kind: IpcProcessGdalErrorKind::FileNotFound, + details: _details, + }, + }) if file_not_found_as_no_data => Ok(None), + Err(other_err) => Err(other_err), + }?; - Some(GdalReadAdvise { - gdal_read_widow: read_window, - read_window_bounds, - bounds_of_target: tile.grid_bounds, - flip_y, - }) + Ok(res) } - fn intersect_tile_and_dataset( + async fn load_tile_grid_props( &self, - tile: &SpatialGridDefinition, - actual_gdal_dataset_spatial_grid_definition: SpatialGridDefinition, - flip_y: bool, - ) -> Option<( - SpatialGridDefinition, - SpatialGridDefinition, - SpatialGridDefinition, - SpatialGridDefinition, - )> { - // This is the intersection of grid of the gdal file and the global grid we use. Usually the dataset is inside the global dataset grid. - // IF the intersection is empty the we return early and load nothing - // The intersection uses the geo_transform of the gdal dataset which enables us to adress gdal pixels starting at 0,0 - let actual_bounds_to_use_original_resolution = actual_gdal_dataset_spatial_grid_definition - .intersection(&self.original_dataset_grid)?; - - let tile_with_overview_resolution_in_actual_space = if let Some(tile) = tile - .with_moved_origin_exact_grid( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .origin_coordinate(), - ) { - tile - } else { - // special case where the original resolution tile's origin is not a valid pixel edge in the overview - // in this case we need to snap to the nearest pixel edge in the original resolution + dataset_params: GdalDatasetParameters, + reader_mode: GdalReaderMode, + tile_information: TileInformation, + ) -> Result>, GdalProcessPoolError> { + let ds_spatial_grid = dataset_params.spatial_grid_definition(); + let tile_spatial_grid = tile_information.spatial_grid_definition(); + let Some(local_read_advise) = + reader_mode.tiling_to_dataset_read_advise(&ds_spatial_grid, &tile_spatial_grid) + else { trace!( - "overview tile grid {:?} does not align with actual datasets grid {:?}, use nearest original edge instead", - tile, actual_gdal_dataset_spatial_grid_definition + "no read advise returned for tile {:?}, skipping file.", + tile_information.global_tile_position, ); - - // move to pixel edge nearest to actual grid edge, the result is within one pixel distance in overview space - let nearest_in_overview_space = tile.with_moved_origin_to_nearest_grid_edge( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .origin_coordinate(), - ); - - // replace the origin with the desired one. this forces the overview grid on the actual grid - nearest_in_overview_space.replace_origin( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .origin_coordinate(), - ) + return Ok(None); }; - let tile_with_original_resolution_in_actual_space = - tile_with_overview_resolution_in_actual_space.with_changed_resolution( - actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .spatial_resolution(), - ); - - let tile_intersection_original_resolution_actual_space = - &tile_with_original_resolution_in_actual_space - .intersection(&actual_bounds_to_use_original_resolution)?; + let file_tile = self + .load_tile_data_process::(dataset_params, local_read_advise) + .await?; - let tile_intersection_for_read_window = if flip_y { - tile_intersection_original_resolution_actual_space - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )) - } else { - *tile_intersection_original_resolution_actual_space - }; - - let tile_intersection_for_read_window_snapped_to_overview_level = - tile_intersection_original_resolution_actual_space.snap_to_dataset_overview_level( - actual_gdal_dataset_spatial_grid_definition, - self.overview_level, - )?; - - Some(( - tile_with_original_resolution_in_actual_space, - *tile_intersection_original_resolution_actual_space, - tile_intersection_for_read_window, - tile_intersection_for_read_window_snapped_to_overview_level, - )) - } - - fn normalize_y_axis( - &self, - actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, - ) -> (SpatialGridDefinition, bool) { - if actual_gdal_dataset_spatial_grid_definition - .geo_transform() - .y_axis_is_neg() - == self.original_dataset_grid.geo_transform().y_axis_is_neg() - { - (*actual_gdal_dataset_spatial_grid_definition, false) - } else { - ( - actual_gdal_dataset_spatial_grid_definition - .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling - .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( - // second: move the origin to the other end of the y-axis - actual_gdal_dataset_spatial_grid_definition - .grid_bounds() - .axis_size_y() as isize, - 0, - )), - true, - ) - } + Ok(file_tile) } -} -fn read_window_bounds_for_partially_tile( - tile: &SpatialGridDefinition, - actual_gdal_dataset_spatial_grid_definition: SpatialGridDefinition, - tile_intersection_original_resolution_actual_space: SpatialGridDefinition, -) -> geoengine_datatypes::raster::GridBoundingBox<[isize; 2]> { - let readable_area_in_overview_res = tile_intersection_original_resolution_actual_space - .with_changed_resolution(tile.geo_transform().spatial_resolution()); + pub async fn load_tile_from_files_async( + loading_info: MultiBandGdalLoadingInfo, + reader_mode: GdalReaderMode, + tile_information: TileInformation, + time: TimeInterval, + band: u32, + gdal_worker: GdalPoolWorkerInstance, + ) -> Result, MultiBandGdalSourceError> { + debug!( + "loading tile {:?} for time: {}, band: {band}", + tile_information.global_tile_position.inner(), + time.to_string() + ); + let tile_files = loading_info.tile_files(time, tile_information, band); - let readable_area_in_overview_res_and_tile_space = - if tile.is_compatible_grid_generic(&readable_area_in_overview_res) { - readable_area_in_overview_res - } else { - // we need to make the readable area grid compatible to the tile in overview space, as the actual origin does not exist in overview space - let tile_edge_idx = tile.geo_transform.coordinate_to_grid_idx_2d( - readable_area_in_overview_res - .geo_transform() - .origin_coordinate(), - ); - let tile_edge_coord = tile - .geo_transform - .grid_idx_to_pixel_upper_left_coordinate_2d(tile_edge_idx); + debug!( + "tile_files: {}", + tile_files + .iter() + .map(|tf| tf.file_path.display().to_string()) + .collect::>() + .join(", ") + ); - let readable_area_in_overview_res_and_tile_space = - readable_area_in_overview_res.replace_origin(tile_edge_coord); + let mut tile_raster: GridOrEmpty = + GridOrEmpty::from(EmptyGrid::new(tile_information.global_pixel_bounds())); - // if the snapping of the origin causes us to lose a pixel, we extend the bounds by one pixel in the respective direction - // if it is still contained in the actual dataset bounds - let mut extend = [0, 0]; - if tile.geo_transform().origin_coordinate().y - tile_edge_coord.y - > tile.geo_transform().y_pixel_size() - { - let max_index = readable_area_in_overview_res_and_tile_space - .grid_bounds() - .max_index() - .y(); + let mut properties = RasterProperties::default(); + let cache_hint = loading_info.cache_hint(); - let new_border_y = readable_area_in_overview_res_and_tile_space - .geo_transform() - .origin_coordinate() - .y - + (readable_area_in_overview_res_and_tile_space - .geo_transform() - .y_pixel_size() - * (max_index + 1) as f64); + let reader = Self::from(gdal_worker); - if new_border_y - < actual_gdal_dataset_spatial_grid_definition - .spatial_partition() - .lower_right() - .y - { - extend[0] = 1; - } - } - - if tile.geo_transform().origin_coordinate().x - tile_edge_coord.x - > tile.geo_transform().x_pixel_size() + for dataset_params in tile_files { + if let Some(file_tile) = reader + .load_tile_grid_props(dataset_params, reader_mode, tile_information) + .await? { - let max_index = readable_area_in_overview_res_and_tile_space - .grid_bounds() - .max_index() - .x(); - - let new_border_x = readable_area_in_overview_res_and_tile_space - .geo_transform() - .origin_coordinate() - .x - + (readable_area_in_overview_res_and_tile_space - .geo_transform() - .x_pixel_size() - * (max_index + 1) as f64); - - if new_border_x - < actual_gdal_dataset_spatial_grid_definition - .spatial_partition() - .lower_right() - .x - { - extend[1] = 1; - } + tile_raster.grid_blit_from(&file_tile.grid); + properties = file_tile.properties; } - - let bounds = GridBoundingBox2D::new_unchecked( - readable_area_in_overview_res_and_tile_space - .grid_bounds() - .min_index(), - readable_area_in_overview_res_and_tile_space - .grid_bounds() - .max_index() - + GridIdx2D::new_y_x(extend[0], extend[1]), - ); - - SpatialGridDefinition::new( - readable_area_in_overview_res_and_tile_space.geo_transform(), - bounds, - ) - }; - - // Calculate the intersection of the readable area and the tile, result is in geotransform of the tile! - let readable_tile_area = tile - .intersection(&readable_area_in_overview_res_and_tile_space) - .expect("Since there was an intersection earlyer, there must be a part of data to read."); - - // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds - let crop_tl = readable_tile_area.min_index() - tile.min_index(); - let crop_lr = readable_tile_area.max_index() - tile.max_index(); - - let shifted_tl = tile.grid_bounds.min_index() + crop_tl; - let shifted_lr = tile.grid_bounds.max_index() + crop_lr; - - // now we need to adapt the target pixel space read window to the clipped dataset intersection area - let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); - debug_assert!( - tile.grid_bounds().contains(&shifted_readable_bounds), - "readable bounds must be contained in tile bounds" - ); - - shifted_readable_bounds -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct GdalReadWindow { - start_x: isize, // pixelspace origin - start_y: isize, - size_x: usize, // pixelspace size - size_y: usize, -} - -impl GdalReadWindow { - pub fn new(start: GridIdx2D, size: GridShape2D) -> Self { - Self { - start_x: start.x(), - start_y: start.y(), - size_x: size.axis_size_x(), - size_y: size.axis_size_y(), } - } - - pub fn gdal_window_start(&self) -> (isize, isize) { - (self.start_x, self.start_y) - } - - pub fn gdal_window_size(&self) -> (usize, usize) { - (self.size_x, self.size_y) - } -} - -pub struct GridAndProperties { - pub grid: GridOrEmpty, - pub properties: RasterProperties, -} - -#[cfg(test)] -mod tests { - use geoengine_datatypes::{ - primitives::Coordinate2D, - raster::{ - BoundedGrid, GeoTransform, GridBoundingBox2D, GridIdx2D, GridShape2D, - SpatialGridDefinition, - }, - }; - - use crate::source::multi_band_gdal_source::reader::{ - GdalReadWindow, OverviewReaderState, ReaderState, - }; - - #[test] - fn reader_state_dataset_geo_transform() { - let reader_state = ReaderState { - dataset_spatial_grid: SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([1024, 1024]).bounding_box(), - ), - }; - - assert_eq!( - reader_state.dataset_spatial_grid.geo_transform(), - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.) - ); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_no_change() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([1024, 1024]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 512, - size_y: 512, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_shifted() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 180, - start_y: 90, - size_x: 180, - size_y: 90, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - #[test] - fn reader_state_tiling_to_dataset_read_advise_shifted_and_clipped() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])) - .unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 190, - start_y: 100, - size_x: 170, - size_y: 80, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_shifted_flipy() { - let spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let spatial_grid_flipy = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(-180., -90.), 1., 1.), - GridShape2D::new([180, 360]).bounding_box(), - ); - - let reader_state = ReaderState { - dataset_spatial_grid: spatial_grid, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &spatial_grid_flipy, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 180, - start_y: 0, - size_x: 180, - size_y: 90, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() - ); - - assert!(tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_2() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([1024, 1024]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - overview_level: 2, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 2., -2.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 1024, - size_y: 1024, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([2048, 2048]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - overview_level: 4, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 2048, - size_y: 2048, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([4096, 4096]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - overview_level: 4, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 2048, - start_y: 2048, - size_x: 2048, - size_y: 2048, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_lrcrop() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), - GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - overview_level: 4, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 2048, - start_y: 2048, - size_x: 2048 - 16, - size_y: 2048 - 16, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new( - GridIdx2D::new([512, 512]), - GridIdx2D::new([1023 - 4, 1023 - 4]) - ) - .unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) - .unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(16., -16.), 1., -1.), - GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - overview_level: 4, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 2048 - 16, - size_y: 2048 - 16, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop_numbers() { - let original_spatial_grid = SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(17.123_456, -17.123_456), 1., -1.), - GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), - ); - - let reader_state = OverviewReaderState { - original_dataset_grid: original_spatial_grid, - overview_level: 4, - }; - - let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( - &original_spatial_grid, - &SpatialGridDefinition::new( - GeoTransform::new(Coordinate2D::new(1.123_456, -1.123_456), 4., -4.), - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), - ), - ); - - assert!(tiling_to_dataset_read_advise.is_some()); - - let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); - - assert_eq!( - tiling_to_dataset_read_advise.gdal_read_widow, - GdalReadWindow { - start_x: 0, - start_y: 0, - size_x: 2048 - 16, - size_y: 2048 - 16, - }, - ); - - assert_eq!( - tiling_to_dataset_read_advise.read_window_bounds, - GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert_eq!( - tiling_to_dataset_read_advise.bounds_of_target, - GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() - ); - - assert!(!tiling_to_dataset_read_advise.flip_y); - } - - #[test] - fn it_snaps_overview_read_window_to_tile_edge() { - let reader_state = OverviewReaderState { - original_dataset_grid: SpatialGridDefinition { - geo_transform: GeoTransform::new(Coordinate2D { x: -180.0, y: 90.0 }, 0.2, -0.2), - grid_bounds: GridBoundingBox2D::new_unchecked([0, 0], [899, 1799]), - }, - overview_level: 2, - }; - - let actual_gdal_dataset_spatial_grid_definition = SpatialGridDefinition { - geo_transform: GeoTransform::new( - Coordinate2D { - x: -45.0, - y: 22.399_999_999_999_99, - }, - 0.2, - -0.2, - ), - grid_bounds: GridBoundingBox2D::new_unchecked([0, 0], [561, 1124]), - }; - - let tile = SpatialGridDefinition { - geo_transform: GeoTransform::new(Coordinate2D { x: 0.0, y: 0.0 }, 0.4, -0.4), - grid_bounds: GridBoundingBox2D::new_unchecked([-512, -512], [-1, -1]), - }; - - let read_advise = reader_state - .tiling_to_dataset_read_advise(&actual_gdal_dataset_spatial_grid_definition, &tile) - .unwrap(); - - assert_eq!( - read_advise.read_window_bounds, - GridBoundingBox2D::new_unchecked([-56, -113], [-1, -1]) - ); + Ok(RasterTile2D::new_with_properties( + time, + tile_information.global_tile_position, + band, + tile_information.global_geo_transform, + tile_raster.unbounded(), + properties, + cache_hint, + )) } } diff --git a/geoengine/operators/src/source/multi_band_gdal_source/reader_mode.rs b/geoengine/operators/src/source/multi_band_gdal_source/reader_mode.rs new file mode 100644 index 0000000000..725a6c95f7 --- /dev/null +++ b/geoengine/operators/src/source/multi_band_gdal_source/reader_mode.rs @@ -0,0 +1,1045 @@ +use geoengine_datatypes::{ + primitives::AxisAlignedRectangle, + raster::{ + GridBoundingBox2D, GridBounds, GridContains, GridIdx2D, GridShapeAccess, GridSize, + SpatialGridDefinition, + }, +}; +use tracing::{trace, warn}; + +use crate::source::gdal_in::process_common::{GdalReadAdvise, GdalReadWindow}; + +#[allow(dead_code)] +#[derive(Copy, Clone, Debug)] +pub enum GdalReaderMode { + // read the original resolution + OriginalResolution(ReaderState), + // read an overview level of the dataset + OverviewLevel(OverviewReaderState), +} + +impl GdalReaderMode { + /// Returns the read advise for the tiling based bounds + pub fn tiling_to_dataset_read_advise( + &self, + actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, + tile: &SpatialGridDefinition, + ) -> Option { + match self { + GdalReaderMode::OriginalResolution(re) => { + re.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) + } + GdalReaderMode::OverviewLevel(rs) => { + rs.tiling_to_dataset_read_advise(actual_gdal_dataset_spatial_grid_definition, tile) + } + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct ReaderState { + pub dataset_spatial_grid: SpatialGridDefinition, +} + +impl ReaderState { + pub fn tiling_to_dataset_read_advise( + &self, + actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, + tile: &SpatialGridDefinition, + ) -> Option { + // Check if the y_axis is fliped. + let (actual_gdal_dataset_spatial_grid_definition, flip_y) = + if actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .y_axis_is_neg() + == self.dataset_spatial_grid.geo_transform().y_axis_is_neg() + { + (*actual_gdal_dataset_spatial_grid_definition, false) + } else { + ( + actual_gdal_dataset_spatial_grid_definition + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )), + true, + ) + }; + + // Now we can work with a matching dataset. However, we need to reverse the read window later! + + // let's only look at data in the geo engine dataset definition! The intersection is relative to the first elements origin coordinate. + let dataset_gdal_data_intersection = + actual_gdal_dataset_spatial_grid_definition.intersection(&self.dataset_spatial_grid)?; + + // Now, we need the tile in the gdal dataset bounds to identify readable areas + let tile_in_gdal_dataset_bounds = tile.with_moved_origin_exact_grid( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .origin_coordinate, + ); // TODO: raise error if this fails! + + let Some(tile_in_gdal_dataset_bounds) = tile_in_gdal_dataset_bounds else { + warn!("BUG: Tile is not in the dataset bounds, skipping read advise generation"); + return None; + }; + + // Then, calculate the intersection between the datataset and the tile. Again, the intersection is relative to the first elements orrigin coordinate. + let tile_gdal_dataset_intersection = + dataset_gdal_data_intersection.intersection(&tile_in_gdal_dataset_bounds)?; + + // if we need to unflip the dataset grid now is the time to do this. + let tile_intersection_for_read_window = if flip_y { + tile_gdal_dataset_intersection + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )) + } else { + tile_gdal_dataset_intersection + }; + + // generate the read window for GDAL + + let gdal_read_window = GdalReadWindow::new( + tile_intersection_for_read_window.grid_bounds.min_index(), + tile_intersection_for_read_window.grid_bounds.grid_shape(), + ); + + // if the read window has the same shape as the tiling based bounds we can fill that completely + if tile_in_gdal_dataset_bounds == tile_gdal_dataset_intersection { + return Some(GdalReadAdvise { + gdal_read_widow: gdal_read_window, + read_window_bounds: tile.grid_bounds, + bounds_of_target: tile.grid_bounds, + flip_y, + }); + } + + // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds + let crop_tl = + tile_gdal_dataset_intersection.min_index() - tile_in_gdal_dataset_bounds.min_index(); + let crop_lr = + tile_gdal_dataset_intersection.max_index() - tile_in_gdal_dataset_bounds.max_index(); + + let shifted_tl = tile.grid_bounds.min_index() + crop_tl; + let shifted_lr = tile.grid_bounds.max_index() + crop_lr; + + // now we need to adapt the target pixel space read window to the clipped dataset intersection area + let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); + debug_assert!( + tile.grid_bounds().contains(&shifted_readable_bounds), + "readable bounds must be contained in tile bounds" + ); + + Some(GdalReadAdvise { + gdal_read_widow: gdal_read_window, + read_window_bounds: shifted_readable_bounds, + bounds_of_target: tile.grid_bounds, + flip_y, + }) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct OverviewReaderState { + pub original_dataset_grid: SpatialGridDefinition, + pub overview_level: u32, +} + +impl OverviewReaderState { + /// Compute the `ReadAdvise` for a given input gdal dataset (file) and output tile, i.e., which pixels we need to read and where to put them in the output tile. + /// + /// There are some special cases here, because while the grid of the input file must be aligned with the output tile, + /// the overviews of (a) the geo engine raster and (b) the indiviual file at hand, must not necessarily be aligned. + /// This is, e.g., the case if the file's origin distance from the geo engine datasets's global origin is not a multiple of 2. + /// In that case, for building a pixel in the overview we would need to take one pixel from one file, and another pixel from a different file. + /// Currently, we do not do this, however we need to correctly calculate the read window for the overview that + /// (1) reads from the file s.t. the existing overviews (pyramid levels) can be used + /// (2) all pixels in the output tile that can be filled, are filled + pub fn tiling_to_dataset_read_advise( + &self, + actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, // This is the spatial grid of an actual gdal file + tile: &SpatialGridDefinition, // This is a tile inside the grid we use for the global dataset consisting of potentially many gdal files... + ) -> Option { + // Check if the y_axis is fliped. + let (actual_gdal_dataset_spatial_grid_definition, flip_y) = + self.normalize_y_axis(actual_gdal_dataset_spatial_grid_definition); + + let Some(( + tile_with_original_resolution_in_actual_space, + tile_intersection_original_resolution_actual_space, + tile_intersection_for_read_window, + tile_intersection_for_read_window_snapped_to_overview_level, + )) = self.intersect_tile_and_dataset( + tile, + actual_gdal_dataset_spatial_grid_definition, + flip_y, + ) + else { + trace!( + "no intersection between tile {:?} and dataset grid {:?}, skipping read advise generation, grid: {:?}, overview level: {}", + tile, + actual_gdal_dataset_spatial_grid_definition, + self.original_dataset_grid, + self.overview_level + ); + return None; + }; + + // generate the read window for GDAL --> This is what we can read in any case. + let read_window = GdalReadWindow::new( + tile_intersection_for_read_window_snapped_to_overview_level.min_index(), + tile_intersection_for_read_window_snapped_to_overview_level + .grid_bounds() + .grid_shape(), + ); + + // ensure that start and size of the read window are aligned with the overview level + // must start from overview level aligned pixel + debug_assert_eq!(read_window.start_x % self.overview_level as isize, 0); + debug_assert_eq!(read_window.start_y % self.overview_level as isize, 0); + + // must be multiple of overview level in size, unless we are at the edge of the dataset + debug_assert!( + read_window + .size_x + .is_multiple_of(self.overview_level as usize) + || read_window.start_x + read_window.size_x as isize + == actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_x() as isize + ); + debug_assert!( + read_window + .size_y + .is_multiple_of(self.overview_level as usize) + || read_window.start_y + read_window.size_y as isize + == actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize + ); + + let can_fill_whole_tile = tile_intersection_for_read_window.grid_bounds() + == tile_with_original_resolution_in_actual_space.grid_bounds(); + + let read_window_bounds = if can_fill_whole_tile { + // ensure that the read window is `overview_level` times larger than the tile + debug_assert_eq!( + read_window.size_x / tile.grid_bounds().grid_shape().x(), + self.overview_level as usize + ); + debug_assert_eq!( + read_window.size_y / tile.grid_bounds().grid_shape().y(), + self.overview_level as usize + ); + + trace!("Tile is fully contained"); + + tile.grid_bounds + } else { + // IF we can't fill the whole tile, we have to find out which area of the tile we can fill. + read_window_bounds_for_partially_tile( + tile, + actual_gdal_dataset_spatial_grid_definition, + tile_intersection_original_resolution_actual_space, + ) + }; + + Some(GdalReadAdvise { + gdal_read_widow: read_window, + read_window_bounds, + bounds_of_target: tile.grid_bounds, + flip_y, + }) + } + + fn intersect_tile_and_dataset( + &self, + tile: &SpatialGridDefinition, + actual_gdal_dataset_spatial_grid_definition: SpatialGridDefinition, + flip_y: bool, + ) -> Option<( + SpatialGridDefinition, + SpatialGridDefinition, + SpatialGridDefinition, + SpatialGridDefinition, + )> { + // This is the intersection of grid of the gdal file and the global grid we use. Usually the dataset is inside the global dataset grid. + // IF the intersection is empty the we return early and load nothing + // The intersection uses the geo_transform of the gdal dataset which enables us to adress gdal pixels starting at 0,0 + let actual_bounds_to_use_original_resolution = actual_gdal_dataset_spatial_grid_definition + .intersection(&self.original_dataset_grid)?; + + let tile_with_overview_resolution_in_actual_space = if let Some(tile) = tile + .with_moved_origin_exact_grid( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .origin_coordinate(), + ) { + tile + } else { + // special case where the original resolution tile's origin is not a valid pixel edge in the overview + // in this case we need to snap to the nearest pixel edge in the original resolution + trace!( + "overview tile grid {:?} does not align with actual datasets grid {:?}, use nearest original edge instead", + tile, actual_gdal_dataset_spatial_grid_definition + ); + + // move to pixel edge nearest to actual grid edge, the result is within one pixel distance in overview space + let nearest_in_overview_space = tile.with_moved_origin_to_nearest_grid_edge( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .origin_coordinate(), + ); + + // replace the origin with the desired one. this forces the overview grid on the actual grid + nearest_in_overview_space.replace_origin( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .origin_coordinate(), + ) + }; + + let tile_with_original_resolution_in_actual_space = + tile_with_overview_resolution_in_actual_space.with_changed_resolution( + actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .spatial_resolution(), + ); + + let tile_intersection_original_resolution_actual_space = + &tile_with_original_resolution_in_actual_space + .intersection(&actual_bounds_to_use_original_resolution)?; + + let tile_intersection_for_read_window = if flip_y { + tile_intersection_original_resolution_actual_space + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )) + } else { + *tile_intersection_original_resolution_actual_space + }; + + let tile_intersection_for_read_window_snapped_to_overview_level = + tile_intersection_original_resolution_actual_space.snap_to_dataset_overview_level( + actual_gdal_dataset_spatial_grid_definition, + self.overview_level, + )?; + + Some(( + tile_with_original_resolution_in_actual_space, + *tile_intersection_original_resolution_actual_space, + tile_intersection_for_read_window, + tile_intersection_for_read_window_snapped_to_overview_level, + )) + } + + fn normalize_y_axis( + &self, + actual_gdal_dataset_spatial_grid_definition: &SpatialGridDefinition, + ) -> (SpatialGridDefinition, bool) { + if actual_gdal_dataset_spatial_grid_definition + .geo_transform() + .y_axis_is_neg() + == self.original_dataset_grid.geo_transform().y_axis_is_neg() + { + (*actual_gdal_dataset_spatial_grid_definition, false) + } else { + ( + actual_gdal_dataset_spatial_grid_definition + .flip_axis_y() // first: reverse the coordinate system to match the one used by tiling + .shift_bounds_relative_by_pixel_offset(GridIdx2D::new_y_x( + // second: move the origin to the other end of the y-axis + actual_gdal_dataset_spatial_grid_definition + .grid_bounds() + .axis_size_y() as isize, + 0, + )), + true, + ) + } + } +} + +fn read_window_bounds_for_partially_tile( + tile: &SpatialGridDefinition, + actual_gdal_dataset_spatial_grid_definition: SpatialGridDefinition, + tile_intersection_original_resolution_actual_space: SpatialGridDefinition, +) -> geoengine_datatypes::raster::GridBoundingBox<[isize; 2]> { + let readable_area_in_overview_res = tile_intersection_original_resolution_actual_space + .with_changed_resolution(tile.geo_transform().spatial_resolution()); + + let readable_area_in_overview_res_and_tile_space = + if tile.is_compatible_grid_generic(&readable_area_in_overview_res) { + readable_area_in_overview_res + } else { + // we need to make the readable area grid compatible to the tile in overview space, as the actual origin does not exist in overview space + let tile_edge_idx = tile.geo_transform.coordinate_to_grid_idx_2d( + readable_area_in_overview_res + .geo_transform() + .origin_coordinate(), + ); + let tile_edge_coord = tile + .geo_transform + .grid_idx_to_pixel_upper_left_coordinate_2d(tile_edge_idx); + + let readable_area_in_overview_res_and_tile_space = + readable_area_in_overview_res.replace_origin(tile_edge_coord); + + // if the snapping of the origin causes us to lose a pixel, we extend the bounds by one pixel in the respective direction + // if it is still contained in the actual dataset bounds + let mut extend = [0, 0]; + if tile.geo_transform().origin_coordinate().y - tile_edge_coord.y + > tile.geo_transform().y_pixel_size() + { + let max_index = readable_area_in_overview_res_and_tile_space + .grid_bounds() + .max_index() + .y(); + + let new_border_y = readable_area_in_overview_res_and_tile_space + .geo_transform() + .origin_coordinate() + .y + + (readable_area_in_overview_res_and_tile_space + .geo_transform() + .y_pixel_size() + * (max_index + 1) as f64); + + if new_border_y + < actual_gdal_dataset_spatial_grid_definition + .spatial_partition() + .lower_right() + .y + { + extend[0] = 1; + } + } + + if tile.geo_transform().origin_coordinate().x - tile_edge_coord.x + > tile.geo_transform().x_pixel_size() + { + let max_index = readable_area_in_overview_res_and_tile_space + .grid_bounds() + .max_index() + .x(); + + let new_border_x = readable_area_in_overview_res_and_tile_space + .geo_transform() + .origin_coordinate() + .x + + (readable_area_in_overview_res_and_tile_space + .geo_transform() + .x_pixel_size() + * (max_index + 1) as f64); + + if new_border_x + < actual_gdal_dataset_spatial_grid_definition + .spatial_partition() + .lower_right() + .x + { + extend[1] = 1; + } + } + + let bounds = GridBoundingBox2D::new_unchecked( + readable_area_in_overview_res_and_tile_space + .grid_bounds() + .min_index(), + readable_area_in_overview_res_and_tile_space + .grid_bounds() + .max_index() + + GridIdx2D::new_y_x(extend[0], extend[1]), + ); + + SpatialGridDefinition::new( + readable_area_in_overview_res_and_tile_space.geo_transform(), + bounds, + ) + }; + + // Calculate the intersection of the readable area and the tile, result is in geotransform of the tile! + let readable_tile_area = tile + .intersection(&readable_area_in_overview_res_and_tile_space) + .expect("Since there was an intersection earlyer, there must be a part of data to read."); + + // we need to crop the window to the intersection of the tiling based bounds and the dataset bounds + let crop_tl = readable_tile_area.min_index() - tile.min_index(); + let crop_lr = readable_tile_area.max_index() - tile.max_index(); + + let shifted_tl = tile.grid_bounds.min_index() + crop_tl; + let shifted_lr = tile.grid_bounds.max_index() + crop_lr; + + // now we need to adapt the target pixel space read window to the clipped dataset intersection area + let shifted_readable_bounds = GridBoundingBox2D::new_unchecked(shifted_tl, shifted_lr); + debug_assert!( + tile.grid_bounds().contains(&shifted_readable_bounds), + "readable bounds must be contained in tile bounds" + ); + + shifted_readable_bounds +} + +#[cfg(test)] +mod tests { + use geoengine_datatypes::{ + primitives::Coordinate2D, + raster::{ + BoundedGrid, GeoTransform, GridBoundingBox2D, GridIdx2D, GridShape2D, + SpatialGridDefinition, + }, + }; + + use crate::source::{ + gdal_in::process_common::GdalReadWindow, + multi_band_gdal_source::reader_mode::{OverviewReaderState, ReaderState}, + }; + + #[test] + fn reader_state_dataset_geo_transform() { + let reader_state = ReaderState { + dataset_spatial_grid: SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([1024, 1024]).bounding_box(), + ), + }; + + assert_eq!( + reader_state.dataset_spatial_grid.geo_transform(), + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.) + ); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_no_change() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([1024, 1024]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 512, + size_y: 512, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_shifted() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 180, + start_y: 90, + size_x: 180, + size_y: 90, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_shifted_and_clipped() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])) + .unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 190, + start_y: 100, + size_x: 170, + size_y: 80, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([10, 10]), GridIdx2D::new([99, 189])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_shifted_flipy() { + let spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., 90.), 1., -1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let spatial_grid_flipy = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(-180., -90.), 1., 1.), + GridShape2D::new([180, 360]).bounding_box(), + ); + + let reader_state = ReaderState { + dataset_spatial_grid: spatial_grid, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &spatial_grid_flipy, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 180, + start_y: 0, + size_x: 180, + size_y: 90, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([89, 179])).unwrap() + ); + + assert!(tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_2() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([1024, 1024]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + overview_level: 2, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 2., -2.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 1024, + size_y: 1024, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([2048, 2048]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + overview_level: 4, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 2048, + size_y: 2048, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([4096, 4096]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + overview_level: 4, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 2048, + start_y: 2048, + size_x: 2048, + size_y: 2048, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_lrcrop() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.), + GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + overview_level: 4, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 2048, + start_y: 2048, + size_x: 2048 - 16, + size_y: 2048 - 16, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new( + GridIdx2D::new([512, 512]), + GridIdx2D::new([1023 - 4, 1023 - 4]) + ) + .unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([512, 512]), GridIdx2D::new([1023, 1023])) + .unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(16., -16.), 1., -1.), + GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + overview_level: 4, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(0., 0.), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 2048 - 16, + size_y: 2048 - 16, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn reader_state_tiling_to_dataset_read_advise_overview_4_tile_22_ulcrop_numbers() { + let original_spatial_grid = SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(17.123_456, -17.123_456), 1., -1.), + GridShape2D::new([4096 - 16, 4096 - 16]).bounding_box(), + ); + + let reader_state = OverviewReaderState { + original_dataset_grid: original_spatial_grid, + overview_level: 4, + }; + + let tiling_to_dataset_read_advise = reader_state.tiling_to_dataset_read_advise( + &original_spatial_grid, + &SpatialGridDefinition::new( + GeoTransform::new(Coordinate2D::new(1.123_456, -1.123_456), 4., -4.), + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap(), + ), + ); + + assert!(tiling_to_dataset_read_advise.is_some()); + + let tiling_to_dataset_read_advise = tiling_to_dataset_read_advise.unwrap(); + + assert_eq!( + tiling_to_dataset_read_advise.gdal_read_widow, + GdalReadWindow { + start_x: 0, + start_y: 0, + size_x: 2048 - 16, + size_y: 2048 - 16, + }, + ); + + assert_eq!( + tiling_to_dataset_read_advise.read_window_bounds, + GridBoundingBox2D::new(GridIdx2D::new([4, 4]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert_eq!( + tiling_to_dataset_read_advise.bounds_of_target, + GridBoundingBox2D::new(GridIdx2D::new([0, 0]), GridIdx2D::new([511, 511])).unwrap() + ); + + assert!(!tiling_to_dataset_read_advise.flip_y); + } + + #[test] + fn it_snaps_overview_read_window_to_tile_edge() { + let reader_state = OverviewReaderState { + original_dataset_grid: SpatialGridDefinition { + geo_transform: GeoTransform::new(Coordinate2D { x: -180.0, y: 90.0 }, 0.2, -0.2), + grid_bounds: GridBoundingBox2D::new_unchecked([0, 0], [899, 1799]), + }, + overview_level: 2, + }; + + let actual_gdal_dataset_spatial_grid_definition = SpatialGridDefinition { + geo_transform: GeoTransform::new( + Coordinate2D { + x: -45.0, + y: 22.399_999_999_999_99, + }, + 0.2, + -0.2, + ), + grid_bounds: GridBoundingBox2D::new_unchecked([0, 0], [561, 1124]), + }; + + let tile = SpatialGridDefinition { + geo_transform: GeoTransform::new(Coordinate2D { x: 0.0, y: 0.0 }, 0.4, -0.4), + grid_bounds: GridBoundingBox2D::new_unchecked([-512, -512], [-1, -1]), + }; + + let read_advise = reader_state + .tiling_to_dataset_read_advise(&actual_gdal_dataset_spatial_grid_definition, &tile) + .unwrap(); + + assert_eq!( + read_advise.read_window_bounds, + GridBoundingBox2D::new_unchecked([-56, -113], [-1, -1]) + ); + } +} diff --git a/geoengine/operators/src/util/gdal.rs b/geoengine/operators/src/util/gdal.rs index 4d8ecdbeee..0ead094032 100644 --- a/geoengine/operators/src/util/gdal.rs +++ b/geoengine/operators/src/util/gdal.rs @@ -13,7 +13,7 @@ use crate::{ test_data, util::Result, }; -use gdal::{Dataset, DatasetOptions, DriverManager}; +use gdal::{Dataset, DatasetOptions, DriverManager, errors::GdalError}; use geoengine_datatypes::{ collections::VectorDataType, dataset::{DataId, DatasetId, NamedData}, @@ -395,13 +395,20 @@ pub fn gdal_open_dataset(path: &Path) -> Result { /// Opens a Gdal Dataset with the given `path` and `dataset_options`. /// Other crates should use this method for Gdal Dataset access as a workaround to avoid strange errors. pub fn gdal_open_dataset_ex(path: &Path, dataset_options: DatasetOptions) -> Result { + gdal_open_ex_gdal_error(path, dataset_options).context(error::Gdal) +} + +pub fn gdal_open_ex_gdal_error( + path: &Path, + dataset_options: DatasetOptions, +) -> Result { let dataset_options = { let mut dataset_options = dataset_options; dataset_options.open_flags |= gdal::GdalOpenFlags::GDAL_OF_VERBOSE_ERROR; dataset_options }; - Dataset::open_ex(path, dataset_options).context(error::Gdal) + Dataset::open_ex(path, dataset_options) } /// Create a `RasterResultDescriptor` for the given `band` and `dataset`. If the raster data type is diff --git a/geoengine/operators/src/util/gdal_config_options.rs b/geoengine/operators/src/util/gdal_config_options.rs new file mode 100644 index 0000000000..5ff10a4455 --- /dev/null +++ b/geoengine/operators/src/util/gdal_config_options.rs @@ -0,0 +1,119 @@ +use super::Result; + +/// Set thread local gdal options and revert them on drop +pub struct GdalConfigOptions { + original_configs: Vec<(String, Option)>, +} + +impl GdalConfigOptions { + /// Set thread local gdal options and revert them on drop + pub fn new(configs: &[(String, String)]) -> Result { + let mut original_configs = vec![]; + + for (key, value) in configs { + let old = gdal::config::get_config_option(key, "") + .map(|value| if value.is_empty() { None } else { Some(value) })?; + + // TODO: check if overriding existing config (local & global) is ok for the given key + gdal::config::set_config_option(key, value)?; + tracing::trace!("set {key}={value}"); + + original_configs.push((key.clone(), old)); + } + + Ok(Self { original_configs }) + } +} + +impl Drop for GdalConfigOptions { + fn drop(&mut self) { + for (key, value) in &self.original_configs { + if let Some(value) = value { + let _result = gdal::config::set_config_option(key, value); + } else { + let _result = gdal::config::clear_config_option(key); + } + } + } +} + +/// Set thread local gdal options and revert them on drop +pub struct TemporaryGdalThreadLocalConfigOptions { + original_configs: Vec<(String, Option)>, +} + +impl TemporaryGdalThreadLocalConfigOptions { + /// Set thread local gdal options and revert them on drop + pub fn new(configs: &[(String, String)]) -> Result { + let mut original_configs = vec![]; + + for (key, value) in configs { + let old = gdal::config::get_thread_local_config_option(key, "") + .map(|value| if value.is_empty() { None } else { Some(value) })?; + + // TODO: check if overriding existing config (local & global) is ok for the given key + gdal::config::set_thread_local_config_option(key, value)?; + tracing::trace!("set {key}={value}"); + + original_configs.push((key.clone(), old)); + } + + Ok(Self { original_configs }) + } +} + +impl Drop for TemporaryGdalThreadLocalConfigOptions { + fn drop(&mut self) { + for (key, value) in &self.original_configs { + if let Some(value) = value { + let _result = gdal::config::set_thread_local_config_option(key, value); + } else { + let _result = gdal::config::clear_thread_local_config_option(key); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_reverts_config_options() { + let config_options = vec![("foo".to_owned(), "bar".to_owned())]; + + { + let _config = GdalConfigOptions::new(config_options.as_slice()).unwrap(); + + assert_eq!( + gdal::config::get_config_option("foo", "default").unwrap(), + "bar".to_owned() + ); + } + + assert_eq!( + gdal::config::get_config_option("foo", "").unwrap(), + String::new() + ); + } + + #[test] + fn it_reverts_temp_config_options() { + let config_options = vec![("foo".to_owned(), "bar".to_owned())]; + + { + let _config = + TemporaryGdalThreadLocalConfigOptions::new(config_options.as_slice()).unwrap(); + + assert_eq!( + gdal::config::get_config_option("foo", "default").unwrap(), + "bar".to_owned() + ); + } + + assert_eq!( + gdal::config::get_config_option("foo", "").unwrap(), + String::new() + ); + } +} diff --git a/geoengine/operators/src/util/input/float_with_nan_serde.rs b/geoengine/operators/src/util/input/float_with_nan_serde.rs index 066db5423f..e15e3d23f9 100644 --- a/geoengine/operators/src/util/input/float_with_nan_serde.rs +++ b/geoengine/operators/src/util/input/float_with_nan_serde.rs @@ -54,7 +54,11 @@ pub mod float { where D: serde::Deserializer<'de>, { - deserializer.deserialize_any(FloatOrStringVisitor) + if deserializer.is_human_readable() { + deserializer.deserialize_any(FloatOrStringVisitor) + } else { + deserializer.deserialize_f64(FloatOrStringVisitor) + } } /// write no data as either number or "nan" @@ -63,8 +67,12 @@ pub mod float { where S: Serializer, { - if x.is_nan() { - s.serialize_str("nan") + if s.is_human_readable() { + if x.is_nan() { + s.serialize_str("nan") + } else { + s.serialize_f64(*x) + } } else { s.serialize_f64(*x) } @@ -73,6 +81,8 @@ pub mod float { /// Serialize and deserialize float options with special treatment of NaN pub mod float_option { + use core::f64; + use super::*; struct FloatOrStringOptionVisitor; @@ -113,7 +123,13 @@ pub mod float_option { where D: serde::Deserializer<'de>, { - deserializer.deserialize_option(FloatOrStringOptionVisitor) + if deserializer.is_human_readable() { + deserializer.deserialize_option(FloatOrStringOptionVisitor) + } else { + // For binary formats: deserialize f64, then convert NaN → None + let f = deserializer.deserialize_f64(float::FloatOrStringVisitor)?; + if f.is_nan() { Ok(None) } else { Ok(Some(f)) } + } } /// write no data as either number or "nan" @@ -123,10 +139,11 @@ pub mod float_option { where S: Serializer, { - if let Some(x) = x { - float::serialize(x, s) - } else { - s.serialize_none() + match x { + Some(f) if !s.is_human_readable() => s.serialize_f64(*f), + None if !s.is_human_readable() => s.serialize_f64(f64::NAN), + Some(f) => float::serialize(f, s), + None => s.serialize_none(), } } } diff --git a/geoengine/operators/src/util/mod.rs b/geoengine/operators/src/util/mod.rs index 25c4ba6683..3fcdd95bf0 100644 --- a/geoengine/operators/src/util/mod.rs +++ b/geoengine/operators/src/util/mod.rs @@ -1,5 +1,6 @@ mod async_util; pub mod gdal; +mod gdal_config_options; pub mod input; pub mod math; pub mod number_statistics; @@ -11,7 +12,6 @@ pub mod statistics; pub mod stream_zip; pub mod string_token; pub mod sunpos; -mod temporary_gdal_thread_local_config_options; pub mod test; mod wrap_with_projection_and_resample; @@ -23,8 +23,8 @@ use std::sync::{Mutex, MutexGuard}; pub use self::async_util::{ abortable_query_execution, spawn, spawn_blocking, spawn_blocking_with_thread_pool, }; +pub use self::gdal_config_options::{GdalConfigOptions, TemporaryGdalThreadLocalConfigOptions}; pub use self::rayon::create_rayon_thread_pool; -pub use self::temporary_gdal_thread_local_config_options::TemporaryGdalThreadLocalConfigOptions; pub use wrap_with_projection_and_resample::WrapWithProjectionAndResample; pub type Result = std::result::Result; diff --git a/geoengine/operators/src/util/raster_stream_to_geotiff.rs b/geoengine/operators/src/util/raster_stream_to_geotiff.rs index d4a8f9af5f..324a3e4bcb 100644 --- a/geoengine/operators/src/util/raster_stream_to_geotiff.rs +++ b/geoengine/operators/src/util/raster_stream_to_geotiff.rs @@ -1059,7 +1059,6 @@ fn geotiff_to_cog( #[cfg(test)] mod tests { - use std::marker::PhantomData; use std::ops::Add; use crate::engine::{MockExecutionContext, RasterResultDescriptor, TimeDescriptor}; @@ -1084,14 +1083,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1137,14 +1133,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1185,14 +1178,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1238,14 +1228,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1293,14 +1280,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1348,14 +1332,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let mut bytes = raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1423,14 +1404,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1466,14 +1444,11 @@ mod tests { let metadata = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1519,14 +1494,11 @@ mod tests { .tiling_geo_transform() .spatial_to_grid_bounds(&query_bbox); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification: ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let bytes = single_timestep_raster_stream_to_geotiff_bytes( gdal_source.boxed(), @@ -1736,14 +1708,11 @@ mod tests { let tiling_specification = TilingSpecification::new([512, 512].into()); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: metadata.result_descriptor.clone(), - tiling_specification, - overview_level: 0, - meta_data: Box::new(metadata), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + metadata.result_descriptor.clone(), + ctx.tiling_specification(), + Box::new(metadata), + ); let (mut bytes, _) = raster_stream_to_multiband_geotiff_bytes( gdal_source.boxed(), diff --git a/geoengine/operators/src/util/raster_stream_to_png.rs b/geoengine/operators/src/util/raster_stream_to_png.rs index 6c87b71a93..a2568a0e15 100644 --- a/geoengine/operators/src/util/raster_stream_to_png.rs +++ b/geoengine/operators/src/util/raster_stream_to_png.rs @@ -348,7 +348,6 @@ pub enum PngCreationError { #[cfg(test)] mod tests { - use std::marker::PhantomData; use crate::engine::{MockExecutionContext, RasterQueryProcessor}; use crate::{source::GdalSourceProcessor, util::gdal::create_ndvi_meta_data}; @@ -385,14 +384,11 @@ mod tests { let meta_data = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: meta_data.result_descriptor.clone(), + let gdal_source = GdalSourceProcessor::::new_no_overview( + meta_data.result_descriptor.clone(), tiling_specification, - overview_level: 0, - meta_data: Box::new(meta_data), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + Box::new(meta_data), + ); let query = RasterQueryRectangle::new( GridBoundingBox2D::new([-800, -100], [-199, 499]).unwrap(), diff --git a/geoengine/operators/src/util/retry.rs b/geoengine/operators/src/util/retry.rs index 7bcd4366d6..b16c6f4eec 100644 --- a/geoengine/operators/src/util/retry.rs +++ b/geoengine/operators/src/util/retry.rs @@ -42,6 +42,37 @@ where result } +pub fn retry_sync( + mut max_retries: usize, + initial_delay_ms: u64, + exponential_backoff_factor: f64, + max_delay_ms: Option, + mut f: F, + break_early_f: B, +) -> std::result::Result +where + F: FnMut() -> std::result::Result, + B: Fn(&E) -> bool, +{ + let mut result = f(); + let mut sleep_delay = initial_delay_ms as f64; + 'retryloop: while result.is_err() && max_retries > 0 { + std::thread::sleep(Duration::from_millis(sleep_delay as u64)); + result = f(); + if let Err(e) = &result + && break_early_f(e) + { + break 'retryloop; + } + max_retries -= 1; + sleep_delay *= exponential_backoff_factor; + if let Some(max_delay_ms) = max_delay_ms { + sleep_delay = sleep_delay.min(max_delay_ms as f64); + } + } + result +} + #[cfg(test)] mod tests { use std::sync::Arc; diff --git a/geoengine/operators/src/util/temporary_gdal_thread_local_config_options.rs b/geoengine/operators/src/util/temporary_gdal_thread_local_config_options.rs deleted file mode 100644 index f7cf420dac..0000000000 --- a/geoengine/operators/src/util/temporary_gdal_thread_local_config_options.rs +++ /dev/null @@ -1,38 +0,0 @@ -use super::Result; - -/// Set thread local gdal options and revert them on drop -pub struct TemporaryGdalThreadLocalConfigOptions { - original_configs: Vec<(String, Option)>, -} - -impl TemporaryGdalThreadLocalConfigOptions { - /// Set thread local gdal options and revert them on drop - pub fn new(configs: &[(String, String)]) -> Result { - let mut original_configs = vec![]; - - for (key, value) in configs { - let old = gdal::config::get_thread_local_config_option(key, "") - .map(|value| if value.is_empty() { None } else { Some(value) })?; - - // TODO: check if overriding existing config (local & global) is ok for the given key - gdal::config::set_thread_local_config_option(key, value)?; - tracing::trace!("set {key}={value}"); - - original_configs.push((key.clone(), old)); - } - - Ok(Self { original_configs }) - } -} - -impl Drop for TemporaryGdalThreadLocalConfigOptions { - fn drop(&mut self) { - for (key, value) in &self.original_configs { - if let Some(value) = value { - let _result = gdal::config::set_thread_local_config_option(key, value); - } else { - let _result = gdal::config::clear_thread_local_config_option(key); - } - } - } -} diff --git a/geoengine/services/src/api/handlers/plots.rs b/geoengine/services/src/api/handlers/plots.rs index ec0a474841..8509eedcd4 100644 --- a/geoengine/services/src/api/handlers/plots.rs +++ b/geoengine/services/src/api/handlers/plots.rs @@ -14,7 +14,7 @@ use geoengine_datatypes::operations::reproject::reproject_spatial_query; use geoengine_datatypes::plots::PlotOutputFormat; use geoengine_datatypes::primitives::{BoundingBox2D, SpatialResolution}; use geoengine_datatypes::primitives::{PlotQueryRectangle, PlotSeriesSelection}; -use geoengine_datatypes::spatial_reference::SpatialReference; +use geoengine_datatypes::spatial_reference::{DefaultCoordinateProjector, SpatialReference}; use geoengine_operators::engine::{ QueryContext, ResultDescriptor, TypedPlotQueryProcessor, WorkflowOperatorPath, }; @@ -136,7 +136,7 @@ async fn get_plot_handler( let query_rect = if request_spatial_ref == workflow_spatial_ref { Some(query_rect) } else { - let repr_spatial_query = reproject_spatial_query( + let repr_spatial_query = reproject_spatial_query::<_, DefaultCoordinateProjector>( query_rect.spatial_bounds(), workflow_spatial_ref, request_spatial_ref, diff --git a/geoengine/services/src/api/handlers/spatial_references.rs b/geoengine/services/src/api/handlers/spatial_references.rs index e1b93c5917..52f622d691 100755 --- a/geoengine/services/src/api/handlers/spatial_references.rs +++ b/geoengine/services/src/api/handlers/spatial_references.rs @@ -295,7 +295,7 @@ mod tests { use actix_web; use actix_web::http::header; use actix_web_httpauth::headers::authorization::Bearer; - use float_cmp::approx_eq; + use float_cmp::assert_approx_eq; use geoengine_datatypes::primitives::BoundingBox2D; use geoengine_datatypes::spatial_reference::{SpatialReference, SpatialReferenceAuthority}; use tokio_postgres::NoTls; @@ -350,15 +350,15 @@ mod tests { spec.proj_string, "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs", ); - assert!(approx_eq!( + assert_approx_eq!( BoundingBox2D, spec.extent.into(), BoundingBox2D::new_unchecked( (-20_037_508.342_789_244, -20_048_966.104_014_6).into(), (20_037_508.342_789_244, 20_048_966.104_014_594).into() ), - epsilon = 0.000_001 - )); + epsilon = 0.001 // NOTE: changed to match with shortened numbers in static crs-bounds crate. + ); assert_eq!( spec.axis_labels, Some(("Easting".to_owned(), "Northing".to_owned()).into()) @@ -398,22 +398,31 @@ mod tests { SpatialReference::from_str("EPSG:32632").unwrap().into(), ) .unwrap(); + assert_eq!(spec.name, "WGS 84 / UTM zone 32N"); assert_eq!( - SpatialReferenceSpecification { - name: "WGS 84 / UTM zone 32N".to_owned(), - spatial_reference: SpatialReference::new(SpatialReferenceAuthority::Epsg, 32632) - .into(), - proj_string: "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs +type=crs".into(), - extent: BoundingBox2D::new_unchecked( - (166_021.443_080_539_64, 0.0).into(), - (833_978.556_919_460_4, 9_329_005.182_447_437).into() - ) - .into(), - axis_labels: Some(("Easting".to_owned(), "Northing".to_owned()).into()), - axis_order: Some(AxisOrder::EastNorth), - }, - spec + spec.spatial_reference, + SpatialReference::new(SpatialReferenceAuthority::Epsg, 32632).into() ); + assert_eq!( + spec.proj_string, + "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs +type=crs", + ); + assert_approx_eq!( + BoundingBox2D, + spec.extent.into(), + BoundingBox2D::new_unchecked( + (166_021.443_080_539_64, 0.0).into(), + (833_978.556_919_460_4, 9_329_005.182_447_437).into() + ) + .into(), + epsilon = 0.001 // NOTE: changed to match with shortened numbers in static crs-bounds crate. + ); + + assert_eq!( + spec.axis_labels, + Some(("Easting".to_owned(), "Northing".to_owned()).into()) + ); + assert_eq!(spec.axis_order, Some(AxisOrder::EastNorth)); } #[test] diff --git a/geoengine/services/src/api/handlers/wms.rs b/geoengine/services/src/api/handlers/wms.rs index 5435b810f6..e6c9bc9127 100644 --- a/geoengine/services/src/api/handlers/wms.rs +++ b/geoengine/services/src/api/handlers/wms.rs @@ -565,7 +565,6 @@ mod tests { use geoengine_operators::source::GdalSourceProcessor; use geoengine_operators::util::gdal::create_ndvi_meta_data; use std::convert::TryInto; - use std::marker::PhantomData; use tokio_postgres::NoTls; use xml::ParserConfig; @@ -695,14 +694,11 @@ mod tests { let meta_data = create_ndvi_meta_data(); - let gdal_source = GdalSourceProcessor:: { - produced_result_descriptor: meta_data.result_descriptor.clone(), - tiling_specification: exe_ctx.tiling_specification(), - overview_level: 0, - meta_data: Box::new(meta_data), - original_resolution_spatial_grid: None, - _phantom_data: PhantomData, - }; + let gdal_source = GdalSourceProcessor::::new_no_overview( + meta_data.result_descriptor.clone(), + exe_ctx.tiling_specification(), + Box::new(meta_data), + ); let (image_bytes, _) = raster_stream_to_png_bytes( gdal_source.boxed(), diff --git a/geoengine/services/src/config.rs b/geoengine/services/src/config.rs index 9d99e19564..e1b8a24141 100644 --- a/geoengine/services/src/config.rs +++ b/geoengine/services/src/config.rs @@ -363,6 +363,22 @@ impl ConfigElement for Gdal { const KEY: &'static str = "gdal"; } +/// Controlls the global `GdalProcessPool`. +/// - `global_processes`: The total number of GDAL worker processes to spawn and maintain in the pool. +/// - `max_active_global`: The maximum number of active (in-flight) requests allowed across all datasets at any given time. +/// - `max_dataset_processes`: The maximum number of active requests allowed concurrently for the same dataset, enforcing per-dataset concurrency limits. +#[derive(Debug, Deserialize)] +#[allow(clippy::struct_field_names)] +pub struct GdalProcessPool { + pub number_of_processes: u64, + pub max_active_processes: u64, + pub max_dataset_processes: u64, +} + +impl ConfigElement for GdalProcessPool { + const KEY: &'static str = "gdal_process_pool"; +} + #[derive(Debug, Deserialize)] pub struct Session { pub anonymous_access: bool, diff --git a/geoengine/services/src/contexts/mod.rs b/geoengine/services/src/contexts/mod.rs index 793b5eae54..c60a57618a 100644 --- a/geoengine/services/src/contexts/mod.rs +++ b/geoengine/services/src/contexts/mod.rs @@ -17,6 +17,7 @@ use geoengine_datatypes::machine_learning::MlModelName; use geoengine_datatypes::primitives::{RasterQueryRectangle, VectorQueryRectangle}; use geoengine_datatypes::raster::TilingSpecification; use geoengine_operators::cache::cache_operator::InitializedCacheOperator; +use geoengine_operators::cache::new_raster_cache::{NewRasterCacheEnum, RasterCacheOperator}; use geoengine_operators::cache::shared_cache::SharedCache; use geoengine_operators::engine::{ ChunkByteSize, CreateSpan, ExecutionContext, InitializedPlotOperator, @@ -27,23 +28,23 @@ use geoengine_operators::machine_learning::MlModelLoadingInfo; use geoengine_operators::meta::quota::{QuotaCheck, QuotaChecker, QuotaTracking}; use geoengine_operators::meta::wrapper::InitializedOperatorWrapper; use geoengine_operators::mock::MockDatasetDataSourceLoadingInfo; +use geoengine_operators::source::gdal_in::{GdalProcessPool, GdalProcessPoolAccess}; use geoengine_operators::source::{ GdalLoadingInfo, MultiBandGdalLoadingInfo, MultiBandGdalLoadingInfoQueryRectangle, OgrSourceDataset, }; -use rayon::ThreadPool; -use std::str::FromStr; -use std::sync::Arc; -use tokio::sync::RwLock; -use uuid::Uuid; - pub use migrations::{ CurrentSchemaMigration, DatabaseVersion, Migration, MigrationResult, initialize_database, migrate_database, }; pub use postgres::PostgresDb; pub use postgres::{PostgresContext, PostgresSessionContext}; +use rayon::ThreadPool; pub use session::{Session, SessionId}; +use std::str::FromStr; +use std::sync::Arc; +use tokio::sync::RwLock; +use uuid::Uuid; mod db_types; pub(crate) mod migrations; @@ -121,10 +122,12 @@ pub struct QueryContextImpl { tiling_specification: TilingSpecification, thread_pool: Arc, cache: Option>, + new_raster_cache: Option>, quota_tracking: Option, quota_checker: Option, abort_registration: QueryAbortRegistration, abort_trigger: Option, + gdal_process_pool: Arc, } impl QueryContextImpl { @@ -132,6 +135,7 @@ impl QueryContextImpl { chunk_byte_size: ChunkByteSize, tiling_specification: TilingSpecification, thread_pool: Arc, + gdal_process_pool: Arc, ) -> Self { let (abort_registration, abort_trigger) = QueryAbortRegistration::new(); QueryContextImpl { @@ -139,10 +143,12 @@ impl QueryContextImpl { tiling_specification, thread_pool, cache: None, + new_raster_cache: None, quota_tracking: None, quota_checker: None, abort_registration, abort_trigger: Some(abort_trigger), + gdal_process_pool, } } @@ -150,7 +156,10 @@ impl QueryContextImpl { chunk_byte_size: ChunkByteSize, tiling_specification: TilingSpecification, thread_pool: Arc, + gdal_process_pool: Arc, + cache: Option>, + new_raster_cache: Option>, quota_tracking: Option, quota_checker: Option, ) -> Self { @@ -160,10 +169,12 @@ impl QueryContextImpl { tiling_specification, thread_pool, cache, + new_raster_cache, quota_checker, quota_tracking, abort_registration, abort_trigger: Some(abort_trigger), + gdal_process_pool, } } } @@ -203,6 +214,16 @@ impl QueryContext for QueryContextImpl { fn cache(&self) -> Option> { self.cache.clone() } + + fn new_raster_cache(&self) -> Option> { + self.new_raster_cache.clone() + } +} + +impl GdalProcessPoolAccess for QueryContextImpl { + fn get_gdal_pool(&self) -> &Arc { + &self.gdal_process_pool + } } pub struct ExecutionContextImpl @@ -212,6 +233,7 @@ where db: D, thread_pool: Arc, tiling_specification: TilingSpecification, + gdal_process_pool: Arc, } impl ExecutionContextImpl @@ -222,11 +244,13 @@ where db: D, thread_pool: Arc, tiling_specification: TilingSpecification, + gdal_process_pool: Arc, ) -> Self { Self { db, thread_pool, tiling_specification, + gdal_process_pool, } } } @@ -270,7 +294,8 @@ where ) .enabled { - return Box::new(InitializedCacheOperator::new(wrapped)); + return Box::new(RasterCacheOperator::wrap_operator(wrapped)); + //return Box::new(InitializedCacheOperator::new(wrapped)); } wrapped @@ -362,6 +387,15 @@ where } } +impl GdalProcessPoolAccess for ExecutionContextImpl +where + D: DatasetDb + LayerProviderDb, +{ + fn get_gdal_pool(&self) -> &Arc { + &self.gdal_process_pool + } +} + // TODO: use macro(?) for delegating meta_data function to DatasetDB to avoid redundant code #[async_trait] impl diff --git a/geoengine/services/src/contexts/postgres.rs b/geoengine/services/src/contexts/postgres.rs index 62392425a3..fa17c95afd 100644 --- a/geoengine/services/src/contexts/postgres.rs +++ b/geoengine/services/src/contexts/postgres.rs @@ -30,9 +30,11 @@ use bb8_postgres::{ use geoengine_datatypes::machine_learning::MlModelName; use geoengine_datatypes::raster::TilingSpecification; use geoengine_datatypes::util::test::TestDefault; +use geoengine_operators::cache::new_raster_cache::NewRasterCacheEnum; use geoengine_operators::cache::shared_cache::SharedCache; use geoengine_operators::engine::ChunkByteSize; use geoengine_operators::meta::quota::QuotaChecker; +use geoengine_operators::source::gdal_in::{GdalProcessPool, GdalProcessPoolAccess}; use geoengine_operators::util::create_rayon_thread_pool; use rayon::ThreadPool; use snafu::ResultExt; @@ -41,7 +43,6 @@ use std::sync::Arc; use tokio_postgres::error::SqlState; use tracing::info; use uuid::Uuid; - // TODO: do not report postgres error details to user /// A contex with references to Postgres backends of the dbs. Automatically migrates schema on instantiation @@ -62,6 +63,8 @@ where pub(crate) pool: Pool>, volumes: Volumes, tile_cache: Arc, + gdal_process_pool: Arc, + new_raster_cache: Arc, } impl PostgresContext @@ -78,6 +81,7 @@ where query_ctx_chunk_size: ChunkByteSize, quota_config: Quota, oidc_db: OidcManager, + gdal_process_pool_config: crate::config::GdalProcessPool, ) -> Result { let pg_mgr = PostgresConnectionManager::new(config, tls); @@ -93,6 +97,12 @@ where quota_config.increment_quota_buffer_timeout_seconds, ); + let gdal_process_pool: Arc = GdalProcessPool::new( + gdal_process_pool_config.number_of_processes as usize, + gdal_process_pool_config.max_active_processes as usize, + gdal_process_pool_config.max_dataset_processes as usize, + ); + Ok(PostgresContext { task_manager: Default::default(), thread_pool: create_rayon_thread_pool(0), @@ -103,6 +113,8 @@ where pool, volumes: Default::default(), tile_cache: Arc::new(SharedCache::test_default()), + gdal_process_pool, + new_raster_cache: Arc::new(NewRasterCacheEnum::test_default()), }) } @@ -113,6 +125,7 @@ where oidc_db: OidcManager, cache_config: Cache, quota_config: Quota, + gdal_process_pool_config: crate::config::GdalProcessPool, ) -> Result { let pg_mgr = PostgresConnectionManager::new(config, tls); @@ -128,6 +141,12 @@ where quota_config.increment_quota_buffer_timeout_seconds, ); + let gdal_process_pool: Arc = GdalProcessPool::new( + gdal_process_pool_config.number_of_processes as usize, + gdal_process_pool_config.max_active_processes as usize, + gdal_process_pool_config.max_dataset_processes as usize, + ); + Ok(PostgresContext { task_manager: Default::default(), thread_pool: create_rayon_thread_pool(0), @@ -141,6 +160,8 @@ where SharedCache::new(cache_config.size_in_mb, cache_config.landing_zone_ratio) .expect("tile cache creation should work because the config is valid"), ), + gdal_process_pool, + new_raster_cache: Arc::new(NewRasterCacheEnum::test_default()), }) } @@ -158,6 +179,7 @@ where oidc_config: Oidc, cache_config: Cache, quota_config: Quota, + gdal_process_pool_config: crate::config::GdalProcessPool, ) -> Result { let pg_mgr = PostgresConnectionManager::new(config, tls); @@ -173,6 +195,12 @@ where quota_config.increment_quota_buffer_timeout_seconds, ); + let gdal_process_pool: Arc = GdalProcessPool::new( + gdal_process_pool_config.number_of_processes as usize, + gdal_process_pool_config.max_active_processes as usize, + gdal_process_pool_config.max_dataset_processes as usize, + ); + let app_ctx = PostgresContext { task_manager: Default::default(), thread_pool: create_rayon_thread_pool(0), @@ -186,6 +214,8 @@ where SharedCache::new(cache_config.size_in_mb, cache_config.landing_zone_ratio) .expect("tile cache creation should work because the config is valid"), ), + gdal_process_pool, + new_raster_cache: Arc::new(NewRasterCacheEnum::test_default()), }; if created_schema { @@ -277,6 +307,18 @@ where } } +impl GdalProcessPoolAccess for PostgresContext +where + Tls: MakeTlsConnect + Clone + Send + Sync + 'static + std::fmt::Debug, + >::Stream: Send + Sync, + >::TlsConnect: Send, + <>::TlsConnect as TlsConnect>::Future: Send, +{ + fn get_gdal_pool(&self) -> &Arc { + &self.gdal_process_pool + } +} + enum DatabaseStatus { Unitialized, InitializedClearDatabase, @@ -356,7 +398,9 @@ where self.context.query_ctx_chunk_size, self.context.exe_ctx_tiling_spec, self.context.thread_pool.clone(), + self.context.gdal_process_pool.clone(), Some(self.context.tile_cache.clone()), + Some(self.context.new_raster_cache.clone()), Some( self.context .quota @@ -371,6 +415,7 @@ where self.db(), self.context.thread_pool.clone(), self.context.exe_ctx_tiling_spec, + self.context.gdal_process_pool.clone(), )) } diff --git a/geoengine/services/src/datasets/external/copernicus_dataspace/stac.rs b/geoengine/services/src/datasets/external/copernicus_dataspace/stac.rs index b8c2d8ea67..9490ef2ad0 100644 --- a/geoengine/services/src/datasets/external/copernicus_dataspace/stac.rs +++ b/geoengine/services/src/datasets/external/copernicus_dataspace/stac.rs @@ -1,7 +1,7 @@ use geoengine_datatypes::{ - operations::reproject::{CoordinateProjection, CoordinateProjector, ReprojectClipped}, + operations::reproject::ReprojectClipped, primitives::{AxisAlignedRectangle, BoundingBox2D, DateTime, Duration, QueryRectangle}, - spatial_reference::SpatialReference, + spatial_reference::{CoordinateProjection, DefaultCoordinateProjector, SpatialReference}, }; use snafu::{ResultExt, Snafu}; use url::Url; @@ -47,8 +47,9 @@ fn bbox_time_query( let time_start = query.time_interval().start(); let time_end = query.time_interval().end(); + // Note: this uses the `DefaultCoordinateProjector`, which may change. let projector = - CoordinateProjector::from_known_srs(query_projection, SpatialReference::epsg_4326()) + DefaultCoordinateProjector::from_known_srs(query_projection, SpatialReference::epsg_4326()) .context(CannotReprojectBbox)?; let bbox = query.spatial_bounds(); // TODO: use SpatialPartition2D directly diff --git a/geoengine/services/src/datasets/external/sentinel_s2_l2a_cogs/mod.rs b/geoengine/services/src/datasets/external/sentinel_s2_l2a_cogs/mod.rs index bd2435d5cb..e6931dd619 100644 --- a/geoengine/services/src/datasets/external/sentinel_s2_l2a_cogs/mod.rs +++ b/geoengine/services/src/datasets/external/sentinel_s2_l2a_cogs/mod.rs @@ -17,15 +17,15 @@ use crate::workflows::workflow::Workflow; use async_trait::async_trait; use geoengine_datatypes::dataset::{DataId, DataProviderId, LayerId, NamedData}; use geoengine_datatypes::operations::image::{RasterColorizer, RgbaColor}; -use geoengine_datatypes::operations::reproject::{ - CoordinateProjection, CoordinateProjector, Reproject, -}; +use geoengine_datatypes::operations::reproject::Reproject; use geoengine_datatypes::primitives::{AxisAlignedRectangle, CacheTtlSeconds}; use geoengine_datatypes::primitives::{ DateTime, Duration, RasterQueryRectangle, TimeInstance, TimeInterval, VectorQueryRectangle, }; use geoengine_datatypes::raster::{GeoTransform, SpatialGridDefinition}; -use geoengine_datatypes::spatial_reference::{SpatialReference, SpatialReferenceAuthority}; +use geoengine_datatypes::spatial_reference::{ + CoordinateProjection, DefaultCoordinateProjector, SpatialReference, SpatialReferenceAuthority, +}; use geoengine_operators::engine::{ MetaData, MetaDataProvider, OperatorName, RasterBandDescriptors, RasterOperator, RasterResultDescriptor, SpatialGridDescriptor, TypedOperator, VectorResultDescriptor, @@ -661,7 +661,10 @@ impl SentinelS2L2aCogsMetaData { let native_spatial_ref = SpatialReference::new(SpatialReferenceAuthority::Epsg, self.zone.epsg_code()); let epsg_4326_ref = SpatialReference::epsg_4326(); - let projector = CoordinateProjector::from_known_srs(native_spatial_ref, epsg_4326_ref)?; + // Note: this uses the `DefaultCoordinateProjector` which may change. + // TODO: use area provider!!! + let projector = + DefaultCoordinateProjector::from_known_srs(native_spatial_ref, epsg_4326_ref)?; let native_bounds = self.zone.native_extent(); // request all features in zone in order to be able to determine the temporal validity of individual tile @@ -930,8 +933,8 @@ impl MetaDataProvider) { @@ -1207,7 +1206,7 @@ mod tests { "HEAD", "/sentinel-s2-l2a-cogs/36/M/WC/2021/9/S2B_36MWC_20210923_0_L2A/B04.tif", )) - .times(7) + .times(5) //7 .respond_with(responders::cycle![ // first fail responders::status_code(500), @@ -1220,8 +1219,8 @@ mod tests { head_success_response(), // -> GET COG header fails head_success_response(), // -> GET COG header times out head_success_response(), // -> GET COG header succeeds, GET tile fails - head_success_response(), // -> GET COG header succeeds, GET tile times out - head_success_response(), // -> GET COG header succeeds, GET tile IReadBlock failed + //head_success_response(), // -> GET COG header succeeds, GET tile times out + //head_success_response(), // -> GET COG header succeeds, GET tile IReadBlock failed ]), ); @@ -1260,7 +1259,7 @@ mod tests { ), request::headers(contains(("range", "bytes=0-16383"))), ]) - .times(5) + .times(4) .respond_with(responders::cycle![ // first fail responders::status_code(500), @@ -1514,6 +1513,7 @@ mod tests { assert_eq!(result.len(), 1); assert!(result[0].is_ok()); } + */ #[test] fn make_unique_timestamps_no_dups() { diff --git a/geoengine/services/src/datasets/external/stac/loading_info.rs b/geoengine/services/src/datasets/external/stac/loading_info.rs index 68739b0aeb..10ddbb7a89 100644 --- a/geoengine/services/src/datasets/external/stac/loading_info.rs +++ b/geoengine/services/src/datasets/external/stac/loading_info.rs @@ -4,15 +4,15 @@ use crate::util::join_base_url_and_path; use async_trait::async_trait; use chrono::DateTime as ChronoDateTime; use geoengine_datatypes::dataset::DataId; -use geoengine_datatypes::operations::reproject::{ - CoordinateProjection, CoordinateProjector, ReprojectClipped, -}; +use geoengine_datatypes::operations::reproject::ReprojectClipped; use geoengine_datatypes::primitives::{ AxisAlignedRectangle, CacheHint, RasterQueryRectangle, TimeDimension, TimeInstance, TimeInterval, TryRegularTimeFillIterExt, VectorQueryRectangle, }; use geoengine_datatypes::raster::{GeoTransform, GridBoundingBox2D, GridIdx2D, RasterDataType}; -use geoengine_datatypes::spatial_reference::SpatialReference; +use geoengine_datatypes::spatial_reference::{ + CoordinateProjection, DefaultCoordinateProjector, SpatialReference, +}; use geoengine_operators::engine::{ MetaData, MetaDataProvider, RasterBandDescriptors, RasterResultDescriptor, TimeDescriptor, VectorResultDescriptor, @@ -613,9 +613,11 @@ fn stac_query_bbox( spatial_bounds: geoengine_datatypes::primitives::SpatialPartition2D, spatial_reference: SpatialReference, ) -> geoengine_operators::util::Result { - let projector = - CoordinateProjector::from_known_srs(spatial_reference, SpatialReference::epsg_4326()) - .map_err(|_e| geoengine_operators::error::Error::InvalidDataProviderConfig)?; + let projector = DefaultCoordinateProjector::from_known_srs( + spatial_reference, + SpatialReference::epsg_4326(), + ) + .map_err(|_e| geoengine_operators::error::Error::InvalidDataProviderConfig)?; spatial_bounds .as_bbox() diff --git a/geoengine/services/src/server.rs b/geoengine/services/src/server.rs index 9d60ec57de..6ba72b6289 100644 --- a/geoengine/services/src/server.rs +++ b/geoengine/services/src/server.rs @@ -130,6 +130,7 @@ pub async fn start_server(static_files_dir: Option) -> Result<()> { let open_telemetry: crate::config::OpenTelemetry = get_config_element()?; let cache_config: crate::config::Cache = get_config_element()?; let quota_config: crate::config::Quota = get_config_element()?; + let gdal_process_pool_config: crate::config::GdalProcessPool = get_config_element()?; if user_config.registration { info!("User Registration: enabled"); @@ -180,6 +181,7 @@ pub async fn start_server(static_files_dir: Option) -> Result<()> { web_config, cache_config, quota_config, + gdal_process_pool_config, ) .await } @@ -194,6 +196,7 @@ async fn start_postgres( web_config: config::Web, cache_config: crate::config::Cache, quota_config: crate::config::Quota, + gdal_process_pool_config: crate::config::GdalProcessPool, ) -> Result<()> { { let db_config: DatabaseConnectionConfig = @@ -211,6 +214,7 @@ async fn start_postgres( oidc_config, cache_config, quota_config, + gdal_process_pool_config, ) .await?; diff --git a/geoengine/services/src/util/tests.rs b/geoengine/services/src/util/tests.rs index ed7510faf8..3e4f162c9a 100644 --- a/geoengine/services/src/util/tests.rs +++ b/geoengine/services/src/util/tests.rs @@ -1004,6 +1004,7 @@ where get_config_element::().unwrap(), OidcManager::default, f, + get_config_element::().unwrap(), ) .await } @@ -1020,6 +1021,7 @@ pub async fn with_temp_context_from_spec( quota_config: Quota, oidc_db: impl FnOnce() -> OidcManager + std::panic::UnwindSafe + Send + 'static, f: F, + gdal_process_pool_config: crate::config::GdalProcessPool, ) -> R where F: FnOnce(PostgresContext, DatabaseConnectionConfig) -> Fut @@ -1043,6 +1045,7 @@ where query_ctx_chunk_size, quota_config, oidc_db(), + gdal_process_pool_config, ) .await .unwrap(); diff --git a/geoengine/services/src/workflows/websocket_stream.rs b/geoengine/services/src/workflows/websocket_stream.rs index 60dafcc50e..ffb2dcf272 100644 --- a/geoengine/services/src/workflows/websocket_stream.rs +++ b/geoengine/services/src/workflows/websocket_stream.rs @@ -5,7 +5,7 @@ use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, stream::BoxStrea use geoengine_datatypes::{ collections::FeatureCollectionIpc, primitives::{RasterQueryRectangle, VectorQueryRectangle}, - raster::raster_tile_2d_to_arrow_ipc_file, + raster::arrow_conversion::raster_tile_2d_to_arrow_ipc_file, }; use geoengine_operators::{ call_on_generic_raster_processor, call_on_generic_vector_processor,