A new python geohash library released!

The goal of this article is to explain how you can create your own python library with rust. Rust is not magic but the language have a lot of fresh and new features. Maybe I made a lot of mistakes, don’t hesitate to correct in the coments.

The situation

For a crazy side project, I need geohash encoding/decoding but after writing my code I discover georust and its geohash implementations.

Geohash is a way to encode a position for more information the Wikipedia article is a good starting point to understand how it works.

In python, you have 3 libraries :

After testing this 3 librairies for my side project I was not satisfied to 100%. And after trying geohash-rust, I would like to have the same in my python app without rewrite in rust.

Rust allow ffi to be used in Python. Ffi is not very fun in action. Fortunately there is Pyo3, rust bindings for python3.

Pyo3 purpose to use your rust code in python or python with rust. It also have a complete API for binding python code and rust code.

First step is to prepare your project like other rust project.

$ cargo new geohashrs

Then prepare the cargo.toml for compilation and the publication. Don’t hesitate to add the metadata.

Now, we are ready to code.

[package]
name = "geohashrs"
version = "0.1.4"
authors = ["nabil@eml.cc"]
edition = "2018"
license = "Apache-2.0"
repository = "https://github.com/blackrez/geohashrs"
homepage = "https://github.com/blackrez/geohashrs"
readme = "README.md"

[lib]
name = "geohashrs"
crate-type = ["cdylib", "rlib"]

[package.metadata.maturin]
maintainer = "Nabil Servais"
maintainer-email = "nabil@eml.cc"
requires-python = ">=3.5"
classifier = ["Programming Language :: Python"]

[dependencies]
geohash = "0.9.0"

[dependencies.pyo3]
version = "0.11.1"
features = ["extension-module"]

The API will be simple for the first version. Just encode and decode, usage of neighbours and direction will be added in the future.

Pyo3 a great binding

Pyo3 permits to bridge python and rust, this is a huge advantage when you code for 2 langages. Just use the type in rust and use it Python.

/// Encode coords into geohash
#[pyfunction]
fn geohash_encode(x: f64, y: f64, level: usize) -> PyResult<String> {
let c = Coordinate { x: x, y: y };
let geohash_string = geohash::encode(c, level).expect("Invalid coordinate");
Ok(geohash_string)
}

// Decode geohash into Python list
#[pyfunction]
fn geohash_decode(hash: &str) -> PyResult<Vec<f64>> {
let (c, _, _) = decode(&hash).expect("Invalid hash string");
let vec = vec![c.x, c.y];
Ok(vec)
}

The wrappers use macro for pyo3, it is also managed by the lib at the build

#[pymodule]
fn geohashrs(py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(geohash_encode))?;
m.add_wrapped(wrap_pyfunction!(geohash_decode))?;
Ok(())
}

After writing the code, we need to build the library.

Maturin

Cargo works with your libs but it will generate libgehashrs.so and not geohashrs. And you need to specify a setuptools file…

But the Pyo3 have a way to

Maturin is a great tools for generate the wheels and also build the libs.

$ pip install maturin
$ maturin build

And you will find your lib.

Don’t hesitate to use a virtualenv. Maturin is written in rust but it works in python.

And the publication

Just use :

$ maturin publish

Maturin will publish after asking some questions.

It also possible to use a docker images for generate more wheels version and use twine.

Now I can use my libs in my application using pip.

https://pypi.org/project/geohashrs/

What’s next ?

  • Add tests
  • Add CI/CD
  • Add neighbours and directions

Thanks for the reading.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Nabil Servais

Nabil Servais

Data engineer lost in the south of France.