Test de vitesse de projection géométrique

8

Dernièrement, j'utilise les classes de projection OGR fournies avec ogr / gdal, mais pyproj m'a été recommandé, alors j'ai pensé essayer. Pour m'aider à décider si je devais faire le changement, j'ai fait un test de vitesse. Ce qui suit est un petit exemple (presque) reproductible que j'ai trouvé pour tester les deux. Je ne sais pas si ce test est totalement équitable, donc les commentaires et recommandations sont les bienvenus!

Les importations d'abord, pour être sûrs de commencer sur un pied d'égalité:

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

Parce que je stocke les géométries galbées dans une «série» de pandas, les fonctions doivent fonctionner Series.apply(). Ici, je définis deux fonctions (une utilisant 'ogr.osr' et une utilisant 'pyproj') pour effectuer des transformations de coordonnées dans un appel à 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)

Chacune de ces fonctions prend un code EPSG comme entrée pour les systèmes de référence de coordonnées d'origine et de destination. Les deux bibliothèques offrent des moyens alternatifs pour définir les informations de projection, mais les codes EPSG gardent le code assez simple pour l'instant.

Voici les résultats, en utilisant la %timeitfonction magique dans 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

De toute évidence, la version «ogr.osr» est plus rapide, mais est-ce une comparaison équitable? La version «pyproj» se fait sur des points individuels et est principalement exécutée en Python, tandis que la version «ogr.osr» opère directement sur l'objet géométrique OGR. Y a-t-il une meilleure façon de comparer ces derniers?

Carson Farmer
la source

Réponses:

7

Pyproj est une extension Python C qui utilise la bibliothèque PROJ4 et osgeo.ogr est une extension Python C qui utilise la bibliothèque PROJ4. Si vous ne considérez que la projection de coordonnées, dans le test le plus juste, elles seraient presque égales.

La transformation de Pyproj peut fonctionner sur des tableaux de valeurs de coordonnées, vous n'avez donc qu'à l'appeler une fois par ligne ou anneau au lieu de pour chaque paire. Cela devrait accélérer un peu les choses. Exemple: https://gist.github.com/sgillies/3642564#file-2-py-L10 .

(Mise à jour) De plus, Shapely fournit une fonction qui transforme les géométries dans 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
la source
+1. Shapely Points, LinearRings et LineStrings ont également une interface de tableau numpy , vous pouvez donc faire quelque chose commeprojected_coords = numpy.vstack(pyproj.transform(origin, target, *numpy.array(geom).T)).T
om_henners
C'est génial @sgillies. Pour une raison quelconque, ma version de galbée n'a pas de transformation? galbée .__ version__: '1.2.17'. Je vais essayer de saisir la source directement.
Carson Farmer
Oops désolé. À venir dans la version 1.2.18 (ce week-end, probablement).
sgillies