web-dev-qa-db-ja.com

球形ポリゴンの図心(重心)の位置を特定する

単位球にドレープされた任意の形状の図心を、形状境界の入力が順序付けられた(時計回りまたは反cw)頂点で見つけるための最良の方法を見つけようとしています。頂点の密度は境界に沿って不規則であるため、頂点間の弧の長さは一般に等しくありません。形状が非常に大きい(半球の半分)可能性があるため、ウィキペディアで詳しく説明されているように、頂点を平面に投影して平面法を使用することは一般に不可能です(申し訳ありませんが、新規参入者として2つ以上のハイパーリンクは許可されていません)。少し良いアプローチは、球座標で操作される平面ジオメトリの使用を含みますが、ここでも、うまく説明されているように、大きなポリゴンではこの方法は失敗します ここ 。その同じページで、「Cffk」が強調表示されています この論文 これは球面三角形の重心を計算する方法を説明しています。私はこのメソッドを実装しようとしましたが、成功しませんでした。誰かが問題を見つけられることを望んでいますか?

比較しやすいように、変数の定義は論文と同様にしています。入力(データ)は経度/緯度座標のリストであり、コードによって[x、y、z]座標に変換されます。三角形のそれぞれについて、1つの点を+ z極に任意に固定し、他の2つの頂点は、ポリゴン境界に沿った1対の隣接する点で構成されています。コードは、ポリゴンの各境界セグメントを三角形の辺として順番に使用して、境界に沿って(任意のポイントから開始して)ステップします。サブセントロイドは、これらの個々の球面三角形のそれぞれに対して決定され、三角形の面積に従って重み付けされ、合計されてポリゴンの重心が計算されます。コードの実行時にエラーは発生しませんが、返される重心の合計が明らかに間違っています(重心の位置が明確な、非常に基本的な形状をいくつか実行しました)。返された重心の位置に適切なパターンは見つかりませんでした...したがって、現時点では、数学またはコードのいずれかで何が問題になっているのかわかりません(ただし、疑いは数学です)。

以下のコードは、試してみたい場合はそのままコピーアンドペーストで機能するはずです。 matplotlibとnumpyがインストールされている場合、結果がプロットされます(インストールされていない場合、プロットは無視されます)。コードの下の経度/緯度データをexample.txtというテキストファイルに入れるだけです。

from math import *
try:
    import matplotlib as mpl
    import matplotlib.pyplot
    from mpl_toolkits.mplot3d import Axes3D
    import numpy
    plotting_enabled = True
except ImportError:
    plotting_enabled = False

def sph_car(point):
    if len(point) == 2:
        point.append(1.0)
    rlon = radians(float(point[0]))
    rlat = radians(float(point[1]))
    x = cos(rlat) * cos(rlon) * point[2]
    y = cos(rlat) * sin(rlon) * point[2]
    z = sin(rlat) * point[2]
    return [x, y, z]

def xprod(v1, v2):
    x = v1[1] * v2[2] - v1[2] * v2[1]
    y = v1[2] * v2[0] - v1[0] * v2[2]
    z = v1[0] * v2[1] - v1[1] * v2[0]
    return [x, y, z]

def dprod(v1, v2):
    dot = 0
    for i in range(3):
        dot += v1[i] * v2[i]
    return dot

def plot(poly_xyz, g_xyz):
    fig = mpl.pyplot.figure()
    ax = fig.add_subplot(111, projection='3d')
    # plot the unit sphere
    u = numpy.linspace(0, 2 * numpy.pi, 100)
    v = numpy.linspace(-1 * numpy.pi / 2, numpy.pi / 2, 100)
    x = numpy.outer(numpy.cos(u), numpy.sin(v))
    y = numpy.outer(numpy.sin(u), numpy.sin(v))
    z = numpy.outer(numpy.ones(numpy.size(u)), numpy.cos(v))
    ax.plot_surface(x, y, z, rstride=4, cstride=4, color='w', linewidth=0,
                    alpha=0.3)
    # plot 3d and flattened polygon
    x, y, z = Zip(*poly_xyz)
    ax.plot(x, y, z)
    ax.plot(x, y, zs=0)
    # plot the alleged 3d and flattened centroid
    x, y, z = g_xyz
    ax.scatter(x, y, z, c='r')
    ax.scatter(x, y, 0, c='r')
    # display
    ax.set_xlim3d(-1, 1)
    ax.set_ylim3d(-1, 1)
    ax.set_zlim3d(0, 1)
    mpl.pyplot.show()


lons, lats, v = list(), list(), list()
# put the two-column data at the bottom of the question into a file called
# example.txt in the same directory as this script
with open('example.txt') as f:
    for line in f.readlines():
        sep = line.split()
        lons.append(float(sep[0]))
        lats.append(float(sep[1]))
# convert spherical coordinates to cartesian
for lon, lat in Zip(lons, lats):
    v.append(sph_car([lon, lat, 1.0]))

# z unit vector/pole ('north pole'). This is an arbitrary point selected to act as one
#(fixed) vertex of the summed spherical triangles. The other two vertices of any
#triangle are composed of neighboring vertices from the polygon boundary.
np = [0.0, 0.0, 1.0]
# Gx,Gy,Gz are the cartesian coordinates of the calculated centroid
Gx, Gy, Gz = 0.0, 0.0, 0.0
for i in range(-1, len(v) - 1):
    # cycle through the boundary vertices of the polygon, from 0 to n
    if all((v[i][0] != v[i+1][0],
            v[i][1] != v[i+1][1],
            v[i][2] != v[i+1][2])):
        # this just ignores redundant points which are common in my larger input files
        # A,B,C are the internal angles in the triangle: 'np-v[i]-v[i+1]-np'
        A = asin(sqrt((dprod(np, xprod(v[i], v[i+1])))**2
                      / ((1 - (dprod(v[i+1], np))**2) * (1 - (dprod(np, v[i]))**2))))
        B = asin(sqrt((dprod(v[i], xprod(v[i+1], np)))**2
                      / ((1 - (dprod(np , v[i]))**2) * (1 - (dprod(v[i], v[i+1]))**2))))
        C = asin(sqrt((dprod(v[i + 1], xprod(np, v[i])))**2
                      / ((1 - (dprod(v[i], v[i+1]))**2) * (1 - (dprod(v[i+1], np))**2))))
        # A/B/Cbar are the vertex angles, such that if 'O' is the sphere center, Abar
        # is the angle (v[i]-O-v[i+1]) 
        Abar = acos(dprod(v[i], v[i+1]))
        Bbar = acos(dprod(v[i+1], np))
        Cbar = acos(dprod(np, v[i]))
        # e is the 'spherical excess', as defined on wikipedia
        e = A + B + C - pi
        # mag1/2/3 are the magnitudes of vectors np,v[i] and v[i+1].
        mag1 = 1.0
        mag2 = float(sqrt(v[i][0]**2 + v[i][1]**2 + v[i][2]**2))
        mag3 = float(sqrt(v[i+1][0]**2 + v[i+1][1]**2 + v[i+1][2]**2))
        # vec1/2/3 are cross products, defined here to simplify the equation below.
        vec1 = xprod(np, v[i])
        vec2 = xprod(v[i], v[i+1])
        vec3 = xprod(v[i+1], np)
        # multiplying vec1/2/3 by e and respective internal angles, according to the 
        #posted paper
        for x in range(3):
            vec1[x] *= Cbar / (2 * e * mag1 * mag2
                               * sqrt(1 - (dprod(np, v[i])**2)))
            vec2[x] *= Abar / (2 * e * mag2 * mag3
                               * sqrt(1 - (dprod(v[i], v[i+1])**2)))
            vec3[x] *= Bbar / (2 * e * mag3 * mag1
                               * sqrt(1 - (dprod(v[i+1], np)**2)))
        Gx += vec1[0] + vec2[0] + vec3[0]
        Gy += vec1[1] + vec2[1] + vec3[1]
        Gz += vec1[2] + vec2[2] + vec3[2]

approx_expected_Gxyz = (0.78, -0.56, 0.27)
print('Approximate Expected Gxyz: {0}\n'
      '              Actual Gxyz: {1}'
      ''.format(approx_expected_Gxyz, (Gx, Gy, Gz)))
if plotting_enabled:
    plot(v, (Gx, Gy, Gz))

提案や洞察を事前に感謝します。

編集:これは、ポリゴンを使用した単位球の投影と、コードから計算した結果の重心を示す図です。明らかに、ポリゴンはかなり小さく凸状であるため、図心は間違っていますが、図心はその周囲から外れています。 polygon and centroid

