Comment déplacer facilement toutes les entités d'un jeu de données vectoriel?

33

Supposons que je construise un fichier de formes et que toutes les entités aient leurs sommets décalés de manière constante. Quel est le moyen le plus simple de décaler toutes les entités (d'où la position (x, y) de leurs sommets) d'un décalage arbitraire? J'ai beaucoup de fichiers auxquels j'appliquerais cette correction, donc une réponse Bash / OGR serait préférable :)

Finalement, j'ai utilisé Spatialite pour cela, car il a la fonction intéressante ShiftCoords. Cependant, le fil était très instructif! Merci a tous!

Jose
la source
J'adore cette entrée. Cette page entière est un superbe exemple de Q & R qui a bien tourné. Une question simple, clairement décrite, et chaque réponse donne une solution unique, valide et complète. C'est beau. J'ai voté chacun, chacun pour son mérite.
mat wilkie
@Jose Cet article nécessite une petite mise à jour en raison des améliorations relativement récentes de la bibliothèque GDAL. Il existe maintenant une solution one-liner (voir la réponse ci-dessous)! Il est possible d'utiliser la fonction SpatiaLite ShiftCoords directement avec l'utilitaire ogr2ogr.
Antonio Falciano

Réponses:

21

Utiliser JEQL Cela peut être fait avec trois lignes:

ShapefileReader t file: "shapefile.shp";
out = select * except (GEOMETRY), Geom.translate(GEOMETRY,100,100) from t;
ShapefileWriter out file: "ahapefile_shift.shp";
David Bitner
la source
en pointe! agréable!
WolfOdrade
Nice, David. C'est serré.
sgillies
1
Je dois juste signaler ... "un fichier de poche?"
WolfOdrade
J'ai fini par utiliser la fonction de traduction de Spatialite, qui est similaire à celle que vous suggérez ici.
Jose
30

J'ai conçu Fiona (un wrapper OGR) pour simplifier ce type de traitement.

from fiona import collection
import logging

log = logging.getLogger()

# A few functions to shift coords. They call eachother semi-recursively.
def shiftCoords_Point(coords, delta):
    # delta is a (delta_x, delta_y [, delta_y]) tuple
    return tuple(c + d for c, d in zip(coords, delta))

def shiftCoords_LineString(coords, delta):
    return list(shiftCoords_Point(pt_coords, delta) for pt_coords in coords)

def shiftCoords_Polygon(coords, delta):
    return list(
        shiftCoords_LineString(ring_coords, delta) for ring_coords in coords)

# We'll use a map of these functions in the processing code below.
shifters = {
    'Point': shiftCoords_Point,
    'LineString': shiftCoords_LineString,
    'Polygon': shiftCoords_Polygon }

# Example 2D shift, 1 unit eastward and northward
delta = (1.0, 1.0)

with collection("original.shp", "r") as source:

    # Create a sink for processed features with the same format and 
    # coordinate reference system as the source.
    with collection(
            "shifted.shp", 
            "w",
            driver=source.driver,
            schema=source.schema,
            crs=source.crs
            ) as sink:

        for rec in source:
            try:
                g = rec['geometry']
                g['coordinates'] = shifters[g['type']](
                    g['coordinates'], delta )
                rec['geometry'] = g
                sink.write(rec)
            except Exception, e:
                log.exception("Error processing record %s:", rec)

Mise à jour : j'ai mis une version différente et plus précise de ce script à l' adresse http://sgillies.net/blog/1128/geoprocessing-for-hip-translating-features .

sgillies
la source
2
"Le géotraitement pour les hipsters" Je souhaiterais pouvoir passer au moins 10 fois ce super titre
Ragi Yaser Burhum
13

Et bien que le message soit tagué avec python, JEQL ayant déjà été mentionné, voici un exemple avec JavaScript (en utilisant GeoScript ).

/**
 * Shift all coords in all features for all layers in some directory
 */

var Directory = require("geoscript/workspace").Directory;
var Layer = require("geoscript/layer").Layer;

// offset for all geometry coords
var dx = dy = 10;

var dir = Directory("./data");
dir.names.forEach(function(name) {
    var orig = dir.get(name);
    var shifted = Layer({
        schema: orig.schema.clone({name: name + "-shifted"})
    });
    orig.features.forEach(function(feature) {
        var clone = feature.clone();
        clone.geometry = feature.geometry.transform({dx: dx, dy: dy});
        shifted.add(clone);
    });
    dir.add(shifted);
});
Tim Schaub
la source
13

Utilisation de GDAL> = 1.10.0 compilé avec SQLite et SpatiaLite:

ogr2ogr data_shifted.shp data.shp -dialect sqlite -sql "SELECT ShiftCoords(geometry,1,10) FROM data"

où shiftX = 1 et shiftY = 10.

Antonio Falciano
la source
1
Brilliant - une solution CLI simple sur une ligne.
Dave X
court et facile!
Kurt
8

Le module GRASS GIS v.edit :

Un emplacement et un jeu de cartes existants dans la projection correspondante sont supposés.

Dans un script shell:

#!/bin/bash

for file in `ls | grep \.shp$ | sed 's/\.shp$//g'`
do
    v.in.ogr dsn=./${file}.shp output=$file
    v.edit map=$file tool=move move=1,1 where="1=1"
    v.out.ogr input=$file type=point,line,boundary,area dsn=./${file}_edit.shp
done

ou dans un script Python:

#!/usr/bin/env python

import os
from grass.script import core as grass

for file in os.listdir("."):
    if file.endswith(".shp"):
        f = file.replace(".shp","")
        grass.run_command("v.in.ogr", dsn=file, output=f)
        grass.run_command("v.edit", map=f, tool="move", move="1,1", where="1=1")
        grass.run_command("v.out.ogr", input=f, type="point,line,boundary,area", dsn="./%s_moved.shp" % f)
