![GitHub Workflow Status](https://img.shields.io/github/workflow/status/georust/proj/proj%20ci) # PROJ Coordinate transformation via bindings to the [PROJ](https://proj.org) v9.2 API. Two coordinate transformation operations are currently provided: _projection_ (and inverse projection) and _conversion_. _Projection_ is intended for transformations between geodetic and projected coordinates and vice versa (inverse projection), while _conversion_ is intended for transformations between projected coordinate systems. The PROJ [documentation](https://proj.org/operations/index.html) explains the distinction between these operations in more detail. This crate depends on [`libproj v9.2.x`](https://proj.org), accessed via the [`proj-sys`](https://docs.rs/proj-sys) crate. By default, `proj-sys` will try to find a pre-existing installation of libproj on your system. If an appropriate version of libproj cannot be found, the build script will attempt to build libproj from source. You may specify a from-source build with the [`bundled_proj` feature](#feature-flags). Out of the box, any `(x, y)` numeric tuple can be provided as input to proj. You can [conform your own types](#conform-your-own-types) to the [Coord](https://docs.rs/proj/latest/proj/trait.Coord.html) trait to pass them in directly and avoid intermediate allocations. There is a [`geo-types` feature](#feature-flags), enabled by default, which implements this trait for types in the [`geo-types` crate](https://docs.rs/geo-types). Methods for [conversion](https://docs.rs/proj/latest/proj/struct.Proj.html#method.convert_array) and [projection](https://docs.rs/proj/latest/proj/struct.Proj.html#method.project_array) of slices of `Coord`s are also available. ## Examples ### Convert from [NAD 83 US Survey Feet](https://epsg.io/2230) to [NAD 83 Meters](https://epsg.io/26946) Using EPSG Codes ```rust use proj::Proj; let from = "EPSG:2230"; let to = "EPSG:26946"; let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); let result = ft_to_m .convert((4760096.421921f64, 3744293.729449f64)) .unwrap(); assert_relative_eq!(result.0, 1450880.2910605003); assert_relative_eq!(result.1, 1141263.0111604529); ``` ### Convert from [NAD 83 US Survey Feet](https://epsg.io/2230) to [NAD 83 Meters](https://epsg.io/26946) Using the `pipeline` Operator Note that as of v5.0.0, PROJ uses the [`pipeline`](https://proj.org/operations/pipeline.html) operator, which allows an arbitrary number of steps in a conversion. The example below works as follows: - define the operation as a `pipeline` operation - define `step` 1 as an `inv`erse transform, yielding geodetic coordinates - define `step` 2 as a forward transform to projected coordinates, yielding metres. ```rust use proj::Proj; let ft_to_m = Proj::new(" +proj=pipeline +step +inv +proj=lcc +lat_1=33.88333333333333 +lat_2=32.78333333333333 +lat_0=32.16666666666666 +lon_0=-116.25 +x_0=2000000.0001016 +y_0=500000.0001016001 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs +step +proj=lcc +lat_1=33.88333333333333 +lat_2=32.78333333333333 +lat_0=32.16666666666666 +lon_0=-116.25 +x_0=2000000 +y_0=500000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs ").unwrap(); // The Presidio, approximately let result = ft_to_m.convert((4760096.421921f64, 3744293.729449f64)).unwrap(); assert_relative_eq!(result.0, 1450880.2910605003); assert_relative_eq!(result.1, 1141263.01116045); ``` ### Inverse Projection from [Stereo70](https://epsg.io/3844) to Geodetic ```rust use proj::Proj; // Carry out an inverse projection from Pulkovo 1942(58) / Stereo70 (EPSG 3844) // into geodetic lon and lat coordinates (in radians) let stereo70 = Proj::new(" +proj=sterea +lat_0=46 +lon_0=25 +k=0.99975 +x_0=500000 +y_0=500000 +ellps=krass +towgs84=33.4,-146.6,-76.3,-0.359,-0.053,0.844,-0.84 +units=m +no_defs ").unwrap(); let geodetic_radians_point = stereo70.project( (500119.70352012233f64, 500027.77896348457f64), true ).unwrap(); assert_relative_eq!(geodetic_radians_point.0, 0.436332, epsilon=1e-5); assert_relative_eq!(geodetic_radians_point.1, 0.802851, epsiolon=1e-5); ``` ## Usage There are two options for creating a transformation: 1. If you don't require additional [grids](#grid-file-download) or other customisation: - Call `Proj::new` or `Proj::new_known_crs`. This creates a transformation instance ([`Proj`](https://docs.rs/proj/latest/proj/struct.Proj.html)) 2. If you require a grid for the transformation you wish to carry out, or you need to customise the search path or the grid endpoint: - Create a new [`ProjBuilder`](https://docs.rs/proj/latest/proj/struct.ProjBuilder.html) by calling `ProjBuilder::new()`. It may be modified to enable network downloads, disable the grid, cache or modify search paths; - Call [`ProjBuilder.proj()`](https://docs.rs/proj/latest/proj/struct.ProjBuilder.html#method.proj) or [`ProjBuilder.proj_known_crs()`](https://docs.rs/proj/latest/proj/struct.ProjBuilder.html#method.proj_known_crs). This creates a transformation instance (`Proj`) **Note**: 1. Both `ProjBuilder` and `Proj` implement the [`Info`](https://docs.rs/proj/latest/proj/trait.Info.html) trait, which can be used to get information about the current state of the `PROJ` instance; 2. `Proj::new()` and `ProjBuilder::proj()` have the same signature; 3. `Proj::new_known_crs()` and `ProjBuilder::proj_known_crs()` have the same signature. ## Requirements By default, the crate requires `libproj` 9.2.x to be present on your system. While it may be backwards-compatible with older PROJ 6 versions, this is neither tested nor supported. If a suitable library can't be found, `proj` will attempt to build `libproj` from source. ## Feature Flags - `geo-types`: include [trait impls for `geo-types`](https://docs.rs/proj/latest/proj/trait.Coord.html#impl-Coord%3CT%3E-for-Coordinate%3CT%3E). See [example](#integration-with-geo-types). - `pkg_config`: enables the use of `pkg-config` when linking against `libproj` — note that `pkg-config` must be available on your system. - `bundled_proj`: builds `libproj` from source bundled in the `proj-sys` crate. Note that this feature requires Sqlite3 and `libtiff` to be present on your system. - `network`: exposes APIs which, when enabled, can fetch grid data from the internet to improve projection accuracy. See [`enable_network`](https://docs.rs/proj/latest/proj/struct.ProjBuilder.html#method.enable_network) for details. ### Network, Cache, and Search Path Functionality #### Grid File Download `proj` supports [network grid download](https://proj.org/usage/network.html) functionality via the [`network` feature](#feature-flags). Network access is **disabled** by default, and can be activated by passing a `true` `bool` to [`enable_network()`](https://docs.rs/proj/latest/proj/struct.ProjBuilder.html#method.enable_network). Network functionality status can be queried with `network_enabled`, and the download endpoint can be queried and set using `get_url_endpoint` and `set_url_endpoint`. ##### Grid File Cache Up to 300 mb of downloaded grids are cached to save bandwidth: This cache can be enabled or disabled using [`grid_cache_enable`](https://docs.rs/proj/latest/proj/struct.ProjBuilder.html#method.grid_cache_enable). #### Search Path Modification The path used to search for resource files can be modified using [`set_search_paths`](https://docs.rs/proj/latest/proj/struct.ProjBuilder.html#method.set_search_paths) ### Conform your own types If you have your own geometric types, you can conform them to the `Coord` trait and use `proj` without any intermediate allocation. ```rust use proj::{Proj, Coord}; struct MyPointOfInterest { lat: f64, lon: f64, } impl Coord for MyPointOfInterest { fn x(&self) -> f64 { self.lon } fn y(&self) -> f64 { self.lat } fn from_xy(x: f64, y: f64) -> Self { Self { lon: x, lat: y } } } let donut_shop = MyPointOfInterest { lat: 34.095620, lon: -118.283555 }; let from = "EPSG:4326"; let to = "EPSG:3309"; let proj = Proj::new_known_crs(&from, &to, None).unwrap(); let result = proj.convert(donut_shop).unwrap(); assert_relative_eq!(result.x(), 158458.67251293268); assert_relative_eq!(result.y(), -434296.8803996085); ``` ### Integration with `geo-types` If you've enabled the `geo-types` feature, you can skip allocating an intermediate representation, and pass the [`geo-types`](https://crates.io/crates/geo-types) directly. ```rust use approx::assert_relative_eq; use proj::Proj; use geo_types::Point; let my_point = Point::new(4760096.421921f64, 3744293.729449f64); let from = "EPSG:2230"; let to = "EPSG:26946"; let nad_ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); let result = nad_ft_to_m.convert(my_point).unwrap(); assert_relative_eq!(result.x(), 1450880.2910605003f64); assert_relative_eq!(result.y(), 1141263.0111604529f64); ``` You can also transform entire geometries from `geo-types` by using the `Transform` trait. ```rust use proj::{Proj, Transform}; use geo_types::{Coordinate, line_string}; let line = line_string![ (x: -116.590457069172_f64, y: 32.55730630167689), (x: -116.590411068973, y: 32.55714830169309), ]; let proj = Proj::new_known_crs("EPSG:4326", "EPSG:6366", None).unwrap(); // create a new line with a different projection let new_line = line.transformed(&proj).unwrap(); assert_eq!(new_line[0], Coordinate { x: 538447.8454476658, y: 3602285.563945497, }); assert_eq!(new_line[1], Coordinate { x: 538452.2313532799, y: 3602268.065714932, }); // or transform the original in-place let mut line = line; line.transform(&proj).unwrap(); assert_eq!(line[0], Coordinate { x: 538447.8454476658, y: 3602285.563945497, }); assert_eq!(line[1], Coordinate { x: 538452.2313532799, y: 3602268.065714932, }); ``` License: MIT/Apache-2.0