編集:これは上記のものと非常によく似た座標のセットですが、私が通常使用する元の[lon、lat]形式です(更新されたコードによって[x、y、z]に変換されます)。

  -39.366295      -1.633460
  -47.282630      -0.740433
  -53.912136       0.741380
  -59.004217       2.759183
  -63.489005       5.426812
  -68.566001       8.712068
  -71.394853      11.659135
  -66.629580      15.362600
  -67.632276      16.827507
  -66.459524      19.069327
  -63.819523      21.446736
  -61.672712      23.532143
  -57.538431      25.947815
  -52.519889      28.691766
  -48.606227      30.646295
  -45.000447      31.089437
  -41.549866      32.139873
  -36.605156      32.956277
  -32.010080      34.156692
  -29.730629      33.756566
  -26.158767      33.714080
  -25.821513      34.179648
  -23.614658      36.173719
  -20.896869      36.977645
  -17.991994      35.600074
  -13.375742      32.581447
  -9.554027      28.675497
  -7.825604      26.535234
  -7.825604      26.535234
  -9.094304      23.363132
  -9.564002      22.527385
  -9.713885      22.217165
  -9.948596      20.367878
  -10.496531      16.486580
  -11.151919      12.666850
  -12.350144       8.800367
  -15.446347       4.993373
  -20.366139       1.132118
  -24.784805      -0.927448
  -31.532135      -1.910227
  -39.366295      -1.633460

編集:さらにいくつかの例... [1,0,0]を中心とする完全な正方形を定義する4つの頂点で、期待される結果が得られます。 enter image description here ただし、非対称の三角形からは、どこにも近くない図心が得られます...図心は実際には球の反対側にあります(ここでは対蹠地として前面に投影されています)。 enter image description here 興味深いことに、重心の推定は、リストを反転すると(時計回りから反時計回りに、またはその逆に)、重心が対応して正確に反転するという意味で「安定」しているように見えます。

38
Nordlendingen

これでうまくいくと思います。以下のコードをコピーして貼り付けるだけで、この結果を再現できるはずです。

  • 緯度と経度のデータをlongitude and latitude.txtというファイルに保存する必要があります。コードの下に含まれている元のサンプルデータをコピーして貼り付けることができます。
  • Mplotlibがある場合は、以下のプロットが追加で生成されます
  • 非自明な計算のために、私は何が起こっているのかを説明するリンクを含めました
  • 下のグラフでは、参照ベクトルが非常に短い(r = 1/10)ため、3D重心が見やすくなっています。スケーリングを簡単に削除して、精度を最大化できます。
  • 運用上の注意:ほとんどすべてを書き直したため、元のコードがどこで機能していなかったか正確にはわかりません。ただし、少なくとも時計回り/反時計回りの三角形の頂点を処理する必要性は考慮されていなかったと思います。

Working Centroid

伝説:

  • (黒い線)参照ベクトル
  • (小さな赤い点)球面三角形の3D重心
  • (大きな赤/青/緑のドット)3D重心/表面に投影/ xy平面に投影
  • (青/緑の線)球形ポリゴンとxy平面への投影

from math import *
try:
    import matplotlib as mpl
    import matplotlib.pyplot
    from mpl_toolkits.mplot3d import Axes3D
    import numpy
    plotting_enabled = True
except ImportError:
    plotting_enabled = False


def main():
    # get base polygon data based on unit sphere
    r = 1.0
    polygon = get_cartesian_polygon_data(r)
    point_count = len(polygon)
    reference = ok_reference_for_polygon(polygon)
    # decompose the polygon into triangles and record each area and 3d centroid
    areas, subcentroids = list(), list()
    for ia, a in enumerate(polygon):
        # build an a-b-c point set
        ib = (ia + 1) % point_count
        b, c = polygon[ib], reference
        if points_are_equivalent(a, b, 0.001):
            continue  # skip nearly identical points
        # store the area and 3d centroid
        areas.append(area_of_spherical_triangle(r, a, b, c))
        tx, ty, tz = Zip(a, b, c)
        subcentroids.append((sum(tx)/3.0,
                             sum(ty)/3.0,
                             sum(tz)/3.0))
    # combine all the centroids, weighted by their areas
    total_area = sum(areas)
    subxs, subys, subzs = Zip(*subcentroids)
    _3d_centroid = (sum(a*subx for a, subx in Zip(areas, subxs))/total_area,
                    sum(a*suby for a, suby in Zip(areas, subys))/total_area,
                    sum(a*subz for a, subz in Zip(areas, subzs))/total_area)
    # shift the final centroid to the surface
    surface_centroid = scale_v(1.0 / mag(_3d_centroid), _3d_centroid)
    plot(polygon, reference, _3d_centroid, surface_centroid, subcentroids)