Webrian
la source
8

Une autre option consisterait à utiliser les options de reprojection simplement dans ogr2ogr, une approche certainement plus astucieuse que les approches JEQL, Fiona ou GeoScript, mais néanmoins efficace. Notez que les projections de et vers n'ont pas vraiment besoin d'être la projection réelle du fichier de formes original tant que la seule chose qui change entre les projections utilisées dans les s_srs et les t_srs est le faux est et nord. Dans cet exemple, je n'utilise que Google Mercator. Je suis sûr qu'il y a un système de coordonnées beaucoup plus simple à utiliser comme base, mais celui-ci était juste devant moi pour copier / coller.

ogr2ogr -s_srs EPSG:900913 -t_srs 'PROJCS["Google Mercator",GEOGCS["WGS 84",DATUM["World Geodetic System 1984",SPHEROID["WGS 84",6378137.0,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0.0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.017453292519943295],AXIS["Geodetic latitude",NORTH],AXIS["Geodetic longitude",EAST],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["semi_minor",6378137.0],PARAMETER["latitude_of_origin",0.0],PARAMETER["central_meridian",0.0],PARAMETER["scale_factor",1.0],PARAMETER["false_easting",1000.0],PARAMETER["false_northing",1000.0],UNIT["m",1.0],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","900913"]]' -f "ESRI Shapefile" shift.shp original.shp

Ou pour enregistrer en tapant / collant, enregistrez ce qui suit dans projcs.txt(comme ci-dessus, mais en supprimant les guillemets simples):

-s_srs EPSG:900913 -t_srs PROJCS["Google Mercator",GEOGCS["WGS 84",DATUM["World Geodetic System 1984",SPHEROID["WGS 84",6378137.0,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0.0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.017453292519943295],AXIS["Geodetic latitude",NORTH],AXIS["Geodetic longitude",EAST],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["semi_minor",6378137.0],PARAMETER["latitude_of_origin",0.0],PARAMETER["central_meridian",0.0],PARAMETER["scale_factor",1.0],PARAMETER["false_easting",1000.0],PARAMETER["false_northing",1000.0],UNIT["m",1.0],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","900913"]]

puis lancez:

ogr2ogr --optfile projcs.txt shifted.shp input.shp
David Bitner
la source
2
Cela tourne au géolocalisation du golf! La prochaine étape consisterait à pirater votre table EPSG pour éliminer les longs projets littéraux PROJCS;)
sgillies
@sgillies, inutile de pirater epsg, enregistrez simplement les projets dans un fichier et utilisez-les --optfile, par exemple ogr2ogr --optfile projcs.txt shifted.shp input.shp. Je vais le incorporer dans la réponse.
Matt Wilkie
7

Une option R utilisant le paquet maptools et sa fonction elide:

shift.xy <- c(1, 2)
library(maptools)
files <- list.files(pattern = "shp$")
for (fi in files) {
  xx <- readShapeSpatial(fi)
  ## update the geometry with elide arguments
  shifted <- elide(xx, shift = shift.xy)
  ## write out a new shapfile
  writeSpatialShape(shifted, paste("shifted", fi, sep = ""))
}
mdsumner
la source
4

En utilisant l’analyseur de fichier de formes dans les fonctions géographiques, vous pouvez utiliser XSLT pour effectuer le processus. Bien sûr, vous auriez besoin de reconvertir le fichier de formes après :-).

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0" xmlns:gml="http://www.opengis.net/gml">
    <xsl:param name="x_shift" select="0.0"/>
    <xsl:param name="y_shift" select="0.0"/>

    <!-- first the identity transform makes sure everything gets copied -->
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <!-- for any element with coordinate strings, apply the translation factors -->
    <!-- note that a schema-aware processor could use the schema type names to simplify -->
    <xsl:template match="gml:pos|gml:posList|gml:lowerCorner|gml:upperCorner">
        <xsl:copy>
            <!-- this xpath parses the ordinates, assuming x y ordering (shapefiles), applies translation factors -->
            <xsl:value-of select="
                for $i in tokenize(.,'\s+') return 
                  if ($i[(position() mod 2) ne 0]) then 
                    number($i)+$x_shift 
                  else 
                    number($i)+$y_shift
             "/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
Peter Rushforth
la source
4

Voici une version de Groovy GeoScript:

import geoscript.workspace.Directory
import geoscript.layer.Layer

int dx = 10
int dy = 10

def dir = new Directory("./data")
dir.layers.each{name ->
    def orig = dir.get(name)
    def shifted = dir.create("${name}-shifted", orig.schema.fields)
    shifted.add(orig.cursor.collect{f ->
        f.geom = f.geom.translate(dx, dy)
        f
    })
}  
jericks
la source
0

Voici la version OGR

driver = ogr.GetDriverByName ("Fichier de formes ESRI")

def move (dx, dy, dz):

dataSource = driver.Open(path,1)
layer = dataSource.GetLayer(0)
for feature in layer:
    get_poly = feature.GetGeometryRef()
    get_ring = get_poly.GetGeometryRef(0)
    points   = get_ring.GetPointCount()
    set_ring = ogr.Geometry(ogr.wkbLinearRing)
    for p in xrange(points):
        x,y,z = get_ring.GetPoint(p)
        x += dx
        y += dy
        z += dz
        set_ring.AddPoint(x,y)
        print x,y,z
set_poly = ogr.Geometry(ogr.wkbPolygon)
set_poly.AddGeometry(set_ring)

feature.SetGeometry(set_poly)
layer.SetFeature(feature)

del layer
del feature
del dataSource   
Moshe Yaniv
la source