├── .gitignore ├── src ├── conversion │ ├── mod.rs │ ├── macros.rs │ ├── geojson.rs │ ├── geotypes.rs │ └── coords.rs ├── errors.rs ├── utils.rs └── lib.rs ├── Cargo.toml ├── LICENSE-MIT ├── .github └── workflows │ ├── build_doc.yml │ └── test.yml ├── examples ├── basic_usage.rs ├── skeleton_geojson.rs └── abc.geojson ├── .travis.yml ├── CHANGELOG.md ├── benches └── bench.rs ├── README.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | .idea/ -------------------------------------------------------------------------------- /src/conversion/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | 4 | mod coords; 5 | pub use coords::*; 6 | 7 | #[cfg(feature = "geojson")] 8 | mod geojson; 9 | #[cfg(feature = "geojson")] 10 | pub use self::geojson::*; 11 | 12 | #[cfg(feature = "geo-types")] 13 | mod geotypes; 14 | #[cfg(feature = "geo-types")] 15 | pub use geotypes::*; 16 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::_string; 2 | use anyhow::Error; 3 | use sfcgal_sys::w_sfcgal_get_last_error; 4 | 5 | /// Type alias for [`Result`] with the error type set to [`Error`]. 6 | pub type Result = std::result::Result; 7 | 8 | pub fn get_last_error() -> String { 9 | let message = unsafe { w_sfcgal_get_last_error() }; 10 | _string(message) 11 | } 12 | -------------------------------------------------------------------------------- /src/conversion/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! make_sfcgal_multi_geom { 2 | ($c_geom: expr, $iter: expr) => {{ 3 | let out_multi = unsafe { $c_geom }; 4 | check_null_geom(out_multi)?; 5 | for single_sfcgal_geom in $iter { 6 | unsafe { 7 | sfcgal_geometry_collection_add_geometry( 8 | out_multi, 9 | single_sfcgal_geom as *mut sfcgal_geometry_t, 10 | ) 11 | }; 12 | } 13 | unsafe { SFCGeometry::new_from_raw(out_multi, true) } 14 | }}; 15 | } 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sfcgal" 3 | version = "0.8.2" 4 | edition = "2021" 5 | 6 | authors = ["Matthieu Viry "] 7 | license = "MIT/Apache-2.0" 8 | description = """ 9 | High-level bindings to SFCGAL and conversion from/to other geometry libraries. 10 | """ 11 | 12 | readme = "README.md" 13 | homepage = "https://github.com/mthh/sfcgal-rs" 14 | repository = "https://github.com/mthh/sfcgal-rs" 15 | documentation = "https://mthh.github.io/sfcgal-rs/sfcgal/" 16 | 17 | keywords = ["geometry", "geospatial", "gis", "sfcgal"] 18 | categories = ["algorithms", "api-bindings"] 19 | 20 | [dependencies] 21 | sfcgal-sys = "0.9.0" 22 | anyhow = "1.0" 23 | libc = "0.2" 24 | num-traits = "0.2" 25 | enum-primitive-derive = "0.3" 26 | approx = "0.5" 27 | 28 | geo-types = { version = "0.7", optional = true } 29 | geojson = { version = ">= 0.19, <= 0.24.2", optional = true } 30 | 31 | [features] 32 | default = ["geo-types", "geojson"] 33 | geo-types = ["dep:geo-types"] 34 | geojson = ["dep:geojson"] 35 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Matthieu Viry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /.github/workflows/build_doc.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - "v*.*.*" 5 | 6 | env: 7 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 8 | 9 | name: Build documentation on new release 10 | jobs: 11 | doc: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - uses: actions/checkout@v3 15 | - run: | 16 | sudo apt-get install -y libboost1.74-all-dev libmpfr-dev libgmp-dev cmake wget unzip clang libstdc++-11-dev 17 | wget https://github.com/CGAL/cgal/releases/download/v5.6.1/CGAL-5.6.1-library.tar.xz 18 | wget https://gitlab.com/SFCGAL/SFCGAL/-/archive/v2.1.0/SFCGAL-v2.1.0.zip 19 | tar -xf CGAL-5.6.1-library.tar.xz 20 | unzip SFCGAL-v2.1.0.zip 21 | cd CGAL-5.6.1 && cmake . && sudo make install && cd .. 22 | cd $(ls -d */ | grep SFC) && cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install && cd .. 23 | - run: cargo test --all-features 24 | - run: | 25 | cargo doc 26 | echo "" > target/doc/index.html 27 | sudo pip install ghp-import 28 | ghp-import -n target/doc 29 | git push -fq https://${GITHUB_TOKEN}@github.com/mthh/sfcgal-rs.git gh-pages -------------------------------------------------------------------------------- /examples/basic_usage.rs: -------------------------------------------------------------------------------- 1 | extern crate sfcgal; 2 | use sfcgal::{CoordSeq, SFCGeometry, ToCoordinates, ToSFCGAL}; 3 | 4 | fn main() { 5 | // create a linestring from WKT: 6 | let line_3d = SFCGeometry::new("LINESTRING(-0.5 -0.5 2.5, 0.0 0.0 4.0)").unwrap(); 7 | 8 | // create a polygon as Vec of 3-member tuples... 9 | let coords_polygon = vec![ 10 | vec![ 11 | // Exterior ring 12 | (-1., -1., 3.0), 13 | (1., -1., 3.0), 14 | (1., 1., 3.0), 15 | (-1., 1., 3.0), 16 | (-1., -1., 3.0), 17 | ], 18 | vec![ 19 | // 1 interior ring 20 | (0.1, 0.1, 3.0), 21 | (0.1, 0.9, 3.0), 22 | (0.9, 0.9, 3.0), 23 | (0.9, 0.1, 3.0), 24 | (0.1, 0.1, 3.0), 25 | ], 26 | ]; 27 | // ...by using the CoordSeq enum variants to match the wanted SFCGAL geometry type 28 | // (returns a SFCGeometry) 29 | let polygon_3d = CoordSeq::Polygon(coords_polygon).to_sfcgal().unwrap(); 30 | 31 | let intersects = line_3d.intersects_3d(&polygon_3d).unwrap(); 32 | assert!(intersects); 33 | let intersection = line_3d.intersection_3d(&polygon_3d).unwrap(); 34 | let coords_intersection: CoordSeq<(f64, f64, f64)> = intersection.to_coordinates().unwrap(); 35 | println!( 36 | "{:?} and {:?} intersects at {:?}", 37 | line_3d, polygon_3d, coords_intersection 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | 7 | name: Build and run tests 8 | jobs: 9 | test: 10 | runs-on: ubuntu-22.04 11 | strategy: 12 | matrix: 13 | rust: 14 | - stable 15 | - beta 16 | - nightly 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - run: | 21 | sudo apt-get install -y libboost1.74-all-dev libmpfr-dev libgmp-dev cmake wget unzip clang libstdc++-11-dev 22 | wget https://github.com/CGAL/cgal/releases/download/v5.6.1/CGAL-5.6.1-library.tar.xz 23 | wget https://gitlab.com/SFCGAL/SFCGAL/-/archive/v2.1.0/SFCGAL-v2.1.0.zip 24 | tar -xf CGAL-5.6.1-library.tar.xz 25 | unzip SFCGAL-v2.1.0.zip 26 | cd CGAL-5.6.1 && cmake . && sudo make install && cd .. 27 | cd $(ls -d */ | grep SFC) && cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install && cd .. 28 | 29 | - uses: actions-rs/toolchain@v1 30 | with: 31 | profile: minimal 32 | toolchain: ${{ matrix.rust }} 33 | override: true 34 | components: rustfmt, clippy 35 | 36 | - uses: actions-rs/cargo@v1 37 | with: 38 | command: build 39 | args: --all-features 40 | 41 | - uses: actions-rs/cargo@v1 42 | with: 43 | command: test 44 | args: --all-features 45 | 46 | - uses: actions-rs/cargo@v1 47 | with: 48 | command: fmt 49 | args: --all -- --check 50 | 51 | - uses: actions-rs/cargo@v1 52 | with: 53 | command: clippy 54 | args: -- -D warnings 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | dist: xenial 3 | sudo: required 4 | compiler: 5 | - clang 6 | rust: 7 | - stable 8 | - beta 9 | - nightly 10 | before_install: 11 | - sudo apt-get update 12 | - sudo apt-get install --force-yes clang-3.9 libclang-3.9-dev libboost-chrono1.58-dev 13 | libboost-program-options1.58-dev libboost-filesystem1.58-dev libboost-timer1.58-dev 14 | libboost-test1.58-dev libboost-thread1.58-dev libboost-system1.58-dev libboost-serialization1.58-dev 15 | libmpfr-dev libgmp-dev cmake libwebkit2gtk-4.0-dev 16 | - wget https://github.com/CGAL/cgal/releases/download/releases%2FCGAL-5.0.3/CGAL-5.0.3.tar.xz 17 | - tar xJf CGAL-5.0.3.tar.xz 18 | - cd CGAL-5.0.3 && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=$HOME/CGAL-5.0.3 .. && make && sudo make install && cd ../.. 19 | - wget https://gitlab.com/Oslandia/SFCGAL/-/archive/v1.3.8/SFCGAL-v1.3.8.tar.gz 20 | - tar -xzvf SFCGAL-v1.3.8.tar.gz 21 | - cd SFCGAL-v1.3.8 && cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install && cd .. 22 | after_success: | 23 | [ $TRAVIS_BRANCH = master ] && 24 | [ $TRAVIS_PULL_REQUEST = false ] && 25 | cargo doc && 26 | echo "" > target/doc/index.html && 27 | sudo pip install ghp-import && 28 | ghp-import -n target/doc && 29 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 30 | env: 31 | global: 32 | secure: g3Sylqx0tjcapQrjZNLeY37N5Yg0c5I8BHixsY2CU9v6s+bcKr50C5EGZuw6sOY2Qpj9ggFYIqqcbiEpg8C1GYJ4gnE1P791eJ6aIrTTLNSiss9VBwg6FC/tdtxdRQT2rcaiOMpU4Dp/wTGi7AcQFf9IhdMEtcg2QVGBAwD4x9jEeZwwBc2580jFCz7O4dXMLUHwINWQXzCbqGPqezP+r35VMEFmveykjrJPONS0DTYP0I1mKubCz4CehVFootqTjP/2JseXZouMv5XuzXDlPvRIXsZkN6c7e1xxEVEofVVSbo8PON8/ozOVdROO9UVIHR7pH+rdlh6ZECOXl3t1+hlHPJRjk7dx5DlbovOhSrQNy2nRfnpOgZWYGN9bLHnzgJ7ZiWUiDmnRYwi98yExZyYJ4Av+WWDgAhEl10lZXWaLGWVPutaTYWQcvQ0DZg5vMyjXzK5/e8YC8DOPG+UdsAgXEW+6brQBfhsi+2H6cUu4VhsTDVCYRndKJpsJhnDE0zHNveev4YKfIGazVm4pvlGQytyGQSdOT3G5frHLZYTd95sg7k0O/t8kciGChZWOOKKYJlvc02V+EYRB2ch9S9LdItPtS//qYTpIscT+ne0I6IVY5IHntWjp5XM/wSFDGWl1oJgSiZLFaoBXnN1qidBIHJcpxKTVc1dZb9hUp5Y= 33 | -------------------------------------------------------------------------------- /examples/skeleton_geojson.rs: -------------------------------------------------------------------------------- 1 | extern crate geo_types; 2 | extern crate geojson; 3 | extern crate sfcgal; 4 | 5 | use geojson::{Feature, FeatureCollection, GeoJson, Geometry, Value}; 6 | use sfcgal::{FromGeoJSON, SFCGeometry, ToGeoJSON}; 7 | use std::{fs::File, io::Read}; 8 | 9 | type Pt3d = (f64, f64, f64); 10 | 11 | fn main() { 12 | let path = "examples/abc.geojson"; 13 | let mut file = File::open(path).unwrap_or_else(|err| { 14 | println!("Unable to open layer at path: \"{}\"\nError: {}", path, err); 15 | std::process::exit(1) 16 | }); 17 | let mut raw_json = String::new(); 18 | file.read_to_string(&mut raw_json).unwrap(); 19 | let decoded_geojson = raw_json.parse::().unwrap(); 20 | let features = match decoded_geojson { 21 | GeoJson::FeatureCollection(collection) => collection.features, 22 | _ => panic!("Error: expected a Feature collection!"), 23 | }; 24 | let new_features = features 25 | .iter() 26 | .filter_map(|feature| { 27 | if let Some(geom) = &feature.geometry { 28 | if let Value::Polygon(..) = geom.value { 29 | let polygon_sfc = SFCGeometry::from_geojson::(&geom.value).unwrap(); 30 | let skeleton = polygon_sfc.straight_skeleton().unwrap(); 31 | let geojson_geom: Value = skeleton.to_geojson::().unwrap(); 32 | return Some(Feature { 33 | bbox: None, 34 | geometry: Some(Geometry { 35 | value: geojson_geom, 36 | bbox: None, 37 | foreign_members: None, 38 | }), 39 | id: None, 40 | properties: feature.properties.clone(), 41 | foreign_members: None, 42 | }); 43 | } 44 | } 45 | None 46 | }) 47 | .collect::>(); 48 | 49 | let result_geojson = GeoJson::FeatureCollection(FeatureCollection { 50 | foreign_members: None, 51 | bbox: None, 52 | features: new_features, 53 | }) 54 | .to_string(); 55 | 56 | println!("{}", result_geojson); 57 | } 58 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CStr, os::raw::c_char}; 2 | 3 | use approx::AbsDiff; 4 | use sfcgal_sys::{sfcgal_geometry_t, sfcgal_prepared_geometry_t}; 5 | 6 | use crate::errors::get_last_error; 7 | use crate::Result; 8 | 9 | pub(crate) fn _string(raw_ptr: *const c_char) -> String { 10 | let c_str = unsafe { CStr::from_ptr(raw_ptr) }; 11 | 12 | std::str::from_utf8(c_str.to_bytes()).unwrap().to_string() 13 | } 14 | 15 | pub(crate) fn check_null_geom(g: *const sfcgal_geometry_t) -> Result<()> { 16 | if g.is_null() { 17 | return Err(format_err!( 18 | "Error - Encountered a null Geometry : {}", 19 | get_last_error() 20 | )); 21 | } 22 | 23 | Ok(()) 24 | } 25 | 26 | pub(crate) fn check_null_prepared_geom(g: *mut sfcgal_prepared_geometry_t) -> Result<()> { 27 | if g.is_null() { 28 | return Err(format_err!( 29 | "Error - Encountered a null Prepared Geometry : {}", 30 | get_last_error() 31 | )); 32 | } 33 | 34 | Ok(()) 35 | } 36 | 37 | pub(crate) fn check_predicate(val: i32) -> Result { 38 | match val { 39 | 1 => Ok(true), 40 | 0 => Ok(false), 41 | _ => Err(format_err!("SFCGAL error: {}", get_last_error())), 42 | } 43 | } 44 | 45 | pub(crate) fn check_computed_value(val: f64) -> Result { 46 | if AbsDiff::default().eq(&val, &-1.0) { 47 | Err(format_err!("SFCGAL error: {}", get_last_error())) 48 | } else { 49 | Ok(val) 50 | } 51 | } 52 | 53 | pub(crate) fn check_nan_value(val: f64) -> Result { 54 | if val.is_nan() { 55 | Err(format_err!("SFCGAL error: {}", get_last_error())) 56 | } else { 57 | Ok(val) 58 | } 59 | } 60 | 61 | // Use the size returned by the C API to build the string 62 | // (as it seems to not always end with a null byte) 63 | // from the pointer to uninitialized memory with give 64 | // to it earlier. 65 | pub(crate) fn _c_string_with_size(raw_ptr: *const c_char, size: usize) -> String { 66 | let slice: &[u8] = unsafe { std::slice::from_raw_parts(raw_ptr as *const u8, size) }; 67 | 68 | let res = std::str::from_utf8(slice).unwrap().to_string(); 69 | 70 | unsafe { libc::free(raw_ptr as *mut libc::c_void) }; 71 | 72 | res 73 | } 74 | 75 | // Get the raw u8 bytes 76 | // The users can proceed to use it however they want. 77 | pub(crate) fn get_raw_bytes(raw_ptr: *const c_char, size: usize) -> Vec { 78 | let slice: &[u8] = unsafe { std::slice::from_raw_parts(raw_ptr as *const u8, size) }; 79 | 80 | slice.to_vec() 81 | } 82 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [0.8.2] - 2025-05-27 5 | ### Changed 6 | - Make `geo-types` and `geojson` optional dependencies [#9](https://github.com/mthh/sfcgal-rs/pull/9), thanks to @TimTheBig. 7 | - Update `sfcgal-sys` to 0.9.0 to ease installation on other platforms than Linux. 8 | - Fix typos in documentation and in some error messages. 9 | 10 | ## [0.8.1] - 2024-11-20 11 | ### Changed 12 | - Fixed the visibility of the `BufferType` enum. 13 | 14 | ## [0.8.0] - 2024-11-14 15 | ### Added 16 | - Update to SFCGAL 2.0.0 and wrap the various new functions from the C API [#7](https://github.com/mthh/sfcgal-rs/pull/7), thanks to @ThomasCartier. 17 | 18 | ## [0.7.0] - 2024-04-16 19 | ### Added 20 | - Update to SFCGAL 1.5.0 and expose the new following functions from the C API: 21 | * `sfcgal_geometry_extrude_polygon_straight_skeleton` and `sfcgal_geometry_extrude_straight_skeleton`. 22 | 23 | ### Changed 24 | - Change code of `GeomType::Triangle` to 17 (to follow upstream changes). 25 | 26 | ## [0.6.0] - 2023-03-29 27 | ### Added 28 | - Update to SFCGAL 1.4.1 and expose the new following functions from the C API: 29 | * `sfcgal_geometry_alpha_shapes` and `sfcgal_geometry_optimal_alpha_shapes`. 30 | 31 | ### Changed 32 | - Update project to 2021 edition. 33 | - Update various dependencies (notably `geo_types` and `geojson`). 34 | - Switch from travis to GitHub actions. 35 | - Apply some clippy suggestions. 36 | 37 | 38 | ## [0.5.0] - 2020-10-18 39 | ### Added 40 | - Update to SFCGAL 1.3.8 and expose the following functions from the C API: 41 | * `sfcgal_geometry_covers`, `sfcgal_geometry_covers_3d`, `sfcgal_geometry_is_measured` and `sfcgal_geometry_line_sub_string` 42 | 43 | 44 | ## [0.4.0] - 2020-09-02 45 | ### Added 46 | - Enable conversion from/to geo_types Triangle and conversion from geo_types Rect 47 | 48 | ### Changed 49 | - Update `geo` (to 0.6) and `sfcgal-sys` (to 0.4). 50 | 51 | 52 | ## [0.3.0] - 2020-07-08 53 | ### Changed 54 | - Use ffi types : size_t, c_char, etc [#1](https://github.com/mthh/sfcgal-rs/pull/1) 55 | - Use less restrictive x.y version number in Cargo.toml [#1](https://github.com/mthh/sfcgal-rs/pull/1) 56 | - Replace deprecated uninitialized() with MaybeUninit [#1](https://github.com/mthh/sfcgal-rs/pull/1) 57 | - Tidy "use ..." statements and other code simplifications [#1](https://github.com/mthh/sfcgal-rs/pull/1) 58 | - Replace deprecated `failure` crate with `anyhow` [#1](https://github.com/mthh/sfcgal-rs/pull/1) 59 | - Update `geo` (to 0.5), `geojson` (0.19) and `sfcgal-sys` (to 0.3). 60 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate geo_types; 3 | extern crate geojson; 4 | extern crate sfcgal; 5 | extern crate test; 6 | 7 | use sfcgal::{FromGeoJSON, SFCGeometry, ToCoordinates, ToGeoJSON, TryInto}; 8 | use std::convert::TryFrom; 9 | use std::io::Read; 10 | use test::Bencher; 11 | 12 | type Point2d = (f64, f64); 13 | type Point3d = (f64, f64, f64); 14 | 15 | fn read_example_file() -> Vec { 16 | let path = "examples/abc.geojson"; 17 | let mut file = std::fs::File::open(path).unwrap_or_else(|err| { 18 | println!("Unable to open layer at path: \"{}\"\nError: {}", path, err); 19 | std::process::exit(1) 20 | }); 21 | let mut raw_json = String::new(); 22 | file.read_to_string(&mut raw_json).unwrap(); 23 | let decoded_geojson = raw_json.parse::().unwrap(); 24 | let features = match decoded_geojson { 25 | geojson::GeoJson::FeatureCollection(ref collection) => &collection.features, 26 | _ => panic!("Error: expected a Feature collection!"), 27 | }; 28 | let mut geoms = Vec::new(); 29 | features 30 | .iter() 31 | .map(|feature| { 32 | if feature.geometry.is_some() { 33 | geoms.push(feature.geometry.clone().take().unwrap()); 34 | } 35 | }) 36 | .for_each(drop); 37 | geoms 38 | } 39 | 40 | fn make_sfgal_geom() -> SFCGeometry { 41 | let geoms = read_example_file(); 42 | SFCGeometry::from_geojson::(&geoms[0].value).unwrap() 43 | } 44 | 45 | // #[bench] 46 | // fn bench_geojson_to_geotypes_to_sfcgal_2d(b: &mut Bencher) { 47 | // let mut geom = read_example_file(); 48 | // let g = geom.get_mut(0).unwrap(); 49 | // b.iter(|| { 50 | // let geo_polygon: geo_types::Polygon = g.value.clone().try_into().unwrap(); 51 | // geo_polygon.to_sfcgal().unwrap(); 52 | // }); 53 | // } 54 | // 55 | // #[bench] 56 | // fn bench_geotypes_to_sfcgal_2d(b: &mut Bencher) { 57 | // let mut geom = read_example_file(); 58 | // let g = geom.get_mut(0).unwrap(); 59 | // let geo_polygon: geo_types::Polygon = g.value.clone().try_into().unwrap(); 60 | // b.iter(|| { 61 | // geo_polygon.to_sfcgal().unwrap(); 62 | // }); 63 | // } 64 | 65 | #[bench] 66 | fn bench_geojson_to_sfcgal_3d(b: &mut Bencher) { 67 | let geom = read_example_file(); 68 | b.iter(|| SFCGeometry::from_geojson::(&geom[0].value).unwrap()); 69 | } 70 | 71 | #[bench] 72 | fn bench_geojson_to_sfcgal_2d(b: &mut Bencher) { 73 | let geom = read_example_file(); 74 | b.iter(|| SFCGeometry::from_geojson::(&geom[0].value).unwrap()); 75 | } 76 | 77 | #[bench] 78 | fn bench_sfcgal_2d_to_coordinates(b: &mut Bencher) { 79 | let geom = make_sfgal_geom(); 80 | b.iter(|| geom.to_coordinates::().unwrap()); 81 | } 82 | 83 | #[bench] 84 | fn bench_sfcgal_2d_to_geojson(b: &mut Bencher) { 85 | let geom = make_sfgal_geom(); 86 | b.iter(|| { 87 | let _coords = geom.to_geojson::().unwrap(); 88 | }); 89 | } 90 | 91 | #[bench] 92 | fn bench_sfcgal_2d_to_geotype_geometry(b: &mut Bencher) { 93 | let geom = make_sfgal_geom(); 94 | b.iter(|| { 95 | let _polyg: geo_types::Geometry = TryInto::try_into(geom.clone()).unwrap(); 96 | }); 97 | } 98 | 99 | #[bench] 100 | fn bench_sfcgal_2d_to_geotype_concrete_type(b: &mut Bencher) { 101 | let geom = make_sfgal_geom(); 102 | b.iter(|| { 103 | let _polyg: geo_types::Polygon = 104 | geo_types::Polygon::try_from(TryInto::try_into(geom.clone()).unwrap()).unwrap(); 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "sfcgal"] 2 | #![doc(html_root_url = "https://mthh.github.io/sfcgal-rs/")] 3 | //! Rust bindings providing a high-level API to [`SFCGAL`](http://oslandia.github.io/SFCGAL/) 4 | //! library and conversion to / from other geometry crates from Rust ecosystem. 5 | //! Based on the [sfcgal-sys](https://github.com/mthh/sfcgal-sys) crate exposing low-level bindings. 6 | //! 7 | //! Allows notably reading from / writing to WKT as well as interoperability 8 | //! with [geojson](https://crates.io/crates/geojson) and [geo-types](https://crates.io/crates/geo) crates. 9 | //! It also offers an API 10 | //! to manipulate SFCGAL geometries from/to coordinates (represented as tuples 11 | //! of 2 or 3 positions). 12 | //! 13 | //! #### Example 14 | //! ```rust 15 | //! # extern crate anyhow; 16 | //! # fn fun() -> Result<(), anyhow::Error> { 17 | //! extern crate sfcgal; 18 | //! use sfcgal::{SFCGeometry, CoordSeq, ToGeoJSON, ToSFCGAL, Point2d, Point3d}; 19 | //! 20 | //! // Create SFCGAL geometries from coordinates.. 21 | //! let coords_linestring = vec![(-0.5, -0.5, 2.5), (0., 0., 4.0)]; 22 | //! let coords_polygon = vec![ 23 | //! vec![(-1., -1., 3.0), (1., -1., 3.0), (1., 1., 3.0), (-1., 1., 3.0), (-1., -1., 3.0)], // Exterior ring 24 | //! vec![(0.1, 0.1, 3.0), (0.1, 0.9, 3.0), (0.9, 0.9, 3.0), (0.9, 0.1, 3.0), (0.1, 0.1, 3.0)], // 1 interior ring 25 | //! ]; 26 | //! 27 | //! // .. by using the CoordSeq enum variants to match the wanted SFCGAL geometry type: 28 | //! let line_3d = CoordSeq::Linestring(coords_linestring).to_sfcgal()?; 29 | //! let polygon_3d = CoordSeq::Polygon(coords_polygon).to_sfcgal()?; 30 | //! 31 | //! // .. 32 | //! assert!(line_3d.intersects_3d(&polygon_3d)?); 33 | //! let intersection = line_3d.intersection_3d(&polygon_3d)?; 34 | //! 35 | //! // Get the geojson representation of the geometry with 3-member coordinates : 36 | //! let geom = intersection.to_geojson::()?; 37 | //! // Or the wkt representation with a floating precision of 1 : 38 | //! let wkt = intersection.to_wkt_decim(1)?; 39 | //! # Ok(()) 40 | //! # } 41 | //! # fn main() { fun().unwrap(); } 42 | //! ``` 43 | #[macro_use] 44 | extern crate anyhow; 45 | 46 | #[macro_use] 47 | extern crate enum_primitive_derive; 48 | 49 | #[allow(unused_imports)] 50 | #[macro_use] 51 | extern crate approx; 52 | 53 | use sfcgal_sys::sfcgal_version; 54 | 55 | mod conversion; 56 | mod errors; 57 | mod geometry; 58 | mod utils; 59 | 60 | pub use conversion::CoordSeq; 61 | #[cfg(feature = "geo-types")] 62 | pub use conversion::TryInto; 63 | #[cfg(feature = "geojson")] 64 | pub use conversion::{FromGeoJSON, ToGeoJSON}; 65 | pub use errors::Result; 66 | pub use geometry::{BufferType, GeomType, Orientation, SFCGeometry}; 67 | 68 | /// Type alias for manipulating 2d coordinates, represented as (x, y). 69 | pub type Point2d = (f64, f64); 70 | 71 | /// Type alias for manipulating 3d coordinates, represented as (x, y, z). 72 | pub type Point3d = (f64, f64, f64); 73 | 74 | /// Convert object to a [`SFCGeometry`] (implemented on [`CoordSeq`] and [geo-types](https://docs.rs/geo-types/) geometries) 75 | /// 76 | /// [`SFCGeometry`]: struct.SFCGeometry.html 77 | /// [`CoordSeq`]: enum.CoordSeq.html 78 | pub trait ToSFCGAL { 79 | fn to_sfcgal(&self) -> Result; 80 | } 81 | 82 | /// Convert object to a [`CoordSeq`] holding coordinates and information about 83 | /// geometry type. 84 | /// 85 | /// [`CoordSeq`]: enum.CoordSeq.html 86 | pub trait ToCoordinates { 87 | fn to_coordinates(&self) -> Result> 88 | where 89 | T: conversion::CoordType + conversion::FromSFCGALGeom; 90 | } 91 | 92 | /// Display SFCGAL version information. 93 | pub fn version() -> String { 94 | utils::_string(unsafe { sfcgal_version() }) 95 | } 96 | 97 | #[cfg(test)] 98 | 99 | mod tests { 100 | 101 | use super::version; 102 | 103 | #[test] 104 | fn display_version() { 105 | assert!(version().starts_with("2.")); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sfcgal-rs 2 | 3 | [![Build and run tests](https://github.com/mthh/sfcgal-rs/actions/workflows/test.yml/badge.svg)](https://github.com/mthh/sfcgal-rs/actions/workflows/test.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/sfcgal.svg)](https://crates.io/crates/sfcgal) 5 | [![Documentation](https://img.shields.io/badge/documentation-0.8.2-green)](https://mthh.github.io/sfcgal-rs/sfcgal/) 6 | 7 | 8 | Rust bindings providing a high-level API to [`SFCGAL`](https://sfcgal.gitlab.io/SFCGAL/) library and conversion to / from other geometry crates from Rust ecosystem. 9 | Based on the [sfcgal-sys](https://github.com/mthh/sfcgal-sys) crate exposing low-level bindings. 10 | 11 | Some key features of the underlying library: 12 | - Supports ISO 19107 and [OGC Simple Features Access 1.2](http://www.opengeospatial.org/standards/sfa) for 3D operations. 13 | - Reads and writes WKT with exact rational number representation of coordinates for 2D and 3D geometries. 14 | - Intersection, difference and union. 15 | - Straight skeleton, tesselation, Minkovski sum, alpha shapes and convex hull. 16 | 17 | > [!IMPORTANT] 18 | > Note that the required version of SFCGAL is currently 2.x.x (latest version on 2025-05-27 is 2.1.0). 19 | > If you want to use SFCCAL 1.5.x, you can use the 0.7.x version of this crate. 20 | > If you want to use SFCCAL 1.4.x, you can use the 0.6.x version of this crate. 21 | 22 | ## Usage 23 | 24 | __Example with 3-member tuples for 3d coordinates and WKT__: 25 | ```rust 26 | extern crate sfcgal; 27 | use sfcgal::{SFCGeometry, CoordSeq, ToCoordinates, ToSFCGAL}; 28 | 29 | // create a linestring from WKT: 30 | let line_3d = SFCGeometry::new("LINESTRING(-0.5 -0.5 2.5, 0.0 0.0 4.0)")?; 31 | 32 | // create a polygon as Vec of 3-member tuples... 33 | let coords_polygon = vec![ 34 | vec![ // Exterior ring 35 | (-1., -1., 3.0), 36 | (1., -1., 3.0), 37 | (1., 1., 3.0), 38 | (-1., 1., 3.0), 39 | (-1., -1., 3.0), 40 | ], 41 | vec![ // 1 interior ring 42 | (0.1, 0.1, 3.0), 43 | (0.1, 0.9, 3.0), 44 | (0.9, 0.9, 3.0), 45 | (0.9, 0.1, 3.0), 46 | (0.1, 0.1, 3.0), 47 | ], 48 | ]; 49 | // ...by using the CoordSeq enum variants to match the wanted SFCGAL geometry type 50 | // (returns a SFCGeometry) 51 | let polygon_3d = CoordSeq::Polygon(coords_polygon).to_sfcgal()?; 52 | 53 | // ... 54 | let intersection = line_3d.intersection_3d(&polygon_3d)?; 55 | 56 | // Retrieve coordinates of the resulting geometry as 3-member tuples: 57 | let coords_intersection: CoordSeq<(f64, f64, f64)> = intersection.to_coordinates()?; 58 | 59 | println!("{:?} and {:?} intersects at {:?}", line_3d, polygon_3d, coords_intersection); 60 | ``` 61 | 62 | __Example with [geo-types](https://github.com/georust/geo)__: 63 | ```rust 64 | extern crate geo_types; 65 | extern crate sfcgal; 66 | 67 | use geo_types::{LineString, Polygon}; 68 | use sfcgal::ToSFCGAL; 69 | 70 | 71 | // create a geo_types Polygon: 72 | let polygon = Polygon::new( 73 | LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.,), (0., 0.)]), 74 | vec![LineString::from( 75 | vec![(0.1, 0.1), (0.1, 0.9,), (0.9, 0.9), (0.9, 0.1), (0.1, 0.1)])]); 76 | 77 | // create a geo_types LineString: 78 | let line = LineString::from(vec![(-0.5, -0.5), (1.3, 1.), (1.1, 1.9)]); 79 | 80 | // convert them to sfcgal geometries: 81 | let polyg_sfc = polygon.to_sfcgal().unwrap(); 82 | let line_sfc = line.to_sfcgal().unwrap(); 83 | 84 | // Use SFCGAL operations: 85 | assert!(polyg_sfc.intersects(&line_sfc).unwrap(), true); 86 | ``` 87 | 88 | ### Examples 89 | 90 | See `examples/skeleton_geojson.rs` for an example of working with some other crates from Rust-geo ecosystem. 91 | 92 | 93 | ### Motivation 94 | 95 | Needed a SFCGAL feature for a side-project in Rust and I thought it would be a good opportunity to try using [bindgen](https://github.com/rust-lang/rust-bindgen) on SFCGAL C API. 96 | In the end a large part of the API is wrapped, so maybe it could be reused or improved by the community now it's published on [crates.io](https://crates.io/crates/sfcgal). 97 | 98 | ## Other SFCGAL bindings 99 | 100 | Those Rust bindings are community-driven. If you are looking for official bindings, you can check the [PySFCGAL](https://gitlab.com/sfcgal/pysfcgal) project 101 | which is developed by the SFCGAL team. 102 | 103 | ## License 104 | 105 | Licensed under either of 106 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 107 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 108 | 109 | at your option. 110 | 111 | ### Contribution 112 | 113 | Unless you explicitly state otherwise, any contribution intentionally submitted 114 | for inclusion in the work by you shall be dual licensed as above, without any 115 | additional terms or conditions. 116 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/conversion/geojson.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | conversion::coords::{CoordType, FromSFCGALGeom, ToSFCGALGeom}, 3 | CoordSeq, Point2d, Point3d, Result, SFCGeometry, ToCoordinates, ToSFCGAL, 4 | }; 5 | use anyhow::Error; 6 | use geojson::Value as GeometryValue; 7 | 8 | /// Conversion from [`SFCGeometry`] (implemented on [geo-types](https://docs.rs/geo-types/) geometries) 9 | /// 10 | /// [`SFCGeometry`]: struct.SFCGeometry.html 11 | pub trait TryIntoCoords { 12 | type Err; 13 | fn try_into(&self) -> Result; 14 | } 15 | 16 | /// Convert coordinates to `sfcgal_geometry_t`. 17 | pub trait FromSlice { 18 | fn from_slice(pt: &[f64]) -> Result 19 | where 20 | Self: std::marker::Sized; 21 | } 22 | 23 | /// Convert coordinates to `sfcgal_geometry_t`. 24 | pub trait ToVec { 25 | fn to_vec(&self) -> Vec; 26 | } 27 | 28 | impl ToVec for Point2d { 29 | fn to_vec(&self) -> Vec { 30 | vec![self.0, self.1] 31 | } 32 | } 33 | 34 | impl ToVec for Point3d { 35 | fn to_vec(&self) -> Vec { 36 | vec![self.0, self.1, self.2] 37 | } 38 | } 39 | 40 | impl FromSlice for Point2d { 41 | fn from_slice(pt: &[f64]) -> Result { 42 | let mut it = pt.iter(); 43 | Ok((*it.next().unwrap(), *it.next().unwrap())) 44 | } 45 | } 46 | 47 | impl FromSlice for Point3d { 48 | fn from_slice(pt: &[f64]) -> Result { 49 | let mut it = pt.iter(); 50 | Ok(( 51 | *it.next().unwrap(), 52 | *it.next().unwrap(), 53 | *it.next().unwrap_or(&(0.0f64)), 54 | )) 55 | } 56 | } 57 | 58 | /// Implements conversion from CoordSeq to geo_types::Geometry 59 | /// (better use TryInto for SFCGeometry if the intend 60 | /// is to convert SFCGAL Geometries to geo_types ones) 61 | impl TryIntoCoords> for GeometryValue 62 | where 63 | T: FromSlice + CoordType, 64 | { 65 | type Err = Error; 66 | fn try_into(&self) -> Result> 67 | where 68 | T: FromSlice + CoordType, 69 | { 70 | match *self { 71 | GeometryValue::Point(ref pt) => Ok(CoordSeq::Point(T::from_slice(pt)?)), 72 | GeometryValue::MultiPoint(ref pts) => { 73 | let _pts = pts 74 | .iter() 75 | .map(|p| T::from_slice(p)) 76 | .collect::>>(); 77 | Ok(CoordSeq::Multipoint(_pts?)) 78 | } 79 | GeometryValue::LineString(ref pts) => { 80 | let _pts = pts 81 | .iter() 82 | .map(|p| T::from_slice(p)) 83 | .collect::>>(); 84 | Ok(CoordSeq::Linestring(_pts?)) 85 | } 86 | GeometryValue::MultiLineString(ref lines) => { 87 | let _pts = lines 88 | .iter() 89 | .map(|pts| pts.iter().map(|p| T::from_slice(p)).collect()) 90 | .collect::>>>(); 91 | Ok(CoordSeq::Multilinestring(_pts?)) 92 | } 93 | GeometryValue::Polygon(ref rings) => { 94 | let _pts = rings 95 | .iter() 96 | .map(|pts| pts.iter().map(|p| T::from_slice(p)).collect()) 97 | .collect::>>>(); 98 | Ok(CoordSeq::Polygon(_pts?)) 99 | } 100 | GeometryValue::MultiPolygon(ref polygons) => { 101 | let _pts = polygons 102 | .iter() 103 | .map(|rings| { 104 | rings 105 | .iter() 106 | .map(|pts| pts.iter().map(|p| T::from_slice(p)).collect()) 107 | .collect() 108 | }) 109 | .collect::>>>>(); 110 | Ok(CoordSeq::Multipolygon(_pts?)) 111 | } 112 | GeometryValue::GeometryCollection(ref geoms) => { 113 | let _geoms = geoms 114 | .iter() 115 | .map(|geom| TryIntoCoords::try_into(&geom.value)) 116 | .collect::>>>(); 117 | Ok(CoordSeq::Geometrycollection(_geoms?)) 118 | } 119 | } 120 | } 121 | } 122 | 123 | /// Conversion from GeoJson to SFCGAL Geometries. 124 | pub trait FromGeoJSON { 125 | type Err; 126 | fn from_geojson( 127 | geom: &GeometryValue, 128 | ) -> Result; 129 | } 130 | 131 | /// Conversion from SFCGAL Geometries to GeoJson. 132 | pub trait ToGeoJSON { 133 | type Err; 134 | fn to_geojson( 135 | &self, 136 | ) -> Result; 137 | } 138 | 139 | /// Conversion from SFCGAL Geometries to GeoJson. 140 | /// 141 | /// Allows to choose if coordinates of constructed geojson have to be 2d or 3d. 142 | /// ``` rust 143 | /// use sfcgal::{SFCGeometry, ToGeoJSON}; 144 | /// type Point3d = (f64, f64, f64); 145 | /// 146 | /// let input_wkt = "POINT (0.1 0.9 1.0)"; 147 | /// let pt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 148 | /// let pt_geojson = pt_sfcgal.to_geojson::().unwrap(); 149 | /// ``` 150 | impl ToGeoJSON for SFCGeometry { 151 | type Err = Error; 152 | fn to_geojson( 153 | &self, 154 | ) -> Result { 155 | let cs: CoordSeq = self.to_coordinates()?; 156 | cs.to_geojson::() 157 | } 158 | } 159 | 160 | impl ToGeoJSON for CoordSeq 161 | where 162 | U: FromSlice + CoordType + ToVec + FromSFCGALGeom, 163 | { 164 | type Err = Error; 165 | fn to_geojson( 166 | &self, 167 | ) -> Result { 168 | match self { 169 | CoordSeq::Point(pt) => Ok(GeometryValue::Point(pt.to_vec())), 170 | CoordSeq::Multipoint(pts) => Ok(GeometryValue::MultiPoint( 171 | pts.iter().map(|p| p.to_vec()).collect(), 172 | )), 173 | CoordSeq::Linestring(pts) => Ok(GeometryValue::LineString( 174 | pts.iter().map(|p| p.to_vec()).collect(), 175 | )), 176 | CoordSeq::Multilinestring(lines) => Ok(GeometryValue::MultiLineString( 177 | lines 178 | .iter() 179 | .map(|pts| pts.iter().map(|p| p.to_vec()).collect()) 180 | .collect(), 181 | )), 182 | CoordSeq::Polygon(rings) => Ok(GeometryValue::Polygon( 183 | rings 184 | .iter() 185 | .map(|pts| pts.iter().map(|p| p.to_vec()).collect()) 186 | .collect(), 187 | )), 188 | CoordSeq::Multipolygon(polygons) => Ok(GeometryValue::MultiPolygon( 189 | polygons 190 | .iter() 191 | .map(|rings| { 192 | rings 193 | .iter() 194 | .map(|pts| pts.iter().map(|p| p.to_vec()).collect()) 195 | .collect() 196 | }) 197 | .collect(), 198 | )), 199 | CoordSeq::Geometrycollection(geoms) => Ok(GeometryValue::GeometryCollection( 200 | geoms 201 | .iter() 202 | .map(|geom| { 203 | Ok(geojson::Geometry { 204 | bbox: None, 205 | value: geom.to_geojson::()?, 206 | foreign_members: None, 207 | }) 208 | }) 209 | .collect::>>()?, 210 | )), 211 | _ => unimplemented!(), 212 | } 213 | } 214 | } 215 | 216 | /// Conversion from GeoJson to SFCGAL geometries. 217 | /// 218 | /// Allows to choose if the constructed SFCGAL geometries will be 2d or 3d. 219 | /// ``` rust 220 | /// use sfcgal::{SFCGeometry, FromGeoJSON}; 221 | /// type Point2d = (f64, f64); 222 | /// 223 | /// let geom = geojson::Geometry { 224 | /// bbox: None, 225 | /// value: geojson::Value::LineString(vec![vec![101.0, 0.0], vec![102.0, 1.0]]), 226 | /// foreign_members: None, 227 | /// }; 228 | /// let line_sfcgal = SFCGeometry::from_geojson::(&geom.value).unwrap(); 229 | /// ``` 230 | impl FromGeoJSON for SFCGeometry { 231 | type Err = Error; 232 | fn from_geojson( 233 | geom: &GeometryValue, 234 | ) -> Result { 235 | let coords: CoordSeq = TryIntoCoords::try_into(geom)?; 236 | coords.to_sfcgal() 237 | } 238 | } 239 | 240 | #[cfg(test)] 241 | mod tests { 242 | use super::{Point2d, Point3d, ToGeoJSON, TryIntoCoords}; 243 | use crate::*; 244 | 245 | #[test] 246 | fn point_2d_sfcgal_to_geojson_to_sfcgal() { 247 | let input_wkt = "POINT (0.1 0.9)"; 248 | let pt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 249 | let pt_geojson = pt_sfcgal.to_geojson::().unwrap(); 250 | let pt_sfcgal_new = SFCGeometry::from_geojson::(&pt_geojson).unwrap(); 251 | assert_eq!( 252 | pt_sfcgal.to_wkt_decim(1).unwrap(), 253 | pt_sfcgal_new.to_wkt_decim(1).unwrap() 254 | ); 255 | let a: CoordSeq = TryIntoCoords::try_into(&pt_geojson).unwrap(); 256 | let b = a.to_sfcgal().unwrap(); 257 | assert_eq!( 258 | pt_sfcgal.to_wkt_decim(1).unwrap(), 259 | b.to_wkt_decim(1).unwrap() 260 | ); 261 | } 262 | 263 | #[test] 264 | fn point_3d_sfcgal_to_geojson_to_sfcgal() { 265 | let input_wkt = "POINT (0.1 0.9 1.0)"; 266 | let pt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 267 | let pt_geojson = pt_sfcgal.to_geojson::().unwrap(); 268 | let pt_sfcgal_new = SFCGeometry::from_geojson::(&pt_geojson).unwrap(); 269 | assert_eq!( 270 | pt_sfcgal.to_wkt_decim(1).unwrap(), 271 | pt_sfcgal_new.to_wkt_decim(1).unwrap() 272 | ); 273 | let a: CoordSeq = TryIntoCoords::try_into(&pt_geojson).unwrap(); 274 | let b = a.to_sfcgal().unwrap(); 275 | assert_eq!( 276 | pt_sfcgal.to_wkt_decim(1).unwrap(), 277 | b.to_wkt_decim(1).unwrap() 278 | ); 279 | } 280 | #[test] 281 | fn multipoint_2d_sfcgal_to_geojson_to_sfcgal() { 282 | let input_wkt = "MULTIPOINT ((0.1 0.9),(2.3 3.4))"; 283 | let multipt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 284 | let multipt_geojson = multipt_sfcgal.to_geojson::().unwrap(); 285 | let multipt_sfcgal_new = SFCGeometry::from_geojson::(&multipt_geojson).unwrap(); 286 | assert_eq!( 287 | multipt_sfcgal.to_wkt_decim(1).unwrap(), 288 | multipt_sfcgal_new.to_wkt_decim(1).unwrap() 289 | ); 290 | } 291 | 292 | #[test] 293 | fn multipoint_3d_sfcgal_to_geojson_to_sfcgal() { 294 | let input_wkt = "MULTIPOINT ((0.1 0.9 1.1),(2.3 3.4 1.1))"; 295 | let multipt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 296 | let multipt_geojson = multipt_sfcgal.to_geojson::().unwrap(); 297 | let multipt_sfcgal_new = SFCGeometry::from_geojson::(&multipt_geojson).unwrap(); 298 | assert_eq!( 299 | multipt_sfcgal.to_wkt_decim(1).unwrap(), 300 | multipt_sfcgal_new.to_wkt_decim(1).unwrap() 301 | ); 302 | } 303 | #[test] 304 | fn line_2d_sfcgal_to_geojson_to_sfcgal() { 305 | let input_wkt = "LINESTRING (10.0 1.0, 1.0 2.0)"; 306 | let line_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 307 | let line_geojson = line_sfcgal.to_geojson::().unwrap(); 308 | let line_sfcgal_new = SFCGeometry::from_geojson::(&line_geojson).unwrap(); 309 | assert_eq!( 310 | line_sfcgal.to_wkt_decim(1).unwrap(), 311 | line_sfcgal_new.to_wkt_decim(1).unwrap() 312 | ); 313 | let a: CoordSeq = TryIntoCoords::try_into(&line_geojson).unwrap(); 314 | let b = a.to_sfcgal().unwrap(); 315 | assert_eq!( 316 | line_sfcgal.to_wkt_decim(1).unwrap(), 317 | b.to_wkt_decim(1).unwrap() 318 | ); 319 | } 320 | #[test] 321 | fn line_3d_sfcgal_to_geojson_to_sfcgal() { 322 | let input_wkt = "LINESTRING (10.0 1.0 2.0, 1.0 2.0 1.7)"; 323 | let line_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 324 | let line_geojson = line_sfcgal.to_geojson::().unwrap(); 325 | let line_sfcgal_new = SFCGeometry::from_geojson::(&line_geojson).unwrap(); 326 | assert_eq!( 327 | line_sfcgal.to_wkt_decim(1).unwrap(), 328 | line_sfcgal_new.to_wkt_decim(1).unwrap() 329 | ); 330 | let a: CoordSeq = TryIntoCoords::try_into(&line_geojson).unwrap(); 331 | let b = a.to_sfcgal().unwrap(); 332 | assert_eq!( 333 | line_sfcgal.to_wkt_decim(1).unwrap(), 334 | b.to_wkt_decim(1).unwrap() 335 | ); 336 | } 337 | #[test] 338 | fn multiline_2d_sfcgal_to_geojson_to_sfcgal() { 339 | let input_wkt = "MULTILINESTRING (\ 340 | (-0.0 -0.0,0.5 0.5),\ 341 | (1.0 -0.0,0.5 0.5),\ 342 | (1.0 1.0,0.5 0.5),\ 343 | (-0.0 1.0,0.5 0.5))"; 344 | let multiline_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 345 | let multiline_geojson = multiline_sfcgal.to_geojson::().unwrap(); 346 | let multiline_sfcgal_new = 347 | SFCGeometry::from_geojson::(&multiline_geojson).unwrap(); 348 | assert_eq!( 349 | multiline_sfcgal.to_wkt_decim(1).unwrap(), 350 | multiline_sfcgal_new.to_wkt_decim(1).unwrap() 351 | ); 352 | let a: CoordSeq = TryIntoCoords::try_into(&multiline_geojson).unwrap(); 353 | let b = a.to_sfcgal().unwrap(); 354 | assert_eq!( 355 | multiline_sfcgal.to_wkt_decim(1).unwrap(), 356 | b.to_wkt_decim(1).unwrap() 357 | ); 358 | } 359 | #[test] 360 | fn multiline_3d_sfcgal_to_geojson_to_sfcgal() { 361 | let input_wkt = "MULTILINESTRING (\ 362 | (-0.0 -0.0 1.3,0.5 0.5 1.3),\ 363 | (1.0 -0.0 1.3,0.5 0.5 1.3),\ 364 | (1.0 1.0 1.3,0.5 0.5 1.3),\ 365 | (-0.0 1.0 1.3,0.5 0.5 1.3))"; 366 | let multiline_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 367 | let multiline_geojson = multiline_sfcgal.to_geojson::().unwrap(); 368 | let multiline_sfcgal_new = 369 | SFCGeometry::from_geojson::(&multiline_geojson).unwrap(); 370 | assert_eq!( 371 | multiline_sfcgal.to_wkt_decim(1).unwrap(), 372 | multiline_sfcgal_new.to_wkt_decim(1).unwrap() 373 | ); 374 | let a: CoordSeq = TryIntoCoords::try_into(&multiline_geojson).unwrap(); 375 | let b = a.to_sfcgal().unwrap(); 376 | assert_eq!( 377 | multiline_sfcgal.to_wkt_decim(1).unwrap(), 378 | b.to_wkt_decim(1).unwrap() 379 | ); 380 | } 381 | #[test] 382 | fn polyg_2d_sfcgal_to_geojson_to_sfcgal() { 383 | let input_wkt = "POLYGON ((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 0.0))"; 384 | let polyg_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 385 | let polyg_geojson = polyg_sfcgal.to_geojson::().unwrap(); 386 | let polyg_sfcgal_new = SFCGeometry::from_geojson::(&polyg_geojson).unwrap(); 387 | assert_eq!( 388 | polyg_sfcgal.to_wkt_decim(1).unwrap(), 389 | polyg_sfcgal_new.to_wkt_decim(1).unwrap() 390 | ); 391 | let a: CoordSeq = TryIntoCoords::try_into(&polyg_geojson).unwrap(); 392 | let b = a.to_sfcgal().unwrap(); 393 | assert_eq!( 394 | polyg_sfcgal.to_wkt_decim(1).unwrap(), 395 | b.to_wkt_decim(1).unwrap() 396 | ); 397 | } 398 | #[test] 399 | fn polyg_3d_sfcgal_to_geojson_to_sfcgal() { 400 | let input_wkt = "POLYGON ((0.0 0.0 2.0, 1.0 0.0 2.0, 1.0 1.0 2.0, 0.0 0.0 2.0))"; 401 | let polyg_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 402 | let polyg_geojson = polyg_sfcgal.to_geojson::().unwrap(); 403 | let polyg_sfcgal_new = SFCGeometry::from_geojson::(&polyg_geojson).unwrap(); 404 | assert_eq!( 405 | polyg_sfcgal.to_wkt_decim(1).unwrap(), 406 | polyg_sfcgal_new.to_wkt_decim(1).unwrap() 407 | ); 408 | let a: CoordSeq = TryIntoCoords::try_into(&polyg_geojson).unwrap(); 409 | let b = a.to_sfcgal().unwrap(); 410 | assert_eq!( 411 | polyg_sfcgal.to_wkt_decim(1).unwrap(), 412 | b.to_wkt_decim(1).unwrap() 413 | ); 414 | } 415 | #[test] 416 | fn multipolyg_2d_sfcgal_to_geojson_to_sfcgal() { 417 | let input_wkt = "MULTIPOLYGON (((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 0.0)))"; 418 | let multipolyg_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 419 | let multipolyg_geojson = multipolyg_sfcgal.to_geojson::().unwrap(); 420 | let multipolyg_sfcgal_new = 421 | SFCGeometry::from_geojson::(&multipolyg_geojson).unwrap(); 422 | assert_eq!( 423 | multipolyg_sfcgal.to_wkt_decim(1).unwrap(), 424 | multipolyg_sfcgal_new.to_wkt_decim(1).unwrap() 425 | ); 426 | } 427 | #[test] 428 | fn multipolyg_3d_sfcgal_to_geojson_to_sfcgal() { 429 | let input_wkt = "MULTIPOLYGON (((0.0 0.0 2.0, 1.0 0.0 2.0, 1.0 1.0 2.0, 0.0 0.0 2.0)))"; 430 | let multipolyg_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 431 | let multipolyg_geojson = multipolyg_sfcgal.to_geojson::().unwrap(); 432 | let multipolyg_sfcgal_new = 433 | SFCGeometry::from_geojson::(&multipolyg_geojson).unwrap(); 434 | assert_eq!( 435 | multipolyg_sfcgal.to_wkt_decim(1).unwrap(), 436 | multipolyg_sfcgal_new.to_wkt_decim(1).unwrap() 437 | ); 438 | let a: CoordSeq = TryIntoCoords::try_into(&multipolyg_geojson).unwrap(); 439 | let b = a.to_sfcgal().unwrap(); 440 | assert_eq!( 441 | multipolyg_sfcgal.to_wkt_decim(1).unwrap(), 442 | b.to_wkt_decim(1).unwrap() 443 | ); 444 | } 445 | #[test] 446 | fn geomcollection_2d_sfcgal_to_geojson_to_sfcgal() { 447 | let input_wkt = "GEOMETRYCOLLECTION (POINT (1.0 1.0),LINESTRING (10.0 1.0,1.0 2.0))"; 448 | let multipolyg_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 449 | let multipolyg_geojson = multipolyg_sfcgal.to_geojson::().unwrap(); 450 | let multipolyg_sfcgal_new = 451 | SFCGeometry::from_geojson::(&multipolyg_geojson).unwrap(); 452 | assert_eq!( 453 | multipolyg_sfcgal.to_wkt_decim(1).unwrap(), 454 | multipolyg_sfcgal_new.to_wkt_decim(1).unwrap() 455 | ); 456 | } 457 | #[test] 458 | fn geomcollection_3d_sfcgal_to_geojson_to_sfcgal() { 459 | let input_wkt = 460 | "GEOMETRYCOLLECTION (POINT (1.0 1.0 4.0),LINESTRING (10.0 1.0 4.0,1.0 2.0 4.0))"; 461 | let multipolyg_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 462 | let multipolyg_geojson = multipolyg_sfcgal.to_geojson::().unwrap(); 463 | let multipolyg_sfcgal_new = 464 | SFCGeometry::from_geojson::(&multipolyg_geojson).unwrap(); 465 | assert_eq!( 466 | multipolyg_sfcgal.to_wkt_decim(1).unwrap(), 467 | multipolyg_sfcgal_new.to_wkt_decim(1).unwrap() 468 | ); 469 | let a: CoordSeq = TryIntoCoords::try_into(&multipolyg_geojson).unwrap(); 470 | let c = a.to_geojson::().unwrap(); 471 | let b = a.to_sfcgal().unwrap(); 472 | let d = SFCGeometry::from_geojson::(&c).unwrap(); 473 | assert_eq!( 474 | multipolyg_sfcgal.to_wkt_decim(1).unwrap(), 475 | b.to_wkt_decim(1).unwrap() 476 | ); 477 | assert_eq!( 478 | multipolyg_sfcgal.to_wkt_decim(1).unwrap(), 479 | d.to_wkt_decim(1).unwrap() 480 | ); 481 | } 482 | } 483 | -------------------------------------------------------------------------------- /src/conversion/geotypes.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | conversion::coords::{CoordSeq, CoordType, ToSFCGALGeom}, 3 | utils::check_null_geom, 4 | GeomType, Point2d, Result, SFCGeometry, ToCoordinates, ToSFCGAL, 5 | }; 6 | use anyhow::Error; 7 | use sfcgal_sys::{ 8 | sfcgal_geometry_collection_add_geometry, sfcgal_geometry_collection_create, 9 | sfcgal_geometry_collection_geometry_n, sfcgal_geometry_collection_num_geometries, 10 | sfcgal_geometry_t, sfcgal_linestring_add_point, sfcgal_linestring_create, 11 | sfcgal_linestring_num_points, sfcgal_linestring_point_n, sfcgal_multi_linestring_create, 12 | sfcgal_multi_point_create, sfcgal_multi_polygon_create, sfcgal_point_create_from_xy, 13 | sfcgal_point_x, sfcgal_point_y, sfcgal_polygon_add_interior_ring, 14 | sfcgal_polygon_create_from_exterior_ring, sfcgal_polygon_exterior_ring, 15 | sfcgal_polygon_interior_ring_n, sfcgal_polygon_num_interior_rings, sfcgal_triangle_create, 16 | sfcgal_triangle_set_vertex_from_xy, 17 | }; 18 | use std::convert::{Into, TryFrom}; 19 | use std::iter::FromIterator; 20 | 21 | /// Conversion from [`SFCGeometry`] (implemented on [geo-types](https://docs.rs/geo-types/) geometries) 22 | /// 23 | /// [`SFCGeometry`]: struct.SFCGeometry.html 24 | pub trait TryInto { 25 | type Err; 26 | fn try_into(self) -> Result; 27 | } 28 | 29 | impl CoordType for geo_types::Point {} 30 | impl CoordType for geo_types::Coord {} 31 | 32 | impl ToSFCGALGeom for geo_types::Point { 33 | fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> { 34 | let g = unsafe { sfcgal_point_create_from_xy(self.x(), self.y()) }; 35 | check_null_geom(g)?; 36 | Ok(g) 37 | } 38 | } 39 | 40 | impl ToSFCGALGeom for geo_types::Coord { 41 | fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> { 42 | let g = unsafe { sfcgal_point_create_from_xy(self.x, self.y) }; 43 | check_null_geom(g)?; 44 | Ok(g) 45 | } 46 | } 47 | 48 | /// Implements conversion from CoordSeq to geo_types::Geometry 49 | /// (better use TryInto for SFCGeometry if the intend 50 | /// is to convert SFCGAL Geometries to geo_types ones) 51 | impl TryInto> for CoordSeq { 52 | type Err = Error; 53 | 54 | fn try_into(self) -> Result> { 55 | match self { 56 | CoordSeq::Point(pt) => Ok(geo_types::Point(pt.into()).into()), 57 | CoordSeq::Multipoint(pts) => Ok(geo_types::MultiPoint::from_iter(pts).into()), 58 | CoordSeq::Linestring(pts) => Ok(geo_types::LineString::from_iter(pts).into()), 59 | CoordSeq::Multilinestring(lines) => { 60 | Ok(geo_types::MultiLineString( 61 | lines.into_iter() 62 | .map(geo_types::LineString::from) 63 | .collect() 64 | ).into()) 65 | }, 66 | CoordSeq::Polygon(rings) => { 67 | let mut it = rings.into_iter(); 68 | let exterior = geo_types::LineString::from(it.next().unwrap()); 69 | let interiors = it.map(geo_types::LineString::from).collect::>>(); 70 | Ok(geo_types::Polygon::new(exterior, interiors).into()) 71 | }, 72 | CoordSeq::Multipolygon(polygons) => { 73 | let polys = polygons.into_iter().map(|p| { 74 | let a: geo_types::Geometry = TryInto::try_into(CoordSeq::Polygon(p))?; 75 | if let Ok(poly) = geo_types::Polygon::try_from(a) { 76 | Ok(poly) 77 | } else { 78 | Err(format_err!("Error while building geo_types::MultiPolygon")) 79 | } 80 | }).collect::>>>()?; 81 | Ok(geo_types::MultiPolygon(polys).into()) 82 | }, 83 | CoordSeq::Geometrycollection(collection) => { 84 | Ok(geo_types::Geometry::GeometryCollection( 85 | geo_types::GeometryCollection(collection 86 | .into_iter() 87 | .map(TryInto::try_into) 88 | .collect::>>>()? 89 | ) 90 | )) 91 | }, 92 | CoordSeq::Triangle(pts) => { 93 | Ok(geo_types::Geometry::Triangle( 94 | geo_types::Triangle(pts[0].into(), pts[1].into(), pts[2].into()) 95 | )) 96 | }, 97 | _ => Err( 98 | format_err!( 99 | "Conversion from CoordSeq variants `Solid`, `Multisolid`, `Triangulatedsurface` and `Polyhedralsurface` are not yet implemented!")) 100 | } 101 | } 102 | } 103 | 104 | /// Implements faillible conversion from SFCGeometry to geo_types::Geometry. 105 | /// 106 | /// This is notably faillible because some types of [`SFCGeometry`] like GeoTypes::Polyhedralsurface 107 | /// don't have equivalents in geo_types::Geometry. 108 | /// Please note that geo_types Coordinate and Point primitives are 2d only, so 109 | /// every information about z coordinate (if any) won't be taken into account. 110 | impl TryInto> for SFCGeometry { 111 | type Err = Error; 112 | 113 | fn try_into(self) -> Result> { 114 | match self._type()? { 115 | GeomType::Point => { 116 | let c = self.to_coordinates::()?; 117 | let p: geo_types::Point = match c { 118 | CoordSeq::Point(pt) => pt.into(), 119 | _ => unimplemented!(), 120 | }; 121 | Ok(geo_types::Geometry::Point(p)) 122 | } 123 | GeomType::Multipoint => { 124 | let c = self.to_coordinates::()?; 125 | let p: geo_types::MultiPoint = match c { 126 | CoordSeq::Multipoint(pts) => pts.into(), 127 | _ => unimplemented!(), 128 | }; 129 | Ok(geo_types::Geometry::MultiPoint(p)) 130 | } 131 | GeomType::Linestring => Ok(geo_types::Geometry::LineString(geo_line_from_sfcgal( 132 | unsafe { self.c_geom.as_ref() }, 133 | )?)), 134 | GeomType::Multilinestring => { 135 | let ngeoms = 136 | unsafe { sfcgal_geometry_collection_num_geometries(self.c_geom.as_ref()) }; 137 | let mut lines = Vec::with_capacity(ngeoms); 138 | for i in 0..ngeoms { 139 | let geom = 140 | unsafe { sfcgal_geometry_collection_geometry_n(self.c_geom.as_ref(), i) }; 141 | lines.push(geo_line_from_sfcgal(geom)?); 142 | } 143 | Ok(geo_types::Geometry::MultiLineString( 144 | geo_types::MultiLineString(lines), 145 | )) 146 | } 147 | GeomType::Polygon => { 148 | let nrings = unsafe { sfcgal_polygon_num_interior_rings(self.c_geom.as_ref()) }; 149 | let exterior_sfcgal = unsafe { sfcgal_polygon_exterior_ring(self.c_geom.as_ref()) }; 150 | let exterior_geo = geo_line_from_sfcgal(exterior_sfcgal)?; 151 | let mut interiors_geo = Vec::with_capacity(nrings); 152 | for i in 0..nrings { 153 | let line_sfcgal = 154 | unsafe { sfcgal_polygon_interior_ring_n(self.c_geom.as_ref(), i) }; 155 | interiors_geo.push(geo_line_from_sfcgal(line_sfcgal)?); 156 | } 157 | 158 | Ok(geo_types::Geometry::Polygon(geo_types::Polygon::new( 159 | exterior_geo, 160 | interiors_geo, 161 | ))) 162 | } 163 | GeomType::Multipolygon => { 164 | let ngeoms = 165 | unsafe { sfcgal_geometry_collection_num_geometries(self.c_geom.as_ref()) }; 166 | let mut vec_polygons = Vec::with_capacity(ngeoms); 167 | for i in 0..ngeoms { 168 | let _polyg = 169 | unsafe { sfcgal_geometry_collection_geometry_n(self.c_geom.as_ref(), i) }; 170 | let nrings = unsafe { sfcgal_polygon_num_interior_rings(_polyg) }; 171 | let exterior_sfcgal = unsafe { sfcgal_polygon_exterior_ring(_polyg) }; 172 | let exterior_geo = geo_line_from_sfcgal(exterior_sfcgal)?; 173 | let mut interiors_geo = Vec::with_capacity(nrings); 174 | for j in 0..nrings { 175 | let line_sfcgal = unsafe { sfcgal_polygon_interior_ring_n(_polyg, j) }; 176 | interiors_geo.push(geo_line_from_sfcgal(line_sfcgal)?); 177 | } 178 | vec_polygons.push(geo_types::Polygon::new(exterior_geo, interiors_geo)); 179 | } 180 | 181 | Ok(geo_types::MultiPolygon(vec_polygons).into()) 182 | } 183 | GeomType::Geometrycollection => { 184 | let c = self.to_coordinates::()?; 185 | let p = match c { 186 | CoordSeq::Geometrycollection(g) => { 187 | g.into_iter() 188 | .map(TryInto::try_into) 189 | .collect::>>>()? 190 | } 191 | _ => unimplemented!(), 192 | }; 193 | Ok(geo_types::Geometry::GeometryCollection( 194 | geo_types::GeometryCollection(p), 195 | )) 196 | } 197 | GeomType::Triangle => { 198 | let coords = match self.to_coordinates::()? { 199 | CoordSeq::Triangle(t) => t, 200 | _ => unimplemented!(), 201 | }; 202 | Ok(geo_types::Geometry::Triangle(geo_types::Triangle( 203 | coords[0].into(), 204 | coords[1].into(), 205 | coords[2].into(), 206 | ))) 207 | } 208 | _ => Err(format_err!( 209 | "Conversion from SFCGeometry of type `Solid`, `Multisolid`, \ 210 | `Triangulatedsurface` and `Polyhedralsurface` \ 211 | to geo_types::Geometry are not yet implemented!" 212 | )), 213 | } 214 | } 215 | } 216 | 217 | fn geo_line_from_sfcgal( 218 | sfcgal_geom: *const sfcgal_geometry_t, 219 | ) -> Result> { 220 | let n_points = unsafe { sfcgal_linestring_num_points(sfcgal_geom) }; 221 | let mut v_points = Vec::with_capacity(n_points); 222 | for i in 0..n_points { 223 | let pt_sfcgal = unsafe { sfcgal_linestring_point_n(sfcgal_geom, i) }; 224 | check_null_geom(pt_sfcgal)?; 225 | let pt_geom = geo_point_from_sfcgal(pt_sfcgal); 226 | v_points.push(pt_geom); 227 | } 228 | Ok(geo_types::LineString::from(v_points)) 229 | } 230 | 231 | fn geo_point_from_sfcgal(geom: *const sfcgal_geometry_t) -> geo_types::Point { 232 | let x = unsafe { sfcgal_point_x(geom) }; 233 | let y = unsafe { sfcgal_point_y(geom) }; 234 | geo_types::Point::new(x, y) 235 | } 236 | 237 | /// Create a `SFCGeometry` from a geo-types Point 238 | impl ToSFCGAL for geo_types::Point { 239 | fn to_sfcgal(&self) -> Result { 240 | unsafe { SFCGeometry::new_from_raw(sfcgal_point_create_from_xy(self.x(), self.y()), true) } 241 | } 242 | } 243 | 244 | /// Create a `SFCGeometry` from a geo-types MultiPoint 245 | impl ToSFCGAL for geo_types::MultiPoint { 246 | fn to_sfcgal(&self) -> Result { 247 | make_sfcgal_multi_geom!( 248 | sfcgal_multi_point_create(), 249 | self.0 250 | .iter() 251 | .map(|pt| pt.to_sfcgeometry()) 252 | .collect::>>()? 253 | ) 254 | } 255 | } 256 | 257 | /// Create a `SFCGeometry` from a geo-types Line 258 | impl ToSFCGAL for geo_types::Line { 259 | fn to_sfcgal(&self) -> Result { 260 | let out_linestring = unsafe { sfcgal_linestring_create() }; 261 | check_null_geom(out_linestring)?; 262 | let start = unsafe { sfcgal_point_create_from_xy(self.start.x, self.start.y) }; 263 | let end = unsafe { sfcgal_point_create_from_xy(self.end.x, self.end.y) }; 264 | check_null_geom(start)?; 265 | check_null_geom(end)?; 266 | unsafe { 267 | sfcgal_linestring_add_point(out_linestring, start); 268 | sfcgal_linestring_add_point(out_linestring, end); 269 | SFCGeometry::new_from_raw(out_linestring, true) 270 | } 271 | } 272 | } 273 | /// Create a `SFCGeometry` from a geo-types LineString 274 | impl ToSFCGAL for geo_types::LineString { 275 | fn to_sfcgal(&self) -> Result { 276 | let line = self.0.to_sfcgeometry()?; 277 | unsafe { SFCGeometry::new_from_raw(line, true) } 278 | } 279 | } 280 | 281 | /// Create a `SFCGeometry` from a geo-types MultiLineString 282 | impl ToSFCGAL for geo_types::MultiLineString { 283 | fn to_sfcgal(&self) -> Result { 284 | make_sfcgal_multi_geom!( 285 | sfcgal_multi_linestring_create(), 286 | self.0 287 | .iter() 288 | .map(|line| line.0.to_sfcgeometry()) 289 | .collect::>>()? 290 | ) 291 | } 292 | } 293 | 294 | /// Create a `SFCGeometry` from a geo-types Triangle 295 | impl ToSFCGAL for geo_types::Triangle { 296 | fn to_sfcgal(&self) -> Result { 297 | let out_triangle = unsafe { sfcgal_triangle_create() }; 298 | check_null_geom(out_triangle)?; 299 | let geo_types::Triangle(c0, c1, c2) = self; 300 | unsafe { 301 | sfcgal_triangle_set_vertex_from_xy(out_triangle, 0, c0.x, c0.y); 302 | sfcgal_triangle_set_vertex_from_xy(out_triangle, 1, c1.x, c1.y); 303 | sfcgal_triangle_set_vertex_from_xy(out_triangle, 2, c2.x, c2.y); 304 | SFCGeometry::new_from_raw(out_triangle, true) 305 | } 306 | } 307 | } 308 | 309 | fn geo_polygon_to_sfcgal( 310 | exterior: &Vec, 311 | interiors: &[geo_types::LineString], 312 | ) -> Result<*mut sfcgal_geometry_t> 313 | where 314 | T: ToSFCGALGeom + CoordType, 315 | { 316 | let out_polygon = 317 | unsafe { sfcgal_polygon_create_from_exterior_ring(exterior.to_sfcgeometry()?) }; 318 | check_null_geom(out_polygon)?; 319 | for ring in interiors.iter() { 320 | unsafe { sfcgal_polygon_add_interior_ring(out_polygon, ring.0.to_sfcgeometry()?) }; 321 | } 322 | Ok(out_polygon) 323 | } 324 | 325 | /// Create a `SFCGeometry` from a geo-types Polygon 326 | impl ToSFCGAL for geo_types::Polygon { 327 | fn to_sfcgal(&self) -> Result { 328 | // let geo_types::Polygon{exterior, interiors} = self; 329 | let (exterior, interiors) = (self.exterior(), self.interiors()); 330 | let out_polygon = geo_polygon_to_sfcgal(&exterior.0, interiors)?; 331 | unsafe { SFCGeometry::new_from_raw(out_polygon, true) } 332 | } 333 | } 334 | 335 | /// Create a `SFCGeometry` from a geo-types MultiPolygon 336 | impl ToSFCGAL for geo_types::MultiPolygon { 337 | fn to_sfcgal(&self) -> Result { 338 | make_sfcgal_multi_geom!( 339 | sfcgal_multi_polygon_create(), 340 | self.0 341 | .iter() 342 | .map(|polygon| { 343 | // let geo_types::Polygon{ref exterior, ref interiors} = polygon; 344 | let (exterior, interiors) = (polygon.exterior(), polygon.interiors()); 345 | geo_polygon_to_sfcgal(&exterior.0, interiors) 346 | }) 347 | .collect::>>()? 348 | ) 349 | } 350 | } 351 | 352 | /// Create a `SFCGeometry` from a geo-types GeometryCollection 353 | impl ToSFCGAL for geo_types::GeometryCollection { 354 | fn to_sfcgal(&self) -> Result { 355 | make_sfcgal_multi_geom!( 356 | sfcgal_geometry_collection_create(), 357 | self.0 358 | .iter() 359 | .map(|geom| { 360 | let mut _g = geom.to_sfcgal()?; 361 | _g.owned = false; 362 | Ok(_g.c_geom.as_ptr()) 363 | }) 364 | .collect::>>()? 365 | ) 366 | } 367 | } 368 | 369 | /// Create a `SFCGeometry` from a geo-types Rect 370 | impl ToSFCGAL for geo_types::Rect { 371 | fn to_sfcgal(&self) -> Result { 372 | let poly = self.to_polygon(); 373 | poly.to_sfcgal() 374 | } 375 | } 376 | 377 | /// Create a `SFCGeometry` from any geo-type Geometry 378 | impl ToSFCGAL for geo_types::Geometry { 379 | fn to_sfcgal(&self) -> Result { 380 | match *self { 381 | geo_types::Geometry::Point(ref c) => c.to_sfcgal(), 382 | geo_types::Geometry::Line(ref c) => c.to_sfcgal(), 383 | geo_types::Geometry::LineString(ref c) => c.to_sfcgal(), 384 | geo_types::Geometry::Polygon(ref c) => c.to_sfcgal(), 385 | geo_types::Geometry::MultiPoint(ref c) => c.to_sfcgal(), 386 | geo_types::Geometry::MultiLineString(ref c) => c.to_sfcgal(), 387 | geo_types::Geometry::MultiPolygon(ref c) => c.to_sfcgal(), 388 | geo_types::Geometry::GeometryCollection(ref c) => c.to_sfcgal(), 389 | geo_types::Geometry::Rect(ref c) => c.to_sfcgal(), 390 | geo_types::Geometry::Triangle(ref c) => c.to_sfcgal(), 391 | } 392 | } 393 | } 394 | 395 | #[cfg(test)] 396 | mod tests { 397 | use super::TryInto; 398 | use crate::{GeomType, SFCGeometry, ToSFCGAL}; 399 | use geo_types::{ 400 | Coord, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Triangle, 401 | }; 402 | use std::convert::TryFrom; 403 | 404 | #[test] 405 | fn point_geo_to_sfcgal_to_geo() { 406 | let pt = Point::new(0.1, 0.9); 407 | let pt_sfcgal = pt.to_sfcgal().unwrap(); 408 | assert!(pt_sfcgal.is_valid().unwrap()); 409 | let pt: Point = 410 | geo_types::Point::try_from(TryInto::try_into(pt_sfcgal).unwrap()).unwrap(); 411 | assert_eq!(pt.x(), 0.1); 412 | assert_eq!(pt.y(), 0.9); 413 | } 414 | 415 | #[test] 416 | fn point_sfcgal_try_into_geo() { 417 | let pt_sfcgal = SFCGeometry::new("POINT (0.1 0.9)").unwrap(); 418 | let pt: Point = 419 | geo_types::Point::try_from(TryInto::try_into(pt_sfcgal).unwrap()).unwrap(); 420 | assert_ulps_eq!(pt.x(), 0.1); 421 | assert_ulps_eq!(pt.y(), 0.9); 422 | } 423 | 424 | #[test] 425 | fn multipoint_geo_to_sfcgal_to_geo() { 426 | let multipt = MultiPoint::from(vec![Point::new(0., 0.), Point::new(1., 1.)]); 427 | let mpt_sfcgal = multipt.to_sfcgal().unwrap(); 428 | assert!(mpt_sfcgal.is_valid().unwrap()); 429 | let mpt: MultiPoint = 430 | geo_types::MultiPoint::try_from(TryInto::try_into(mpt_sfcgal).unwrap()).unwrap(); 431 | 432 | assert_eq!(mpt.0[0].x(), 0.); 433 | assert_eq!(mpt.0[0].y(), 0.); 434 | assert_eq!(mpt.0[1].x(), 1.); 435 | assert_eq!(mpt.0[1].y(), 1.); 436 | } 437 | 438 | #[test] 439 | fn linestring_geo_to_sfcgal_to_geo() { 440 | let linestring = LineString::from(vec![Point::new(0., 0.), Point::new(1., 1.)]); 441 | let line_sfcgal = linestring.to_sfcgal().unwrap(); 442 | assert!(line_sfcgal.is_valid().unwrap()); 443 | let linestring_geo: LineString = 444 | geo_types::LineString::try_from(TryInto::try_into(line_sfcgal).unwrap()).unwrap(); 445 | assert_eq!(linestring_geo.0[0].x, 0.); 446 | assert_eq!(linestring_geo.0[0].y, 0.); 447 | assert_eq!(linestring_geo.0[1].x, 1.); 448 | assert_eq!(linestring_geo.0[1].y, 1.); 449 | } 450 | 451 | #[test] 452 | fn multilinestring_geo_to_sfcgal_to_geo() { 453 | let multilinestring = MultiLineString::from(LineString::from(vec![ 454 | Point::new(0., 0.), 455 | Point::new(1., 1.), 456 | ])); 457 | let mls_sfcgal = multilinestring.to_sfcgal().unwrap(); 458 | assert!(mls_sfcgal.is_valid().unwrap()); 459 | let mls: MultiLineString = 460 | geo_types::MultiLineString::try_from(TryInto::try_into(mls_sfcgal).unwrap()).unwrap(); 461 | assert_eq!(mls.0[0].0[0].x, 0.); 462 | assert_eq!(mls.0[0].0[0].y, 0.); 463 | assert_eq!(mls.0[0].0[1].x, 1.); 464 | assert_eq!(mls.0[0].0[1].y, 1.); 465 | } 466 | 467 | #[test] 468 | fn triangle_geo_to_sfcgal_to_geo() { 469 | let tri = Triangle( 470 | Coord::from((0., 0.)), 471 | Coord::from((1., 0.)), 472 | Coord::from((0.5, 1.)), 473 | ); 474 | let tri_sfcgal = tri.to_sfcgal().unwrap(); 475 | assert!(tri_sfcgal.is_valid().unwrap()); 476 | assert_eq!(tri_sfcgal._type().unwrap(), GeomType::Triangle); 477 | let tri_geo: geo_types::Geometry = TryInto::try_into(tri_sfcgal).unwrap(); 478 | match tri_geo { 479 | geo_types::Geometry::Triangle(t) => { 480 | assert_eq!(t.0.x, 0.); 481 | assert_eq!(t.0.y, 0.); 482 | assert_eq!(t.1.x, 1.); 483 | assert_eq!(t.1.y, 0.); 484 | assert_eq!(t.2.x, 0.5); 485 | assert_eq!(t.2.y, 1.); 486 | } 487 | _ => panic!("Bad conversion when converting Triangle"), 488 | } 489 | } 490 | 491 | #[test] 492 | fn polygon_geo_to_sfcgal_to_geo() { 493 | let polygon = Polygon::new( 494 | LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)]), 495 | vec![LineString::from(vec![ 496 | (0.1, 0.1), 497 | (0.1, 0.9), 498 | (0.9, 0.9), 499 | (0.9, 0.1), 500 | (0.1, 0.1), 501 | ])], 502 | ); 503 | let poly_sfcgal = polygon.to_sfcgal().unwrap(); 504 | let polyg: Polygon = 505 | geo_types::Polygon::try_from(TryInto::try_into(poly_sfcgal).unwrap()).unwrap(); 506 | let interiors = polyg.interiors(); 507 | assert_eq!( 508 | polyg.exterior(), 509 | &LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.,), (0., 0.)]) 510 | ); 511 | assert_eq!(interiors[0].0[0].x, 0.1); 512 | assert_eq!(interiors[0].0[0].y, 0.1); 513 | assert_eq!(interiors[0].0[2].x, 0.9); 514 | assert_eq!(interiors[0].0[2].y, 0.9); 515 | assert_eq!(interiors[0].0[3].x, 0.9); 516 | assert_eq!(interiors[0].0[3].y, 0.1); 517 | } 518 | 519 | #[test] 520 | fn multipolygon_geo_to_sfcgal_to_geo() { 521 | let multipolygon = MultiPolygon(vec![Polygon::new( 522 | LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)]), 523 | vec![LineString::from(vec![ 524 | (0.1, 0.1), 525 | (0.1, 0.9), 526 | (0.9, 0.9), 527 | (0.9, 0.1), 528 | (0.1, 0.1), 529 | ])], 530 | )]); 531 | let mutlipolygon_sfcgal = multipolygon.to_sfcgal().unwrap(); 532 | let mpg: MultiPolygon = 533 | geo_types::MultiPolygon::try_from(TryInto::try_into(mutlipolygon_sfcgal).unwrap()) 534 | .unwrap(); 535 | 536 | assert_eq!( 537 | mpg.0[0].exterior(), 538 | &LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.,), (0., 0.)]) 539 | ); 540 | assert_eq!( 541 | mpg.0[0].interiors()[0], 542 | LineString::from(vec![ 543 | (0.1, 0.1), 544 | (0.1, 0.9,), 545 | (0.9, 0.9), 546 | (0.9, 0.1), 547 | (0.1, 0.1) 548 | ]) 549 | ); 550 | } 551 | 552 | #[test] 553 | fn geometrycollection_sfcgal_to_geo_to_sfcgal() { 554 | let input_wkt = "GEOMETRYCOLLECTION (POINT (4.0 6.0),LINESTRING (4.0 6.0,7.0 10.0))"; 555 | let gc_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 556 | let gc: geo_types::Geometry = TryInto::try_into(gc_sfcgal).unwrap(); 557 | if let geo_types::Geometry::GeometryCollection(_gc) = &gc { 558 | assert_eq!( 559 | Point::new(4., 6.), 560 | geo_types::Point::try_from(_gc.0[0].clone()).unwrap() 561 | ); 562 | assert_eq!( 563 | LineString::from(vec![(4., 6.), (7., 10.)]), 564 | geo_types::LineString::try_from(_gc.0[1].clone()).unwrap(), 565 | ); 566 | let gc_sfcgal = _gc.to_sfcgal().unwrap(); 567 | assert_eq!(input_wkt, gc_sfcgal.to_wkt_decim(1).unwrap()); 568 | } else { 569 | panic!("Error while deconstructing geometrycollection"); 570 | } 571 | } 572 | } 573 | -------------------------------------------------------------------------------- /src/conversion/coords.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | utils::check_null_geom, GeomType, Point2d, Point3d, Result, SFCGeometry, ToCoordinates, 3 | ToSFCGAL, 4 | }; 5 | use sfcgal_sys::{ 6 | sfcgal_geometry_collection_add_geometry, sfcgal_geometry_collection_create, 7 | sfcgal_geometry_collection_geometry_n, sfcgal_geometry_collection_num_geometries, 8 | sfcgal_geometry_delete, sfcgal_geometry_t, sfcgal_linestring_add_point, 9 | sfcgal_linestring_create, sfcgal_linestring_num_points, sfcgal_linestring_point_n, 10 | sfcgal_multi_linestring_create, sfcgal_multi_point_create, sfcgal_multi_polygon_create, 11 | sfcgal_point_create_from_xy, sfcgal_point_create_from_xyz, sfcgal_point_x, sfcgal_point_y, 12 | sfcgal_point_z, sfcgal_polygon_add_interior_ring, sfcgal_polygon_create_from_exterior_ring, 13 | sfcgal_polygon_exterior_ring, sfcgal_polygon_interior_ring_n, 14 | sfcgal_polygon_num_interior_rings, sfcgal_polyhedral_surface_add_polygon, 15 | sfcgal_polyhedral_surface_create, sfcgal_polyhedral_surface_num_polygons, 16 | sfcgal_polyhedral_surface_polygon_n, sfcgal_solid_add_interior_shell, sfcgal_solid_create, 17 | sfcgal_solid_create_from_exterior_shell, sfcgal_solid_num_shells, sfcgal_solid_shell_n, 18 | sfcgal_triangle_create, sfcgal_triangle_set_vertex, sfcgal_triangle_vertex, 19 | sfcgal_triangulated_surface_add_triangle, sfcgal_triangulated_surface_create, 20 | sfcgal_triangulated_surface_num_triangles, sfcgal_triangulated_surface_triangle_n, 21 | }; 22 | 23 | pub trait CoordType {} 24 | impl CoordType for Point2d {} 25 | impl CoordType for Point3d {} 26 | 27 | /// Convert a point passed as a raw `sfcgal_geometry_t` to a tuple of coordinates. 28 | pub trait FromSFCGALGeom { 29 | fn from_sfcgeometry(geom: *const sfcgal_geometry_t) -> Self; 30 | } 31 | 32 | /// Convert coordinates to `sfcgal_geometry_t`. 33 | pub trait ToSFCGALGeom { 34 | fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t>; 35 | } 36 | 37 | impl FromSFCGALGeom for Point2d { 38 | fn from_sfcgeometry(c_geom: *const sfcgal_geometry_t) -> Point2d { 39 | let x = unsafe { sfcgal_point_x(c_geom) }; 40 | let y = unsafe { sfcgal_point_y(c_geom) }; 41 | (x, y) 42 | } 43 | } 44 | 45 | impl FromSFCGALGeom for Point3d { 46 | fn from_sfcgeometry(c_geom: *const sfcgal_geometry_t) -> Point3d { 47 | let x = unsafe { sfcgal_point_x(c_geom) }; 48 | let y = unsafe { sfcgal_point_y(c_geom) }; 49 | let z = unsafe { sfcgal_point_z(c_geom) }; 50 | (x, y, z) 51 | } 52 | } 53 | 54 | impl ToSFCGALGeom for Point2d { 55 | fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> { 56 | let g = unsafe { sfcgal_point_create_from_xy(self.0, self.1) }; 57 | check_null_geom(g)?; 58 | Ok(g) 59 | } 60 | } 61 | 62 | impl ToSFCGALGeom for Point3d { 63 | fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> { 64 | let g = unsafe { sfcgal_point_create_from_xyz(self.0, self.1, self.2) }; 65 | check_null_geom(g)?; 66 | Ok(g) 67 | } 68 | } 69 | 70 | impl ToSFCGALGeom for Vec 71 | where 72 | T: ToSFCGALGeom + CoordType, 73 | { 74 | fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> { 75 | let out_linestring = unsafe { sfcgal_linestring_create() }; 76 | check_null_geom(out_linestring)?; 77 | for point in self { 78 | let sf_pt_geom: *mut sfcgal_geometry_t = point.to_sfcgeometry()?; 79 | unsafe { sfcgal_linestring_add_point(out_linestring, sf_pt_geom) }; 80 | } 81 | Ok(out_linestring) 82 | } 83 | } 84 | 85 | /// Coordinates corresponding to the shapes described by SFCGAL Geometry types. 86 | /// 87 | /// Used to construct SFCGeometry from coordinates or to retrieve coordinates from SFCGeometry. 88 | /// ``` rust 89 | /// use sfcgal::{CoordSeq, ToSFCGAL}; 90 | /// 91 | /// let coordinates = CoordSeq::Linestring(vec![(-0.5, -0.5, 2.5), (0., 0., 4.0)]); 92 | /// let line_3d = coordinates.to_sfcgal().unwrap(); 93 | /// assert!(line_3d.is_valid().unwrap()); 94 | /// ``` 95 | /// ``` rust 96 | /// use sfcgal::{SFCGeometry, CoordSeq, Point3d, ToCoordinates}; 97 | /// 98 | /// let line_3d = SFCGeometry::new("LINESTRING (3.5 5.6 1.0,4.8 10.5 1.0)").unwrap(); 99 | /// let coordinates = line_3d.to_coordinates::().unwrap(); 100 | /// // assert_eq!(coordinates, CoordSeq::Linestring(vec![(3.5, 5.6, 1.0), (4.8, 10.5, 1.0)])); 101 | /// ``` 102 | #[derive(Debug, Clone, Hash, PartialEq)] 103 | pub enum CoordSeq { 104 | /// A Point is described by a tuple of 2 or 3 coordinates. 105 | Point(T), 106 | /// A Linestring is described by Vec of Point. 107 | Linestring(Vec), 108 | /// A Polygon is described by a Vec of closed Linestring : the first one 109 | /// is the exterior ring and the others are interior rings. 110 | Polygon(Vec>), 111 | /// A Multipoint is described by a Vec of Points. 112 | Multipoint(Vec), 113 | /// A Multilinestring is described by a Vec of Linestrings. 114 | Multilinestring(Vec>), 115 | /// A Multipolygon is described by a Vec of Polygons. 116 | Multipolygon(Vec>>), 117 | /// A Geometrycollection is described by a Vec of any of these CoordSeq geometries. 118 | Geometrycollection(Vec>), 119 | /// A Polyhedralsurface is described by a Vec of Polygons. 120 | Polyhedralsurface(Vec>>), 121 | /// A Triangulatedsurface is described by a Vec of Triangles. 122 | Triangulatedsurface(Vec>), 123 | /// A Triangle is described by a Vec of 3 Points. 124 | Triangle(Vec), 125 | /// A Solid is described by a Vec of Polyhedralsurfaces : the first one 126 | /// is the exterior shell and the others are interior shells. 127 | Solid(Vec>>>), 128 | /// A Multisolid is described by a Vec of Solids. 129 | Multisolid(Vec>>>>), 130 | } 131 | 132 | fn coords_polygon_to_sfcgal(rings: &[Vec]) -> Result<*mut sfcgal_geometry_t> 133 | where 134 | T: ToSFCGALGeom + CoordType, 135 | { 136 | let out_polygon = 137 | unsafe { sfcgal_polygon_create_from_exterior_ring(rings[0].to_sfcgeometry()?) }; 138 | check_null_geom(out_polygon)?; 139 | for ring in rings.iter().skip(1) { 140 | unsafe { sfcgal_polygon_add_interior_ring(out_polygon, ring.to_sfcgeometry()?) }; 141 | } 142 | Ok(out_polygon) 143 | } 144 | 145 | fn coords_polyhedralsurface_to_sfcgal( 146 | polyhedres: &[Vec>], 147 | ) -> Result<*mut sfcgal_geometry_t> 148 | where 149 | T: ToSFCGALGeom + CoordType, 150 | { 151 | let out_surf = unsafe { sfcgal_polyhedral_surface_create() }; 152 | check_null_geom(out_surf)?; 153 | for poly in polyhedres { 154 | unsafe { sfcgal_polyhedral_surface_add_polygon(out_surf, coords_polygon_to_sfcgal(poly)?) }; 155 | } 156 | Ok(out_surf) 157 | } 158 | 159 | unsafe fn pts_to_triangle_sfcgal(pts: &[T]) -> Result<*mut sfcgal_geometry_t> 160 | where 161 | T: ToSFCGALGeom, 162 | { 163 | let out_triangle = sfcgal_triangle_create(); 164 | check_null_geom(out_triangle)?; 165 | let p0: *mut sfcgal_geometry_t = pts[0].to_sfcgeometry()?; 166 | let p1: *mut sfcgal_geometry_t = pts[1].to_sfcgeometry()?; 167 | let p2: *mut sfcgal_geometry_t = pts[2].to_sfcgeometry()?; 168 | sfcgal_triangle_set_vertex(out_triangle, 0, p0); 169 | sfcgal_triangle_set_vertex(out_triangle, 1, p1); 170 | sfcgal_triangle_set_vertex(out_triangle, 2, p2); 171 | sfcgal_geometry_delete(p0); 172 | sfcgal_geometry_delete(p1); 173 | sfcgal_geometry_delete(p2); 174 | Ok(out_triangle) 175 | } 176 | 177 | /// Convert coordinates (tuple of 2 or 3 members) to [`SFCGeometry`] using 178 | /// the corresponding [`CoordSeq`] variant of the wanted geometry. 179 | /// 180 | /// [`SFCGeometry`]: struct.SFCGeometry.html 181 | /// [`CoordSeq`]: enum.CoordSeq.html 182 | impl ToSFCGAL for CoordSeq { 183 | /// Convert the coordinates of this [`CoordSeq`] to [`SFCGeometry`]. 184 | /// 185 | /// [`SFCGeometry`]: struct.SFCGeometry.html 186 | /// [`CoordSeq`]: enum.CoordSeq.html 187 | fn to_sfcgal(&self) -> Result { 188 | match self { 189 | CoordSeq::Point(pt) => unsafe { SFCGeometry::new_from_raw(pt.to_sfcgeometry()?, true) }, 190 | CoordSeq::Multipoint(pts) => make_sfcgal_multi_geom!( 191 | sfcgal_multi_point_create(), 192 | pts.iter() 193 | .map(|p| p.to_sfcgeometry()) 194 | .collect::>>()? 195 | ), 196 | CoordSeq::Linestring(pts) => { 197 | let out_linestring = pts.to_sfcgeometry()?; 198 | unsafe { SFCGeometry::new_from_raw(out_linestring, true) } 199 | } 200 | CoordSeq::Multilinestring(ref linestring_list) => make_sfcgal_multi_geom!( 201 | sfcgal_multi_linestring_create(), 202 | linestring_list 203 | .iter() 204 | .map(|l| l.to_sfcgeometry()) 205 | .collect::>>()? 206 | ), 207 | CoordSeq::Polygon(ref rings) => { 208 | let out_polygon = coords_polygon_to_sfcgal(rings)?; 209 | unsafe { SFCGeometry::new_from_raw(out_polygon, true) } 210 | } 211 | CoordSeq::Multipolygon(ref polygons) => make_sfcgal_multi_geom!( 212 | sfcgal_multi_polygon_create(), 213 | polygons 214 | .iter() 215 | .map(|p| coords_polygon_to_sfcgal(p)) 216 | .collect::>>()? 217 | ), 218 | CoordSeq::Geometrycollection(ref geoms) => { 219 | let out_geom_collection = unsafe { sfcgal_geometry_collection_create() }; 220 | check_null_geom(out_geom_collection)?; 221 | for g_geom in geoms { 222 | let mut sfcgeom = g_geom.to_sfcgal()?; 223 | unsafe { 224 | // ^^ sfcgeom is a SFCGeometry struct on which we own the inner C geometry, 225 | // as we are passing it to `sfcgal_geometry_collection_add_geometry` 226 | // we are not responsible for its deallocation anymore : 227 | sfcgeom.owned = false; 228 | sfcgal_geometry_collection_add_geometry( 229 | out_geom_collection, 230 | sfcgeom.c_geom.as_ptr(), 231 | ); 232 | }; 233 | } 234 | unsafe { SFCGeometry::new_from_raw(out_geom_collection, true) } 235 | } 236 | CoordSeq::Triangle(ref pts) => unsafe { 237 | SFCGeometry::new_from_raw(pts_to_triangle_sfcgal(pts)?, true) 238 | }, 239 | CoordSeq::Triangulatedsurface(ref triangles) => unsafe { 240 | let out_surface = sfcgal_triangulated_surface_create(); 241 | check_null_geom(out_surface)?; 242 | triangles 243 | .iter() 244 | .map(|pts| { 245 | sfcgal_triangulated_surface_add_triangle( 246 | out_surface, 247 | pts_to_triangle_sfcgal(pts)?, 248 | ); 249 | Ok(()) 250 | }) 251 | .collect::>>()?; 252 | SFCGeometry::new_from_raw(out_surface, true) 253 | }, 254 | CoordSeq::Polyhedralsurface(ref polygons) => { 255 | let out_surface = coords_polyhedralsurface_to_sfcgal(polygons)?; 256 | unsafe { SFCGeometry::new_from_raw(out_surface, true) } 257 | } 258 | CoordSeq::Solid(ref polyhedres) => { 259 | let n_part = polyhedres.len(); 260 | let out_solid = if n_part == 0 { 261 | unsafe { sfcgal_solid_create() } 262 | } else if n_part == 1 { 263 | let exterior = coords_polyhedralsurface_to_sfcgal(&polyhedres[0])?; 264 | unsafe { sfcgal_solid_create_from_exterior_shell(exterior) } 265 | } else { 266 | let exterior = coords_polyhedralsurface_to_sfcgal(&polyhedres[0])?; 267 | let r_solid = unsafe { sfcgal_solid_create_from_exterior_shell(exterior) }; 268 | polyhedres 269 | .iter() 270 | .skip(1) 271 | .map(|poly| { 272 | unsafe { 273 | sfcgal_solid_add_interior_shell( 274 | r_solid, 275 | coords_polyhedralsurface_to_sfcgal(poly)?, 276 | ) 277 | }; 278 | Ok(()) 279 | }) 280 | .collect::>>()?; 281 | r_solid 282 | }; 283 | unsafe { SFCGeometry::new_from_raw(out_solid, true) } 284 | } 285 | _ => unimplemented!(), 286 | } 287 | } 288 | } 289 | 290 | macro_rules! sfcgal_pt_to_coords { 291 | ($geom: expr, T) => {{ 292 | T::from_sfcgeometry($geom) 293 | }}; 294 | } 295 | 296 | macro_rules! sfcgal_line_to_coords { 297 | ($geom: expr, $type_pt: ident) => {{ 298 | let g = $geom; 299 | let n_points = unsafe { sfcgal_linestring_num_points(g) }; 300 | let mut v_points = Vec::with_capacity(n_points as usize); 301 | for i in 0..n_points { 302 | let pt_sfcgal = unsafe { sfcgal_linestring_point_n(g, i) }; 303 | // check_null_geom(g)?; 304 | v_points.push(sfcgal_pt_to_coords!(pt_sfcgal, $type_pt)); 305 | } 306 | v_points 307 | }}; 308 | } 309 | 310 | macro_rules! sfcgal_polygon_to_coords { 311 | ($geom: expr, $type_pt: ident) => {{ 312 | let g = $geom; 313 | let nrings = unsafe { sfcgal_polygon_num_interior_rings(g) }; 314 | let exterior_sfcgal = unsafe { sfcgal_polygon_exterior_ring(g) }; 315 | let mut rings = Vec::with_capacity(nrings as usize + 1); 316 | rings.push(sfcgal_line_to_coords!(exterior_sfcgal, $type_pt)); 317 | for ix in 0..nrings { 318 | let line_sfcgal = unsafe { sfcgal_polygon_interior_ring_n(g, ix) }; 319 | rings.push(sfcgal_line_to_coords!(line_sfcgal, $type_pt)); 320 | } 321 | rings 322 | }}; 323 | } 324 | 325 | macro_rules! sfcgal_triangle_to_coords { 326 | ($geom: expr, $type_pt: ident) => {{ 327 | let g = $geom; 328 | let (p0, p1, p2) = unsafe { 329 | ( 330 | sfcgal_pt_to_coords!(sfcgal_triangle_vertex(g, 0), $type_pt), 331 | sfcgal_pt_to_coords!(sfcgal_triangle_vertex(g, 1), $type_pt), 332 | sfcgal_pt_to_coords!(sfcgal_triangle_vertex(g, 2), $type_pt), 333 | ) 334 | }; 335 | vec![p0, p1, p2] 336 | }}; 337 | } 338 | 339 | macro_rules! sfcgal_polyhedral_surface_to_coords { 340 | ($geom: expr, $type_pt: ident) => {{ 341 | let g = $geom; 342 | let ngeoms = unsafe { sfcgal_polyhedral_surface_num_polygons(g) }; 343 | let mut polygons = Vec::with_capacity(ngeoms as usize); 344 | for ix in 0..ngeoms { 345 | let poly = unsafe { sfcgal_polyhedral_surface_polygon_n(g, ix) }; 346 | polygons.push(sfcgal_polygon_to_coords!(poly, $type_pt)); 347 | } 348 | polygons 349 | }}; 350 | } 351 | 352 | fn get_nb_geometry(geom: *const sfcgal_geometry_t) -> usize { 353 | unsafe { sfcgal_geometry_collection_num_geometries(geom) } 354 | } 355 | 356 | fn get_geom_at_index(geom: *const sfcgal_geometry_t, ix: usize) -> *const sfcgal_geometry_t { 357 | unsafe { sfcgal_geometry_collection_geometry_n(geom, ix) } 358 | } 359 | 360 | /// Convert a [`SFCGeometry`], given it's internal [`GeomType`], to the corresponding [`CoordSeq`] 361 | /// holding its coordinates (as tuple of 2 or 3 members). 362 | /// 363 | /// [`SFCGeometry`]: struct.SFCGeometry.html 364 | /// [`GeomType`]: enum.GeomType.html 365 | /// [`CoordSeq`]: enum.CoordSeq.html 366 | impl ToCoordinates for SFCGeometry { 367 | fn to_coordinates(&self) -> Result> 368 | where 369 | T: FromSFCGALGeom + CoordType, 370 | { 371 | match self._type()? { 372 | GeomType::Point => Ok(CoordSeq::Point(sfcgal_pt_to_coords!( 373 | unsafe { self.c_geom.as_ref() }, 374 | T 375 | ))), 376 | GeomType::Multipoint => { 377 | let geom = unsafe { self.c_geom.as_ref() }; 378 | let ngeoms = get_nb_geometry(geom); 379 | let mut pts = Vec::with_capacity(ngeoms); 380 | for ix in 0..ngeoms { 381 | pts.push(sfcgal_pt_to_coords!(get_geom_at_index(geom, ix), T)); 382 | } 383 | Ok(CoordSeq::Multipoint(pts)) 384 | } 385 | GeomType::Linestring => Ok(CoordSeq::Linestring(sfcgal_line_to_coords!( 386 | unsafe { self.c_geom.as_ref() }, 387 | T 388 | ))), 389 | GeomType::Multilinestring => { 390 | let geom = unsafe { self.c_geom.as_ref() }; 391 | let ngeoms = get_nb_geometry(geom); 392 | let mut lines = Vec::with_capacity(ngeoms); 393 | for ix in 0..ngeoms { 394 | lines.push(sfcgal_line_to_coords!(get_geom_at_index(geom, ix), T)); 395 | } 396 | Ok(CoordSeq::Multilinestring(lines)) 397 | } 398 | GeomType::Polygon => { 399 | let geom = unsafe { self.c_geom.as_ref() }; 400 | Ok(CoordSeq::Polygon(sfcgal_polygon_to_coords!(geom, T))) 401 | } 402 | GeomType::Multipolygon => { 403 | let geom = unsafe { self.c_geom.as_ref() }; 404 | let ngeoms = get_nb_geometry(geom); 405 | let mut polygons = Vec::with_capacity(ngeoms); 406 | for ix in 0..ngeoms { 407 | polygons.push(sfcgal_polygon_to_coords!(get_geom_at_index(geom, ix), T)); 408 | } 409 | Ok(CoordSeq::Multipolygon(polygons)) 410 | } 411 | GeomType::Geometrycollection => { 412 | let geom = unsafe { self.c_geom.as_ref() }; 413 | let ngeoms = get_nb_geometry(geom); 414 | let mut geoms = Vec::with_capacity(ngeoms); 415 | for ix in 0..ngeoms { 416 | let inner_geom = unsafe { 417 | // Todo : document what we are doing that 418 | SFCGeometry::new_from_raw( 419 | sfcgal_geometry_collection_geometry_n(geom, ix) 420 | as *mut sfcgal_geometry_t, 421 | false, 422 | )? 423 | }; 424 | let coords: CoordSeq = inner_geom.to_coordinates()?; 425 | geoms.push(coords); 426 | } 427 | Ok(CoordSeq::Geometrycollection(geoms)) 428 | } 429 | GeomType::Triangle => Ok(CoordSeq::Triangle(sfcgal_triangle_to_coords!( 430 | unsafe { self.c_geom.as_ref() }, 431 | T 432 | ))), 433 | GeomType::Triangulatedsurface => { 434 | let geom = unsafe { self.c_geom.as_ref() }; 435 | let ngeoms = unsafe { sfcgal_triangulated_surface_num_triangles(geom) }; 436 | let mut triangles = Vec::with_capacity(ngeoms); 437 | for ix in 0..ngeoms { 438 | let triangle = unsafe { sfcgal_triangulated_surface_triangle_n(geom, ix) }; 439 | triangles.push(sfcgal_triangle_to_coords!(triangle, T)); 440 | } 441 | Ok(CoordSeq::Triangulatedsurface(triangles)) 442 | } 443 | GeomType::Polyhedralsurface => Ok(CoordSeq::Polyhedralsurface( 444 | sfcgal_polyhedral_surface_to_coords!(unsafe { self.c_geom.as_ref() }, T), 445 | )), 446 | GeomType::Solid => { 447 | let geom = unsafe { self.c_geom.as_ref() }; 448 | let ngeoms = unsafe { sfcgal_solid_num_shells(geom) }; 449 | let mut polyhedres = Vec::with_capacity(ngeoms); 450 | for ix in 0..ngeoms { 451 | let poly = unsafe { sfcgal_solid_shell_n(geom, ix) }; 452 | polyhedres.push(sfcgal_polyhedral_surface_to_coords!(poly, T)); 453 | } 454 | Ok(CoordSeq::Solid(polyhedres)) 455 | } 456 | GeomType::Multisolid => { 457 | let geom_ms = unsafe { self.c_geom.as_ref() }; 458 | let n_solides = get_nb_geometry(geom_ms); 459 | let mut solides = Vec::with_capacity(n_solides); 460 | for ix_geom in 0..n_solides { 461 | let solid = get_geom_at_index(geom_ms, ix_geom); 462 | let n_shell = unsafe { sfcgal_solid_num_shells(solid) }; 463 | let mut polyhedres = Vec::with_capacity(n_shell); 464 | for ix in 0..n_shell { 465 | let poly = unsafe { sfcgal_solid_shell_n(solid, ix) }; 466 | polyhedres.push(sfcgal_polyhedral_surface_to_coords!(poly, T)); 467 | } 468 | solides.push(polyhedres); 469 | } 470 | Ok(CoordSeq::Multisolid(solides)) 471 | } 472 | } 473 | } 474 | } 475 | 476 | #[cfg(test)] 477 | mod tests { 478 | use super::{Point2d, Point3d}; 479 | use crate::*; 480 | 481 | #[test] 482 | fn point_2d_sfcgal_to_coordinates_to_sfcgal() { 483 | let input_wkt = "POINT (0.1 0.9)"; 484 | let pt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 485 | let coords: CoordSeq = pt_sfcgal.to_coordinates().unwrap(); 486 | if let CoordSeq::Point(ref coords) = coords { 487 | assert_ulps_eq!(coords.0, 0.1); 488 | assert_ulps_eq!(coords.1, 0.9); 489 | } else { 490 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 491 | } 492 | let pt_sfcgal = coords.to_sfcgal().unwrap(); 493 | assert_eq!(input_wkt, pt_sfcgal.to_wkt_decim(1).unwrap()) 494 | } 495 | 496 | #[test] 497 | fn point_3d_sfcgal_to_coordinates_to_sfcgal() { 498 | let input_wkt = "POINT Z (0.1 0.9 1.0)"; 499 | let pt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 500 | let coords: CoordSeq = pt_sfcgal.to_coordinates().unwrap(); 501 | if let CoordSeq::Point(ref coords) = coords { 502 | assert_ulps_eq!(coords.0, 0.1); 503 | assert_ulps_eq!(coords.1, 0.9); 504 | assert_ulps_eq!(coords.2, 1.0); 505 | } else { 506 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 507 | } 508 | let pt_sfcgal = coords.to_sfcgal().unwrap(); 509 | assert_eq!(input_wkt, pt_sfcgal.to_wkt_decim(1).unwrap()) 510 | } 511 | 512 | #[test] 513 | fn multipoint_2d_sfcgal_to_coordinates_to_sfcgal() { 514 | let input_wkt = "MULTIPOINT ((3.5 5.6),(4.8 10.5))"; 515 | let multipt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 516 | let coords: CoordSeq = multipt_sfcgal.to_coordinates().unwrap(); 517 | if let CoordSeq::Multipoint(ref coords) = coords { 518 | assert_ulps_eq!(coords[0].0, 3.5); 519 | assert_ulps_eq!(coords[0].1, 5.6); 520 | assert_ulps_eq!(coords[1].0, 4.8); 521 | assert_ulps_eq!(coords[1].1, 10.5); 522 | } else { 523 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 524 | } 525 | let mpt_sfcgal = coords.to_sfcgal().unwrap(); 526 | assert_eq!(input_wkt, mpt_sfcgal.to_wkt_decim(1).unwrap()); 527 | } 528 | 529 | #[test] 530 | fn multipoint_3d_sfcgal_to_coordinates_to_sfcgal() { 531 | let input_wkt = "MULTIPOINT Z ((3.5 5.6 1.0),(4.8 10.5 1.0))"; 532 | let multipt_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 533 | let coords: CoordSeq = multipt_sfcgal.to_coordinates().unwrap(); 534 | if let CoordSeq::Multipoint(ref coords) = coords { 535 | assert_ulps_eq!(coords[0].0, 3.5); 536 | assert_ulps_eq!(coords[0].1, 5.6); 537 | assert_ulps_eq!(coords[0].2, 1.0); 538 | assert_ulps_eq!(coords[1].0, 4.8); 539 | assert_ulps_eq!(coords[1].1, 10.5); 540 | assert_ulps_eq!(coords[1].2, 1.0); 541 | } else { 542 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 543 | } 544 | let mpt_sfcgal = coords.to_sfcgal().unwrap(); 545 | assert_eq!(input_wkt, mpt_sfcgal.to_wkt_decim(1).unwrap()); 546 | } 547 | 548 | #[test] 549 | fn linestring_2d_sfcgal_to_coordinates() { 550 | let input_wkt = "LINESTRING (3.5 5.6,4.8 10.5)"; 551 | let linestring_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 552 | let coords: CoordSeq = linestring_sfcgal.to_coordinates().unwrap(); 553 | if let CoordSeq::Linestring(ref coords) = coords { 554 | assert_ulps_eq!(coords[0].0, 3.5); 555 | assert_ulps_eq!(coords[0].1, 5.6); 556 | assert_ulps_eq!(coords[1].0, 4.8); 557 | assert_ulps_eq!(coords[1].1, 10.5); 558 | } else { 559 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 560 | } 561 | let line_sfcgal = coords.to_sfcgal().unwrap(); 562 | assert_eq!(input_wkt, line_sfcgal.to_wkt_decim(1).unwrap()); 563 | } 564 | 565 | #[test] 566 | fn linestring_3d_sfcgal_to_coordinates() { 567 | let input_wkt = "LINESTRING Z (3.5 5.6 1.0,4.8 10.5 1.0)"; 568 | let linestring_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 569 | let coords: CoordSeq = linestring_sfcgal.to_coordinates().unwrap(); 570 | if let CoordSeq::Linestring(ref coords) = coords { 571 | assert_ulps_eq!(coords[0].0, 3.5); 572 | assert_ulps_eq!(coords[0].1, 5.6); 573 | assert_ulps_eq!(coords[0].2, 1.0); 574 | assert_ulps_eq!(coords[1].0, 4.8); 575 | assert_ulps_eq!(coords[1].1, 10.5); 576 | assert_ulps_eq!(coords[1].2, 1.0); 577 | } else { 578 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 579 | } 580 | let line_sfcgal = coords.to_sfcgal().unwrap(); 581 | assert_eq!(input_wkt, line_sfcgal.to_wkt_decim(1).unwrap()); 582 | } 583 | 584 | #[test] 585 | fn multilinestring_2d_sfcgal_to_coordinates() { 586 | let input_wkt = "MULTILINESTRING ((3.5 5.6,4.8 10.5),(8.9 12.9,2.1 3.5),(1.1 4.8,6.2 7.5))"; 587 | let mls_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 588 | let coords: CoordSeq = mls_sfcgal.to_coordinates().unwrap(); 589 | if let CoordSeq::Multilinestring(ref coords) = coords { 590 | assert_ulps_eq!(coords[0][0].0, 3.5); 591 | assert_ulps_eq!(coords[0][0].1, 5.6); 592 | assert_ulps_eq!(coords[1][1].0, 2.1); 593 | assert_ulps_eq!(coords[1][1].1, 3.5); 594 | assert_ulps_eq!(coords[2][0].0, 1.1); 595 | assert_ulps_eq!(coords[2][0].1, 4.8); 596 | } else { 597 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 598 | } 599 | let mls_sfcgal = coords.to_sfcgal().unwrap(); 600 | assert_eq!(input_wkt, mls_sfcgal.to_wkt_decim(1).unwrap()); 601 | } 602 | 603 | #[test] 604 | fn multilinestring_3d_sfcgal_to_coordinates() { 605 | let input_wkt = "MULTILINESTRING Z ((3.5 5.6 1.0,4.8 10.5 1.0),(8.9 12.9 4.0,2.1 3.5 4.0),(1.1 4.8 4.0,6.2 7.5 4.0))"; 606 | let multilinestring_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 607 | let coords: CoordSeq = multilinestring_sfcgal.to_coordinates().unwrap(); 608 | if let CoordSeq::Multilinestring(ref coords) = coords { 609 | assert_ulps_eq!(coords[0][0].0, 3.5); 610 | assert_ulps_eq!(coords[0][0].1, 5.6); 611 | assert_ulps_eq!(coords[0][0].2, 1.0); 612 | assert_ulps_eq!(coords[1][1].0, 2.1); 613 | assert_ulps_eq!(coords[1][1].1, 3.5); 614 | assert_ulps_eq!(coords[1][1].2, 4.0); 615 | assert_ulps_eq!(coords[2][0].0, 1.1); 616 | assert_ulps_eq!(coords[2][0].1, 4.8); 617 | assert_ulps_eq!(coords[2][0].2, 4.0); 618 | } else { 619 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 620 | } 621 | let mls_sfcgal = coords.to_sfcgal().unwrap(); 622 | assert_eq!(input_wkt, mls_sfcgal.to_wkt_decim(1).unwrap()); 623 | } 624 | 625 | #[test] 626 | fn triangle_2d_sfcgal_to_coordinates_to_sfcgal() { 627 | let input_wkt = "TRIANGLE ((0.0 1.0,1.0 0.0,1.0 1.0,0.0 1.0))"; 628 | let triangle_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 629 | let coords: CoordSeq = triangle_sfcgal.to_coordinates().unwrap(); 630 | if let CoordSeq::Triangle(ref pts) = coords { 631 | assert_ulps_eq!(pts[0].0, 0.0); 632 | assert_ulps_eq!(pts[0].1, 1.0); 633 | assert_ulps_eq!(pts[1].0, 1.0); 634 | assert_ulps_eq!(pts[1].1, 0.0); 635 | assert_ulps_eq!(pts[2].0, 1.0); 636 | assert_ulps_eq!(pts[2].1, 1.0); 637 | } else { 638 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 639 | } 640 | let triangle_sfcgal = coords.to_sfcgal().unwrap(); 641 | assert_eq!(input_wkt, triangle_sfcgal.to_wkt_decim(1).unwrap()); 642 | } 643 | 644 | #[test] 645 | fn solid_3d_sfcgal_to_coordinates() { 646 | let input_wkt = "SOLID Z ((((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),\ 647 | ((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),\ 648 | ((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),\ 649 | ((1 0 0,1 1 0,1 1 1,1 0 1,1 0 0)),\ 650 | ((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),\ 651 | ((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0))))"; 652 | let cube = SFCGeometry::new(input_wkt).unwrap(); 653 | let coords: CoordSeq = cube.to_coordinates().unwrap(); 654 | if let CoordSeq::Solid(ref polys) = coords { 655 | let coords_1st_face_polygon = &polys[0][0]; 656 | assert_eq!( 657 | format!("{:?}", coords_1st_face_polygon), 658 | "[[(0.0, 0.0, 0.0), (0.0, 0.0, 1.0), (0.0, 1.0, 1.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0)]]"); 659 | assert_eq!(polys[0].len(), 6usize); 660 | } else { 661 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 662 | } 663 | let cube_sfcgal = coords.to_sfcgal().unwrap(); 664 | assert_eq!(input_wkt, cube_sfcgal.to_wkt_decim(0).unwrap()); 665 | } 666 | 667 | #[test] 668 | fn solid_with_interior_shell_3d_sfcgal_to_coordinates() { 669 | let input_wkt = "SOLID Z ((\ 670 | ((0.0 0.0 0.0,0.0 1.0 0.0,1.0 1.0 0.0,1.0 0.0 0.0,0.0 0.0 0.0)),\ 671 | ((1.0 0.0 0.0,1.0 1.0 0.0,1.0 1.0 1.0,1.0 0.0 1.0,1.0 0.0 0.0)),\ 672 | ((0.0 1.0 0.0,0.0 1.0 1.0,1.0 1.0 1.0,1.0 1.0 0.0,0.0 1.0 0.0)),\ 673 | ((0.0 0.0 1.0,0.0 1.0 1.0,0.0 1.0 0.0,0.0 0.0 0.0,0.0 0.0 1.0)),\ 674 | ((1.0 0.0 1.0,1.0 1.0 1.0,0.0 1.0 1.0,0.0 0.0 1.0,1.0 0.0 1.0)),\ 675 | ((1.0 0.0 0.0,1.0 0.0 1.0,0.0 0.0 1.0,0.0 0.0 0.0,1.0 0.0 0.0))\ 676 | ),(\ 677 | ((0.0 0.0 0.0,0.0 0.5 0.0,0.5 0.5 0.0,0.5 0.0 0.0,0.0 0.0 0.0)),\ 678 | ((0.5 0.0 0.0,0.5 0.5 0.0,0.5 0.5 0.5,0.5 0.0 0.5,0.5 0.0 0.0)),\ 679 | ((0.0 0.5 0.0,0.0 0.5 0.5,0.5 0.5 0.5,0.5 0.5 0.0,0.0 0.5 0.0)),\ 680 | ((0.0 0.0 0.5,0.0 0.5 0.5,0.0 0.5 0.0,0.0 0.0 0.0,0.0 0.0 0.5)),\ 681 | ((0.5 0.0 0.5,0.5 0.5 0.5,0.0 0.5 0.5,0.0 0.0 0.5,0.5 0.0 0.5)),\ 682 | ((0.5 0.0 0.0,0.5 0.0 0.5,0.0 0.0 0.5,0.0 0.0 0.0,0.5 0.0 0.0))\ 683 | ))"; 684 | let cube = SFCGeometry::new(input_wkt).unwrap(); 685 | let coords: CoordSeq = cube.to_coordinates().unwrap(); 686 | if let CoordSeq::Solid(ref polys) = coords { 687 | let num_shell = polys.len(); 688 | assert_eq!(num_shell, 2usize); 689 | let coords_1st_face_polygon = &polys[0][0]; 690 | assert_eq!( 691 | format!("{:?}", coords_1st_face_polygon), 692 | "[[(0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)]]"); 693 | assert_eq!(polys[0].len(), 6usize); 694 | } else { 695 | panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.") 696 | } 697 | let cube_sfcgal = coords.to_sfcgal().unwrap(); 698 | assert_eq!(input_wkt, cube_sfcgal.to_wkt_decim(1).unwrap()); 699 | } 700 | 701 | #[test] 702 | fn geometrycollection_3d_sfcgal_to_coordinates() { 703 | let input_wkt = 704 | "GEOMETRYCOLLECTION Z (POINT Z (4.0 6.0 1.0),LINESTRING Z (4.0 6.0 1.0,7.0 10.0 1.0))"; 705 | let gc_sfcgal = SFCGeometry::new(input_wkt).unwrap(); 706 | let coords: CoordSeq = gc_sfcgal.to_coordinates().unwrap(); 707 | if let CoordSeq::Geometrycollection(ref seq_coords) = coords { 708 | if let CoordSeq::Point(ref coords) = &seq_coords[0] { 709 | assert_ulps_eq!(coords.0, 4.0); 710 | assert_ulps_eq!(coords.1, 6.0); 711 | assert_ulps_eq!(coords.2, 1.0); 712 | } else { 713 | panic!("Error with first member of geometry collection"); 714 | } 715 | if let CoordSeq::Linestring(ref coords) = &seq_coords[1] { 716 | assert_ulps_eq!(coords[0].0, 4.0); 717 | assert_ulps_eq!(coords[0].1, 6.0); 718 | assert_ulps_eq!(coords[0].2, 1.0); 719 | assert_ulps_eq!(coords[1].0, 7.0); 720 | assert_ulps_eq!(coords[1].1, 10.0); 721 | assert_ulps_eq!(coords[1].2, 1.0); 722 | } else { 723 | panic!("Error with second member of geometry collection"); 724 | } 725 | } 726 | let gc_sfcgal = coords.to_sfcgal().unwrap(); 727 | assert_eq!(input_wkt, gc_sfcgal.to_wkt_decim(1).unwrap()); 728 | } 729 | 730 | #[test] 731 | fn sfcgal_geometry_construction_from_coordinates() { 732 | let coords_point = (1.1, 1.1); 733 | let coords_multipoint = vec![(1.1, 1.1), (2.2, 2.2)]; 734 | let coords_linestring = vec![(1.1, 1.1), (2.2, 2.2)]; 735 | let coords_multilinestring = vec![ 736 | vec![(1.1, 1.1), (2.2, 2.2)], 737 | vec![(3.1, 3.1), (5.2, 5.2), (5.2, 5.2)], 738 | vec![(1.1, 1.1), (2.2, 2.2), (5.2, 5.2)], 739 | ]; 740 | let coords_polygon = vec![ 741 | vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)], // Exterior ring 742 | vec![(0.1, 0.1), (0.1, 0.9), (0.9, 0.9), (0.9, 0.1), (0.1, 0.1)], // 1 interior ring 743 | ]; 744 | let coords_multipolygon = vec![ 745 | vec![ 746 | // First polygon 747 | vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)], // Exterior ring 748 | vec![(0.1, 0.1), (0.1, 0.9), (0.9, 0.9), (0.9, 0.1), (0.1, 0.1)], // 1 interior ring 749 | ], 750 | vec![ 751 | // second polygon 752 | vec![(2., 2.), (6., 2.), (6., 6.), (2., 6.), (2., 2.)], // Exterior ring 753 | ], 754 | ]; 755 | let pt_sfcgal = CoordSeq::Point(coords_point).to_sfcgal().unwrap(); 756 | let multipt_sfcgal = CoordSeq::Multipoint(coords_multipoint).to_sfcgal().unwrap(); 757 | let line_sfcgal = CoordSeq::Linestring(coords_linestring).to_sfcgal().unwrap(); 758 | let multiline_sfcgal = CoordSeq::Multilinestring(coords_multilinestring) 759 | .to_sfcgal() 760 | .unwrap(); 761 | let polygon_sfcgal = CoordSeq::Polygon(coords_polygon).to_sfcgal().unwrap(); 762 | let multipolygon_sfcgal = CoordSeq::Multipolygon(coords_multipolygon) 763 | .to_sfcgal() 764 | .unwrap(); 765 | assert_eq!("POINT (1.1 1.1)", pt_sfcgal.to_wkt_decim(1).unwrap()); 766 | assert_eq!( 767 | "MULTIPOINT ((1.1 1.1),(2.2 2.2))", 768 | multipt_sfcgal.to_wkt_decim(1).unwrap() 769 | ); 770 | assert_eq!( 771 | "LINESTRING (1.1 1.1,2.2 2.2)", 772 | line_sfcgal.to_wkt_decim(1).unwrap() 773 | ); 774 | assert_eq!( 775 | "MULTILINESTRING ((1.1 1.1,2.2 2.2),\ 776 | (3.1 3.1,5.2 5.2,5.2 5.2),\ 777 | (1.1 1.1,2.2 2.2,5.2 5.2))", 778 | multiline_sfcgal.to_wkt_decim(1).unwrap(), 779 | ); 780 | assert_eq!( 781 | "POLYGON ((0.0 0.0,1.0 0.0,1.0 1.0,0.0 1.0,0.0 0.0),\ 782 | (0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1))", 783 | polygon_sfcgal.to_wkt_decim(1).unwrap(), 784 | ); 785 | assert_eq!( 786 | "MULTIPOLYGON (((0.0 0.0,1.0 0.0,1.0 1.0,0.0 1.0,0.0 0.0),\ 787 | (0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),\ 788 | ((2.0 2.0,6.0 2.0,6.0 6.0,2.0 6.0,2.0 2.0)))", 789 | multipolygon_sfcgal.to_wkt_decim(1).unwrap(), 790 | ); 791 | } 792 | 793 | #[test] 794 | fn sfcgal_geometry_construction_from_coordinates_3d() { 795 | let coords_point = (1.1, 1.1, 1.1); 796 | let coords_multipoint = vec![(1.1, 1.1, 5.0), (2.2, 2.2, 5.0)]; 797 | let coords_linestring = vec![(1.1, 1.1, 5.0), (2.2, 2.2, 5.0)]; 798 | let coords_multilinestring = vec![ 799 | vec![(1.1, 1.1, 3.0), (2.2, 2.2, 3.0)], 800 | vec![(3.1, 3.1, 3.0), (5.2, 5.2, 3.0), (5.2, 5.2, 3.0)], 801 | vec![(1.1, 1.1, 3.0), (2.2, 2.2, 3.0), (5.2, 5.2, 3.0)], 802 | ]; 803 | let coords_polygon = vec![ 804 | vec![ 805 | (0., 0., 3.0), 806 | (1., 0., 3.0), 807 | (1., 1., 3.0), 808 | (0., 1., 3.0), 809 | (0., 0., 3.0), 810 | ], // Exterior ring 811 | vec![ 812 | (0.1, 0.1, 3.0), 813 | (0.1, 0.9, 3.0), 814 | (0.9, 0.9, 3.0), 815 | (0.9, 0.1, 3.0), 816 | (0.1, 0.1, 3.0), 817 | ], // 1 interior ring 818 | ]; 819 | let coords_multipolygon = vec![ 820 | vec![ 821 | // First polygon 822 | vec![ 823 | (0., 0., 3.0), 824 | (1., 0., 3.0), 825 | (1., 1., 3.0), 826 | (0., 1., 3.0), 827 | (0., 0., 3.0), 828 | ], // Exterior ring 829 | vec![ 830 | (0.1, 0.1, 3.0), 831 | (0.1, 0.9, 3.0), 832 | (0.9, 0.9, 3.0), 833 | (0.9, 0.1, 3.0), 834 | (0.1, 0.1, 3.0), 835 | ], // 1 interior ring 836 | ], 837 | vec![ 838 | // second polygon 839 | vec![ 840 | (2., 2., 3.), 841 | (6., 2., 3.), 842 | (6., 6., 3.), 843 | (2., 6., 3.), 844 | (2., 2., 3.), 845 | ], // Exterior ring 846 | ], 847 | ]; 848 | let pt_sfcgal = CoordSeq::Point(coords_point).to_sfcgal().unwrap(); 849 | let multipt_sfcgal = CoordSeq::Multipoint(coords_multipoint).to_sfcgal().unwrap(); 850 | let line_sfcgal = CoordSeq::Linestring(coords_linestring).to_sfcgal().unwrap(); 851 | let multiline_sfcgal = CoordSeq::Multilinestring(coords_multilinestring) 852 | .to_sfcgal() 853 | .unwrap(); 854 | let polygon_sfcgal = CoordSeq::Polygon(coords_polygon).to_sfcgal().unwrap(); 855 | let multipolygon_sfcgal = CoordSeq::Multipolygon(coords_multipolygon) 856 | .to_sfcgal() 857 | .unwrap(); 858 | assert_eq!("POINT Z (1.1 1.1 1.1)", pt_sfcgal.to_wkt_decim(1).unwrap()); 859 | assert_eq!( 860 | "MULTIPOINT Z ((1.1 1.1 5.0),(2.2 2.2 5.0))", 861 | multipt_sfcgal.to_wkt_decim(1).unwrap() 862 | ); 863 | assert_eq!( 864 | "LINESTRING Z (1.1 1.1 5.0,2.2 2.2 5.0)", 865 | line_sfcgal.to_wkt_decim(1).unwrap() 866 | ); 867 | assert_eq!( 868 | "MULTILINESTRING Z ((1.1 1.1 3.0,2.2 2.2 3.0),(3.1 3.1 3.0,5.2 5.2 3.0,5.2 5.2 3.0),\ 869 | (1.1 1.1 3.0,2.2 2.2 3.0,5.2 5.2 3.0))", 870 | multiline_sfcgal.to_wkt_decim(1).unwrap(), 871 | ); 872 | assert_eq!( 873 | "POLYGON Z ((0.0 0.0 3.0,1.0 0.0 3.0,1.0 1.0 3.0,0.0 1.0 3.0,0.0 0.0 3.0),\ 874 | (0.1 0.1 3.0,0.1 0.9 3.0,0.9 0.9 3.0,0.9 0.1 3.0,0.1 0.1 3.0))", 875 | polygon_sfcgal.to_wkt_decim(1).unwrap(), 876 | ); 877 | assert_eq!( 878 | "MULTIPOLYGON Z (((0.0 0.0 3.0,1.0 0.0 3.0,1.0 1.0 3.0,0.0 1.0 3.0,0.0 0.0 3.0),\ 879 | (0.1 0.1 3.0,0.1 0.9 3.0,0.9 0.9 3.0,0.9 0.1 3.0,0.1 0.1 3.0)),\ 880 | ((2.0 2.0 3.0,6.0 2.0 3.0,6.0 6.0 3.0,2.0 6.0 3.0,2.0 2.0 3.0)))", 881 | multipolygon_sfcgal.to_wkt_decim(1).unwrap(), 882 | ); 883 | } 884 | } 885 | -------------------------------------------------------------------------------- /examples/abc.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 4 | 5 | "features": [ 6 | { "type": "Feature", "properties": { "DPTO": "05", "NOMBRE_DPT": "ANTIOQUIA", "AREA": 63351855546.894997, "PERIMETER": 1963728.8430000001, "HECTARES": 6335185.5549999997 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -76.307296752200472, 8.619299889431787 ], [ -76.298103332708664, 8.616399764640406 ], [ -76.291702270917597, 8.617500305932477 ], [ -76.276199340202012, 8.60879993391571 ], [ -76.262901306766281, 8.603500365680683 ], [ -76.251899719395169, 8.593600272668734 ], [ -76.239799499831705, 8.589599609021819 ], [ -76.227096558161534, 8.583700180478338 ], [ -76.227699279851606, 8.579099655292133 ], [ -76.217300414647411, 8.571599959760825 ], [ -76.216201782878116, 8.56400012950135 ], [ -76.2144012458417, 8.551300048733623 ], [ -76.212097167646547, 8.53460025816843 ], [ -76.211502074416103, 8.521900177648424 ], [ -76.208099365347678, 8.501000404898789 ], [ -76.205200195353854, 8.487700462096564 ], [ -76.201698302399166, 8.471599578043385 ], [ -76.200599669998809, 8.46399974867111 ], [ -76.197700500248644, 8.451299668030343 ], [ -76.195899962638137, 8.437999725351512 ], [ -76.195396422992715, 8.426500319746971 ], [ -76.196502685512499, 8.418399811395048 ], [ -76.200500489077498, 8.41670036392434 ], [ -76.21949767980999, 8.402299880581914 ], [ -76.234001159013445, 8.388500214292852 ], [ -76.246597290220947, 8.375300407594413 ], [ -76.254096984081045, 8.369500160819292 ], [ -76.26560211262651, 8.349900245476581 ], [ -76.283500671542413, 8.318300247253106 ], [ -76.299003601033903, 8.294599532291839 ], [ -76.312301636521667, 8.27449989342478 ], [ -76.327796936282567, 8.252599716319498 ], [ -76.342796324962663, 8.232999801601681 ], [ -76.35489654479052, 8.200099945604444 ], [ -76.364700317065129, 8.189200402062419 ], [ -76.372703551546039, 8.164400100997586 ], [ -76.382713318076185, 8.150289534980686 ], [ -76.38539886561469, 8.146499634355122 ], [ -76.392898559939283, 8.133299827578782 ], [ -76.392303466207267, 8.130999565744737 ], [ -76.395202637481333, 8.119400025262548 ], [ -76.399200438830036, 8.103300094042563 ], [ -76.401000976396304, 8.098099707726169 ], [ -76.405601500724131, 8.087699889299177 ], [ -76.406700134284293, 8.083100318228139 ], [ -76.413002013618225, 8.073300361051198 ], [ -76.413597107349261, 8.064100265994645 ], [ -76.414802550773103, 8.062299728308437 ], [ -76.416496277655469, 8.043299674415833 ], [ -76.415298462252196, 8.026499748710286 ], [ -76.416496276034181, 8.019000053667131 ], [ -76.417602538908469, 7.998799801233367 ], [ -76.420501708317218, 7.984399795952933 ], [ -76.421600341650446, 7.981500148043183 ], [ -76.422798156237675, 7.973400115699819 ], [ -76.428497313787602, 7.957300186323215 ], [ -76.431999206799503, 7.948599814554803 ], [ -76.448097228574142, 7.920400142718326 ], [ -76.455596924520663, 7.909999846600579 ], [ -76.460800170647062, 7.893899917181079 ], [ -76.463699340511795, 7.884099960838045 ], [ -76.468299866107898, 7.876599789389874 ], [ -76.481498718506842, 7.852399826229758 ], [ -76.490196228674506, 7.835100174637252 ], [ -76.497100829651856, 7.798099995088911 ], [ -76.500076293687272, 7.774499893377604 ], [ -76.501098632706558, 7.766399860187631 ], [ -76.504501341938649, 7.739200114899043 ], [ -76.509101866986455, 7.718500136990073 ], [ -76.51429748543967, 7.693699836334832 ], [ -76.51429748504637, 7.678100109201839 ], [ -76.518898010714238, 7.657899856971198 ], [ -76.518302917529226, 7.645699977453701 ], [ -76.514801024667747, 7.637000084542388 ], [ -76.509597778099405, 7.62029981633042 ], [ -76.505599974885072, 7.609899997174081 ], [ -76.5027008054589, 7.597700119608963 ], [ -76.487701416210328, 7.580299853717709 ], [ -76.481903075636083, 7.560699939735807 ], [ -76.477302551711603, 7.535200119636533 ], [ -76.466300963754591, 7.529399872094436 ], [ -76.45600128146117, 7.510900020478868 ], [ -76.446098327905631, 7.489500046027097 ], [ -76.435798644200474, 7.471499919130086 ], [ -76.429397582671299, 7.458799838469258 ], [ -76.425399780875338, 7.447800159416569 ], [ -76.410400390081477, 7.422900199662032 ], [ -76.404602050715397, 7.411300183164449 ], [ -76.396499633693054, 7.403800011357053 ], [ -76.379798889603649, 7.390399932152191 ], [ -76.36710357696515, 7.371300220644879 ], [ -76.35900115903398, 7.363200187981914 ], [ -76.346900939301534, 7.353300095130255 ], [ -76.34169769203919, 7.351600169861399 ], [ -76.030403136722299, 7.353099823211561 ], [ -75.911697387186592, 7.368800163483882 ], [ -75.841400147214145, 7.388700008248152 ], [ -75.83560180639391, 7.409500121270003 ], [ -75.825798034684652, 7.408899784895779 ], [ -75.819503784989564, 7.416900157283263 ], [ -75.810302734457593, 7.425000190449654 ], [ -75.800498962895148, 7.441699981776589 ], [ -75.786697387031012, 7.460100173888282 ], [ -75.774597168080035, 7.482600212897952 ], [ -75.762496948573386, 7.499899864474544 ], [ -75.746398925915699, 7.519499778862913 ], [ -75.734298705607131, 7.531499862409381 ], [ -75.723297118397838, 7.546500205822959 ], [ -75.714698790796035, 7.560900211197019 ], [ -75.700798034150111, 7.583399772785804 ], [ -75.696800232553201, 7.60360002469076 ], [ -75.688796996918967, 7.621500015124971 ], [ -75.68190002523589, 7.641600132227116 ], [ -75.682403563770421, 7.656099796739453 ], [ -75.676101685265067, 7.678599834534111 ], [ -75.671501159188097, 7.689000129808149 ], [ -75.662902832181146, 7.700500010691221 ], [ -75.649597167246398, 7.713099955785207 ], [ -75.626602172343425, 7.726299761887654 ], [ -75.60810089076179, 7.730899811442847 ], [ -75.592597962026929, 7.733699797919725 ], [ -75.585098266814768, 7.733699798964517 ], [ -75.574699402442377, 7.726099967997214 ], [ -75.564903258923707, 7.715700148721089 ], [ -75.555702209651329, 7.712200165257186 ], [ -75.545303343948561, 7.708099842657556 ], [ -75.536102294210167, 7.7052001956411 ], [ -75.527999877535876, 7.705100058961048 ], [ -75.521102904488998, 7.705100059195811 ], [ -75.513000488728238, 7.705599784455725 ], [ -75.507202147916061, 7.7067999836268 ], [ -75.503196715480072, 7.707300186485617 ], [ -75.5026016231933, 7.713099955763663 ], [ -75.497398375763268, 7.719999789993336 ], [ -75.491699218583378, 7.725800036612297 ], [ -75.489997863274382, 7.734399794828462 ], [ -75.493400572877093, 7.746600151278394 ], [ -75.4835968023731, 7.750000000086353 ], [ -75.477302550547591, 7.759799957358343 ], [ -75.472702026428692, 7.769000052592753 ], [ -75.46980285570308, 7.775899886378943 ], [ -75.473297119958929, 7.788599968710695 ], [ -75.475601195900765, 7.794400215964197 ], [ -75.473899840680076, 7.807700157303518 ], [ -75.457702636072071, 7.809999942247721 ], [ -75.450798034985283, 7.81110000619061 ], [ -75.431198119357845, 7.820199966345384 ], [ -75.411598204723319, 7.824800014855439 ], [ -75.396598815096667, 7.830500126472528 ], [ -75.374702454094233, 7.840199947028877 ], [ -75.360900878203083, 7.84649992010868 ], [ -75.346496582565564, 7.856800079086724 ], [ -75.342498778520522, 7.87529993086194 ], [ -75.341300963841249, 7.890299797057037 ], [ -75.334999084706809, 7.909900189275217 ], [ -75.333801269586132, 7.918000221099718 ], [ -75.327522278026549, 7.925164222821929 ], [ -75.321701050010589, 7.931799888338591 ], [ -75.30909729028113, 7.932400227432549 ], [ -75.301002503118127, 7.933499813729177 ], [ -75.289497374747043, 7.945600032772019 ], [ -75.289497375894001, 7.951300144075124 ], [ -75.285400391393665, 7.962299823728302 ], [ -75.281402588540274, 7.966899872493454 ], [ -75.272796630197064, 7.967400073373012 ], [ -75.267601012485812, 7.967999934788215 ], [ -75.260696411042929, 7.969099998655555 ], [ -75.257202148331018, 7.979499816374121 ], [ -75.254302977694138, 7.991600035899619 ], [ -75.244003296563577, 8.00080013320437 ], [ -75.236503600588122, 8.009499549855864 ], [ -75.228996276286253, 8.024399758010198 ], [ -75.216300964587134, 8.031900405998972 ], [ -75.214546203617729, 8.031888007587055 ], [ -75.201896666772939, 8.031800269821382 ], [ -75.176002502512944, 8.032299995700857 ], [ -75.154602050639824, 8.032199859032419 ], [ -75.129302978532181, 8.036199570313752 ], [ -75.105102538623441, 8.038399696822756 ], [ -75.090103148898081, 8.036600113714602 ], [ -75.073402405389643, 8.035900116605159 ], [ -75.056602477966933, 8.041099547910022 ], [ -75.029502868789208, 8.043199539900465 ], [ -74.994400024062372, 8.047699929010028 ], [ -74.970199585140264, 8.048199653660571 ], [ -74.95860290613534, 8.050499916488477 ], [ -74.933296203428043, 8.068300247922801 ], [ -74.910797119709684, 8.088399887575772 ], [ -74.907402039221068, 8.10280036956898 ], [ -74.895896911004215, 8.124699592315679 ], [ -74.87909698448594, 8.144900322562115 ], [ -74.86360168438911, 8.162099838521881 ], [ -74.854400634022582, 8.172499656241579 ], [ -74.839996337498476, 8.182200432411355 ], [ -74.833938599069938, 8.185330391530504 ], [ -74.819801330300194, 8.179300308304244 ], [ -74.806503296332664, 8.167099952087218 ], [ -74.794998168340513, 8.161299705059276 ], [ -74.788101197025739, 8.151399612475986 ], [ -74.778800964767569, 8.139200210579524 ], [ -74.749397278681641, 8.116000174865549 ], [ -74.729202271029564, 8.096300124365625 ], [ -74.707901000402529, 8.073100090464719 ], [ -74.677299500273705, 8.048700332965288 ], [ -74.650802613080103, 8.018600464502898 ], [ -74.626602172071188, 7.995900153406605 ], [ -74.602897644021155, 7.976200103433199 ], [ -74.569503783355785, 7.941999912294763 ], [ -74.555160522072626, 7.927527904692943 ], [ -74.547599793271573, 7.919899940617243 ], [ -74.538902283115036, 7.904900074560325 ], [ -74.538299560775471, 7.886400223182825 ], [ -74.539497375712756, 7.856900215316822 ], [ -74.534797667831597, 7.818799972585014 ], [ -74.534202576191745, 7.796800136447047 ], [ -74.523803711612771, 7.766200065175438 ], [ -74.515197753487428, 7.741899967358815 ], [ -74.500801085635146, 7.727399826062634 ], [ -74.480598449901919, 7.709400176490059 ], [ -74.477096557973027, 7.697199821479246 ], [ -74.489799498927212, 7.678800105207938 ], [ -74.51509857234845, 7.648900031303961 ], [ -74.532997131861194, 7.634500026404125 ], [ -74.552597046544463, 7.624199867864868 ], [ -74.56349944982594, 7.615600109749266 ], [ -74.570999145022242, 7.603499889790426 ], [ -74.570396422689939, 7.578599930273358 ], [ -74.574401855431816, 7.564799785479422 ], [ -74.579597473615323, 7.540599822696255 ], [ -74.597999571955356, 7.494999886054433 ], [ -74.611297606983086, 7.468500136893604 ], [ -74.615898132525928, 7.454100131555791 ], [ -74.614097594529937, 7.428699970774118 ], [ -74.609497070232308, 7.421700000787445 ], [ -74.600898742141652, 7.417600154306814 ], [ -74.595703124664567, 7.407199860051865 ], [ -74.592201233018528, 7.400300026644373 ], [ -74.588203430467004, 7.393899917458589 ], [ -74.580703735412612, 7.385200024236288 ], [ -74.569702147892286, 7.372399806367962 ], [ -74.557601927966019, 7.370100021965413 ], [ -74.541496276689969, 7.357299805039477 ], [ -74.530502319075779, 7.345099925679798 ], [ -74.526496886592426, 7.333000183309344 ], [ -74.521301268689825, 7.326600075614266 ], [ -74.516098023015473, 7.319099902602219 ], [ -74.506896973320423, 7.317299842505785 ], [ -74.497100830004868, 7.322500229007937 ], [ -74.488998413611554, 7.324699878443813 ], [ -74.482101439910608, 7.324699879046628 ], [ -74.474601746333136, 7.327000140543915 ], [ -74.471099853839121, 7.330999851736218 ], [ -74.469398498351794, 7.344299792882623 ], [ -74.466499327800307, 7.351799964339437 ], [ -74.456199645437351, 7.361000060237968 ], [ -74.438301085893841, 7.377099991312115 ], [ -74.434303284058132, 7.390399933589222 ], [ -74.43080139124902, 7.405399799624212 ], [ -74.429100036353248, 7.414599895878156 ], [ -74.426200866294295, 7.423799991522642 ], [ -74.422203064406688, 7.4341998107133 ], [ -74.411796570455209, 7.44460010530709 ], [ -74.405502320053301, 7.45319986282502 ], [ -74.402000427649583, 7.460700034953506 ], [ -74.391700745461719, 7.469299793653055 ], [ -74.389396667250239, 7.471000194762328 ], [ -74.38649749723514, 7.462399960132668 ], [ -74.381301880558141, 7.445600033532304 ], [ -74.377799987504488, 7.434000015169925 ], [ -74.375503539669111, 7.419000149299273 ], [ -74.375503540795961, 7.40740013196023 ], [ -74.370300293737486, 7.398200035029475 ], [ -74.362800597993953, 7.383699894009925 ], [ -74.364501952756783, 7.370999813029774 ], [ -74.372001648128844, 7.359499931630591 ], [ -74.382400513194312, 7.353199958359061 ], [ -74.391601562192363, 7.341700076335869 ], [ -74.397300719868909, 7.336500168194715 ], [ -74.405403136909513, 7.329599858167777 ], [ -74.420997619111432, 7.321000099961039 ], [ -74.426200866759515, 7.31059980394875 ], [ -74.42669677740065, 7.299099922389804 ], [ -74.42500305169132, 7.282299995074723 ], [ -74.422096252542502, 7.264999867027433 ], [ -74.422698974211229, 7.261499881786997 ], [ -74.420898437354055, 7.25050020151183 ], [ -74.419937133772152, 7.245166778483359 ], [ -74.41860198916082, 7.237800120848056 ], [ -74.423202513910496, 7.208399773003961 ], [ -74.426101684961864, 7.192800044472687 ], [ -74.427803039798306, 7.18699979725579 ], [ -74.427200318232593, 7.170199870809124 ], [ -74.416297913439038, 7.145899771818477 ], [ -74.409301757405444, 7.127999783349965 ], [ -74.406402588213609, 7.110099793041921 ], [ -74.400100708538261, 7.093299865207624 ], [ -74.38569641165347, 7.073599814733377 ], [ -74.379302978476375, 7.056300163722769 ], [ -74.372398375614381, 7.042399882792965 ], [ -74.37180328428235, 7.02159976939058 ], [ -74.37349700987204, 7.006000041131371 ], [ -74.37180328390771, 7.000800133157362 ], [ -74.365402221714803, 6.997300147356194 ], [ -74.358497620401081, 6.997300147349663 ], [ -74.355102538338286, 6.997200011430809 ], [ -74.345298766593388, 6.987999916110029 ], [ -74.335502623778481, 6.982699871493575 ], [ -74.316398620933697, 6.979199886456398 ], [ -74.309501647968276, 6.984300136648636 ], [ -74.299697875263476, 6.986599922893151 ], [ -74.296531677134055, 6.986405849873758 ], [ -74.289901733333181, 6.986000060524481 ], [ -74.285301209044349, 6.983699797747298 ], [ -74.277198792257565, 6.984799861104574 ], [ -74.267997742169499, 6.986499786714583 ], [ -74.261703491838063, 6.98530006373822 ], [ -74.248397827735488, 6.985199928232845 ], [ -73.954002380666353, 7.263599872631961 ], [ -73.955703735089614, 7.242800236196601 ], [ -73.954597473709853, 7.210400103853263 ], [ -73.951698303520587, 7.178100108569347 ], [ -73.950500487792993, 7.172900199327838 ], [ -73.945899963938444, 7.155499935436602 ], [ -73.93720245303308, 7.124899865028645 ], [ -73.937797546903028, 7.106400013229865 ], [ -73.940101623740702, 7.083899974564238 ], [ -73.928497314170841, 7.057799816051282 ], [ -73.91639709514105, 7.0376000403798 ], [ -73.907798767180267, 7.029399871113755 ], [ -73.900802611678117, 7.007999896239512 ], [ -73.896202086686444, 6.997000216582398 ], [ -73.896202086640727, 6.992400169888879 ], [ -73.9037017829137, 6.978000164218696 ], [ -73.926200867405029, 6.957300185921373 ], [ -73.953201294117491, 6.929100037083215 ], [ -73.972297669129233, 6.918799877069158 ], [ -74.008598327496358, 6.90799999239546 ], [ -74.021202087340797, 6.897600174382771 ], [ -74.053497315109709, 6.872399807433127 ], [ -74.075714111472948, 6.855112552557614 ], [ -74.084602356436449, 6.848199844757631 ], [ -74.113403320784727, 6.824100018286071 ], [ -74.128997803268859, 6.801599979885607 ], [ -74.136497497768218, 6.785500048957986 ], [ -74.142799377266144, 6.767000198126182 ], [ -74.145698547703333, 6.757199763912408 ], [ -74.154899597440661, 6.748600005861758 ], [ -74.171600341355841, 6.734799861463346 ], [ -74.185401917286939, 6.722700119287145 ], [ -74.200401306101085, 6.712399959390295 ], [ -74.226402282613535, 6.701000212914264 ], [ -74.24130249053718, 6.698100090236055 ], [ -74.255699158460729, 6.688899994148797 ], [ -74.278198241748882, 6.667699814056143 ], [ -74.28859710693466, 6.647500038434039 ], [ -74.301300048791049, 6.644700049902644 ], [ -74.312202454199877, 6.633699893764672 ], [ -74.335296630531374, 6.618199825820587 ], [ -74.34449768108108, 6.612500189899801 ], [ -74.355400084703618, 6.60850000334515 ], [ -74.377899169242824, 6.608600139907257 ], [ -74.392303466961181, 6.603499889083563 ], [ -74.405601501211436, 6.588500023494138 ], [ -74.418197632603622, 6.575799942426088 ], [ -74.421699523664572, 6.563700198660403 ], [ -74.432098388128779, 6.551099777834082 ], [ -74.438400268224015, 6.542399883662521 ], [ -74.440696717124695, 6.537799834931589 ], [ -74.444198608750753, 6.530900000885353 ], [ -74.43379974313244, 6.509500027053331 ], [ -74.431503295461198, 6.496799945878533 ], [ -74.431999206661189, 6.482900142352193 ], [ -74.431396483532836, 6.465600013090715 ], [ -74.4238967903662, 6.453999996091643 ], [ -74.417602538715045, 6.432000160328367 ], [ -74.417602538866319, 6.422800064291422 ], [ -74.409500121755144, 6.405399800198412 ], [ -74.396202086983564, 6.39260005970881 ], [ -74.394500732572851, 6.386899948311367 ], [ -74.399101257659126, 6.377099990120219 ], [ -74.422698974143586, 6.371399879233325 ], [ -74.438301086245673, 6.369100093410602 ], [ -74.447502135461406, 6.366899967886194 ], [ -74.460800171230233, 6.346700191726443 ], [ -74.475196838526742, 6.327099800696312 ], [ -74.485496521455019, 6.312099933081873 ], [ -74.499900817027594, 6.300099849818397 ], [ -74.51550292968291, 6.279300213325259 ], [ -74.52359771768117, 6.271900177612745 ], [ -74.530502318629672, 6.265500069033788 ], [ -74.531600951475767, 6.263800143813858 ], [ -74.543701171199714, 6.248799801443227 ], [ -74.551788330348046, 6.247377871790396 ], [ -74.555801391531148, 6.254700183420697 ], [ -74.560997008729188, 6.252999782254268 ], [ -74.566802979157245, 6.251299857942371 ], [ -74.575401306497298, 6.236299992254269 ], [ -74.578300476734483, 6.234600067810606 ], [ -74.585800170667454, 6.22300004905169 ], [ -74.592697143679629, 6.210899829762742 ], [ -74.60189819281554, 6.19770002447226 ], [ -74.604797364099468, 6.18440008237509 ], [ -74.608200074087961, 6.167699814188511 ], [ -74.608200073137212, 6.157899856107093 ], [ -74.613403320582108, 6.145199775100927 ], [ -74.622596740471408, 6.130799769744842 ], [ -74.6295013435481, 6.110000132693978 ], [ -74.63009643617211, 6.092700004144659 ], [ -74.624298095154572, 6.079400062750502 ], [ -74.613899230365035, 6.070700169192125 ], [ -74.611099243153774, 6.067200184435919 ], [ -74.607002259140913, 6.061999797275286 ], [ -74.601799011147406, 6.06139993732088 ], [ -74.593803404925339, 6.060200213688776 ], [ -74.589698791294865, 6.058400153394282 ], [ -74.587402343438782, 6.048600196384561 ], [ -74.591400146862412, 6.043399811266537 ], [ -74.594299315884669, 6.036499977084485 ], [ -74.598396301367046, 6.032499790530019 ], [ -74.601196288630973, 6.029600143665665 ], [ -74.604698181169141, 6.022099971084328 ], [ -74.605796814234552, 6.014599799638958 ], [ -74.602401732706198, 6.008200169230649 ], [ -74.593200683276081, 5.999499798144618 ], [ -74.589103699296018, 5.992599964131569 ], [ -74.587402342973903, 5.983300209723472 ], [ -74.587402343154849, 5.979899883615309 ], [ -74.590797424798225, 5.972899913188385 ], [ -74.601799011973696, 5.96840000173416 ], [ -74.611000060929328, 5.964900017232493 ], [ -74.618499755452476, 5.962100029338193 ], [ -74.625999449836769, 5.956900119392801 ], [ -74.625396729180437, 5.938399791686935 ], [ -74.625396729268658, 5.929800033345536 ], [ -74.621398925368482, 5.919899940398596 ], [ -74.617301940135619, 5.910699844870726 ], [ -74.608100891165734, 5.893899917175168 ], [ -74.609199523578852, 5.888700008028772 ], [ -74.623596192050883, 5.882400035942647 ], [ -74.635696411634981, 5.876699923675515 ], [ -74.643203734782375, 5.872099877119889 ], [ -74.651298522924137, 5.869200230308164 ], [ -74.660499573081353, 5.856599807274106 ], [ -74.659896849754702, 5.848999977260608 ], [ -74.662834167950891, 5.842849731231567 ], [ -74.66400146409805, 5.840400218191689 ], [ -74.663398743376263, 5.829400062609719 ], [ -74.667396544916443, 5.820199966860978 ], [ -74.673202514875655, 5.797100066386489 ], [ -74.674301148114353, 5.779200076774659 ], [ -74.674301146641554, 5.761899948885797 ], [ -74.674301148053914, 5.758399963659738 ], [ -74.676597595393389, 5.758399964234384 ], [ -74.684700012398167, 5.750999927058 ], [ -74.694999695285162, 5.752699852418464 ], [ -74.698501586888682, 5.762000084472129 ], [ -74.70480346668127, 5.762599945810258 ], [ -74.715202330943484, 5.762599944937661 ], [ -74.722702026121084, 5.759799957917917 ], [ -74.733703612524678, 5.742499828784326 ], [ -74.7410964968211, 5.731500148688215 ], [ -74.747497559420211, 5.716000080597836 ], [ -74.754997253160369, 5.704999923102951 ], [ -74.757797240813545, 5.690000057082944 ], [ -74.765296935582214, 5.68310022384923 ], [ -74.769897461157669, 5.682600021196157 ], [ -74.777999877965883, 5.680300235892611 ], [ -74.790100096975195, 5.678599833618816 ], [ -74.798202514324899, 5.677499770332662 ], [ -74.806800843042311, 5.679299830707008 ], [ -74.821800232731363, 5.68629980167941 ], [ -74.827598571331791, 5.689199924715101 ], [ -74.835098266312414, 5.690899849244293 ], [ -74.842002868942529, 5.694399834263236 ], [ -74.85350036695381, 5.69619989378328 ], [ -74.858703612937603, 5.700300216861715 ], [ -74.861000060697194, 5.708899974828697 ], [ -74.863296509507578, 5.714200020508022 ], [ -74.863899230481806, 5.720499991705456 ], [ -74.866798401105257, 5.724599838092438 ], [ -74.872596740711472, 5.730899811334936 ], [ -74.877197265776601, 5.733900069916044 ], [ -74.885803222516472, 5.732200145904032 ], [ -74.893302918088565, 5.730500220950756 ], [ -74.901397704799123, 5.727600098347397 ], [ -74.908302307640582, 5.726500035105986 ], [ -74.913497925351294, 5.7252998344227 ], [ -74.918098448936931, 5.723599911593095 ], [ -74.943496704303755, 5.717999935449892 ], [ -74.955596923605952, 5.715700149545249 ], [ -74.965400695386322, 5.714600086687542 ], [ -74.975799560788388, 5.713500023131319 ], [ -74.985603331700943, 5.711800097926382 ], [ -74.995903015273257, 5.711200236537353 ], [ -75.009201049550569, 5.707799910648606 ], [ -75.016700744650549, 5.705599785045396 ], [ -75.0192413332569, 5.694767951359036 ], [ -75.020103454379353, 5.691100120245491 ], [ -75.026496887195663, 5.681300163474965 ], [ -75.035102844604168, 5.674399852560847 ], [ -75.047798156273302, 5.669899940758192 ], [ -75.060501099037083, 5.666500090925254 ], [ -75.06970214819647, 5.662499903859283 ], [ -75.079498291080611, 5.659599781299574 ], [ -75.091003418084156, 5.653900146599941 ], [ -75.094497681477065, 5.653900146597934 ], [ -75.111198425304764, 5.649300097779927 ], [ -75.115798949845072, 5.648200034784886 ], [ -75.1197967525693, 5.641900061785792 ], [ -75.121002197865849, 5.630899905342226 ], [ -75.122703552318569, 5.62109994851687 ], [ -75.126701354585251, 5.612400055049961 ], [ -75.125000000090864, 5.598000049804416 ], [ -75.124397278269868, 5.591599940426548 ], [ -75.124397278618503, 5.580699920189326 ], [ -75.13069915852256, 5.573800086455836 ], [ -75.139999388851805, 5.570899964110204 ], [ -75.146896361636436, 5.567999838926053 ], [ -75.153198241419247, 5.563499927493074 ], [ -75.156700134846105, 5.554800034351662 ], [ -75.15899658162617, 5.545599937094218 ], [ -75.161300660011364, 5.537499904404314 ], [ -75.166999816984898, 5.531199932816367 ], [ -75.171600340988121, 5.52769994704504 ], [ -75.179702758943307, 5.522600174653419 ], [ -75.192398071659696, 5.520299911654422 ], [ -75.204498291324455, 5.518000126386876 ], [ -75.219497680394838, 5.516900062224889 ], [ -75.235000610933696, 5.512400150979241 ], [ -75.247100829270764, 5.491099834182938 ], [ -75.254600524543577, 5.473199844609219 ], [ -75.274200440066053, 5.464600085527011 ], [ -75.286300659862093, 5.460000037669307 ], [ -75.288002013508745, 5.458300113884607 ], [ -75.30300140454257, 5.460100173688548 ], [ -75.313400268422555, 5.460199832298622 ], [ -75.323196410729466, 5.457300186047446 ], [ -75.336502074750882, 5.456799983979224 ], [ -75.339897155971641, 5.458000183271492 ], [ -75.344001769326496, 5.464300156381754 ], [ -75.347396851270631, 5.47469997435169 ], [ -75.349700926949282, 5.485099792147727 ], [ -75.349700928059704, 5.497799873736586 ], [ -75.350898742413719, 5.502500057369155 ], [ -75.353797913098674, 5.516900063279623 ], [ -75.36650085386627, 5.533199787584718 ], [ -75.367599487258843, 5.555099963343858 ], [ -75.370498657926021, 5.568399905796595 ], [ -75.375198363626936, 5.574800015074887 ], [ -75.374000549033042, 5.586900234808908 ], [ -75.374000550129892, 5.594999790087257 ], [ -75.376899720059484, 5.597899913879664 ], [ -75.386100769143965, 5.601399898931557 ], [ -75.39250183024815, 5.601399899340069 ], [ -75.40170288026286, 5.599699974176667 ], [ -75.409202575142785, 5.605500221097 ], [ -75.402801514132406, 5.620500087630719 ], [ -75.399398803460798, 5.634399891668952 ], [ -75.393096923782252, 5.65110015912428 ], [ -75.393600463087239, 5.65339994498265 ], [ -75.396499633549695, 5.659200190797349 ], [ -75.405197143952478, 5.66440010047533 ], [ -75.412101746314548, 5.668499945951522 ], [ -75.4196014410267, 5.677199841180947 ], [ -75.425903320483044, 5.683599948801881 ], [ -75.43399810862833, 5.683599948582906 ], [ -75.441497803543328, 5.687099934319432 ], [ -75.453598022220916, 5.687200070220872 ], [ -75.455902099949625, 5.683100224122439 ], [ -75.469802856075276, 5.676799774306257 ], [ -75.484703063808254, 5.665900229745103 ], [ -75.498596190643894, 5.66249990549339 ], [ -75.507797241698952, 5.660200119153466 ], [ -75.514099121518441, 5.668399811190201 ], [ -75.52570343009377, 5.671299934610329 ], [ -75.537200927341829, 5.673099994405761 ], [ -75.54299926789534, 5.681799889025226 ], [ -75.551002501843982, 5.686999797744119 ], [ -75.5590972903683, 5.691699981181134 ], [ -75.56199645947018, 5.700300216418926 ], [ -75.563201903740477, 5.707799912342032 ], [ -75.566596984071168, 5.713600159551485 ], [ -75.572998046780086, 5.716499804788838 ], [ -75.575302124636906, 5.717700005369108 ], [ -75.582199096362473, 5.717199802460556 ], [ -75.592597962101237, 5.721300124309132 ], [ -75.604698181183039, 5.725299835876327 ], [ -75.607261657533854, 5.725541115286956 ], [ -75.618499755782437, 5.726600169311515 ], [ -75.625137328761028, 5.725503445245245 ], [ -75.625999450116481, 5.717299938786939 ], [ -75.622497559295482, 5.701200008275241 ], [ -75.618499756023027, 5.688399792031974 ], [ -75.617301941699935, 5.679800033676292 ], [ -75.623100280397054, 5.671700001225102 ], [ -75.626289367160396, 5.667068958555729 ], [ -75.627098082672703, 5.665900231200006 ], [ -75.624198912907303, 5.651500225268046 ], [ -75.61959838917673, 5.63649988115167 ], [ -75.62190246646675, 5.616799832102517 ], [ -75.626502990302413, 5.600100040126812 ], [ -75.626502990277046, 5.597799777295953 ], [ -75.617301941261687, 5.574100017231886 ], [ -75.615501403879747, 5.556099891012488 ], [ -75.616699219025023, 5.542900085035252 ], [ -75.616699219209281, 5.531300068160841 ], [ -75.615501403045471, 5.522600173552284 ], [ -75.614303588724098, 5.515100002175176 ], [ -75.623596191454496, 5.512899875336894 ], [ -75.633903503521665, 5.518700122371741 ], [ -75.652397156410672, 5.519899844922824 ], [ -75.659301758424988, 5.519400119883818 ], [ -75.667999267906367, 5.51770019611271 ], [ -75.67030334395308, 5.51480007166515 ], [ -75.680099486947128, 5.512499809916428 ], [ -75.693901061327651, 5.510799885049132 ], [ -75.706001281988833, 5.507999897028081 ], [ -75.709503173837604, 5.505099774136963 ], [ -75.712303161744913, 5.504000186203532 ], [ -75.716903686882418, 5.504600047267615 ], [ -75.728500365880876, 5.506400109153295 ], [ -75.735397339456028, 5.512199878695483 ], [ -75.738296509496791, 5.518000125868249 ], [ -75.740600586535507, 5.520299911871129 ], [ -75.744102477412341, 5.523799895839939 ], [ -75.744102478199707, 5.527200221643493 ], [ -75.749801636456624, 5.526100159013776 ], [ -75.754402161277753, 5.524400234101335 ], [ -75.757896423105365, 5.5232000353633 ], [ -75.764198303577928, 5.523799895698547 ], [ -75.772300719373504, 5.525000094934626 ], [ -75.786697387808431, 5.522200106882323 ], [ -75.797096251701348, 5.518799781916561 ], [ -75.810302735034512, 5.514200210813313 ], [ -75.8229980469753, 5.514299869794116 ], [ -75.834602356048919, 5.514900207065788 ], [ -75.840896606805458, 5.514299870055166 ], [ -75.851799011079891, 5.505199909595316 ], [ -75.859901427746365, 5.498300075356069 ], [ -75.868598937758364, 5.497700214888265 ], [ -75.882400512234568, 5.489099979658747 ], [ -75.885299682781792, 5.483300208897013 ], [ -75.886505127028002, 5.475137710456404 ], [ -75.892700195270578, 5.47300004892414 ], [ -75.896202087536082, 5.471799849879046 ], [ -75.902000426692993, 5.471899985447932 ], [ -75.911796570004725, 5.472499846995217 ], [ -75.919799805538261, 5.470799922293083 ], [ -75.933700561097453, 5.470799922594551 ], [ -75.939399718536976, 5.470900058406201 ], [ -75.955001831395634, 5.472099780905832 ], [ -75.977500915477023, 5.470399856693331 ], [ -75.986701964522993, 5.470499992372013 ], [ -76.003997802297434, 5.473400115957308 ], [ -76.017303467168915, 5.471799849662152 ], [ -76.023002623858986, 5.475299835627226 ], [ -76.02819824252542, 5.486299990892151 ], [ -76.037399292630312, 5.488599776695822 ], [ -76.046699524762715, 5.491499900836129 ], [ -76.064002990125502, 5.514100075505284 ], [ -76.066299439041174, 5.530300139534973 ], [ -76.067497253378122, 5.543000221833534 ], [ -76.06520080598159, 5.55340004016182 ], [ -76.069801330220216, 5.57250022853025 ], [ -76.077903747684815, 5.583499909125613 ], [ -76.089996337602315, 5.589900017402388 ], [ -76.09519958503256, 5.598000049192391 ], [ -76.106697082899586, 5.61129999115471 ], [ -76.121101379997725, 5.62470006976247 ], [ -76.126899718618574, 5.636300087655404 ], [ -76.132698058929904, 5.650700092463448 ], [ -76.129203795912005, 5.652999877835491 ], [ -76.129798888622616, 5.659999847665076 ], [ -76.128097534439178, 5.672100066703205 ], [ -76.128601073923775, 5.685400009618149 ], [ -76.123497009176106, 5.70209980060041 ], [ -76.122901916794987, 5.712500096037084 ], [ -76.138999938455726, 5.729899883809471 ], [ -76.129203795971975, 5.740799904047627 ], [ -76.128700256392449, 5.745999813047876 ], [ -76.131599425828369, 5.755300044513666 ], [ -76.140800475453801, 5.773200034701071 ], [ -76.147697448834663, 5.784200192018016 ], [ -76.159797668364334, 5.805699825279717 ], [ -76.159301758209651, 5.824100018390882 ], [ -76.155197143507152, 5.843200206564823 ], [ -76.154701232762179, 5.864500046274123 ], [ -76.15239715606431, 5.882999897269412 ], [ -76.149497985299334, 5.900300025798547 ], [ -76.146102905905323, 5.915299891821761 ], [ -76.149497985597236, 5.929200172706545 ], [ -76.150100708134559, 5.935599804578244 ], [ -76.141998291511939, 5.944200039709225 ], [ -76.142097472417802, 5.953999995740546 ], [ -76.139198303772559, 5.963300228698016 ], [ -76.13279724164282, 5.970699787023576 ], [ -76.131698608039002, 5.974800109160809 ], [ -76.140899659016839, 5.981699943951297 ], [ -76.150100707713094, 5.983500002991316 ], [ -76.15709686283013, 5.985300063253448 ], [ -76.164001465464807, 5.98589992447953 ], [ -76.176696777599275, 5.987699986054476 ], [ -76.181297302099765, 5.988299847430947 ], [ -76.188346862264297, 5.989463330088754 ], [ -76.191596985506237, 5.989999770307866 ], [ -76.197402954718982, 5.990600109007086 ], [ -76.203796387425811, 5.991300106429891 ], [ -76.212997436175399, 5.992400169640391 ], [ -76.219299316453061, 5.99359989086673 ], [ -76.222801208969344, 5.999999999971044 ], [ -76.226196289502951, 6.003499985362176 ], [ -76.232002257718705, 6.006400108043566 ], [ -76.237800597853848, 6.012199879202346 ], [ -76.244102477871962, 6.020299911445419 ], [ -76.244697571259351, 6.029500007156579 ], [ -76.247596740283967, 6.041699886163888 ], [ -76.251602172483558, 6.062500000481654 ], [ -76.25910186726918, 6.079899787059229 ], [ -76.259201050417275, 6.099500178500501 ], [ -76.265502930299206, 6.113999842985375 ], [ -76.265502930012403, 6.131299972770482 ], [ -76.261497498084921, 6.147999762630806 ], [ -76.255699158161065, 6.173999786515278 ], [ -76.263198851697695, 6.186699867739748 ], [ -76.277099609554199, 6.196000099001987 ], [ -76.289802550902394, 6.194900036692442 ], [ -76.304199219123618, 6.192699909162012 ], [ -76.321998596629641, 6.190499782286074 ], [ -76.336326598819994, 6.187493800579197 ], [ -76.338203430032678, 6.187099933466214 ], [ -76.361801148269322, 6.177299976465262 ], [ -76.386596679231118, 6.175700188511868 ], [ -76.416000366925786, 6.174699784056574 ], [ -76.434997558184364, 6.173600197114252 ], [ -76.456901549721849, 6.171400069961346 ], [ -76.482902526389253, 6.168000221539479 ], [ -76.502998351678755, 6.166399955561493 ], [ -76.515098571619475, 6.166399956475759 ], [ -76.541702270965743, 6.16650009193499 ], [ -76.564102172298703, 6.16489982615013 ], [ -76.581398010357631, 6.157999992762819 ], [ -76.605598450181063, 6.156400203544358 ], [ -76.626998900857515, 6.157599926160161 ], [ -76.648300171215027, 6.163499831480825 ], [ -76.671401978183141, 6.1630001069977 ], [ -76.703598022351301, 6.164899826175072 ], [ -76.716560364426982, 6.168775558153636 ], [ -76.723571778106432, 6.18216037780984 ], [ -76.720779418109899, 6.188662529178067 ], [ -76.717529297196776, 6.195628642446014 ], [ -76.722640991099297, 6.199808596892784 ], [ -76.722175598774655, 6.208168507104146 ], [ -76.720779418363051, 6.213741778699536 ], [ -76.727287292819511, 6.221172333385065 ], [ -76.727752684709088, 6.237427234065986 ], [ -76.731002806810665, 6.238820552192989 ], [ -76.738433838608543, 6.241607189045734 ], [ -76.744003296746186, 6.243929386018725 ], [ -76.757469176610144, 6.252289294785504 ], [ -76.758399964131911, 6.259255409040238 ], [ -76.755149841512477, 6.269008636029354 ], [ -76.751899718666408, 6.278761386053188 ], [ -76.757934570441606, 6.286192416976645 ], [ -76.767692566171036, 6.286656855861374 ], [ -76.780227661212095, 6.28619241700711 ], [ -76.786727906082902, 6.294552326900998 ], [ -76.785804749352209, 6.308949470424943 ], [ -76.792770384915144, 6.317309379329037 ], [ -76.791839599652903, 6.327991008943566 ], [ -76.785339355675831, 6.329384327763397 ], [ -76.781623840335286, 6.333564280845983 ], [ -76.794624328216983, 6.345871447744817 ], [ -76.791839599611436, 6.354695797078685 ], [ -76.782554626059095, 6.358179092962148 ], [ -76.776512145872985, 6.363287448152252 ], [ -76.775123596397805, 6.370254039664343 ], [ -76.782089232647479, 6.371647358284509 ], [ -76.789985656312325, 6.377220631425899 ], [ -76.787658690618926, 6.383722305977217 ], [ -76.780693054983004, 6.38372230586729 ], [ -76.772331237282259, 6.381864547059075 ], [ -76.769081115862988, 6.388831139128172 ], [ -76.772796630583699, 6.398584366638087 ], [ -76.790908813236101, 6.408801555945206 ], [ -76.79880523758284, 6.416232586361292 ], [ -76.798339843231886, 6.424592018662285 ], [ -76.789520264284405, 6.425985335965937 ], [ -76.776512145758005, 6.422269821769438 ], [ -76.768157958471491, 6.426449775663951 ], [ -76.766761780205414, 6.437131881694521 ], [ -76.772796630211673, 6.443169117007646 ], [ -76.77883911180912, 6.439454078927294 ], [ -76.790443420806326, 6.435738563336868 ], [ -76.798339844399081, 6.438525200554849 ], [ -76.799736023549443, 6.44642019258162 ], [ -76.791374207118565, 6.458495616850308 ], [ -76.785339355159877, 6.469177245615622 ], [ -76.785804749357752, 6.476608277138213 ], [ -76.782089233296801, 6.475679397493541 ], [ -76.778373718862468, 6.467783927098191 ], [ -76.771408080766093, 6.470570564378135 ], [ -76.771873473786613, 6.482181549718671 ], [ -76.790443420393487, 6.49611425432809 ], [ -76.807167053401628, 6.510511398263146 ], [ -76.817848205106785, 6.491469859291483 ], [ -76.82481384219686, 6.490076542367889 ], [ -76.822959899682544, 6.525372982116305 ], [ -76.828529358676505, 6.52908849666798 ], [ -76.840606688674043, 6.521657944046866 ], [ -76.845710753603754, 6.524444103265264 ], [ -76.853141783897442, 6.558115482497395 ], [ -76.868469237937362, 6.572977065738489 ], [ -76.880081177579541, 6.571119308746522 ], [ -76.88658142114258, 6.563224315726276 ], [ -76.890296936070399, 6.563224315803184 ], [ -76.899124146285459, 6.574834823517397 ], [ -76.902374267723815, 6.589231968312541 ], [ -76.894943237054974, 6.607344628086797 ], [ -76.884262084763705, 6.611060142265512 ], [ -76.86708068808997, 6.609666824203305 ], [ -76.862434386743388, 6.623135566043085 ], [ -76.874969482783229, 6.631959438442435 ], [ -76.886116027566089, 6.631030559371333 ], [ -76.90933990471261, 6.628243922473041 ], [ -76.915374756444365, 6.639854907893487 ], [ -76.910270690112682, 6.650536536722552 ], [ -76.898193360051977, 6.666791915383499 ], [ -76.909339904414949, 6.674222468611949 ], [ -76.919090271420643, 6.667256355386577 ], [ -76.923271179259771, 6.651465415380915 ], [ -76.927917480081945, 6.649607657548402 ], [ -76.937210083841265, 6.6519298547982 ], [ -76.94184875443797, 6.661218642449542 ], [ -76.940460205716747, 6.686762332956602 ], [ -76.944633484490055, 6.68908405239077 ], [ -76.946029663050453, 6.687226772066896 ], [ -76.947425841504284, 6.6816535000102 ], [ -76.950675965117611, 6.677473544581378 ], [ -76.960426330369089, 6.677009105846712 ], [ -76.966468810526678, 6.677937984039827 ], [ -76.968788147555898, 6.68444013646069 ], [ -76.967391967880545, 6.689084053013271 ], [ -76.950210570896658, 6.699766159320014 ], [ -76.938133240361694, 6.715092181639669 ], [ -76.939994812701698, 6.722058773715097 ], [ -76.946495055831491, 6.725774288060038 ], [ -76.960426329820663, 6.728096485599782 ], [ -76.968788147418437, 6.741564750679825 ], [ -76.970184325631195, 6.749460219790848 ], [ -76.965538024902528, 6.762464046065533 ], [ -76.967857360146724, 6.774074554093547 ], [ -76.966468810854877, 6.784292220614355 ], [ -76.962287902157527, 6.793580531790207 ], [ -76.962753296074524, 6.795902728553458 ], [ -76.969253539552369, 6.79776048656489 ], [ -76.976684570041954, 6.798689364535768 ], [ -76.976684569816868, 6.808442592100732 ], [ -76.975753784226086, 6.813550949536213 ], [ -76.969718933256814, 6.81680202422539 ], [ -76.955322266285307, 6.817266464977058 ], [ -76.942314148268579, 6.814479827590168 ], [ -76.937210083528797, 6.814479827245133 ], [ -76.935348510916839, 6.81587314536346 ], [ -76.933494568340947, 6.823304175967198 ], [ -76.92838287312064, 6.838165760343648 ], [ -76.918632507934888, 6.848615647189761 ], [ -76.903305052907172, 6.835611343965126 ], [ -76.895874023056749, 6.842577934515257 ], [ -76.883331299259467, 6.852795123835589 ], [ -76.85639953670541, 6.84582900929199 ], [ -76.850357055014257, 6.839791297297632 ], [ -76.839675903427704, 6.831431389218758 ], [ -76.838279723520927, 6.835146904309796 ], [ -76.835960388948337, 6.840720176302679 ], [ -76.82342529236584, 6.837004661032569 ], [ -76.821563721040476, 6.84072017599898 ], [ -76.821098328372102, 6.846757888681441 ], [ -76.835494995532244, 6.858832836355566 ], [ -76.832710266475658, 6.861619472251839 ], [ -76.820167542090289, 6.862083911405152 ], [ -76.80066680870091, 6.863941669366966 ], [ -76.795089721595858, 6.871836661649083 ], [ -76.793235778927155, 6.877874374216316 ], [ -76.791374205750472, 6.889020919602314 ], [ -76.796951294106833, 6.902024746582192 ], [ -76.801589965621929, 6.921530723415123 ], [ -76.800132750940264, 6.936585426522976 ], [ -76.8037490851017, 6.951627254801634 ], [ -76.814834595080129, 6.965312004557134 ], [ -76.827949524702035, 6.958639145361171 ], [ -76.831680298576813, 6.958639144797749 ], [ -76.836700439486847, 6.970099926502584 ], [ -76.838401794232013, 6.984000206818294 ], [ -76.840202331952483, 6.994400024132235 ], [ -76.838996886860954, 7.001900195805661 ], [ -76.834396362586716, 7.009399890332928 ], [ -76.832702637057935, 7.010499953500752 ], [ -76.830398559429227, 7.01109981620971 ], [ -76.812499999976836, 7.011600017683171 ], [ -76.795196533136789, 7.012700080180277 ], [ -76.781402587412629, 7.012599944692007 ], [ -76.755996704431638, 7.012499809307531 ], [ -76.733497620348658, 7.018799781072187 ], [ -76.722000121698727, 7.026199818514402 ], [ -76.715698242813886, 7.02680015643963 ], [ -76.705902098925634, 7.026100158358671 ], [ -76.680496215777097, 7.011600017578649 ], [ -76.663200378569712, 7.007500172269244 ], [ -76.647598267439676, 7.000500202855816 ], [ -76.633796692067065, 6.998099804138895 ], [ -76.611297608129931, 6.99860000632218 ], [ -76.588798523796456, 6.986400127918771 ], [ -76.573799132716829, 6.973599910279644 ], [ -76.555999756448713, 6.970600128272714 ], [ -76.553596496269066, 6.972400188204005 ], [ -76.549598694420283, 6.981599808503677 ], [ -76.546699523796477, 6.988500118393782 ], [ -76.546699524106643, 7.003499984764122 ], [ -76.547302246929277, 7.023700237609537 ], [ -76.544998169705622, 7.043399811460723 ], [ -76.543296813744035, 7.060100078057858 ], [ -76.533500671046582, 7.078599930634432 ], [ -76.529502868947276, 7.08780002635113 ], [ -76.520896910956395, 7.099299906982525 ], [ -76.514907837169076, 7.113348484361485 ], [ -76.51450347856418, 7.114299773956939 ], [ -76.506500244807938, 7.126999854914017 ], [ -76.502998351826079, 7.140200138574913 ], [ -76.510498047724596, 7.154099940796082 ], [ -76.516296387185221, 7.162199974451346 ], [ -76.522102355562666, 7.198699951640399 ], [ -76.526100159318318, 7.226399898090898 ], [ -76.542297362884227, 7.254199981339668 ], [ -76.557800292808835, 7.262400150985607 ], [ -76.572082518822228, 7.285744667706773 ], [ -76.587051391521072, 7.303156853240242 ], [ -76.619003296613585, 7.316299915277726 ], [ -76.641120911479888, 7.325762272204508 ], [ -76.658233642589892, 7.339203358518386 ], [ -76.672286987149391, 7.34806203903612 ], [ -76.685111999110433, 7.356615542699362 ], [ -76.706802368947265, 7.370667934963394 ], [ -76.720245362118803, 7.386552810665438 ], [ -76.72757721007909, 7.40304851481145 ], [ -76.737655639867185, 7.42626524008464 ], [ -76.752548217913258, 7.469726086283322 ], [ -76.752624512477425, 7.469948768103376 ], [ -76.762092590912559, 7.502329348748492 ], [ -76.80417633036096, 7.576720714392995 ], [ -76.820915222497646, 7.588069439451381 ], [ -76.84729766906203, 7.588920593145363 ], [ -76.860916137575558, 7.594878673703435 ], [ -76.897232055314959, 7.588920593292987 ], [ -76.954826355046464, 7.619845866323083 ], [ -76.957145691646687, 7.620437622431166 ], [ -76.983764648620337, 7.627222538781142 ], [ -77.001640320076632, 7.657863616241186 ], [ -77.021003723814331, 7.676700116022838 ], [ -77.038299560706761, 7.690700055017727 ], [ -77.067703247151954, 7.714499950336459 ], [ -77.088500975880081, 7.729000090739278 ], [ -77.101699829027964, 7.740600109758954 ], [ -77.11499786443953, 7.766600132326289 ], [ -77.132896423438766, 7.795000077008549 ], [ -77.149002075973868, 7.822199822303564 ], [ -77.136398315596722, 7.834899902054923 ], [ -77.125396728551308, 7.831399917243702 ], [ -77.115600585389288, 7.834199905740764 ], [ -77.108703612841637, 7.848599911586888 ], [ -77.099502563756573, 7.85729980403739 ], [ -77.085601807471136, 7.86359977786103 ], [ -77.071197509569686, 7.870399952654133 ], [ -77.063201904954681, 7.88310003244265 ], [ -77.039596557435118, 7.910200118574379 ], [ -77.036697387407486, 7.918200015912432 ], [ -77.029800415364932, 7.941899776166035 ], [ -77.033798218122612, 7.956299781115145 ], [ -77.030403137839826, 7.971300124505867 ], [ -77.019401550455271, 7.980000019111343 ], [ -77.018898010116942, 7.99039983792354 ], [ -77.021697997774112, 7.998499870203624 ], [ -77.021697997470042, 8.005999564408652 ], [ -77.005599975593512, 8.029600143883824 ], [ -76.992401123797507, 8.042799950052878 ], [ -76.983306885227663, 8.047850608907428 ], [ -76.97920227102388, 8.053129196781102 ], [ -76.967445373638455, 8.0661125191042 ], [ -76.963623046123004, 8.070486069135816 ], [ -76.962532044204423, 8.080053328965057 ], [ -76.964164734578773, 8.091259956591466 ], [ -76.968544007115625, 8.101920127427055 ], [ -76.972091674441188, 8.11422061839848 ], [ -76.971549987481268, 8.137181282530731 ], [ -76.97236633265176, 8.162055969965632 ], [ -76.973731995435216, 8.178730011392295 ], [ -76.976470948057369, 8.182556151819126 ], [ -76.985488891854502, 8.204423905159937 ], [ -76.992866515408295, 8.219183921339114 ], [ -76.993965148572727, 8.23011779865006 ], [ -76.996421814556328, 8.245697975147722 ], [ -76.99669647232048, 8.250891686259649 ], [ -76.996696472439041, 8.256359100500953 ], [ -76.99778747492563, 8.261279106274118 ], [ -76.999977112391619, 8.266199111837876 ], [ -77.001098632132909, 8.273300170868918 ], [ -76.994201659868978, 8.27849960341716 ], [ -76.983802795151817, 8.268600464325857 ], [ -76.973503113570288, 8.259300232614324 ], [ -76.97810363706769, 8.248999595477057 ], [ -76.986701965029866, 8.235699654072528 ], [ -76.985000610955353, 8.221300125211638 ], [ -76.97059631298859, 8.207900046937183 ], [ -76.953796386330694, 8.189900398194405 ], [ -76.946899413975061, 8.173199653229695 ], [ -76.953201294145558, 8.168600082055011 ], [ -76.957801818464773, 8.166899681383779 ], [ -76.966957091752619, 8.163392067106514 ], [ -76.962997437055535, 8.152999877211972 ], [ -76.955001830776595, 8.146599768790475 ], [ -76.953201294383092, 8.132200240738747 ], [ -76.965400696100318, 8.111186981470963 ], [ -76.953201293129482, 8.103899956028036 ], [ -76.94969940186192, 8.100399971104455 ], [ -76.938201903570203, 8.114199638976091 ], [ -76.925498961873046, 8.11940002412973 ], [ -76.91690063512965, 8.138400077426924 ], [ -76.90309906037065, 8.137200354731242 ], [ -76.89209747315158, 8.140000343346392 ], [ -76.866798400144916, 8.143400192683893 ], [ -76.869102477646265, 8.134099960018483 ], [ -76.879402160678268, 8.129599571276531 ], [ -76.890403747455764, 8.130200385842945 ], [ -76.896102905690469, 8.119199752133909 ], [ -76.909400939923913, 8.107700347485348 ], [ -76.909400939449981, 8.102501869982882 ], [ -76.873703002166607, 8.115699767899633 ], [ -76.867301941058074, 8.11040019984762 ], [ -76.856903076550552, 8.101200103866827 ], [ -76.864402771415456, 8.096599579393127 ], [ -76.878303528600554, 8.092000007356585 ], [ -76.873100281040522, 8.083299637170942 ], [ -76.861000061661684, 8.078599930041822 ], [ -76.85630035320753, 8.061900139170653 ], [ -76.868400573645573, 8.059599876203475 ], [ -76.886299133645934, 8.066599845767124 ], [ -76.873001099195506, 8.053299904227051 ], [ -76.870697022271344, 8.03359985322148 ], [ -76.894897461258608, 8.037799835826995 ], [ -76.908203125363997, 8.043600082505746 ], [ -76.919197081702052, 8.049400329280394 ], [ -76.936500548511546, 8.049500464746933 ], [ -76.945098876228727, 8.038599967860076 ], [ -76.946800231941054, 8.023599623778873 ], [ -76.951400756071664, 8.004500388790323 ], [ -76.95020294180361, 7.977900027713577 ], [ -76.947402954768748, 7.955399989392421 ], [ -76.942703247746437, 7.940899849634561 ], [ -76.937499999872941, 7.933400153610016 ], [ -76.906402588262907, 7.920599938019723 ], [ -76.894302368469354, 7.918799876581459 ], [ -76.879302977968152, 7.917600154272262 ], [ -76.852798462549416, 7.918600083205192 ], [ -76.841796874690658, 7.920300006677618 ], [ -76.829200743868469, 7.923699855959845 ], [ -76.813598633322897, 7.925399780063828 ], [ -76.799797057516699, 7.931099892178585 ], [ -76.7761001581773, 7.935599803828714 ], [ -76.768600463065169, 7.939599991483486 ], [ -76.761199950788111, 7.955200195373815 ], [ -76.75540161201431, 7.971899986151526 ], [ -76.754302979176231, 7.999599932957758 ], [ -76.754302978858803, 8.024499894008853 ], [ -76.752601622767017, 8.038299560988904 ], [ -76.748497008968116, 8.061400413697449 ], [ -76.748497009059662, 8.071200371263885 ], [ -76.749099732171857, 8.090900420777288 ], [ -76.758399964210014, 8.090299605848754 ], [ -76.760696410435827, 8.0915002814552 ], [ -76.760696411341797, 8.105999945872648 ], [ -76.759002685153092, 8.127900123898417 ], [ -76.755500793097809, 8.149299621014864 ], [ -76.754898070558625, 8.167200088613612 ], [ -76.762397766910681, 8.193200111196189 ], [ -76.774002074405971, 8.225600243066868 ], [ -76.777999877630151, 8.256199837270453 ], [ -76.783798218297918, 8.278800011352208 ], [ -76.789596558385384, 8.294400215395825 ], [ -76.789001465437423, 8.308199882840229 ], [ -76.777496338313711, 8.319199562617626 ], [ -76.77059936591165, 8.333000182796907 ], [ -76.769996642722703, 8.341099738463264 ], [ -76.769996642453194, 8.349200249442239 ], [ -76.769500732352867, 8.363599777565362 ], [ -76.769996643657805, 8.366499900782713 ], [ -76.772903441652034, 8.37629985800378 ], [ -76.783302306742939, 8.394900321857216 ], [ -76.798301696605307, 8.43360042608799 ], [ -76.805198670224968, 8.445799828091676 ], [ -76.81680297888397, 8.458000182687007 ], [ -76.826599120499523, 8.48750019030726 ], [ -76.842796325788342, 8.508299827604025 ], [ -76.863502502897788, 8.52229976724977 ], [ -76.882499694283609, 8.532799720723823 ], [ -76.900398254355608, 8.540300368881196 ], [ -76.92579650837574, 8.55080032309929 ], [ -76.938499449953909, 8.547400473730557 ], [ -76.937301635971011, 8.541600227717669 ], [ -76.936203003028723, 8.538200378557121 ], [ -76.947700500663515, 8.538800239588721 ], [ -76.952301025234846, 8.549799918851667 ], [ -76.948303222500869, 8.56420040205635 ], [ -76.940200805331543, 8.578000069358527 ], [ -76.921798706039567, 8.591300010820568 ], [ -76.907402038897473, 8.606200218675275 ], [ -76.907997131861947, 8.615500450898921 ], [ -76.889503478818824, 8.632100105098827 ], [ -76.874496459871978, 8.638400077498941 ], [ -76.850303649148856, 8.640600205035936 ], [ -76.83480071944507, 8.644000053422907 ], [ -76.809402465884844, 8.651399612147731 ], [ -76.788696288993577, 8.654800414494019 ], [ -76.763900756285523, 8.662799835167256 ], [ -76.741996764325961, 8.667300223853394 ], [ -76.721801758048287, 8.673000336548464 ], [ -76.6986999504294, 8.681599616314449 ], [ -76.690101623396231, 8.690799712619958 ], [ -76.684303284548051, 8.70170021107692 ], [ -76.676902770887949, 8.712100028195223 ], [ -76.667602538644587, 8.724200249015505 ], [ -76.645202637204321, 8.741999625632287 ], [ -76.620399475752393, 8.749400139034933 ], [ -76.616302490054096, 8.751700400535135 ], [ -76.613502502536875, 8.752300262477354 ], [ -76.601898193459604, 8.764900206919709 ], [ -76.597297667607933, 8.765500068854935 ], [ -76.586997986489621, 8.77350044261693 ], [ -76.581199645800041, 8.776399613269517 ], [ -76.570800781799107, 8.784999847059916 ], [ -76.560997009963685, 8.793100357890815 ], [ -76.538597106591922, 8.803899765155952 ], [ -76.521301270356759, 8.81200027443049 ], [ -76.497100830747968, 8.823399543477313 ], [ -76.491897582219195, 8.830300330917199 ], [ -76.47339630063091, 8.848699570524783 ], [ -76.458503723748507, 8.867199898384762 ], [ -76.458503722778772, 8.874099732066794 ], [ -76.469398498540883, 8.883899687929963 ], [ -76.464202881171957, 8.886199951205292 ], [ -76.451599121552675, 8.887299537655714 ], [ -76.441802978110843, 8.876299858711276 ], [ -76.437103271555472, 8.86820030149461 ], [ -76.421600341275422, 8.857199668091789 ], [ -76.415199279672194, 8.850799561377116 ], [ -76.404800414913211, 8.842100143581789 ], [ -76.399101256613434, 8.833399772625329 ], [ -76.397300720427381, 8.825300215968038 ], [ -76.394401549714956, 8.813199997226281 ], [ -76.391601562226555, 8.796999931796899 ], [ -76.389801024746106, 8.791199683508548 ], [ -76.387496948168689, 8.77910041825332 ], [ -76.383499145142466, 8.76749992444994 ], [ -76.381202698421632, 8.753600120506333 ], [ -76.37999725337302, 8.740300177985311 ], [ -76.377098082924789, 8.729299545288535 ], [ -76.374198913096876, 8.71030044502808 ], [ -76.364997863527194, 8.695199966871462 ], [ -76.359802245567948, 8.681300163755102 ], [ -76.349403382000688, 8.652999878714812 ], [ -76.337303162425499, 8.638500212981464 ], [ -76.333801269161341, 8.635600090850646 ], [ -76.330398559756645, 8.633799553684369 ], [ -76.322303771406354, 8.625699997078787 ], [ -76.307296752200472, 8.619299889431787 ] ] ] } } ] } 7 | --------------------------------------------------------------------------------