PostGIS: analisar geometria wkb com OGR

8

Estou tentando extrair uma LineStringgeometria do PostGIS e analisá-la com OGR (python bindinds).

from osgeo import ogr
import psycopg2

connection = psycopg2.connect("...")
cursor = connection.cursor()

query = "SELECT geom FROM points LIMIT 1"

cursor.execute(query)
row = cursor.fetchone()

wkb = row[0]
geom = ogr.CreateGeometryFromWkb(wkb)

cursor.close()
connection.close()

Eu tentei:

wkb = bin(int(row[0], 16))

e:

SELECT ST_AsEWKB(geom) FROM points LIMIT 1

OGR não deseja analisá-lo. Continua dando o seguinte erro:

ERROR 3: OGR Error: Unsupported geometry type
ilia choly
fonte
2
Mistype nesta linha; verifique se ele não está no seu código: geom = org.CreateGeometryFromWkb(wkb)( ogrnão deve estar org).
Arthur

Respostas:

10

Internamente, o PostGIS armazena geometrias em uma especificação binária, mas é consultado e visto do lado de fora como uma string codificada em hexadecimal. Existem duas variações populares do binário conhecido (WKB) :

  • EWKB (via ST_AsEWKB) - uma especificação WKB estendida projetada pelo PostGIS .
  • OGC WKB (via ST_AsBinary) - especificado pelo OGC e ISO. Por um tempo que era 2D-only, mas mais tarde estendido para suporte Z, Me ZMgeometrias.

As duas características são idênticas para geometrias 2D, mas são diferentes para geometrias de ordem superior com Z, Me ZMcoordenadas.


As versões mais antigas do GDAL / OGR (1.x) compreendem apenas o EWKB para geometrias 3D, portanto, para esses eu recomendo o uso ST_AsEWKB. (Mas se você tiver apenas geometrias 2D, qualquer um dos formatos estará correto). Por exemplo:

import psycopg2
from osgeo import ogr

ogr.UseExceptions()    
conn = psycopg2.connect('dbname=postgis user=postgres')
curs = conn.cursor()

curs.execute("select ST_AsEWKB('POINT Z (1 2 3)'::geometry) AS g")
b = bytes(curs.fetchone()[0])
print(b.encode('hex'))  # 0101000080000000000000f03f00000000000000400000000000000840
g = ogr.CreateGeometryFromWkb(b)
print(g.ExportToWkt())  # POINT (1 2 3)

curs.execute("select ST_AsBinary('POINT Z (1 2 3)'::geometry) AS g")
b = bytes(curs.fetchone()[0])
print(b.encode('hex'))  # 01e9030000000000000000f03f00000000000000400000000000000840
g = ogr.CreateGeometryFromWkb(b)
# RuntimeError: OGR Error: Unsupported geometry type

Além disso, observe que as versões mais antigas do GDAL / OGR não oferecem suporte a Mcoordenadas e elas serão analisadas, mas ignoradas.


Com o GDAL 2.0 e mais recente , o ISO WKT / WKB é suportado . Isso significa que CreateGeometryFromWkbpode ler o sabor WKB (sem especificar) e ExportToIsoWkt()mostra a saída com uma sintaxe WKT moderna.

import psycopg2
from osgeo import ogr

ogr.UseExceptions()
conn = psycopg2.connect('dbname=postgis user=postgres')
curs = conn.cursor()

curs.execute("select ST_AsEWKB('POINT Z (1 2 3)'::geometry) AS g")
b = bytes(curs.fetchone()[0])
print(b.encode('hex'))  # 0101000080000000000000f03f00000000000000400000000000000840
g = ogr.CreateGeometryFromWkb(b)
print(g.ExportToIsoWkt())  # POINT Z (1 2 3)

curs.execute("select ST_AsBinary('POINT Z (1 2 3)'::geometry) AS g")
b = bytes(curs.fetchone()[0])
print(b.encode('hex'))  # 01e9030000000000000000f03f00000000000000400000000000000840
g = ogr.CreateGeometryFromWkb(b)
print(g.ExportToIsoWkt())  # POINT Z (1 2 3)

Além disso, o GDAL 2.1 ou posterior criará / exportará WKT / WKB com Mou ZMcoordenadas conforme o esperado.

