38 votos

Localizando el centroide (centro de masa) de los polígonos esféricos

Estoy tratando de encontrar la mejor manera de localizar el centroide de una forma arbitraria que se extiende sobre una esfera unitaria, con la entrada ordenada (en el sentido de las agujas del reloj o en sentido contrario a las agujas del reloj) vértices para el límite de la forma. La densidad de los vértices es irregular a lo largo del límite, por lo que las longitudes de arco entre ellos no son generalmente iguales. Debido a que las formas pueden ser muy grandes (medio hemisferio), generalmente no es posible proyectar simplemente los vértices a un plano y usar métodos planares, como se detalla en Wikipedia (lo siento, no se me permiten más de 2 hipervínculos como recién llegado). Un enfoque ligeramente mejor implica el uso de geometría plana manipulada en coordenadas esféricas, pero de nuevo, con grandes polígonos este método falla, como se ilustra muy bien aquí . En esa misma página, 'Cffk' resaltó este documento que describe un método para calcular el centroide de los triángulos esféricos. He tratado de implementar este método, pero sin éxito, y espero que alguien pueda detectar el problema

He mantenido las definiciones de las variables similares a las del documento para facilitar la comparación. La entrada (datos) es una lista de coordenadas de longitud/latitud, convertidas a coordenadas [x,y,z] por el código. Para cada uno de los triángulos he fijado arbitrariamente un punto como el polo +z, los otros dos vértices están compuestos por un par de puntos vecinos a lo largo del límite del polígono. El código camina a lo largo del límite (comenzando en un punto arbitrario), usando cada segmento del límite del polígono como un lado del triángulo a su vez. Se determina un subcentroide para cada uno de estos triángulos esféricos individuales y se ponderan de acuerdo con el área del triángulo y se suman para calcular el centroide total del polígono. No obtengo ningún error al ejecutar el código, pero los centros totales devueltos están claramente equivocados (he ejecutado algunas formas muy básicas en las que la ubicación del centroide es inequívoca). No he encontrado ningún patrón sensato en la ubicación de los centroides devueltos... así que por el momento no estoy seguro de lo que va mal, ni en las matemáticas ni en el código (aunque, la sospecha es la matemática).

El código de abajo debería funcionar con copiar y pegar como si quisiera probarlo. Si tienes instalado matplotlib y numpy, éste trazará los resultados (ignorará el trazado si no lo haces). Sólo tienes que poner los datos de longitud/latitud debajo del código en un archivo de texto llamado 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))

Gracias de antemano por cualquier sugerencia o perspicacia.

EDITORIAL: Aquí hay una figura que muestra una proyección de la unidad esfera con un polígono y el centroide resultante que calculo del código. Claramente, el centroide está equivocado ya que el polígono es bastante pequeño y convexo, pero aún así el centroide cae fuera de su perímetro. polygon and centroid

EDITAR: Aquí hay un conjunto de coordenadas muy similar a las de arriba, pero en el formato original [lon,lat] que utilizo normalmente (que ahora se convierte en [x,y,z] por el código actualizado).

  -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

EDITORIAL: Un par de ejemplos más... con 4 vértices definiendo un cuadrado perfecto centrado en [1,0,0] obtengo el resultado esperado: enter image description here Sin embargo, de un triángulo no simétrico obtengo un centroide que no está en ninguna parte cerca... el centroide en realidad cae en el lado más lejano de la esfera (aquí proyectado en el lado frontal como la antípoda): enter image description here Curiosamente, la estimación del centroide parece "estable" en el sentido de que si invierto la lista (pasar del sentido de las agujas del reloj al sentido contrario o viceversa) el centroide se invierte exactamente.

13voto

kobejohn Puntos 2485

Creo que esto lo hará. Deberías poder reproducir este resultado con sólo copiar y pegar el código de abajo.

  • Necesitarás tener los datos de latitud y longitud en un archivo llamado longitude and latitude.txt . Puede copiar-pegar los datos originales de la muestra que se incluyen debajo del código.
  • Si tiene mplotlib producirá adicionalmente la siguiente trama
  • Para los cálculos no obvios, he incluido un enlace que explica lo que está pasando
  • En el siguiente gráfico, el vector de referencia es muy corto (r = 1/10), de modo que los 3d-centroides son más fáciles de ver. Se puede eliminar fácilmente la escala para maximizar la precisión.
  • Nota para la operación: Reescribí casi todo, así que no estoy seguro de dónde no funcionaba el código original. Sin embargo, al menos creo que no se tuvo en cuenta la necesidad de manejar los vértices de los triángulos en el sentido de las agujas del reloj y en sentido contrario.

Working Centroid

Leyenda:

  • (línea negra) vector de referencia
  • (pequeños puntos rojos) triángulo esférico 3d-centroides
  • (gran punto rojo/azul/verde) 3d-centroide / proyectado a la superficie / proyectado al plano xy
  • (líneas azules / verdes) el polígono esférico y la proyección sobre el plano 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()

Aquí están los datos de longitud y latitud que puedes pegar en 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

8voto

Don Hatch Puntos 458

