Desempenho no cálculo de estatísticas de varredura no PostGIS

9

Eu estou tentando calcular estatísticas raster (min, max, média) para cada polígono em uma camada de vetor usando PostgreSQL / PostGIS.

Esta resposta GIS.SE descreve como fazer isso, calculando a interseção entre o polígono e a varredura e calculando uma média ponderada: https://gis.stackexchange.com/a/19858/12420

Estou usando a seguinte consulta (onde demestá minha varredura, topo_area_su_regioné meu vetor e toidé um ID exclusivo:

SELECT toid, Min((gv).val) As MinElevation, Max((gv).val) As MaxElevation, Sum(ST_Area((gv).geom) * (gv).val) / Sum(ST_Area((gv).geom)) as MeanElevation FROM (SELECT toid, ST_Intersection(rast, geom) AS gv FROM topo_area_su_region,dem WHERE ST_Intersects(rast, geom)) foo GROUP BY toid ORDER BY toid;

Isso funciona, mas é muito lento. Minha camada vetorial possui recursos de 2489k, cada um levando cerca de 90ms para processar - levaria dias para processar toda a camada. A velocidade do cálculo não parece ser significativamente melhorada se eu calcular apenas o mínimo e o máximo (o que evita as chamadas para ST_Area).

Se eu fizer um cálculo semelhante usando Python (GDAL, NumPy e PIL), posso reduzir significativamente o tempo necessário para processar os dados, se, em vez de vetorizar a varredura (usando ST_Intersection), eu rasterizar o vetor. Veja o código aqui: https://gist.github.com/snorfalorpagus/7320167

Eu realmente não preciso de uma média ponderada - uma abordagem "se tocar, está dentro" é boa o suficiente - e estou razoavelmente certa de que é isso que está atrasando as coisas.

Pergunta : Existe alguma maneira de fazer com que o PostGIS se comporte dessa maneira? ou seja, para retornar os valores de todas as células da varredura que um polígono toca, em vez da interseção exata.

Eu sou muito novo no PostgreSQL / PostGIS, então talvez haja algo mais que não esteja fazendo certo. Estou executando o PostgreSQL 9.3.1 e o PostGIS 2.1 no Windows 7 (2.9GHz i7, 8GB de RAM) e aprimorei a configuração do banco de dados conforme sugerido aqui: http://postgis.net/workshops/postgis-intro/tuning.html

insira a descrição da imagem aqui

Snorfalorpagus
fonte
11
Eu editei minha resposta. Esqueci de dizer que o cruzamento na minha resposta é menos preciso.
23414 Stefan

Respostas:

11

Você está certo, usando ST_Intersectionretarda sua consulta perceptível.

Em vez de usá- ST_Intersectionlo, é melhor recortar ( ST_Clip) sua varredura com os polígonos (seus campos) e despejar o resultado como polígonos ( ST_DumpAsPolygons). Assim, cada célula raster será convertida em um pequeno retângulo de polígono com valores distintos.

Para receber min, max ou média dos despejos, você pode usar as mesmas instruções.

Esta consulta deve fazer o truque:

SELECT 
    toid,
    Min((gv).val) As MinElevation,
    Max((gv).val) As MaxElevation,
    Sum(ST_Area((gv).geom) * (gv).val) / Sum(ST_Area((gv).geom)) as MeanElevation
FROM (
    SELECT 
        toid,
        ST_DumpAsPolygons(ST_Clip(rast, 1, geom, true)) AS gv
    FROM topo_area_su_region,dem 
        WHERE ST_Intersects(rast, geom)) AS foo 
            GROUP BY toid 
            ORDER BY toid;

Na declaração, ST_Clipvocê define a varredura, a banda da varredura (= 1), o polígono e se o corte deve ser VERDADEIRO ou FALSO.

Além disso, você pode usar avg((gv).val)para calcular o valor médio.

EDITAR

O resultado da sua abordagem é o mais exato, mas o mais lento. Os resultados da combinação de ST_Clipe ST_DumpAsPolygonsestão ignorando as células de varredura que se cruzam com menos de 50% (ou 51%) de seu tamanho.

Essas duas capturas de tela de um cruzamento do CORINE Land Use mostram a diferença. Primeira foto com ST_Intersection, segunda foto com ST_Clipe ST_DumpAsPolygons.

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Stefan
fonte