Skip to content

megaserg/geographiclib-cython-bindings

Repository files navigation

Cython extension module for geographiclib

TL;DR

Pythonic geographiclib package reimplements geodesic math in Python. Using Cython bindings for C++ version of geographiclib allows for 60-90x speedup for geodesic functions.

Warning: only Inverse(), Direct(), and InverseLine() for Geodesic.WGS84 are implemented. For GeodesicLine, only Position() is implemented.

# for Ubuntu
sudo apt-get install -y libgeographic-dev
# for macOS:
brew install geographiclib

# and then:
pip install geographiclib-cython-bindings

For instructions on how to build and distribute, read on.

Prerequisites

The following instructions are tested for Ubuntu 16.04, Amazon Linux, and macOS Sierra 10.12.

You will need the C++ geographiclib library (for compile-time and for run-time, too).

  • Option 1 (recommended for Ubuntu and macOS): Install libgeographic-dev package, however it looks like the built-from-sources version is fresher.

    Ubuntu:

    sudo apt-get install -y libgeographic-dev

    macOS:

    brew install geographiclib
  • Option 2 (recommended for Ubuntu and Amazon Linux): Compile and install it from sources. This creates bunch of *.hpp in /usr/local/include/GeographicLib/ and /usr/local/lib/libGeographic.so:

    git clone --depth=1 git://git.code.sourceforge.net/p/geographiclib/code geographiclib
    
    cd geographiclib
    mkdir BUILD
    cd BUILD
    
    cmake ..
    make
    sudo make install

    See installation manual in case of problems.

For development, you will also need Cython:

pip install cython

Description

There are two Cython files: cgeographiclib.pxd describing the C++ API of the libGeographic library, and geographiclib_cython.pyx describing classes that will be visible from Python user code. The .pyx imports .pxd to learn about C++ classes and functions available to be called.

We wrap C++ classes Geodesic and GeodesicLine into Cython classes PyGeodesic and PyGeodesicLine. Additionally, we define a pure Python class Geodesic with a single field WGS84 to mimic the behavior of the official geographiclib package.

There is also setup.py file. This file describes how to build the extension module, using distutils. In there, we specify the library to link with as libraries=['Geographic']. The Geographic stands for libGeographic.so that we previously installed.

There are two options to build the package:

  • One, use Cython's cythonize() function to generate a .cpp file from the .pyx one, and then compile it against the libGeographic.so library.
  • Two, if the .cpp is already provided, just compile it - no Cython required!

Development build

For development, use option 1 by providing --cython flag:

python setup.py build_ext --inplace --cython

The result will be a .so shared library named like geographiclib_cython.cpython-35m-x86_64-linux-gnu.so. build_ext means we're building a C++ extension. --inplace means to put it in the current directory. If you run python from current directory, you'll be able to import geographiclib_cython.

Distribute

To distribute, call sdist with --cython flag to create source distribution (unbuilt):

python setup.py sdist --cython

The result will be a dist/ directory with a distribution named like geographiclib-cython-bindings-1.0.0.tar.gz inside. The archive contains setup.py and geographiclib_cython.cpp, so users can build and install it without having Cython!

To publish to PyPI, run:

twine upload -r pypi dist/*

Install

To install, locate the .tar.gz distribution and run:

pip install geographiclib-cython-bindings-1.0.0.tar.gz

For conda, you might need to activate your environment first:

$ which pip
/home/vagrant/.local/bin/pip

$ source activate root

(root) $ which pip
/home/vagrant/anaconda3/bin/pip

Usage

Let's try to import and use it!

$ ipython
Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul  2 2016, 17:53:06)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.0.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from geographiclib_cython import Geodesic

In [2]: Geodesic.WGS84.Inverse(10, 20, 30, 40)
Out[2]:
{'azi1': 40.319640222045905,
 'azi2': 47.328994793150066,
 'lat1': 10.0,
 'lat2': 30.0,
 'lon1': 20.0,
 'lon2': 40.0,
 's12': 3035728.956905633}

In [3]: Geodesic.WGS84.Direct(10, 20, 40.319640222045905, 3035728.956905633)
Out[3]:
{'azi1': 40.319640222045905,
 'azi2': 47.328994793150066,
 'lat1': 10.0,
 'lat2': 29.999999999999996,
 'lon1': 20.0,
 'lon2': 40.00000000000001,
 's12': 3035728.956905633}

Speedup

>>> import timeit
>>> timeit.timeit('Geodesic.WGS84.Inverse(10, 20, 30, 40)', setup='from geographiclib.geodesic import Geodesic', number=10000)
1.7752603970002383
>>> timeit.timeit('Geodesic.WGS84.Inverse(10, 20, 30, 40)', setup='from geographiclib_cython import Geodesic', number=10000)
0.024111284990794957
>>> timeit.timeit('Geodesic.WGS84.Direct(10, 20, 30, 40000)', setup='from geographiclib.geodesic import Geodesic', number=100000)
7.022558598022442
>>> timeit.timeit('Geodesic.WGS84.Direct(10, 20, 30, 40000)', setup='from geographiclib_cython import Geodesic', number=100000)
0.12127103898092173

Gotchas

  • If you get this error when doing import Geodesic:
ImportError: libGeographic.so.17: cannot open shared object file: No such file or directory

This means that Python interpreter can't find the shared library. For some reason, /usr/local/lib is not searched by default. We need to provide it in the LD_LIBRARY_PATH. If you have installed libGeographic.so somewhere else, provide that directory instead.

export LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH}
  • If you get this error when doing import Geodesic:
ImportError: /opt/anaconda/lib/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /usr/local/lib/libGeographic.so.17)

Do this (solution found here):

conda install libgcc
  • If you get this error when doing import Geodesic:
ImportError: /opt/anaconda/lib/python3.5/site-packages/geographiclib_cython.cpython-35m-x86_64-linux-gnu.so: undefined symbol: _ZNK13GeographicLib8Geodesic11Invej

Then you have an incompatible version of libGeographic installed. Check where it's coming from:

ldd /opt/anaconda/lib/python3.5/site-packages/geographiclib_cython.cpython-35m-x86_64-linux-gnu.so
    linux-vdso.so.1 =>  (0x00007fffac2ea000)
    libGeographic.so.9 => /usr/lib/libGeographic.so.9 (0x00007f86ff6bb000)
    ...

Remove it and remove the .whl from the pip cache (do find ~ -name "*.whl" to find out where the cache is):

rm /usr/lib/libGeographic*
rm ~/.cache/pip/wheels/0e/68/e7/7cadf8180052771a12c112ac3cda44363135ecb14cfd57a500/geographiclib_cython_bindings-1.0.0-cp35m-x86_64.whl

Then do pip install again.

Useful links

About

Cython extension module for C++ geographiclib functions

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages