Étendre la ligne d'une distance spécifiée dans ArcGIS for Desktop?

11

J'ai une couche purement esthétique qui a des symboles de flèche. Certains ne se présentent pas correctement car la ligne est trop petite. J'ai sélectionné peut-être 50 enregistrements pour lesquels je dois prolonger cette ligne d'un nombre donné (ex. 2 mètres). L'outil Etendre la ligne étend uniquement les lignes à une intersection spécifiée, donc cet outil n'est pas ce que je recherche.

J'ai essayé de modifier le champ de longueur de forme, mais cela ne me le permet pas. Existe-t-il un moyen simple de le faire via la calculatrice de champ ou dans la barre d'outils de l'éditeur?

GISKid
la source
1
les données sont-elles en forme, gdb, fgdb? avez-vous de base, standard, avancé?
Brad Nesom
Forme et avancé.
GISKid
Puis-je clarifier, vouliez-vous étendre chaque entité dans un fichier de formes de type polyligne, ou simplement des entités sélectionnées?
Si vous souhaitez baser votre extension sur le point final, vous pouvez aller au sommet précédent et déterminer la pente entre ces deux points. Ensuite, vous pouvez déplacer le sommet du point final de votre distance x en fonction de ladite pente.
Paul
@Paul, j'écris un script pour faire exactement cela, mais c'est un peu plus complexe car vous devez tenir compte des polylignes en plusieurs parties. Autrement dit, vous devez regarder les points de début et de fin et leurs points voisins pour chaque partie. J'ai besoin de savoir que GISKid souhaite tout d'abord étendre toutes les fonctionnalités.

Réponses:

12

Eh bien, je pense que je l'ai obtenu pour les lignes de tout nombre de sommets. Je n'ai pas essayé de lignes en plusieurs parties car je ne l'ai jamais gâché avec arcpy. Le codage a été rendu un peu plus difficile car il n'y a pas d'accès en écriture à la propriété lastPoint pour les objets Geometry. Au lieu d'utiliser la pente (qui était ma pensée initiale), j'ai utilisé le code de cette question SO . Il ne repose pas sur la trigonométrie, il devrait donc être légèrement plus efficace. Le code suivant fonctionne en déplaçant l'extrémité d'une ligne vers une nouvelle coordonnée située le long du prolongement d'une ligne à partir des deux derniers sommets. Je l'ai testé sur un shapefile.

from math import hypot
import collections
from operator import add
import arcpy

layer = arcpy.GetParameterAsText(0)
distance = float(arcpy.GetParameterAsText(1))

#Computes new coordinates x3,y3 at a specified distance
#along the prolongation of the line from x1,y1 to x2,y2
def newcoord(coords, dist):
    (x1,y1),(x2,y2) = coords
    dx = x2 - x1
    dy = y2 - y1
    linelen = hypot(dx, dy)

    x3 = x2 + dx/linelen * dist
    y3 = y2 + dy/linelen * dist    
    return x3, y3

#accumulate([1,2,3,4,5]) --> 1 3 6 10 15
#Equivalent to itertools.accumulate() which isn't present in Python 2.7
def accumulate(iterable):    
    it = iter(iterable)
    total = next(it)
    yield total
    for element in it:
        total = add(total, element)
        yield total

#OID is needed to determine how to break up flat list of data by feature.
coordinates = [[row[0], row[1]] for row in
               arcpy.da.SearchCursor(layer, ["OID@", "SHAPE@XY"], explode_to_points=True)]

oid,vert = zip(*coordinates)

#Construct list of numbers that mark the start of a new feature class.
#This is created by counting OIDS and then accumulating the values.
vertcounts = list(accumulate(collections.Counter(oid).values()))

#Grab the last two vertices of each feature
lastpoint = [point for x,point in enumerate(vert) if x+1 in vertcounts or x+2 in vertcounts]

#Convert flat list of tuples to list of lists of tuples.
#Obtain list of tuples of new end coordinates.
newvert = [newcoord(y, distance) for y in zip(*[iter(lastpoint)]*2)]    

j = 0
with arcpy.da.UpdateCursor(layer, "SHAPE@XY", explode_to_points=True) as rows:
    for i,row in enumerate(rows):
        if i+1 in vertcounts:            
            row[0] = newvert[j]
            j+=1
            rows.updateRow(row)

