Zeile in ArcGIS for Desktop um den angegebenen Abstand erweitern?


11

Ich habe eine rein ästhetische Schicht mit Pfeilsymbolen. Einige werden nicht richtig angezeigt, weil die Linie zu klein ist. Ich habe vielleicht 50 Datensätze ausgewählt, bei denen ich diese Zeile um eine bestimmte Zahl (z. B. 2 Meter) erweitern muss. Das Werkzeug "Linie erweitern" erweitert Linien nur bis zu einem bestimmten Schnittpunkt. Daher ist dieses Werkzeug nicht das, wonach ich suche.

Ich habe versucht, das Feld für die Formlänge zu bearbeiten, aber es lässt mich nicht. Gibt es eine einfache Möglichkeit, dies über den Feldrechner oder in der Editor-Symbolleiste zu tun?


1
Sind die Daten in Form, gdb, fgdb? Haben Sie Basic, Standard, Advanced?
Brad Nesom

Form und fortgeschritten.
GISKid

Kann ich klarstellen, ob Sie jedes Feature in einem Shapefile vom Typ Polylinie oder nur ausgewählte Features erweitern möchten?

Wenn Sie Ihre Erweiterung auf dem Endpunkt basieren möchten, können Sie zum vorherigen Scheitelpunkt gehen und die Steigung zwischen diesen beiden Punkten bestimmen. Dann können Sie den Endpunktscheitelpunkt um Ihre Entfernung x basierend auf der Steigung verschieben.
Paul

@Paul, ich schreibe ein Skript, um genau das zu tun, aber es ist etwas komplexer, weil Sie mehrteilige Polylinien berücksichtigen müssen. Das heißt, Sie müssen die Start- und Endpunkte und ihre Nachbarpunkte für jedes Teil betrachten. Ich muss wissen, dass GISKid zuerst daran interessiert ist, alle Funktionen zu erweitern.

Antworten:


12

Nun, ich glaube, ich habe es für Linien mit beliebiger Scheitelpunktzahl aufgeschrieben. Ich habe keine mehrteiligen Leitungen versucht, da ich noch nie in Arcpy damit herumgespielt habe. Die Codierung wurde etwas erschwert, da für Geometrieobjekte kein Schreibzugriff auf die lastPoint-Eigenschaft besteht. Anstatt die Steigung zu verwenden (was mein erster Gedanke war), habe ich den Code aus dieser SO-Frage verwendet . Es basiert nicht auf Trigonometrie, daher sollte es etwas effizienter sein. Der folgende Code verschiebt den Endpunkt einer Linie auf eine neue Koordinate, die entlang der Verlängerung einer Linie von den letzten beiden Eckpunkten liegt. Ich habe es auf einem Shapefile getestet.

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)

Ich habe die Symbologie für Kategorien, die auf OID basieren, auf Pfeil am Ende gesetzt, damit die Trennung zwischen Features leichter erkennbar ist. Die Beschriftung wurde so eingestellt, dass Scheitelpunkte gezählt werden.Geben Sie hier die Bildbeschreibung ein


Das hat mir sehr geholfen! Dies wäre jedoch in meiner speziellen Situation noch hilfreicher, wenn der Abstandsparameter auf einem Feld in den ursprünglichen Linienmerkmalen basieren könnte. Ich habe versucht, dies selbst zu implementieren und weiß, dass ich die Entfernungen in der Zeile "newvert =" irgendwie durchlaufen müsste, aber es fällt mir schwer, es zu implementieren. Wenn Sie Ihren Code dazu erweitern könnten, wäre ich Ihnen sehr dankbar!
GeoJohn

Vergessen Sie nicht, Ihre Ansicht zu aktualisieren, wenn Sie das Skript über die Python-Konsole ausgeführt haben. Meine Zeilen wurden nach mehreren "erfolglosen" Versuchen sehr lang.
EikeMike

2

Was ist, wenn Sie eine Auswahl der Linien treffen, die Sie erweitern möchten?
Puffern Sie diese Zeilen mit dem gewünschten Erweiterungsbetrag.
Konvertieren Sie das in eine Zeile fc.
Dann bis zur Kreuzung verlängern.
Möglicherweise müssen Sie das andere Ende des Puffers unterbrechen und löschen, um zu verhindern, dass sich die Zeile in der Mitte überlappt. (Ich habe nicht einen Screenshot von dem, was Sie haben oder tun wollen gesehen)
Oder ich denke , es ist ein Werkzeug in ettools (ich überprüft Funktionalität , um zu sehen , und wenn es frei ist)
Es wurde leider nichts nützlich et Tools finde ich tat Finde diesen Thread für einen (alten) VB-Code. und eine Anfrage für etwas Python. Sie können dem folgen und die Website ideas.arcgis.com überprüfen .


2

Hier ist eine Methode, die mit mehrteiligen Polylinien funktioniert, die aus einer beliebigen Anzahl von Knotenpunkten bestehen. Es verwendet das Open Source GIS Whitebox GAT ( http://www.uoguelph.ca/~hydrogeo/Whitebox/ ). Laden Sie einfach Whitebox herunter, öffnen Sie den Scripter (Skriptsymbol in der Symbolleiste), ändern Sie die Skriptsprache in Groovy, fügen Sie den folgenden Code ein und speichern Sie ihn als 'ExtendVectorLines.groovy'. Sie können es entweder über den Scripter ausführen oder beim nächsten Start von Whitebox als Plugin-Tool in der Vector Tools-Toolbox anzeigen. Es werden ein Shapefile und eine größere Entfernung als Eingaben verwendet. Ich werde das Tool in die nächste öffentliche Version von Whitebox GAT aufnehmen.

/*
 * Copyright (C) 2013 Dr. John Lindsay <jlindsay@uoguelph.ca>
 *
 * 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)
}

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein


Ich habe den Code so geändert, dass Sie optional Zeilenanfänge, Zeilenenden oder beide Enden verlängern können. Lassen Sie mich wissen, wenn Sie an dem modifizierten Tool interessiert sind.

Danke für Ihre Hilfe! Ich werde in WhiteBox schauen, ich habe noch nie davon gehört. Cool zu sehen, dass Guelph solche Projekte hat! Ich bin selbst UWindsor-Student.
GISKid

Windsor ist auch ein ausgezeichneter Ort! Ich habe gerade die neueste Version (3.0.5) veröffentlicht und sie enthält das aktualisierte Tool zum Erweitern von Linien. Lassen Sie mich wissen, wenn Sie Probleme oder Feedback für mich haben.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.