5.3. Tipos Geométricos em Python

Tipos Geométricos

A biblioteca Shapely fornece o modelo geométrico e as operações espaciais da OGC-SFS (Seção 5.2) com um estilo Pythonico. Ela utiliza a biblioteca C++ denominada GEOS para representação dos tipos geométricos bem como para as operações espaciais.

5.3.1. Instalação

Para realizar a instalação da Shapely com o gerenciador de pacotes da Anaconda, ative o ambiente virtual no qual deseja instalar a biblioteca e, em seguida, instale o pacote com o mesmo nome da biblioteca, como mostrado abaixo:

conda activate geospatial

conda install shapely

Para verificar se sua instalação foi realizada com sucesso, importe a biblioteca e verifique sua versão:

In [1]: import shapely

In [2]: shapely.__version__
Out[2]: '1.7.1'

Nota

Você deverá instalar a versão 1.7.1 ou superior.

5.3.2. Tipos Geométricos

A biblioteca Shapely suporta os tipos geométricos da OGC-SFS: Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon e GeometryCollection (Figura 5.3).

Como a biblioteca Shapely utiliza a GEOS, as operações suportadas não consideram a componente \(z\) em suas análises. Além disso, os tipos geométricos dessa biblioteca utilizam números em ponto flutuante para representação das coordenadas dos seus elementos geométricos.

5.3.2.1. Pontos (Point)

Para criar objetos do tipo Point é preciso importar esse tipo a partir do módulo geometry do pacote shapely:

from shapely.geometry import Point

O construtor de um objeto Point aceita um par de valores \(x\) e \(y\) e, opcionalmente, um valor de \(z\):

In [1]: pt = Point(5.0, 3.0)

As coordenadas de um ponto podem ser acessadas através dos atributos x e y:

In [1]: pt.x
Out[1]: 5.0
In [1]: pt.y
Out[1]: 3.0

Em um jupyter notebook a expressão abaixo apresenta um figura representando o ponto:

In [1]: pt

Nota

O desenho mostrado acima é possível pois o ambiente Jupyter reconhece que a expressão acima pode ser apresentada por uma figura em SVG.

5.3.2.2. Linhas (LineString)

Assim como o tipo Point, o tipo LineString deve ser importado da seguinte forma:

from shapely.geometry import LineString

O construtor de um objeto LineString aceita como argumento uma sequência de tuplas (x, y) ou (x, y, z):

line = LineString( [ (0, 0), (5, 2), (10, 9), (18, 10) ] )

As coordenadas x e y dos vértices da linha podem ser acessados na forma de arrays:

line.xy

Em um jupyter notebook a expressão abaixo apresenta um figura representando a linha:

line

Uma linha possui comprimento:

line.length

A fronteira da linha é formada pelos pontos inicial e final, portanto, por objetos \(0\)-dimensional:

line.boundary

Para acessar os elementos de uma linha podemos utilizar o atributo coords:

for c in line.coords:
    print(c)

Podemos usar a notação de slice de sequências com objetos do tipo LineString:

line.coords[0:2]
line.coords[1:]

5.3.2.3. Anel (LinearRing)

Para criar objetos do tipo LinearRing é preciso importar a classe LinearRing:

from shapely.geometry import LinearRing

O construtor de um objeto LinearRing aceita como argumento uma sequência de tuplas (x, y) ou (x, y, z):

anel = LinearRing( [ (0, 0), (10, 0), (10, 10), (0, 10), (0, 0) ] )

Nota

A sequência informada pode ser explicitamente fechada como no caso acima ou pode ser aberta. Neste último caso a sequência será implicitamente fechada.

Assim como no caso de linhas, as coordenadas de um anel podem ser acessadas na forma de arrays:

anel.xy

Em um jupyter notebook a expressão abaixo apresenta um figura representando o anel:

anel

Um anel possui comprimento:

anel.length

A fronteira de um anel é o conjunto vazio:

anel.boundary

5.3.2.4. Polígonos

Para criar objetos do tipo Polygon é preciso importar a classe Polygon:

from shapely.geometry import Polygon

O construtor de um objeto Polygon aceita dois argumentos. O primeiro, obrigatório, é uma sequência de tuplas (x, y) ou (x, y, z) que representa o anel externo do polígono. O segundo, opcional, é uma sequência de anéis e representa os anéis internos do polígono:

anel_externo = [ (0, 0), (10, 0), (10, 10), (0, 10), (0, 0) ]

anel_interno = [ (3, 3), (7, 3), (7, 7), (3, 7), (3, 3) ]

poly = Polygon(anel_externo, [anel_interno])

O anel externo pode ser acessado através do operador exterior:

poly.exterior

Os anéis internos podem ser acessados através da propriedade interiors:

poly.interiors
len(poly.interiors)
poly.interiors[0]
poly.interiors[0].xy

Em um jupyter notebook a expressão abaixo apresenta um figura representando o polígono:

poly

Um polígono possui comprimento:

poly.length

Um polígono possui área:

poly.area

A fronteira de um polígono é formada pelos anéis desse polígono, objetos \(1\)-dimensional:

poly.boundary

5.3.2.5. MultiPoint

