Przedłużyć linię o określoną odległość w ArcGIS na komputery?

11

Mam czysto estetyczną warstwę, która ma symbole strzałek. Niektóre nie są wyświetlane poprawnie, ponieważ linia jest zbyt mała. Wybrałem może 50 rekordów, w których muszę przedłużyć tę linię o określoną liczbę (np. 2 metry). Narzędzie przedłużania linii rozciąga tylko linie do określonego skrzyżowania, więc to narzędzie nie jest tym, czego szukam.

Próbowałem edytować pole długości kształtu, ale mi na to nie pozwala. Czy istnieje prosty sposób, aby to zrobić za pomocą kalkulatora pola lub paska narzędzi edytora?

GISKid
źródło
1
czy dane mają kształt, gdb, fgdb? czy masz podstawowy, standardowy, zaawansowany?
Brad Nesom,
Kształt i zaawansowany.
GISKid
Czy mogę wyjaśnić, czy chciałeś rozszerzyć każdą funkcję w pliku kształtu typu polilinii, czy tylko wybrane funkcje?
Jeśli chcesz oprzeć swoje rozszerzenie na punkcie końcowym, możesz przejść do poprzedniego wierzchołka i określić nachylenie między tymi dwoma punktami. Następnie możesz przesunąć wierzchołek punktu końcowego na odległość x na podstawie wspomnianego nachylenia.
Paweł
@Paul, piszę skrypt, aby to zrobić, ale jest to trochę bardziej skomplikowane, ponieważ musisz uwzględnić wieloczęściowe polilinie. Oznacza to, że musisz spojrzeć na punkty początkowy i końcowy oraz punkty sąsiednie dla każdej części. Muszę jednak wiedzieć, że GISKid jest zainteresowany rozszerzeniem wszystkich funkcji w pierwszej kolejności.

Odpowiedzi:

12

Cóż, myślę, że mam to dla linii o dowolnej liczbie wierzchołków. Nie próbowałem linii wieloczęściowych, ponieważ nigdy nie pomyliłem się z nimi w trybie arcpy. Kodowanie zostało nieco utrudnione, ponieważ nie ma dostępu do zapisu do właściwości lastPoint dla obiektów Geometry. Zamiast używać nachylenia (co było moją początkową myślą), użyłem kodu z tego pytania SO . Nie opiera się na trygonometrii, więc powinna być nieco bardziej wydajna. Poniższy kod działa poprzez przeniesienie punktu końcowego linii do nowej współrzędnej, która leży wzdłuż przedłużenia linii od dwóch ostatnich wierzchołków. Przetestowałem to na pliku kształtu.

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)

Ustawiłem symbolikę na strzałkę na końcu dla kategorii opartych na OID, aby łatwiej było dostrzec separację między funkcjami. Etykietowanie ustawiono do zliczania wierzchołków.wprowadź opis zdjęcia tutaj

Paweł
źródło
To mi bardzo pomogło! Byłoby to jednak jeszcze bardziej pomocne w mojej szczególnej sytuacji, gdyby parametr odległości mógł być oparty na polu w oryginalnych elementach linii. Próbowałem to zaimplementować sam i wiem, że musiałbym w jakiś sposób iterować przez odległości w linii „newvert =”, ale trudno mi to zaimplementować. Jeśli byłbyś w stanie rozwinąć swój kod, by to zrobić, byłbym bardzo wdzięczny!
GeoJohn
Nie zapomnij zaktualizować widoku, jeśli skrypt był uruchamiany z poziomu konsoli Python. Moje linie stały się naprawdę długie po kilku „nieudanych” próbach.
EikeMike
2

Co zrobić, jeśli dokonasz wyboru linii, które chcesz przedłużyć.
Buforuj te wiersze o żądane rozszerzenie.
Konwertuj to na linię fc.
Następnie rozciągnij się do skrzyżowania.
Być może będziesz musiał złamać i usunąć drugi koniec bufora, aby uniknąć nakładania się linii na środku. (Nie widziałem zrzutu ekranu tego, co masz lub chcesz zrobić).
Albo myślę, że w ettools jest narzędzie (sprawdzam, czy jest funkcjonalność i czy jest bezpłatne).
Nie znalazłem nic użytecznego w narzędziach et, które zrobiłem znajdź ten wątek dla jakiegoś (starego) kodu vb. i prośba o trochę Pythona. możesz to sprawdzić i zajrzeć na stronę ideas.arcgis.com .

Brad Nesom
źródło
2

Oto metoda, która działa z wieloczęściowymi poliliniami złożonymi z dowolnej liczby punktów węzłów. Wykorzystuje open source GIS Whitebox GAT ( http://www.uoguelph.ca/~hydrogeo/Whitebox/ ). Po prostu pobierz Whitebox, otwórz Scripter (ikona skryptu na pasku narzędzi), zmień język skryptów na Groovy, wklej następujący kod i zapisz go jako „ExtendVectorLines.groovy”. Możesz go uruchomić albo z poziomu Scriptera, albo przy następnym uruchomieniu Whitebox pojawi się jako narzędzie do wtyczek w przyborniku Vector Tools. Jako dane wejściowe pobiera plik kształtu i większą odległość. Dołączę to narzędzie do następnej publicznej wersji 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)
}

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj


źródło
Właśnie zmodyfikowałem kod, abyś mógł opcjonalnie przedłużyć początek linii, koniec linii lub oba końce. Daj mi znać, jeśli jesteś zainteresowany zmodyfikowanym narzędziem.
Dzięki za pomoc! Zajrzę do WhiteBox, nigdy wcześniej o tym nie słyszałem. Fajnie jest zobaczyć, że Guelph ma takie projekty! Sam jestem studentem UWindsor.
GISKid
Windsor to także doskonałe miejsce! Właśnie wydałem najnowszą wersję (3.0.5), która zawiera zaktualizowane narzędzie do przedłużania linii. Daj mi znać, jeśli masz jakieś problemy lub opinie.