def get_cartesian_polygon_data(fixed_radius):
    cartesians = list()
    with open('longitude and latitude.txt') as f:
        for line in f.readlines():
            spherical_point = [float(v) for v in line.split()]
            if len(spherical_point) == 2:
                spherical_point.append(fixed_radius)
            cartesians.append(degree_spherical_to_cartesian(spherical_point))
    return cartesians


def ok_reference_for_polygon(polygon):
    point_count = len(polygon)
    # fix the average of all vectors to minimize float skew
    polyx, polyy, polyz = Zip(*polygon)
    # /10 is for visualization. Remove it to maximize accuracy
    return (sum(polyx)/(point_count*10.0),
            sum(polyy)/(point_count*10.0),
            sum(polyz)/(point_count*10.0))


def points_are_equivalent(a, b, vague_tolerance):
    # vague tolerance is something like a percentage tolerance (1% = 0.01)
    (ax, ay, az), (bx, by, bz) = a, b
    return all(((ax-bx)/ax < vague_tolerance,
                (ay-by)/ay < vague_tolerance,
                (az-bz)/az < vague_tolerance))


def degree_spherical_to_cartesian(point):
    rad_lon, rad_lat, r = radians(point[0]), radians(point[1]), point[2]
    x = r * cos(rad_lat) * cos(rad_lon)
    y = r * cos(rad_lat) * sin(rad_lon)
    z = r * sin(rad_lat)
    return x, y, z


def area_of_spherical_triangle(r, a, b, c):
    # points abc
    # build an angle set: A(CAB), B(ABC), C(BCA)
    # http://math.stackexchange.com/a/66731/25581
    A, B, C = surface_points_to_surface_radians(a, b, c)
    E = A + B + C - pi  # E is called the spherical excess
    area = r**2 * E
    # add or subtract area based on clockwise-ness of a-b-c
    # http://stackoverflow.com/a/10032657/377366
    if clockwise_or_counter(a, b, c) == 'counter':
        area *= -1.0
    return area


def surface_points_to_surface_radians(a, b, c):
    """build an angle set: A(cab), B(abc), C(bca)"""
    points = a, b, c
    angles = list()
    for i, mid in enumerate(points):
        start, end = points[(i - 1) % 3], points[(i + 1) % 3]
        x_startmid, x_endmid = xprod(start, mid), xprod(end, mid)
        ratio = (dprod(x_startmid, x_endmid)
                 / ((mag(x_startmid) * mag(x_endmid))))
        angles.append(acos(ratio))
    return angles


def clockwise_or_counter(a, b, c):
    ab = diff_cartesians(b, a)
    bc = diff_cartesians(c, b)
    x = xprod(ab, bc)
    if x < 0:
        return 'clockwise'
    Elif x > 0:
        return 'counter'
    else:
        raise RuntimeError('The reference point is in the polygon.')


def diff_cartesians(positive, negative):
    return Tuple(p - n for p, n in Zip(positive, negative))


def xprod(v1, v2):
    x = v1[1] * v2[2] - v1[2] * v2[1]
    y = v1[2] * v2[0] - v1[0] * v2[2]
    z = v1[0] * v2[1] - v1[1] * v2[0]
    return [x, y, z]


def dprod(v1, v2):
    dot = 0
    for i in range(3):
        dot += v1[i] * v2[i]
    return dot


def mag(v1):
    return sqrt(v1[0]**2 + v1[1]**2 + v1[2]**2)


def scale_v(scalar, v):
    return Tuple(scalar * vi for vi in v)


def plot(polygon, reference, _3d_centroid, surface_centroid, subcentroids):
    fig = mpl.pyplot.figure()
    ax = fig.add_subplot(111, projection='3d')
    # plot the unit sphere
    u = numpy.linspace(0, 2 * numpy.pi, 100)
    v = numpy.linspace(-1 * numpy.pi / 2, numpy.pi / 2, 100)
    x = numpy.outer(numpy.cos(u), numpy.sin(v))
    y = numpy.outer(numpy.sin(u), numpy.sin(v))
    z = numpy.outer(numpy.ones(numpy.size(u)), numpy.cos(v))
    ax.plot_surface(x, y, z, rstride=4, cstride=4, color='w', linewidth=0,
                    alpha=0.3)
    # plot 3d and flattened polygon
    x, y, z = Zip(*polygon)
    ax.plot(x, y, z, c='b')
    ax.plot(x, y, zs=0, c='g')
    # plot the 3d centroid
    x, y, z = _3d_centroid
    ax.scatter(x, y, z, c='r', s=20)
    # plot the spherical surface centroid and flattened centroid
    x, y, z = surface_centroid
    ax.scatter(x, y, z, c='b', s=20)
    ax.scatter(x, y, 0, c='g', s=20)
    # plot the full set of triangular centroids
    x, y, z = Zip(*subcentroids)
    ax.scatter(x, y, z, c='r', s=4)
    # plot the reference vector used to findsub centroids
    x, y, z = reference
    ax.plot((0, x), (0, y), (0, z), c='k')
    ax.scatter(x, y, z, c='k', marker='^')
    # display
    ax.set_xlim3d(-1, 1)
    ax.set_ylim3d(-1, 1)
    ax.set_zlim3d(0, 1)
    mpl.pyplot.show()