Para criar objetos que representam coleções homogeneas de pontos é preciso importar a classe MultiPoint:

from shapely.geometry import MultiPoint

O construtor de um MultiPoint recebe como argumento uma sequência de valores (x, y) ou (x, y, z):

mpt = MultiPoint( [ (0, 0), (5, 5), (10, 0), (10, 10), (0, 10) ] )

Em um jupyter notebook a expressão abaixo apresenta um figura representando a coleção de pontos:

mpt

Os elementos da coleção podem ser acessados através da propriedade geoms:

for pt in mpt.geoms:
    print(pt.x, pt.y)

5.3.2.6. MultiLineString

Para criar objetos que representam coleções homogeneas de linhas é preciso importar a classe MultiLineString:

from shapely.geometry import MultiLineString

O construtor de uma MultiLineString recebe como argumento uma sequência de linhas:

mline = MultiLineString( [ [ (0, 0), (8, 2), (13, 9) ],
                           [ (21, 11), (30, -1) ] ])

Em um jupyter notebook a expressão abaixo apresenta um figura representando a coleção de linhas:

mline

Uma coleção de linhas possui comprimento:

mline.length

A fronteira de uma coleção de linhas abertas é o conjunto de pontos inicial e final de cada linha, ou seja, objetos \(0\)-dimensional:

mline.boundary

Os elementos das coleção podem ser acessados através da propriedade geoms:

for line in mline.geoms:
    print(line)

5.3.2.7. MultiPolygon

Para criar objetos que representam coleções homogeneas de polígonos é preciso importar a classe MultiPolygon:

from shapely.geometry import MultiPolygon

O construtor de um MultiPolygon recebe como argumento uma sequência de polígonos:

mpoly = MultiPolygon(
         [
           [
               [ (0, 0), (16, 0), (16, 10), (0, 10), (0, 0) ],
               [
                   [ (3, 1), (7, 1), (5, 7), (3, 1) ],
                   [ (8, 1), (12, 1), (10, 9), (8, 1) ]
               ]
           ],
           [
               [ (20, 0), (25, 0), (22, 10), (20, 0) ],
               []
           ]
         ] )

Em um jupyter notebook a expressão abaixo apresenta um figura representando a coleção de polígonos:

mpoly

O mesmo polígono acima pode ser construído de forma mais clara criando os polígonos intermediários e depois criando a coleção:

# Definição do primeiro polígono
shell_1 = LinearRing( [ (0, 0), (16, 0), (16, 10), (0, 10), (0, 0) ] )

hole_11 = LinearRing( [ (3, 1), (7, 1), (5, 7), (3, 1) ] )
hole_12 = LinearRing( [ (8, 1), (12, 1), (10, 9), (8, 1) ] )

poly_1 = Polygon( shell_1, [ hole_11, hole_12 ] )
# Definição do segundo polígono
shell_2 = LinearRing( [ (20, 0), (25, 0), (22, 10), (20, 0) ] )

poly_2 = Polygon(shell_2)
mpoly = MultiPolygon( [ poly_1, poly_2 ] )
mpoly

Uma coleção de polígonos possui comprimento:

mpoly.length

Uma coleção de polígonos possui area:

mpoly.area

A fronteira de uma coleção de polígonos é formada pelos anéis desses polígonos, objetos \(1\)-dimensional:

mpoly.boundary

5.3.3. Relacionamentos Espaciais

Qual o relacionamento espacial entre o ponto de coordenadas (2, 3) e a linha formada pelas coordenadas (1 1, 0 3, 4 3)?

pt = Point(2, 3)


line = LineString( [ (1, 1), (0, 3), (4, 3) ] )

O operador relate pode ser utilizado para determinar a matriz de intersecções, conforme discutido na seção x.x.x:

pt.relate(line)

Podemos também usar esse operador tomando a linha como o objeto da operação e o ponto como argumento:

line.relate(pt)

A linha contém o ponto?

line.contains(pt)

A linha toca o ponto?

line.touches(pt)

A linha cruza o ponto?

line.crosses(pt)

A linha possui alguma interação espacial com o ponto?

line.intersects(pt)

A linha definida anteriormente possui qual relacionamento com o polígono de coordenadas (1 2, 1 4, 3 4, 3 2, 1 2)?

poly = Polygon( [(1, 2), (1, 4), (3, 4), (3, 2), (1, 2)] )
line.relate(poly)
line.intersects(poly)
line.crosses(poly)

A linha (2 1, 2 2, 3 1) toca o polígono anterior?

l2 = LineString( [ (2, 1), (2, 2), (3, 1) ] )

poly.touches(l2)

5.3.4. Operações de Conjunto

p1 = Polygon( [ (1, 2), (1, 4), (3, 4), (3, 2), (1, 2) ] )

p2 = Polygon( [ (2, 1), (2, 3), (4, 3), (4, 1), (2, 1) ] )

g = p1.intersection(p2)

g.wkt
g = p1.union(p2)

g.wkt
g = p1.difference(p2)

g.wkt
g = p1.symmetric_difference(p2)

g.wkt

5.3.5. Formatos

5.3.5.1. OGC WKT (Well-Known Text)

line.wkt
pt.wkt
poly.wkt
g.wkt