弯曲的点对点“路线图”


39

最近,我一直在查看航空公司的网页,这些网页显示了他们从某个城市飞往其服务的所有其他城市的航线。我希望能够在点之间创建类似的弯曲路线。是否有人创建脚本或函数来生成本示例中显示的弧形弧?

飞行路线

在PostGIS中,是否有ST_MakeLine的实现,可让您指定连接2个点时要使用的曲线量?

当我目前正在使用PostGIS和QGIS时,欢迎听到有关其他软件选项的信息,这些选项可能会产生相同的外观。


有人知道这有什么不错的实现吗?例子还是什么?
Mark Boulder 2012年

Answers:


26

创建大圈子可以为您带来理想的效果。

也许在http://lists.osgeo.org/pipermail/postgis-users/2008-February/018620.html上进行了讨论

更新:

我在“可视化全局连接”中遵循了这个想法。这是一个纯粹的基于PostGIS的解决方案,使用重新投影来创建弧。

SELECT ST_Transform(
  ST_Segmentize(
    ST_MakeLine(
      ST_Transform(a.the_geom, 953027),
      ST_Transform(b.the_geom, 953027)
    ), 
  100000), 
4326)

(可在以下位置找到953027的CRS定义:http ://spatialreference.org/ref/esri/53027/ )

在此处输入图片说明


4
我喜欢这个主意,尽管圆圈很大,但您遇到的问题是,在较短的距离处,您最终仍会得到一条直线。我希望能够控制放置在行中的弧的数量(即arclength = distance * 2)。
RyanDalton 2011年

1
这是问题的根本与用大圆一个很好的例子:gc.kls2.com/cgi-bin/...
RyanDalton

1
经过一些额外的研究,我发现这篇文章可能对协助此方法很有用。 mail-archive.com/postgis-users@postgis.refractions.net/…–
RyanDalton

为了将来的读者使用,我想我将继续前进并链接到@underdark最近的博客文章,其中涵盖了该主题。 underdark.wordpress.com/2011/08/20/...
RyanDalton

那很棒!!在我的项目中用于绘制用户签到位置与地点之间的界线,从Forsquare
Lorenzo Barbagli(

24

问题在于弄清楚弯曲弧度以提高其视觉分辨率的程度。

这是一种解决方案(在许多可能的解决方案中)。让我们考虑从共同原点发出的所有弧。弧线在这里最拥挤。为了最好地分离它们,让我们对其进行排列,使它们以相等的角度分布。如果我们绘制从原点到目的地的直线段,这是一个问题,因为通常会有沿不同方向的目的地簇。让我们利用自由度来弯曲圆弧,以便尽可能均匀地隔开离去的角度。

为简单起见,让我们在地图上使用圆弧。从点y到点x的弧中“弯曲”的自然度量是其在y处的方位与从yx处的方位之间的差。这样的弧是yx都位于其上的圆的一个扇区;基本几何显示弯曲角等于弧中夹角的一半

为了描述一种算法,我们需要更多的符号。令y为原点(在地图上投影),令x_1x_2,...,x_n为终点。将a_i定义为从yx_i的方位角,i = 1,2,...,n

首先,假设轴承(都在0到360度之间)按升序排列:这需要我们计算轴承,然后对它们进行排序;两者都是简单的任务。

理想情况下,相对于某些起始轴承,我们希望弧的轴承等于360 / n,2 * 360 / n等。因此,所需轴承与实际轴承之间的差等于i * 360 / n -a_i加上起始轴承a0。最大的差异是这n个差异中的最大值,最小的差异是它们的最小值。让我们将a0设置为最大值和最小值之间的一半;这对于启动轴承是一个很好的选择,因为它可以最大程度地减少可能发生的弯曲。因此,定义

b_i = i * 360 / n - a0 -a_i:

这是弯曲使用

yx绘制一个圆弧,其对角为2 b_i,这是基本几何的问题,因此,我将跳过一些细节,直接看一个示例。这是放置在矩形图中的64、16和4个随机点的解决方案的说明

替代文字

替代文字

替代文字

如您所见,随着目的地点数量的增加,解决方案似乎变得更好n = 4 的解决方案清楚地表明了轴承是如何均匀分布的,在这种情况下,间距等于360/4 = 90度,并且显然可以精确实现间距。

该解决方案并不完美:您可能会识别出一些可以手动调整以改善图形的圆弧。但这不会做得很糟糕,似乎是一个很好的开始。

该算法还具有简单的优点:最复杂的部分是根据目的地对其进行排序。


编码

我不了解PostGIS,但是也许我用来绘制示例的代码可以作为在PostGIS(或任何其他GIS)中实现此算法的指南。

将以下内容视为伪代码(但是Mathematica将执行它:-)。(如果这个网站支持TeX的,如数学,统计信息和TCS的人做的,我能做出这样一个很多更具可读性。)的符号包括:

  • 变量和函数名称区分大小写。
  • [Alpha]是小写希腊字母。([Pi]具有您认为应该拥有的价值。)
  • x [[i]]是数组x(从1开始索引)的元素i。
  • f [a,b]将函数f应用于参数a和b。适当情况下的功能(例如“ Min”和“ Table”)是系统定义的;带有小写字母首字母的函数(例如“ angles”和“ offset”)是用户定义的。注释解释了任何晦涩的系统功能(例如“ Arg”)。
  • Table [f [i],{i,1,n}]创建数组{f [1],f [2],...,f [n]}。
  • Circle [o,r,{a,b}]创建一个半径为r的圆的圆弧,半径为r,从角度a到角度b(均从正东逆时针方向以弧度表示)。
  • Ordering [x]返回x排序元素的索引数组。x [[Ordering [x]]]是x的排序版本。当y与x的长度相同时,y [[Ordering [x]]]与y并行地对y进行排序。

该代码的可执行部分非常简短,少于20行,因为其中一半以上是声明性开销或注释。

画地图

z是目的地列表,y是起点。

circleMap[z_List, y_] := 
Module[{\[Alpha] = angles[y,z], \[Beta], \[Delta], n},
    (* Sort the destinations by bearing *)
    \[Beta] = Ordering[\[Alpha]];
    x = z[[\[Beta] ]]; (* Destinations, sorted by bearing from y *)
    \[Alpha] = \[Alpha][[\[Beta]]]; (* Bearings, in sorted order *)
    \[Delta] = offset[\[Alpha]];
    n = Length[\[Alpha]];
    Graphics[{(* Draw the lines *)
        Gray, Table[circle[y, x[[i]],2 \[Pi] i / n + \[Delta] - \[Alpha][[i]]], 
             {i, 1, Length[\[Alpha]]}],
        (* Draw the destination points *)
        Red, PointSize[0.02], Table[Point[u], {u, x}]
    }]
]

从相对于x-> y轴承的x角度y开始,从点到点创建圆弧\[Beta]

circle[x_, y_, \[Beta]_] /; -\[Pi] < \[Beta] < \[Pi] := 
Module[{v,  \[Rho], r, o, \[Theta], sign},
    If[\[Beta]==0, Return[Line[{x,y}]]];

    (* Obtain the vector from x to y in polar coordinates. *)
    v = y - x; (* Vector from x to y *)
    \[Rho] = Norm[v]; (* Length of v *)
    \[Theta] = Arg[Complex @@ v]; (* Bearing from x to y *)

    (* Compute the radius and center of the circle.*)
    r = \[Rho] / (2 Sin[\[Beta]]); (* Circle radius, up to sign *)
    If[r < 0, sign = \[Pi], sign = 0];
    o = (x+y)/2 + (r/\[Rho]) Cos[\[Beta]]{v[[2]], -v[[1]]}; (* Circle center *)

    (* Create a sector of the circle. *)
    Circle[o, Abs[r], {\[Pi]/2 - \[Beta] + \[Theta] + sign, \[Pi] /2 + \[Beta] + \[Theta] + sign}]
]

计算从原点到点列表的方位角。

angles[origin_, x_] := Arg[Complex@@(#-origin)] & /@ x;

计算一组轴承的残差的中间范围。

x是按顺序排列的轴承列表。理想地,x [[i]]〜2πi/ n。

offset[x_List] :=
Module[
    {n = Length[x], y},
    (* Compute the residuals. *)
    y = Table[x[[i]] - 2 \[Pi] i / n, {i, 1, n}];
    (* Return their midrange. *)
    (Max[y] + Min[y])/2
]

我应该提到的是,此解决方案假定目的地或多或少围绕起点。如果不是这种情况,那么整个概念(等距轴承)并不是一个好主意。但是,可以通过在角度间隙内引入一些伪造的目标并随后删除这些目标(及其弧线)来轻松解决问题。通过计算轴承之间的平均距离并使用该平均距离来识别较大的间隙,可以使该过程自动化。
whuber

漂亮的图形。我想知道航空公司在绘制机上杂志背面显示的路线图时是否使用自动化工具。
Kirk Kuykendall

1
@Kirk他们可能会付钱给人手动做制图:-)。这个问题启发了我,看看简单的方法是否可以创建合理的图形。答案看起来很有希望。顺便说一下,这些图形是由Mathematica 8使用其CirclePoint基元以及一些矢量算法来找到圆心而生成的。
whuber

我喜欢您显示的结果,这是我要走的路。不过,老实说,我认为自己是技术人员,但是您给出的公式让我有些失落,因此如何将其转换为PostGIS代码几乎变得不可能。有人对如何将whuber的概念转换为可行的代码有任何想法吗?我将尝试进行审查并尝试一下,但将不胜感激。
RyanDalton

@ whuber-感谢更新的伪代码。我们必须看看我们是否可以在PostGIS中实际实现它。
RyanDalton



3

我最终尝试使用@NicklasAvén建议的ST_CurveToLine函数来弯曲一组“两点”线串。

我将以下3个坐标集传递给ST_OffsetCurve函数:

  1. 原始行的开始
  2. 平行于原始线的线偏移的中点
  3. 原始行的结尾

我在示例中使用ST_OffsetCurve函数计算偏移量-原始行的长度的1/10。

这是我用来从原始直线生成曲线的SQL:

    ST_CurveToLine('CIRCULARSTRING(' || st_x(st_startpoint(the_geom)) || ' ' || st_y(st_startpoint(the_geom)) || ', ' || st_x(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ' ' || st_y(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ', ' || st_x(st_endpoint(the_geom)) || ' ' ||  st_y(st_endpoint(the_geom)) || ')') AS the_curved_geom

确实有用,但由于某种原因,结果不尊重我的要求。知道为什么吗?
DMS02

您能否提供更多详细信息-输入几何图形,输出图形缺失,不同,生成错误(哪些应用程序-QGIS,PostgreSQL)。
布伦特·爱德华兹

我要在其中插入结果曲线的表格具有force_srid_geom约束。当我执行查询时,我收到一条错误消息,说该查询违反了该约束。对于没有该限制它的工作原理,但随后将其添加到QGIS当它列出了SRID 0.我的查询表:INSERT INTO测试(the_curved_geom)选择[这里您的SQL] FROM线
DMSO 2

尝试在几何列(the_curved_geom)上运行postgis.net/docs/ST_GeometryType.htmlpostgis.net/docs/ST_SRID.html函数,并检查测试表和force_srid_geom是否存在冲突。如果是这样,您可以根据需要变换几何形状/纬线或修改测试表/约束。
布伦特·爱德华兹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.