# run it in a function so the main code can appear at the top
main()

longitude and latitude.txtに貼り付けることができる経度と緯度のデータは次のとおりです

  -39.366295      -1.633460
  -47.282630      -0.740433
  -53.912136       0.741380
  -59.004217       2.759183
  -63.489005       5.426812
  -68.566001       8.712068
  -71.394853      11.659135
  -66.629580      15.362600
  -67.632276      16.827507
  -66.459524      19.069327
  -63.819523      21.446736
  -61.672712      23.532143
  -57.538431      25.947815
  -52.519889      28.691766
  -48.606227      30.646295
  -45.000447      31.089437
  -41.549866      32.139873
  -36.605156      32.956277
  -32.010080      34.156692
  -29.730629      33.756566
  -26.158767      33.714080
  -25.821513      34.179648
  -23.614658      36.173719
  -20.896869      36.977645
  -17.991994      35.600074
  -13.375742      32.581447
  -9.554027      28.675497
  -7.825604      26.535234
  -7.825604      26.535234
  -9.094304      23.363132
  -9.564002      22.527385
  -9.713885      22.217165
  -9.948596      20.367878
  -10.496531      16.486580
  -11.151919      12.666850
  -12.350144       8.800367
  -15.446347       4.993373
  -20.366139       1.132118
  -24.784805      -0.927448
  -31.532135      -1.910227
  -39.366295      -1.633460
13
KobeJohn

重み付きデカルト座標を使用して重心を計算し、その結果を球に投影することをお勧めします(座標の原点が(0, 0, 0)^Tであると仮定)。

ポリゴンのn点を(p[0], p[1], ... p[n-1])とします。近似(デカルト)重心は、次の方法で計算できます。

c = 1 / w * (sum of w[i] * p[i])

一方、wはすべての重みの合計であり、p[i]はポリゴンポイントであり、w[i]はそのポイントの重みです。

w[i] = |p[i] - p[(i - 1 + n) % n]| / 2 + |p[i] - p[(i + 1) % n]| / 2

一方、|x|はベクトルxの長さです。つまりポイントは、前のポリゴンポイントの半分の長さと、次のポリゴンポイントの半分の長さで重み付けされます。

この図心cは、次の方法で球に投影できます。

c' = r * c / |c| 

一方、rは球の半径です。

ポリゴンの方向を考慮すると(ccw, cw)結果は次のようになります。

c' = - r * c / |c|. 
4
David B.

明確にするために:関心のある量は、単位球への真の3D重心(つまり、3D重心、つまり3D重心)の投影です。

気になるのは原点から3D重心への方向だけなので、領域を気にする必要はまったくありません。モーメントを計算する方が簡単です(つまり、3D重心×面積)。単位球上の閉じたパスの左側の領域のモーメントは、パスを歩き回るときの左側の単位ベクトルの積分の半分です。これは、ストークスの定理の非自明な適用によるものです。 http://www.owlnet.rice.edu/~fjones/chap13.pdf 問題13-12を参照してください。

特に、球形ポリゴンの場合、モーメントは(a x b)/ || a x b ||の合計の半分になります。 *(aとbの間の角度)連続する頂点a、bの各ペア。 (これは、パスのまでの領域の場合です。パスのまでの領域の場合は否定します。)

(そして、本当に3D重心が必要な場合は、面積を計算してモーメントをそれで除算するだけです。面積を比較すると、2つの領域のどちらを「ポリゴン」と呼ぶかを選択するのにも役立ちます。)

ここにいくつかのコードがあります。それは本当に簡単です:

#!/usr/bin/python

import math

