Prueba de velocidad de proyección de geometría

8

Últimamente he estado usando las clases de proyección OGR que vienen con ogr / gdal, pero me recomendaron pyproj, así que pensé en probarlo. Para ayudarme a decidir si debería hacer el cambio, hice una prueba de velocidad. El siguiente es un pequeño (casi) ejemplo reproducible que se me ocurrió para probar los dos. No estoy seguro de si esta prueba es totalmente justa, ¡así que los comentarios y recomendaciones son bienvenidos!

Importa primero, para asegurarnos de comenzar con un campo de juego nivelado:

from pandas import Series # This is what our geometries are stored in
from shapely import wkb
import functools
from shapely.geometry import shape
from osgeo import ogr
# The following two imports are the important ones
from pyproj import Proj, transform
from osgeo.osr import SpatialReference, CoordinateTransformation

Debido a que estoy almacenando las geometrías bien formadas en una 'Serie' de pandas, las funciones deben funcionar Series.apply(). Aquí defino dos funciones (una usando 'ogr.osr' y otra usando 'pyproj') para realizar transformaciones de coordenadas dentro de una llamada a Series.apply():

def transform_osr(geoms, origin, target):
    target_crs = SpatialReference()
    target_crs.ImportFromEPSG(origin)
    origin_crs = SpatialReference()
    origin_crs.ImportFromEPSG(origin)
    transformer = CoordinateTransformation(origin_crs, target_crs)
    def trans(geom):
        g = ogr.CreateGeometryFromWkb(geom.wkb)
        if g.Transform(transformer):
            raise Exception("Ahhhh!")
        g = wkb.loads(g.ExportToWkb())
        return g
    return geoms.apply(trans)

def transform_pyproj(geoms, origin, target):
    target = Proj(init="epsg:%s" % target)
    origin = Proj(init="epsg:%s" % origin)
    transformer = functools.partial(transform, origin, target)
    def trans(geom):
        interface = geom.__geo_interface__
        geom_type = interface['type']
        coords = interface['coordinates']
        result = apply_to_coord_pairs(transformer, coords)
        return shape({'coordinates':result, 'type':geom_type})
    def apply_to_coord_pairs(fun, geom):
        return [not all([hasattr(y, "__iter__") for y in x]) and \
                fun(*x) or apply_to_coord_pairs(fun, x) for x in geom]
    return geoms.apply(trans)

Cada una de estas funciones toma un código EPSG como entrada para los sistemas de referencia de coordenadas de origen y destino. Ambas bibliotecas ofrecen formas alternativas de definir información de proyección, pero los códigos EPSG mantienen el código bastante simple por ahora.

Aquí están los resultados, usando la %timeitfunción mágica en ipython:

In [1]: %timeit transform_pyproj(l, 29903, 4326)
1 loops, best of 3: 641 ms per loop

In [2]: %timeit transform_osr(l, 29903, 4326)
10 loops, best of 3: 27.4 ms per loop

Claramente, la versión 'ogr.osr' es más rápida, pero ¿es una comparación justa? La versión 'pyproj' se realiza en puntos individuales y se ejecuta principalmente en Python, mientras que la versión 'ogr.osr' opera directamente en el objeto de geometría OGR. ¿Hay una mejor manera de comparar esto?

Carson Farmer
fuente

Respuestas:

7

Pyproj es una extensión de Python C que usa la biblioteca PROJ4 y osgeo.ogr es una extensión de Python C que usa la biblioteca PROJ4. Si solo está considerando la proyección de coordenadas, en la prueba más justa serían casi iguales.

La transformación de Pyproj puede operar en matrices de valores de coordenadas, por lo que solo necesita llamarla una vez por línea o anillo en lugar de por cada par. Esto debería acelerar las cosas un poco. Ejemplo: https://gist.github.com/sgillies/3642564#file-2-py-L10 .

(Actualización) Además, Shapely proporciona una función que transforma las geometrías en 1.2.16:

Help on function transform in module shapely.ops:

transform(func, geom)
    Applies `func` to all coordinates of `geom` and returns a new
    geometry of the same type from the transformed coordinates.

    `func` maps x, y, and optionally z to output xp, yp, zp. The input
    parameters may iterable types like lists or arrays or single values.
    The output shall be of the same type. Scalars in, scalars out.
    Lists in, lists out.

    For example, here is an identity function applicable to both types
    of input.

      def id_func(x, y, z=None):
          return tuple(filter(None, [x, y, z]))

      g2 = transform(id_func, g1)

    A partially applied transform function from pyproj satisfies the
    requirements for `func`.

      from functools import partial
      import pyproj

      project = partial(
          pyproj.transform,
          pyproj.Proj(init='espg:4326'),
          pyproj.Proj(init='epsg:26913'))

      g2 = transform(project, g1)

    Lambda expressions such as the one in

      g2 = transform(lambda x, y, z=None: (x+1.0, y+1.0), g1)

    also satisfy the requirements for `func`.
sgillies
fuente
+1. También Shapely Points, LinearRings y LineStrings tienen una interfaz de matriz numpy , por lo que puede hacer algo comoprojected_coords = numpy.vstack(pyproj.transform(origin, target, *numpy.array(geom).T)).T
om_henners
Esto es increíble @sgillies. ¿Por alguna razón mi versión de Shapely no tiene transformación? bien formado .__ versión__: '1.2.17'. Intentaré agarrar la fuente directamente.
Carson Farmer
Ups, lo siento. Próximamente en la versión 1.2.18 (este fin de semana, probablemente).
sgillies