Geometrieprojektionsgeschwindigkeitstest

8

In letzter Zeit habe ich die OGR-Projektionsklassen verwendet, die mit ogr / gdal geliefert werden, aber pyproj wurde mir empfohlen, also dachte ich, ich würde es versuchen. Um zu entscheiden, ob ich wechseln soll, habe ich einen Geschwindigkeitstest durchgeführt. Das Folgende ist ein kleines (fast) reproduzierbares Beispiel, das ich mir ausgedacht habe, um die beiden zu testen. Ich bin mir nicht sicher, ob dieser Test völlig fair ist, daher sind Kommentare und Empfehlungen willkommen!

Importiert zuerst, um sicherzustellen, dass wir mit gleichen Wettbewerbsbedingungen beginnen:

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

Da ich die formschönen Geometrien in einer Pandas-Serie speichere, müssen die Funktionen funktionieren Series.apply(). Hier definiere ich zwei Funktionen (eine mit 'ogr.osr' und eine mit 'pyproj'), um Koordinatentransformationen innerhalb eines Aufrufs von durchzuführen 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)

Jede dieser Funktionen verwendet einen EPSG-Code als Eingabe für die Referenzsysteme für Ursprungs- und Zielkoordinaten. Beide Bibliotheken bieten alternative Möglichkeiten zum Definieren von Projektionsinformationen, aber EPSG-Codes halten den Code vorerst recht einfach.

Hier sind die Ergebnisse unter Verwendung der %timeitmagischen Funktion in 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

Natürlich ist die 'ogr.osr'-Version schneller, aber ist es ein fairer Vergleich? Die 'pyproj'-Version wird an einzelnen Punkten erstellt und meistens in Python ausgeführt, während die' ogr.osr'-Version direkt auf das OGR-Geometrieobjekt angewendet wird. Gibt es eine bessere Möglichkeit, diese zu vergleichen?

Carson Farmer
quelle

Antworten:

7

Pyproj ist eine Python C-Erweiterung, die die PROJ4-Bibliothek verwendet, und osgeo.ogr ist eine Python C-Erweiterung, die die PROJ4-Bibliothek verwendet. Wenn Sie nur eine Koordinatenprojektion in Betracht ziehen, sind sie im fairsten Test fast gleich.

Die Pyproj-Transformation kann mit Arrays von Koordinatenwerten ausgeführt werden, sodass Sie sie nur einmal pro Zeile oder Ring anstatt für jedes Paar aufrufen müssen. Dies sollte die Dinge ziemlich beschleunigen. Beispiel: https://gist.github.com/sgillies/3642564#file-2-py-L10 .

(Update) Außerdem bietet Shapely eine Funktion, die Geometrien in 1.2.16 transformiert:

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
quelle
+1. Auch Shapely Points, LinearRings und LineStrings haben eine numpy Array-Schnittstelle , so dass Sie so etwas wieprojected_coords = numpy.vstack(pyproj.transform(origin, target, *numpy.array(geom).T)).T
om_henners
Das ist großartig @sgillies. Aus irgendeinem Grund hat meine Version von Shapely keine Transformation? formschön .__ version__: '1.2.17'. Ich werde versuchen, die Quelle direkt zu greifen.
Carson Farmer
Ups, tut mir Leid. Kommt in Version 1.2.18 (dieses Wochenende wahrscheinlich).
Sgillies