Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/georust/proj

Rust bindings for the latest stable release of PROJ
https://github.com/georust/proj

geocoding geodesy geospatial gis proj rust

Last synced: 7 days ago
JSON representation

Rust bindings for the latest stable release of PROJ

Awesome Lists containing this project

README

        

![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.4 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.4.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