Para aclarar: la cantidad de interés es la proyección del verdadero centroide 3d (es decir, el centro de masa 3d, es decir, el centro de área 3d) sobre la esfera unitaria.

Ya que lo único que te importa es la dirección desde el origen hasta el centroide 3d, no necesitas molestarte con áreas en absoluto; es más fácil calcular el momento (es decir, centroide 3d por área). El momento de la región a la izquierda de una trayectoria cerrada en la esfera unitaria es la mitad de la integral del vector unitario hacia la izquierda al recorrer el camino. Esto se deduce de una aplicación no evidente del teorema de Stokes; véase http://www.owlnet.rice.edu/~fjones/chap13.pdf Problema 13-12.

En particular, para un polígono esférico, el momento es la mitad de la suma de (a x b) / ||a x b|| * (ángulo entre a y b) para cada par de vértices consecutivos a,b. (Esto es para la región a la izquierda del camino; negarlo para la región a la derecha del camino).

(Y si realmente quieres el centroide 3d, sólo tienes que calcular el área y dividir el momento por ella. Comparar áreas también podría ser útil para elegir a cuál de las dos regiones llamar "el polígono").

Aquí hay algo de código; es realmente simple:

#!/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):
    # https://www.plunk.org/~hatch/rightway.html
    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)`

Para el polígono del ejemplo, esto da la respuesta (vector unitario):

[-0.7644875430808217, 0.579935445918147, -0.2814847687566214]

Esto es más o menos lo mismo, pero más preciso, que la respuesta calculada por el código de @KobeJohn, que utiliza tolerancias aproximadas y aproximaciones planas a los subcentroides:

[0.7628095787179151, -0.5977153368303585, 0.24669398601094406]

Las direcciones de las dos respuestas son aproximadamente opuestas (así que supongo que el código de KobeJohn decidió llevar la región a la derecha del camino en este caso).

4voto

David B. Puntos 46

Creo que una buena aproximación sería calcular el centro de masa usando coordenadas cartesianas ponderadas y proyectando el resultado sobre la esfera (suponiendo que el origen de las coordenadas es (0, 0, 0)^T).

Sean (p[0], p[1], ... p[n-1]) los n puntos del polígono. El centroide (cartesiano) aprox. puede ser calculado por:

c = 1 / w * (suma de w[i] * p[i])

mientras que w es la suma de todos los pesos y mientras que p[i] es un punto poligonal y w[i] es un peso para ese punto, por ejemplo.

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

mientras que |x| es la longitud de un vector x. Es decir, un punto se pondera con la mitad de la longitud al punto anterior y la mitad de la longitud al siguiente punto del polígono.

Este centroide C puede ahora proyectarse en la esfera por:

c' = r * c / |c|

mientras que r es el radio de la esfera.

Para considerar la orientación del polígono (ccw, cw) el resultado puede ser

c' = - r * c / |c|.

3voto

Sam Zhu Puntos 31

Lo siento (como nuevo usuario registrado) he tenido que escribir un nuevo post en lugar de votar/comentar la respuesta anterior de Don Hatch. Creo que la respuesta de Don es la mejor y la más elegante. Es matemáticamente rigurosa en el cálculo del centro de masa (primer momento de masa) de una manera sencilla cuando se aplica al polígono esférico.

La respuesta de Kobe John es una buena aproximación, pero sólo es satisfactoria para superficies pequeñas. También he observado algunos fallos en el código. En primer lugar, el punto de referencia debería proyectarse a la superficie esférica para calcular el área esférica real. En segundo lugar, la función points_are_equivalent() podría necesitar ser refinada para evitar la división por cero.

El error de aproximación del método de Kobe reside en el cálculo del centroide de los triángulos esféricos. El subcentroide NO es el centro de masa del triángulo esférico, sino el plano. Esto no es un problema si se trata de determinar ese único triángulo (el signo puede invertirse, véase más adelante). Tampoco es un problema si los triángulos son pequeños (por ejemplo, una triangulación densa del polígono).

Unas cuantas pruebas sencillas podrían ilustrar el error de aproximación. Por ejemplo, si utilizamos sólo cuatro puntos:

10 -20

10 20

-10 20

-10 -20

La respuesta exacta es (1,0,0) y ambos métodos son buenos. Pero si añades algunos puntos más a lo largo de una arista (por ejemplo, añade {10,-15},{10,-10}... a la primera arista), verás que los resultados del método de Kobe empiezan a cambiar. Más aún, si aumenta la longitud de [10,-10] a [100,-100], verá que el resultado de Kobe invierte la dirección. Una posible mejora podría ser añadir otro(s) nivel(es) para el cálculo del subcentroide (básicamente refinar/reducir el tamaño de los triángulos).

Para nuestra aplicación, el límite del área esférica se compone de múltiples arcos y por lo tanto no es un polígono (es decir, el arco no es parte de un gran círculo). Pero esto sólo será un poco más de trabajo para encontrar el n-vector en la integración de la curva.

EDIT: Sustituyendo el cálculo del subcentroide por el que se da en Documento de Brock debería arreglar el método de Kobe. Pero no lo intenté sin embargo.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X