我已经使用样条线工具手动绘制了一些波形,但是这很浪费时间。
如果可能的话,我想画些类似的东西:
Inkscape
Function Plotter
(曲线sin(x)
)。#1 楼
我提出了使用PyQGIS的解决方案。它适用于Linestring和MultiLineString图层。此解决方案基于半圆形环的创建,因此您需要设置直径的值(即下面代码中的
step
变量)。您选择的步骤将不是真正使用的步骤,因为它是根据行长进行调整的(但实际上与初始设置的值相似)。您需要进行一些尝试才能找到step
变量的最佳值。代码还需要第二个(可选)参数(称为
crv_angle
),该参数有助于减小或增大环的曲率。 (我对此进行了一些测试,因此我建议保留默认的45度角,因为它会导致真正的圆环。)您只需要从Python控制台运行以下代码:
from math import sin, cos, radians
step = 3 # choose the proper value (e.g. meters or degrees) with reference to the CRS used
crv_angle = 45 # degrees
def segment(polyline):
for x in range(0, len(polyline) - 1):
first_point = polyline[x]
second_point = polyline[x +1]
seg = QgsGeometry.fromPolyline([first_point, second_point])
tmp_azim = first_point.azimuth(second_point)
len_feat = seg.length()
parts = int(len_feat/step)
real_step = len_feat/parts # this is the real step applied
points = []
current = 0
up = True
while current < len_feat:
if up:
round_angle = radians(90 - (tmp_azim - crv_angle))
up = False
else:
round_angle = radians(90 - (tmp_azim + crv_angle))
up = True
first = seg.interpolate(current)
coord_x, coord_y = (first.asPoint().x(), first.asPoint().y())
p1=QgsPointV2(coord_x, coord_y)
dist_x, dist_y = ((real_step*sin(rad_crv_angle))* cos(round_angle), (real_step*sin(rad_crv_angle)) * sin(round_angle))
p2 = QgsPointV2(coord_x + dist_x, coord_y + dist_y)
points.extend([p1, p2])
current += real_step
second = seg.interpolate(current + real_step)
p3=QgsPointV2(second.asPoint().x(), second.asPoint().y())
points.append(p3)
circularRing = QgsCircularStringV2()
circularRing.setPoints(points) # set points for circular rings
fet = QgsFeature()
fet.setGeometry(QgsGeometry(circularRing))
prov.addFeatures([fet])
layer = iface.activeLayer() # load the input layer as you want
crs = layer.crs().toWkt()
rad_crv_angle = radians(crv_angle)
# Create the output layer
outLayer = QgsVectorLayer('Linestring?crs='+ crs, 'wiggly_line' , 'memory')
prov = outLayer.dataProvider()
fields = layer.pendingFields()
prov.addAttributes(fields)
outLayer.updateFields()
for feat in layer.getFeatures():
geom = feat.geometry()
polyline = geom.asPolyline()
segment(polyline)
# Add the layer to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)
,它将使用以下命令创建一个新的行存储层预期的结果:
评论
哇!我不能停止玩这个,太华丽了。最重要的是,输出本身可以用于进一步的操作,例如缓冲区。谢谢@mgri。最后一件事,有时我会看到在顶点处或顶点附近出现小圆圈。这是可以避免的吗?我可以通过使用Node Tool删除该圆的中心节点来删除它们(所以没什么大不了的)。
– Kazuhito
17年5月27日在5:20
@Kazuhito,请参阅我编辑的代码。离散化似乎出了点问题,我希望现在可以解决。我进行了几次测试,看来效果很好(代码也更具可读性)。
–mgri
17年5月27日在14:16
非常感谢@mgri。有些圈子很小,很抱歉,我无法清楚解释这些圈子的外观。现在,大多数顶点都是“无圆”的。我注意到的唯一区别是该节点(带圆圈)在“节点工具”的“顶点编辑器”表上显示了较小的“ r值”。这很容易管理,并且比我预期的要好得多。再次感谢你。让我接受您的答案作为解决方案。
– Kazuhito
17年5月27日在17:53
谢谢@Kazuhito。对不起,您还没有创建一个完美的解决方案。但是,我希望您会喜欢它(否则,您也可以向我发送一个shapefile示例文件,我将尝试解决此问题)。如果我找到一种更有效的方法,我会发布它!
–mgri
17年5月27日在22:36
非常感谢@mgri。如果我在圆圈的外观上发现任何有特色的图案,将以可复制的示例向您更新。这太令人愉快了(它的输出很漂亮)!
– Kazuhito
17年5月28日在1:59
#2 楼
简短的答案:您可以使用自定义SVG来获取它。长答案:
我相信代表它比修改线的几何形状更好。如果要在几何图形上移动边缘或执行其他操作,管理摆动是否只是几何图形的一部分而不是直线的表示将是一场噩梦。
您可以玩风格标记线。有一种方法可以轻松地接近您所需要的内容,而付出更多的努力,就有可能准确地获得它。
要实现这一点,您可以对线条进行样式设置有两条标记线。
每条标记线都由一个简单的标记(半圆)组成。第一个旋转180。两个都设置为透明。
在“标记”行上,指示其中一个偏移,这样两个符号不会在彼此的前面绘制,而是在侧面并排。如果使用offest = 1/2 *间隔大小,则输出将是正弦曲线。我建议您使用间隔大小,偏移量和符号大小。
这种方法的主要局限性是半圆的直径线,其总和为原始线。如果您的背景是白色(或任何纯色),则可以使用背景色添加第3条简单线。
**编辑**
摆脱中心线的另一种方法是创建一个新的SVG符号。我修改了半曲线,只保留了圆形部分。它可以工作,尽管1/2椭圆可能更有吸引力。屏幕快照是使用符号大小10(间隔4,偏移量2)完成的。
将下面的代码保存在文件half_circle_line.svg中,并确保svg的路径在
QGIS // Settings / Options / System / SVG Paths
中设置<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="11.2889mm" height="11.2889mm"
viewBox="0 0 32 32"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny">
<title>Qt Svg Document</title>
<desc>Generated with Qt</desc>
<defs>
</defs>
<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >
<g fill="#ffffff" fill-opacity="0" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(1,0,0,1,0,0)"
font-family="MS Shell Dlg 2" font-size="8.25" font-weight="400" font-style="normal"
>
<path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M19.1181,16 C19.1181,16 19.1181,14.2779 17.7221,12.8819 16,12.8819 C14.2779,12.8819 12.8819,14.2779 12.8819,16"/>
</g>
</g>
</svg>
评论
好主意。目前正在努力与持久的“中心线” ...:\
– Kazuhito
17年5月26日下午14:57
我也+1。同时,我正在尝试考虑PyQGIS解决方案。 @Kazuhito,请让我知道这是否对您足够或者您更喜欢物理解决方案。
–mgri
17年5月26日14:58
@mgri有了这个答案,我现在有了一个“链”而不是“波浪”(试图对其进行修改)。非常希望有一个物理解决方案。
– Kazuhito
17年5月26日在15:05
JGH除了用白线(即您的底图)遮盖外,您是否有删除“中线”的想法?看起来好像是零散的。
– Kazuhito
17年5月26日在15:22
@Kazuhito-您可以将Pen样式更改为No Pen :)
–约瑟夫
17年5月26日在15:27
#3 楼
在QGIS 3.10中,可以借助必不可少的“几何图形生成器”和自定义的Python表达式函数来动态创建之字形线和波形线。class =“ lang-python prettyprint-override”>
from qgis.core import qgsfunction,QgsExpressionContextUtils,QgsExpression,QgsProject,QgsPoint,QgsGeometry
@qgsfunction(args='auto', group='Custom', usesGeometry=False, referencedColumns=[])
def make_zigzagline(geom,dist,offset,feature,parent):
"""
<style>
span { color: red }
</style>
<h2>converts a linestring to a zig-zag line</h2>
make_zigzagline(<span>geometry</span>,<span>distance(s)</span>,<span>offset</span>)<br/>
<table>
<tr><td><span>geometry</span></td><td>linestring geometry</td></tr>
<tr><td><span>distance(s)</span></td><td>linear point distances (single number or a string of comma separated numbers)</td></tr>
<tr><td><span>offset</span></td><td>perpendicular offset</td></tr>
</table>
<br/><br/>
Examples:
<ul>
<li>make_zigzagline($geometry,'15,30',15) -> zig-zag line</li>
<li>make_zigzagline($geometry,15,15) -> zig-zag line</li>
</ul>
Use smooth function to create wavelines:<br/><br/>
Example:
<ul><li>smooth(make_zigzagline($geometry,'15,30',15),3)</li></ul>
"""
if not type(dist) is str:
dist = str(dist)
dist = [float(n) for n in dist.split(',')]
l = geom.length()
dist_sum = 0
distances = []
while dist_sum + round(sum(dist),2) < l:
for d in dist:
dist_sum += d
distances.append(dist_sum)
# interpolate points on linestring
points2d = [(lambda g: (g.x(), g.y()))(geom.interpolate(d).asPoint()) for d in distances]
vertices = geom.asPolyline()
start = (vertices[0].x(),vertices[0].y())
end = (vertices[-1].x(),vertices[-1].y())
points2d.insert(0,start) # prepend start point
points = [QgsPoint(start[0],start[1])]
i = 0
n = 0
b = -90
for point in points2d[1:]:
pt1 = QgsPoint(points2d[i][0],points2d[i][1])
pt2 = QgsPoint(point[0],point[1])
a = pt1.azimuth(pt2) + b
pt = pt2.project(offset, a)
points.append(pt)
i += 1
n += 1
if n == len(dist):
n = 0
b = -b
points.append(QgsPoint(end[0],end[1])) # append end point
return QgsGeometry.fromPolyline(points)
在Python控制台中加载代码,并使用Geometry生成器设置Linestring层的样式:
评论
太棒了!谢谢@christoph
– Kazuhito
20 Mar 24 '20在8:37
@christoph感谢您提供的出色解决方案。我正在尝试使其也可与多边形一起使用,并在此处gis.stackexchange.com/q/367422/94350创建了一个新问题
–巴斯文
20年7月10日在22:11
@christoph,感谢您提供的非常好的解决方案。但是,每次我尝试打开现场计算器时,QGIS 3.14便开始崩溃。删除自定义功能后,崩溃停止了。您认为它与QGIS版本有关吗?
–马克西姆·里奥(Maxime Riou)
20年7月28日在10:50
@Nico,您尝试过我的最新版本吗? gis.stackexchange.com/questions/376411/…
–christoph
20-11-30在12:26
@Nico,要创建新层,可以通过表达式使用“处理工具几何”
–christoph
20-11-30在12:44
评论
真有趣的问题!您是否正在考虑立即绘制线条的工具(例如,就像使用鼠标绘制简单的线条特征时一样),或者是一种从输入的坐标(最终从点,线或多边形)开始获得此结果的方法? br />@mgri感谢您的评论。我希望使用这条线来标记具有某些不确定性的边界(例如波动的海岸线),因此主要思想是将折线(通过预定义的坐标)转换为摆动。但是立即绘制这种类型的线的想法听起来也很吸引人。
请看看我的解决方案是否有帮助。我没有对其进行广泛的测试,所以请让我知道是否出现问题(我现在无法执行,但是我可能会使用更多信息来编辑答案)。