我有一个纯粹的美学层,上面有箭头符号。由于线条太小,有些显示不正确。我已经选择了50条记录,我需要将该行扩展给定的数字(例如2米)。延伸线工具仅将线延伸到指定的交点,因此该工具不是我想要的。
我尝试过编辑形状长度字段,但它不允许我这样做。是否有通过字段计算器或在编辑器工具栏中执行此操作的简单方法?
我有一个纯粹的美学层,上面有箭头符号。由于线条太小,有些显示不正确。我已经选择了50条记录,我需要将该行扩展给定的数字(例如2米)。延伸线工具仅将线延伸到指定的交点,因此该工具不是我想要的。
我尝试过编辑形状长度字段,但它不允许我这样做。是否有通过字段计算器或在编辑器工具栏中执行此操作的简单方法?
Answers:
好吧,我认为我已经将其用于任何顶点数的行。我没有尝试过多行,因为我从来没有在arcpy中弄过它。因为没有对Geometry对象的lastPoint属性的写访问权,所以编码变得更加困难。我没有使用斜率(这是我最初的想法),而是使用了这个SO问题中的代码。它不依赖于三角函数,因此它应该稍微更高效。以下代码通过将线的端点移动到沿着最后两个顶点的直线延长线的新坐标来工作。我在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)
对于基于OID的类别,我将符号系统设置为末尾的箭头,这样可以更轻松地查看要素之间的分隔。设置标签以计算顶点。
如果选择要扩展的行怎么办。
通过所需的扩展量来缓冲这些行。
将其转换为fc行。
然后延伸到路口。
您可能必须断开并删除缓冲区的另一端,以免重叠在中间的行。(我没有看到您拥有或想要做的屏幕截图),
或者我认为ettools中有一个工具(我正在查看功能是否免费)
我在et工具中找不到有用的工具找到一些(旧的)vb代码的线程。和一些Python的要求。您可以关注它并查看idea.arcgis.com网站。
这是一种适用于由任意数量的节点组成的多段折线的方法。它使用开源的GIS Whitebox GAT(http://www.uoguelph.ca/~hydrogeo/Whitebox/)。只需下载Whitebox,打开Scripter(工具栏上的脚本图标),将脚本语言更改为Groovy,将以下代码粘贴到其中,然后将其保存为“ ExtendVectorLines.groovy”。您可以从Scripter中运行它,也可以在下次启动Whitebox时将其显示为Vector Tools工具箱中的插件工具。它以shapefile和扩展距离作为输入。我将在Whitebox GAT的下一个公开发行版中包含该工具。
/*
* 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)
}