计算 JSON 折线的中点(Python 2.7.0,无库)
Calculate the midpoint of JSON polylines (Python 2.7.0, no libraries)
场景:
我有一个 system 向 Web 服务发出请求。
- Web 服务 returns 一个 JSON 对象。
- JSON 对象包含数组中的折线顶点。
JSON 对象的一个小片段是:
{
"objectIdFieldName": "OBJECTID",
"globalIdFieldName": "",
"geometryType": "esriGeometryPolyline",
"spatialReference": {
"wkid": 476,
"latestWkid": 476
},
"fields": [
{
"name": "OBJECTID",
"alias": "OBJECTID",
"type": "esriFieldTypeOID"
}
],
"features": [
{
"attributes": {
"OBJECTID": 3311
},
"geometry": {
"paths": [
[
[
675844.1562959617,
4861766.9811610579
],
[
675878.30397594348,
4861792.5977392439
],
[
675891.38832408097,
4861800.4024024364
],
[
675902.17710777745,
4861804.9933949765
],
[
675912.27726199664,
4861808.2070551421
],
[
675923.52513550781,
4861810.2730065044
],
[
675934.77300901897,
4861811.1911861338
],
[
675943.03676202707,
4861811.1911861338
],
[
675951.07095439639,
4861810.502546167
],
[
675961.17111910321,
4861808.6661449578
],
[
675970.35304125212,
4861806.1411667075
],
[
675981.51595173683,
4861800.7007851209
],
[
675998.03647276573,
4861792.2469376959
]
]
]
}
},
**The JSON object has been cut off.**
完整的 JSON 对象可以在这里找到:JSON Polylines
问题:
使用 JSON 顶点,我想计算折线的 中点 (见下面的绿点):
- 有些行(OBJECTID 3716 和 3385)是多部分的。在这种情况下,只应为直线的最长部分(而不是其他部分)生成中点。
- 为了解决这个问题,可以将JSON文本保存为文本文件,加载到python脚本中。在这种情况下,可以使用 Python 的 JSON 库——尽管下面提到的 catch。
输出看起来像这样(格式可能不同):
OBJECTID MIDPOINT_X MIDPOINT_Y
2165 676163.9343 4861476.373
2320 676142.0017 4861959.66
2375 676118.1226 4861730.258
2682 676060.3917 4861904.762
2683 675743.1666 4861724.081
2691 676137.4796 4861557.709
3311 675916.9815 4861809.071
3385 676208.669 4861536.555
3546 676262.2623 4861665.146
3547 676167.5738 4861612.699
3548 676021.3677 4861573.141
3549 675914.4334 4861669.87
3550 675866.6003 4861735.572
3551 675800.1232 4861827.482
3552 675681.9432 4861918.989
3716 675979.6493 4861724.323
收获:
这需要在 Python 2.7.0 中完成——因为我的系统使用 Jython 2.7.0.
- 请务必注意,我无法将任何 Python 库 导入到我正在使用的系统中的 Jython 实现中。因此,不幸的是,该脚本不应导入任何 python 库(用于测试的 JSON 库除外)。
是否可以使用 Python 2.7.0(无需导入库)计算 JSON 折线的中点?
是的,您可以仅使用内置 Python 函数/无需导入外部库即可轻松计算多段线的中点。
让我们打破你的要求:
- 遍历对象
features
字段中的特征
- 能够计算折线的长度
- 如果有多个要素,则为每个要素选择最长的折线
- 找到这条折线的中点(如评论中所建议的,您可以通过将其线段的长度求和来沿着折线前进,直到您确定位于中点之间的两个点,并使用 vector math)
所以我们需要一些辅助函数来计算两点之间的距离,然后是折线的距离等等:
# Euclidean distance between two points
def get_distance(p1, p2):
return sum([(x-y)**2 for (x,y) in zip(p1, p2)]) ** (0.5)
# Length of a polyline by summing the length of its segments
def get_distance_line(line):
total = 0
for start_index in range(len(line) - 1):
stop_index = start_index + 1
total += get_distance(line[start_index], line[stop_index])
return total
# Get the polyline with the longest distance
# within a list of polyline
def get_longest(li):
return max(li, key=get_distance_line)
# Compute the target point at `_target_dist`
# of `p1` along the p1-p2 segment
def _get_pt_at_dist(p1, p2, _target_dist):
# Define the vector from p1 to p2
vx = p2[0] - p1[0]
vy = p2[1] - p1[1]
# Compute the length of the vector
lv = (vx ** 2 + vy ** 2) ** 0.5
# Compute the unit vector (the vector with length 1)
nv = [vx / lv, vy / lv]
# Compute the target point
return [
p1[0] + nv[0] * _target_dist,
p1[1] + nv[1] * _target_dist,
]
# Get a point at a specific distance on a Polyline
# - 1st step to find the two points enclosing the `target_dist
# - 2nd step to calculate the midpoint along the 2 previously selected points
def get_point_at_distance(line, target_dist):
sum_dist = 0
for start_index in range(len(line) - 1):
stop_index = start_index + 1
n_dist = get_distance(line[start_index], line[stop_index])
if sum_dist + n_dist > target_dist:
# We have found the two enclosing points
p1, p2 = line[start_index], line[stop_index]
_target_dist = target_dist - sum_dist
return _get_pt_at_dist(p1, p2, _target_dist)
else:
sum_dist += n_dist
raise ValueError("target distance is greater than the length of the line")
让我们遍历您的数据(我将您的对象命名为 dataset
)并使用这些函数计算中点:
result = {}
for ft in dataset['features']:
paths = ft['geometry']['paths']
# Pick the longest path of default to
# the only existing one:
if len(paths) == 1:
p = paths[0]
else:
p = get_longest(paths)
# Compute the distance
# and the half of the distance
# for this polyline
distance_line = get_distance_line(p)
middle_dist = distance_line / 2
# Compute the midpoint and save it
# into a `dict` using the `OBJECTID`
# attribute as a key
midpoint = get_point_at_distance(p, middle_dist)
result[ft['attributes']['OBJECTID']] = midpoint
生成的对象:
{3311: [675916.9814634613, 4861809.071098591],
3385: [676208.6690235228, 4861536.554984818],
2165: [676163.9343346333, 4861476.37263185],
2682: [676060.391694662, 4861904.761846619],
2683: [675743.1665799635, 4861724.081134027],
2691: [676137.4796253176, 4861557.709372229],
2375: [676118.1225925689, 4861730.258496471],
2320: [676142.0016617056, 4861959.660392438],
3716: [675979.6112380569, 4861724.356721315],
3546: [676262.2623328466, 4861665.145686949],
3547: [676167.5737531717, 4861612.6987658115],
3548: [676021.3677275265, 4861573.140917723],
3549: [675914.4334252588, 4861669.870444033],
3550: [675866.6003329497, 4861735.571798388],
3551: [675800.1231731868, 4861827.48182595],
3552: [675681.9432478376, 4861918.988687315]}
OP 注意事项:起初我选择了节点数最多的路径(而不是距离最长的路径 - 使用类似 def get_longest(li): return max(li, key=len)
的路径)并且我有一个(视觉)结果更接近提供的结果,所以也许这就是你说 the longest part of the line
但我不确定的原因!
场景:
我有一个 system 向 Web 服务发出请求。
- Web 服务 returns 一个 JSON 对象。
- JSON 对象包含数组中的折线顶点。
JSON 对象的一个小片段是:
{
"objectIdFieldName": "OBJECTID",
"globalIdFieldName": "",
"geometryType": "esriGeometryPolyline",
"spatialReference": {
"wkid": 476,
"latestWkid": 476
},
"fields": [
{
"name": "OBJECTID",
"alias": "OBJECTID",
"type": "esriFieldTypeOID"
}
],
"features": [
{
"attributes": {
"OBJECTID": 3311
},
"geometry": {
"paths": [
[
[
675844.1562959617,
4861766.9811610579
],
[
675878.30397594348,
4861792.5977392439
],
[
675891.38832408097,
4861800.4024024364
],
[
675902.17710777745,
4861804.9933949765
],
[
675912.27726199664,
4861808.2070551421
],
[
675923.52513550781,
4861810.2730065044
],
[
675934.77300901897,
4861811.1911861338
],
[
675943.03676202707,
4861811.1911861338
],
[
675951.07095439639,
4861810.502546167
],
[
675961.17111910321,
4861808.6661449578
],
[
675970.35304125212,
4861806.1411667075
],
[
675981.51595173683,
4861800.7007851209
],
[
675998.03647276573,
4861792.2469376959
]
]
]
}
},
**The JSON object has been cut off.**
完整的 JSON 对象可以在这里找到:JSON Polylines
问题:
使用 JSON 顶点,我想计算折线的 中点 (见下面的绿点):
- 有些行(OBJECTID 3716 和 3385)是多部分的。在这种情况下,只应为直线的最长部分(而不是其他部分)生成中点。
- 为了解决这个问题,可以将JSON文本保存为文本文件,加载到python脚本中。在这种情况下,可以使用 Python 的 JSON 库——尽管下面提到的 catch。
输出看起来像这样(格式可能不同):
OBJECTID MIDPOINT_X MIDPOINT_Y
2165 676163.9343 4861476.373
2320 676142.0017 4861959.66
2375 676118.1226 4861730.258
2682 676060.3917 4861904.762
2683 675743.1666 4861724.081
2691 676137.4796 4861557.709
3311 675916.9815 4861809.071
3385 676208.669 4861536.555
3546 676262.2623 4861665.146
3547 676167.5738 4861612.699
3548 676021.3677 4861573.141
3549 675914.4334 4861669.87
3550 675866.6003 4861735.572
3551 675800.1232 4861827.482
3552 675681.9432 4861918.989
3716 675979.6493 4861724.323
收获:
这需要在 Python 2.7.0 中完成——因为我的系统使用 Jython 2.7.0.
- 请务必注意,我无法将任何 Python 库 导入到我正在使用的系统中的 Jython 实现中。因此,不幸的是,该脚本不应导入任何 python 库(用于测试的 JSON 库除外)。
是否可以使用 Python 2.7.0(无需导入库)计算 JSON 折线的中点?
是的,您可以仅使用内置 Python 函数/无需导入外部库即可轻松计算多段线的中点。
让我们打破你的要求:
- 遍历对象
features
字段中的特征 - 能够计算折线的长度
- 如果有多个要素,则为每个要素选择最长的折线
- 找到这条折线的中点(如评论中所建议的,您可以通过将其线段的长度求和来沿着折线前进,直到您确定位于中点之间的两个点,并使用 vector math)
所以我们需要一些辅助函数来计算两点之间的距离,然后是折线的距离等等:
# Euclidean distance between two points
def get_distance(p1, p2):
return sum([(x-y)**2 for (x,y) in zip(p1, p2)]) ** (0.5)
# Length of a polyline by summing the length of its segments
def get_distance_line(line):
total = 0
for start_index in range(len(line) - 1):
stop_index = start_index + 1
total += get_distance(line[start_index], line[stop_index])
return total
# Get the polyline with the longest distance
# within a list of polyline
def get_longest(li):
return max(li, key=get_distance_line)
# Compute the target point at `_target_dist`
# of `p1` along the p1-p2 segment
def _get_pt_at_dist(p1, p2, _target_dist):
# Define the vector from p1 to p2
vx = p2[0] - p1[0]
vy = p2[1] - p1[1]
# Compute the length of the vector
lv = (vx ** 2 + vy ** 2) ** 0.5
# Compute the unit vector (the vector with length 1)
nv = [vx / lv, vy / lv]
# Compute the target point
return [
p1[0] + nv[0] * _target_dist,
p1[1] + nv[1] * _target_dist,
]
# Get a point at a specific distance on a Polyline
# - 1st step to find the two points enclosing the `target_dist
# - 2nd step to calculate the midpoint along the 2 previously selected points
def get_point_at_distance(line, target_dist):
sum_dist = 0
for start_index in range(len(line) - 1):
stop_index = start_index + 1
n_dist = get_distance(line[start_index], line[stop_index])
if sum_dist + n_dist > target_dist:
# We have found the two enclosing points
p1, p2 = line[start_index], line[stop_index]
_target_dist = target_dist - sum_dist
return _get_pt_at_dist(p1, p2, _target_dist)
else:
sum_dist += n_dist
raise ValueError("target distance is greater than the length of the line")
让我们遍历您的数据(我将您的对象命名为 dataset
)并使用这些函数计算中点:
result = {}
for ft in dataset['features']:
paths = ft['geometry']['paths']
# Pick the longest path of default to
# the only existing one:
if len(paths) == 1:
p = paths[0]
else:
p = get_longest(paths)
# Compute the distance
# and the half of the distance
# for this polyline
distance_line = get_distance_line(p)
middle_dist = distance_line / 2
# Compute the midpoint and save it
# into a `dict` using the `OBJECTID`
# attribute as a key
midpoint = get_point_at_distance(p, middle_dist)
result[ft['attributes']['OBJECTID']] = midpoint
生成的对象:
{3311: [675916.9814634613, 4861809.071098591],
3385: [676208.6690235228, 4861536.554984818],
2165: [676163.9343346333, 4861476.37263185],
2682: [676060.391694662, 4861904.761846619],
2683: [675743.1665799635, 4861724.081134027],
2691: [676137.4796253176, 4861557.709372229],
2375: [676118.1225925689, 4861730.258496471],
2320: [676142.0016617056, 4861959.660392438],
3716: [675979.6112380569, 4861724.356721315],
3546: [676262.2623328466, 4861665.145686949],
3547: [676167.5737531717, 4861612.6987658115],
3548: [676021.3677275265, 4861573.140917723],
3549: [675914.4334252588, 4861669.870444033],
3550: [675866.6003329497, 4861735.571798388],
3551: [675800.1231731868, 4861827.48182595],
3552: [675681.9432478376, 4861918.988687315]}
OP 注意事项:起初我选择了节点数最多的路径(而不是距离最长的路径 - 使用类似 def get_longest(li): return max(li, key=len)
的路径)并且我有一个(视觉)结果更接近提供的结果,所以也许这就是你说 the longest part of the line
但我不确定的原因!