J'ai défini la symbologie sur flèche à la fin pour les catégories basées sur OID afin qu'il soit plus facile de voir la séparation entre les fonctionnalités. L'étiquetage a été défini pour compter les sommets.entrez la description de l'image ici

Paul
la source
Cela m'a beaucoup aidé! Cependant, cela serait encore plus utile dans ma situation particulière si le paramètre de distance pouvait être basé sur un champ dans les entités linéaires d'origine. J'ai essayé de l'implémenter moi-même et je sais que je devrais en quelque sorte parcourir les distances dans la ligne "newvert =" mais j'ai du mal à l'implémenter. Si vous pouviez développer votre code pour ce faire, je vous serais très reconnaissant!
GeoJohn
N'oubliez pas de mettre à jour votre vue, si vous exécutiez le script depuis la console Python. Mes lignes sont devenues très longues après plusieurs tentatives "infructueuses".
EikeMike
2

Que faire si vous sélectionnez les lignes que vous souhaitez étendre.
Mettez ces lignes en mémoire tampon en fonction de l'extension souhaitée.
Convertissez cela en une ligne fc.
Prolongez ensuite jusqu'à l'intersection.
Vous devrez peut-être casser et supprimer l'autre extrémité du tampon pour éviter de chevaucher la ligne au milieu. (Je n'ai pas vu de capture d'écran de ce que vous avez ou voulez faire)
Ou je pense qu'il y a un outil dans ettools (je vérifie pour voir la fonctionnalité et si c'est gratuit)
Je n'ai rien trouvé d'utile dans et tools que j'ai fait trouver ce fil pour un (ancien) code vb. et une demande de python. vous pouvez le suivre et consulter le site Web ideas.arcgis.com .

Brad Nesom
la source
2

Voici une méthode qui fonctionne avec des polylignes en plusieurs parties composées d'un nombre quelconque de points de nœud. Il utilise le GIS open source GIS Whitebox ( http://www.uoguelph.ca/~hydrogeo/Whitebox/ ). Téléchargez simplement Whitebox, ouvrez le Scripter (icône de script dans la barre d'outils), changez le langage de script en Groovy, collez le code suivant et enregistrez-le en tant que «ExtendVectorLines.groovy». Vous pouvez l'exécuter à partir du Scripter ou, la prochaine fois que vous lancerez Whitebox, il apparaîtra comme un plugin dans la boîte à outils Vector Tools. Il prend un fichier de formes et une distance d'extension comme entrées. J'inclurai l'outil dans la prochaine version publique de Whitebox GAT.