Mike T
fonte
1
Para mim, é o contrário. Com ST_AsEWKB, recebo ERRO 3: Erro OGR: Tipo de geometria não suportado. Mas com ST_AsBinary ele lê bem: MULTILINESTRING ((-4.625433 40.682732, -4.6275242 40.6820109, -4.6293233 40.681392, -4.6301239 40.681117)))
HeikkiVesanto
9

Dentro do banco de dados, as geometrias são armazenadas no disco em um formato usado apenas pelo programa PostGIS. Para que programas externos insiram e recuperem geometrias úteis, eles precisam ser convertidos em um formato que outros aplicativos possam entender. Felizmente, o PostGIS suporta a emissão e o consumo de geometrias em um grande número de formatos:

da Introdução ao PostGIS

Com o formato WKB:

Binário conhecido (WKB):
ST_GeomFromWKB (bytea) retorna geometria
ST_AsBinary (geometry) retorna bytea
ST_AsEWKB (geometria) retorna bytea

ogr reconhece geometrias e não um resultado bytea ( ST_AsEWKB())

# result -> bytea format:
query = "SELECT ST_AsEWKB(geom) FROM points LIMIT 1"
# result -> geometry from bytea:
query = "SELECT ST_GeomFromWKB(ST_AsEWKB(geom)) from points LIMIT 1;"

Teste com uma das minhas tabelas:

nada:

query = """SELECT ST_AsText(ST_AsEWKB(geom)) from mytable;"""
cur = conn.cursor()
cur.execute(query)
row = cur.fetchone()
print row[0]
'01010000208A7A0000DD2571661A9B10410CCD751AEBF70241'

e uma geometria:

query = """SELECT ST_AsText(ST_GeomFromWKB(ST_AsEWKB(geom))) from mytable;"""
# result
cur.execute(query)
row = cur.fetchone()
print row
('POINT(272070.600041 155389.38792)',)

Então vamos tentar:

 query = """SELECT ST_AsText(ST_GeomFromWKB(ST_AsEWKB(geom))) from mytable;"""
 cur = conn.cursor()
 cur.execute(query)
 row = cur.fetchone()    
 wkb = row[0];
 geom = ogr.CreateGeometryFromWkb(wkb)
 ERROR 3: OGR Error: Unsupported geometry type

Por quê ?

Como o resultado da consulta é uma sequência:

'01010000208A7A0000DD2571661A9B10410CCD751AEBF70241'

e não um bytecode.

Você precisa decodificar essa sequência (consulte Criar geometria a partir do WKB no livro de receitas Python GDAL / OGR ).

É por isso que é muito mais fácil usar:

1) outros formatos de saída (WKT, GeoJSON, ...)

 query = """SELECT ST_AsGeoJSON(geom) from mytable;"""  
 cur.execute(query)
 row = cur.fetchone()
 point = ogr.CreateGeometryFromJson(row[0])
 print "%d,%d" % (point.GetX(), point.GetY())
 272070,155389

2) diretamente osgeo.ogr ( como converter a tabela PostGIS para Shapefile em Python?, Por exemplo)

gene
fonte
+1 para a solução ST_AsGeoJSON (geom).
Michael
6

Você desejará usar ST_AsBinary(geom)para converter sua geometria do formato interno PostGIS para WKB que você pode ler com ogr:

cur.execute('SELECT ST_AsBinary(geom) FROM mytable LIMIT 1')
result = cur.fetchone()

Nos termos do Postgres, seu resultado é a bytea. A biblioteca psycpopg2 mapeará isso para um memoryviewtipo Python:

>>>> type(result[0])
<class 'memoryview'>

Basta lançar seu memoryviewpara bytesler o WKB com ogr:

>>>>geom = ogr.CreateGeometryFromWkb(bytes(result[0]))
<osgeo.ogr.Geometry; proxy of <Swig Object of type 'OGRGeometryShadow *' at 0x0000000002D179F0> >

Se você está preocupado com a precisão numérica, evite definitivamente usá-lo ST_AsText(). Essa função converte sua geometria em WKT, truncando suas coordenadas com uma precisão que depende da sua versão e plataforma do PostGIS.

dbaston
fonte