Triangular geometria já foi uma "coisa" em um tempo.
Pode ser feito se você tiver a extensão 3D Analyst, mas se não tiver e precisar entender os princípios e souber trabalhar com NumPy e python... aqui vai.
1 faça um array a partir da geometria (FeatureClassToNumPyArray é um começo. Já postei código antes sobre como fazer isso
2 triangule (eu uso scipy)
3 obtenha o centróide de cada triângulo. (novamente, já postei código para isso.)
4 verifique se os centróides estão dentro da forma original.
Agora, para complicar, polígonos são convexos ou côncavos, com ou sem buracos e podem consistir em mais de uma parte. Então às vezes, você precisa decompor sua geometria para sua forma mais simples e iterar. Formas convexas são chatas, então vou ignorá-las.
Aqui estão 3 formas, C, D, A. C é uma forma côncava sem buracos. D é convexo mas tem um buraco. A é côncavo e tem um buraco.
Suas versões trianguladas.
E a triangulação restrita. Ou seja, a triangulação com os triângulos que não estão no casco externo original ou em um buraco.
Parece que está tudo certo.
Para realizar a triangulação, você pode usar
from scipy.spatial import Delaunay
def triangulate_pnts(pnts):
"""Triangular os pontos e retornar os triângulos.
Parâmetros
----------
pnts : array
Pontos para uma forma ou um grupo de pontos em formato de array.
Pode ser geo.shapes ou np.ndarray.
out : array
Um array de pontos dos triângulos.
.. note::
Os simplices são ordenados no sentido anti-horário, isso é invertido nesta
implementação.
"""
pnts = np.unique(pnts, axis=0) # obter apenas os pontos únicos
avg = np.mean(pnts, axis=0)
p = pnts - avg
tri = Delaunay(p)
simps = tri.simplices
# -- armazenador de índices, preencher com índices, repetir o primeiro e deslocar CL
# traduzir os pontos de volta
z = np.zeros((len(simps), 4), dtype='int32')
z[:, :3] = simps
z[:, 3] = simps[:, 0]
tmp_ = p[z] + avg
new_pnts= []
for i in tmp_: # reordenar no sentido horário
if _bit_area_(i) < 0.0: # -- 2025_10_27
i = i[::-1]
new_pnts.append(i)
return new_pnts
def _bit_area_(a):
"""Mini e_area, usado por `areas` e `centroids`.
Áreas negativas são buracos. Isso é intencionalmente invertido da
fórmula `shoelace`.
"""
a = _base_(a)
x0, y1 = (a.T)[:, 1:] # cruzamento configurado da seguinte forma
x1, y0 = (a.T)[:, :-1]
e0 = np.einsum('...i,...i->...i', x0, y0) # 2024-03-28 modificado
e1 = np.einsum('...i,...i->...i', x1, y1)
return np.sum((e0 - e1) * 0.5)
def _area_centroid_(a):
r"""Calcular área e centróide para um polígono de parte única, `a`.
Isso também é usado para calcular área e centróide para as partes de um array Geo.
Notas
-----
Para formas multipartes, use esta sintaxe:
>>> # retângulo com buraco
>>> a0 = np.array([[[0., 0.], [0., 10.], [10., 10.], [10., 0.], [0., 0.]],
[[2., 2.], [8., 2.], [8., 8.], [2., 8.], [2., 2.]]])
>>> [_area_centroid_(i) for i in a0]
>>> [(100.0, array([ 5.00, 5.00])), (-36.0, array([ 5.00, 5.00]))]
"""
a = _base_(a)
x0, y1 = (a.T)[:, 1:]
x1, y0 = (a.T)[:, :-1]
e0 = np.einsum('...i,...i->...i', x0, y0)
e1 = np.einsum('...i,...i->...i', x1, y1)
t = e1 - e0
area = np.sum((e0 - e1) * 0.5)
x_c = np.sum((x1 + x0) * t, axis=0) / (area * 6.0)
y_c = np.sum((y1 + y0) * t, axis=0) / (area * 6.0)
return area, np.asarray([-x_c, -y_c])
Aqui está C
C
array([[ 0.00, 0.00],
[ 0.00, 10.00],
[ 10.00, 10.00],
[ 10.00, 8.00],
[ 2.00, 8.00],
[ 2.00, 2.00],
[ 10.00, 2.00],
[ 10.00, 0.00],
[ 0.00, 0.00]])
Os centróides, áreas e triangulações podem ser determinados usando o acima.
Testar quais triângulos fazem parte da geometria original envolve usar uma busca ponto-em-polígono. Eu fiz isso há muito tempo na minha encarnação anterior.
'Point in Polygon ... Geometry Mysteries' - Esri Community (não traduzir título do artigo).
'Ele usa a abordagem do número de voltas.' (não traduzir termos técnicos específicos).
'Então se você precisar de uma triangulação Delaunay restrita dos seus objetos de geometria... experimente.'
'Nota:'
'Se quiser ver mais coisas em python, NumPy e ArcPy veja meu site no github.'
'Dan Patterson no github'