def plus(a,b): return [x+y for x,y in Zip(a,b)]
def minus(a,b): return [x-y for x,y in Zip(a,b)]
def cross(a,b): return [a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]]
def dot(a,b): return sum([x*y for x,y in Zip(a,b)])
def length(v): return math.sqrt(dot(v,v))
def normalized(v): l = length(v); return [1,0,0] if l==0 else [x/l for x in v]
def addVectorTimesScalar(accumulator, vector, scalar):
    for i in xrange(len(accumulator)): accumulator[i] += vector[i] * scalar
def angleBetweenUnitVectors(a,b):
    # http://www.plunk.org/~hatch/rightway.php
    if dot(a,b) < 0:
        return math.pi - 2*math.asin(length(plus(a,b))/2.)
    else:
        return 2*math.asin(length(minus(a,b))/2.)

def sphericalPolygonMoment(verts):
    moment = [0.,0.,0.]
    for i in xrange(len(verts)):
        a = verts[i]
        b = verts[(i+1)%len(verts)]
        addVectorTimesScalar(moment, normalized(cross(a,b)),
                                     angleBetweenUnitVectors(a,b) / 2.)
    return moment

if __name__ == '__main__':
    import sys
    def lonlat_degrees_to_xyz(lon_degrees,lat_degrees):
        lon = lon_degrees*(math.pi/180)
        lat = lat_degrees*(math.pi/180)
        coslat = math.cos(lat)
        return [coslat*math.cos(lon), coslat*math.sin(lon), math.sin(lat)]

    verts = [lonlat_degrees_to_xyz(*[float(v) for v in line.split()])
             for line in sys.stdin.readlines()]
    #print "verts = "+`verts`

    moment = sphericalPolygonMoment(verts)
    print "moment = "+`moment`
    print "centroid unit direction = "+`normalized(moment)`

ポリゴンの例では、これは答え(単位ベクトル)を与えます:

[-0.7644875430808217, 0.579935445918147, -0.2814847687566214]

これは、@ KobeJohnのコードによって計算された回答とほぼ同じですが、より正確です。このコードは、サブセントロイドに対して大まかな公差と平面近似を使用します。

[0.7628095787179151, -0.5977153368303585, 0.24669398601094406]

2つの答えの方向はほぼ反対です(したがって、KobeJohnのコードは、この場合、領域をパスのに移動することを決定したと思います)。

2
Don Hatch

申し訳ありませんが、私は(新しく登録されたユーザーとして)ドンハッチによる上記の回答に投票/コメントするだけでなく、新しい投稿を書く必要がありました。ドンの答えは、私が思うに、最高で最もエレガントです。球形ポリゴンに適用する場合、簡単な方法で重心(最初の質量モーメント)を計算するのは数学的に厳密です。

神戸ジョンの答えは良い近似ですが、より小さな領域でのみ満足のいくものです。また、コードにいくつかの不具合があることに気づきました。まず、実際の球面面積を計算するために、基準点を球面に投影する必要があります。次に、ゼロ除算を回避するために、関数points_are_equivalent()を改良する必要がある場合があります。

神戸の方法の近似誤差は、球面三角形の重心の計算にあります。サブ図心は、球面三角形の重心ではなく、平面三角形の重心です。その単一の三角形を決定する場合、これは問題ではありません(符号が反転する可能性があります。以下を参照してください)。三角形が小さい場合(ポリゴンの密な三角形分割など)も問題にはなりません。

いくつかの簡単なテストで、近似誤差を説明できます。たとえば、4つのポイントだけを使用する場合:

10 -20

10 20

-10 20

-10 -20

正確な答えは(1,0,0)で、どちらの方法も適切です。ただし、1つのエッジに沿ってさらにいくつかのポイントを投入すると(たとえば、最初のエッジに{10、-15}、{10、-10} ...を追加すると)、神戸の方法の結果がシフトし始めることがわかります。さらに、経度を[10、-10]から[100、-100]に増やすと、神戸の結果が方向を反転することがわかります。考えられる改善は、サブセントロイド計算用に別のレベルを追加することです(基本的に三角形のサイズを調整/縮小します)。

このアプリケーションでは、球形の領域の境界は複数の円弧で構成されているため、ポリゴンではありません(つまり、円弧は大円の一部ではありません)。しかし、これは、曲線積分でnベクトルを見つけるためのもう少し作業になります。

編集:サブセントロイド計算を ブロックの論文 で与えられたものに置き換えると、神戸の方法が修正されるはずです。しかし、私は試しませんでした。

1
Sam Zhu