/*
 * Copyright (C) 2013 Dr. John Lindsay <[email protected]>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.awt.event.ActionListener
import java.awt.event.ActionEvent
import java.io.File
import java.util.concurrent.Future
import java.util.concurrent.*
import java.util.Date
import java.util.ArrayList
import whitebox.interfaces.WhiteboxPluginHost
import whitebox.geospatialfiles.ShapeFile
import whitebox.geospatialfiles.shapefile.*
import whitebox.ui.plugin_dialog.ScriptDialog
import whitebox.utilities.FileUtilities;
import groovy.transform.CompileStatic

// The following four variables are required for this 
// script to be integrated into the tool tree panel. 
// Comment them out if you want to remove the script.
def name = "ExtendVectorLines"
def descriptiveName = "Extend Vector Lines"
def description = "Extends vector polylines by a specified distance"
def toolboxes = ["VectorTools"]

public class ExtendVectorLines implements ActionListener {
private WhiteboxPluginHost pluginHost
private ScriptDialog sd;
private String descriptiveName

public ExtendVectorLines(WhiteboxPluginHost pluginHost, 
    String[] args, def descriptiveName) {
    this.pluginHost = pluginHost
    this.descriptiveName = descriptiveName

    if (args.length > 0) {
        final Runnable r = new Runnable() {
            @Override
            public void run() {
                execute(args)
            }
        }
        final Thread t = new Thread(r)
        t.start()
    } else {
        // Create a dialog for this tool to collect user-specified
        // tool parameters.
        sd = new ScriptDialog(pluginHost, descriptiveName, this)    

        // Specifying the help file will display the html help
        // file in the help pane. This file should be be located 
        // in the help directory and have the same name as the 
        // class, with an html extension.
        def helpFile = "ExtendVectorLines"
        sd.setHelpFile(helpFile)

        // Specifying the source file allows the 'view code' 
        // button on the tool dialog to be displayed.
        def pathSep = File.separator
        def scriptFile = pluginHost.getResourcesDirectory() + "plugins" + pathSep + "Scripts" + pathSep + "ExtendVectorLines.groovy"
        sd.setSourceFile(scriptFile)

        // add some components to the dialog
        sd.addDialogFile("Input file", "Input Vector Polyline File:", "open", "Vector Files (*.shp), SHP", true, false)
        sd.addDialogFile("Output file", "Output Vector File:", "close", "Vector Files (*.shp), SHP", true, false)
        sd.addDialogDataInput("Distance:", "Enter a distance", "", true, false)

        // resize the dialog to the standard size and display it
        sd.setSize(800, 400)
        sd.visible = true
    }
}

// The CompileStatic annotation can be used to significantly
// improve the performance of a Groovy script to nearly 
// that of native Java code.
@CompileStatic
private void execute(String[] args) {
    try {
        int i, f, progress, oldProgress, numPoints, numParts
        int part, startingPointInPart, endingPointInPart
        double x, y, x1, y1, x2, y2, xSt, ySt, xEnd, yEnd, slope;
        ShapefileRecordData recordData;
        double[][] geometry
        int[] partData
        if (args.length != 3) {
            pluginHost.showFeedback("Incorrect number of arguments given to tool.")
            return
        }
        // read the input parameters
        String inputFile = args[0]
        String outputFile = args[1]
        double d = Double.parseDouble(args[2]) // extended distance

        def input = new ShapeFile(inputFile)

        // make sure that input is of a POLYLINE base shapetype
        ShapeType shapeType = input.getShapeType()
        if (shapeType.getBaseType() != ShapeType.POLYLINE) {
            pluginHost.showFeedback("Input shapefile must be of a POLYLINE base shapetype.")
            return
        }

        int numFeatures = input.getNumberOfRecords()

        // set up the output files of the shapefile and the dbf
        ShapeFile output = new ShapeFile(outputFile, shapeType);
        FileUtilities.copyFile(new File(input.getDatabaseFile()), new File(output.getDatabaseFile()));

        int featureNum = 0;
        for (ShapeFileRecord record : input.records) {
            featureNum++;
            PointsList points = new PointsList();
            recordData = getXYFromShapefileRecord(record);
            geometry = recordData.getPoints();
            numPoints = geometry.length;
            partData = recordData.getParts();
            numParts = partData.length;

            for (part = 0; part < numParts; part++) {
                startingPointInPart = partData[part];
                if (part < numParts - 1) {
                    endingPointInPart = partData[part + 1] - 1;
                } else {
                    endingPointInPart = numPoints - 1;
                }

                // new starting poing
                x1 = geometry[startingPointInPart][0]
                y1 = geometry[startingPointInPart][1]

                x2 = geometry[startingPointInPart + 1][0]
                y2 = geometry[startingPointInPart + 1][2]

                if (x1 - x2 != 0) {
                    slope = Math.atan2((y1 - y2) , (x1 - x2))
                    xSt = x1 + d * Math.cos(slope)
                    ySt = y1 + d * Math.sin(slope)
                } else {
                    xSt = x1
                    if (y2 > y1) {
                        ySt = y1 - d
                    } else {
                        ySt = y1 + d
                    }
                }

                // new ending point
                x1 = geometry[endingPointInPart][0]
                y1 = geometry[endingPointInPart][3]

                x2 = geometry[endingPointInPart - 1][0]
                y2 = geometry[endingPointInPart - 1][4]

                if (x1 - x2 != 0) {
                    slope = Math.atan2((y1 - y2) , (x1 - x2))
                    xEnd = x1 + d * Math.cos(slope)
                    yEnd = y1 + d * Math.sin(slope)
                } else {
                    xEnd = x1
                    if (y2 < y1) {
                        yEnd = y1 - d
                    } else {
                        yEnd = y1 + d
                    }
                }

                points.addPoint(xSt, ySt)
                for (i = startingPointInPart; i <= endingPointInPart; i++) {
                    x = geometry[i][0]
                    y = geometry[i][5]
                    points.addPoint(x, y)
                }
                points.addPoint(xEnd, yEnd)

            }

            for (part = 0; part < numParts; part++) {
                partData[part] += part * 2
            }

            switch (shapeType) {
                case ShapeType.POLYLINE:
                    PolyLine line = new PolyLine(partData, points.getPointsArray());
                    output.addRecord(line);
                    break;
                case ShapeType.POLYLINEZ:
                    PolyLineZ polyLineZ = (PolyLineZ)(record.getGeometry());
                    PolyLineZ linez = new PolyLineZ(partData, points.getPointsArray(), polyLineZ.getzArray(), polyLineZ.getmArray());
                    output.addRecord(linez);
                    break;
                case ShapeType.POLYLINEM:
                    PolyLineM polyLineM = (PolyLineM)(record.getGeometry());
                    PolyLineM linem = new PolyLineM(partData, points.getPointsArray(), polyLineM.getmArray());
                    output.addRecord(linem);
                    break;
            }
        }

        output.write();

        // display the output image
        pluginHost.returnData(outputFile)

        // reset the progress bar
        pluginHost.updateProgress(0)
    } catch (Exception e) {
        pluginHost.showFeedback(e.getMessage())
    }
}


@CompileStatic
private ShapefileRecordData getXYFromShapefileRecord(ShapeFileRecord record) {
    int[] partData;
    double[][] points;
    ShapeType shapeType = record.getShapeType();
    switch (shapeType) {
        case ShapeType.POLYLINE:
            whitebox.geospatialfiles.shapefile.PolyLine recPolyLine =
                    (whitebox.geospatialfiles.shapefile.PolyLine) (record.getGeometry());
            points = recPolyLine.getPoints();
            partData = recPolyLine.getParts();
            break;
        case ShapeType.POLYLINEZ:
            PolyLineZ recPolyLineZ = (PolyLineZ) (record.getGeometry());
            points = recPolyLineZ.getPoints();
            partData = recPolyLineZ.getParts();
            break;
        case ShapeType.POLYLINEM:
            PolyLineM recPolyLineM = (PolyLineM) (record.getGeometry());
            points = recPolyLineM.getPoints();
            partData = recPolyLineM.getParts();
            break;
        default: // should never hit this.
            points = new double[1][2];
            points[1][0] = -1;
            points[1][6] = -1;
            break;
    }
    ShapefileRecordData ret = new ShapefileRecordData(points, partData)
    return ret;
}

@CompileStatic
class ShapefileRecordData {
    private final double[][] points
    private final int[] parts
    ShapefileRecordData(double[][] points, int[] parts) {
        this.points = points
        this.parts = parts
    }

    double[][] getPoints() {
        return points
    }

    int[] getParts() {
        return parts
    }

}

@Override
public void actionPerformed(ActionEvent event) {
    if (event.getActionCommand().equals("ok")) {
        final def args = sd.collectParameters()
        sd.dispose()
        final Runnable r = new Runnable() {
            @Override
            public void run() {
                execute(args)
            }
        }
        final Thread t = new Thread(r)
        t.start()
    }
}
}

if (args == null) {
pluginHost.showFeedback("Plugin arguments not set.")
} else {
def f = new ExtendVectorLines(pluginHost, args, descriptiveName)
}

entrez la description de l'image ici

entrez la description de l'image ici


la source
Je viens de modifier le code afin que vous puissiez éventuellement choisir d'étendre les débuts, les fins de ligne ou les deux extrémités. Faites-moi savoir si vous êtes intéressé par l'outil modifié.
Merci de votre aide! Je vais me pencher sur WhiteBox, je n'en ai jamais entendu parler auparavant. Cool de voir que Guelph a de tels projets! Je suis moi-même un étudiant d'UWindsor.
GISKid
Windsor est aussi un excellent endroit! Je viens de publier la dernière version (3.0.5) et elle inclut l'outil mis à jour pour l'extension des lignes. Faites-moi savoir si vous avez des problèmes ou des commentaires pour moi.