Teste de velocidade de projeção de geometria

8

Ultimamente, tenho usado as classes de projeção OGR que acompanham o ogr / gdal, mas o pyproj foi recomendado para mim, então pensei em tentar. Para me ajudar a decidir se devo fazer a troca, fiz um teste de velocidade. A seguir, é apresentado um pequeno exemplo (quase) reproduzível que criei para testar os dois. Não tenho certeza se esse teste é totalmente justo; portanto, comentários e recomendações são bem-vindos!

Importa primeiro, para garantir que começamos com condições equitativas:

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

Como estou armazenando as geometrias bem torneadas em uma 'série' de pandas, as funções precisam funcionar Series.apply(). Aqui, defino duas funções (uma usando 'ogr.osr' e outra usando 'pyproj') para executar transformações de coordenadas dentro de uma chamada para 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 uma dessas funções usa um código EPSG como entrada para os sistemas de referência de coordenadas de origem e destino. Ambas as bibliotecas oferecem formas alternativas de definir informações de projeção, mas os códigos EPSG mantêm o código bastante simples por enquanto.

Aqui estão os resultados, usando a %timeitfunção mágica no 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, a versão 'ogr.osr' é mais rápida, mas é uma comparação justa? A versão 'pyproj' é feita em pontos individuais e é executada principalmente em Python, enquanto a versão 'ogr.osr' opera diretamente no objeto de geometria OGR. Existe uma maneira melhor de comparar estes?

Carson Farmer
fonte

Respostas:

7

Pyproj é uma extensão Python C que usa a biblioteca PROJ4 e osgeo.ogr é uma extensão Python C que usa a biblioteca PROJ4. Se você está considerando apenas a projeção de coordenadas, no teste mais justo, elas serão quase iguais.

A transformação do Pyproj pode operar em matrizes de valores de coordenadas, portanto, você só precisa chamá-lo uma vez por linha ou toque, em vez de para cada par. Isso deve acelerar bastante as coisas. Exemplo: https://gist.github.com/sgillies/3642564#file-2-py-L10 .

(Atualização) Além disso, o Shapely fornece uma função que transforma geometrias no 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
fonte
+1. Também Shapely Pontos, LinearRings e LineStrings ter uma interface de matriz numpy , para que possa fazer algo assimprojected_coords = numpy.vstack(pyproj.transform(origin, target, *numpy.array(geom).T)).T
om_henners
Isso é incrível @sgillies. Por alguma razão, minha versão do shapely não tem transformação? bem torneado .__ versão__: '1.2.17'. Vou tentar pegar a fonte diretamente.
Carson fazendeiro
OPA, desculpe. Chegando na versão 1.2.18 (neste fim de semana, provavelmente).
Sgillies