Adquirindo velocidade semelhante ao ArcGIS no Postgis

62

Estou usando o Postgis 2.0 há 3/4 de um ano e, embora eu realmente goste de usá-lo, o tempo excessivo de processamento de consultas o tornou basicamente inutilizável para o meu caso de uso.

Eu costumo fazer geoprocessamento pesado em conjuntos de dados municipais que geralmente têm centenas de milhares de multipolígonos. Esses multipolígonos às vezes têm uma forma muito irregular e podem variar de 4 pontos a 78.000 pontos por multipolígono.

Por exemplo, quando cruzo um conjunto de dados de parcelas com 329.152 multipolígonos com um conjunto de dados de jurisdição contendo 525 multipolígonos, recebo as seguintes estatísticas para o tempo total consumido:

ArcGIS 10.0 (on same host with windows 7 OS): 3 minutes
Postgis:56 minutes (not including geometry pre-processing queries)

Em outras palavras, é necessário 1500% mais tempo para fazer essa interseção no Postgis do que no ArcGIS - e essa é uma das minhas consultas mais simples!

Uma das razões pelas quais o ArcGIS supostamente roda mais rápido é devido a melhores índices. Alguns programadores descobriram recentemente como esses índices funcionam e estou pensando se alguém sabe como criar esses índices no Postgis (ou criar tabelas que imitariam os índices). Talvez isso resolvesse a maioria dos problemas de velocidade no Postgis. Só espero que exista alguma maneira, especialmente porque o ArcGIS pode usar apenas 4 GB de RAM, enquanto eu poderia usar até 4 vezes o valor do meu servidor postgis!

Claro que existem muitas razões pelas quais o postgis pode rodar lentamente, portanto fornecerei uma versão detalhada das especificações do meu sistema:

Machine: Dell XPS 8300 
Processor: i7-2600 CPU @ 3.40 GHz 3.40 GHz 
Memory: Total Memory 16.0 GB (10.0 GB on virtual machine)

Platform: Ubuntu Server 12.04 Virtual Box VM

Potgres Version: 9.1.4
Postgis Version: POSTGIS="2.0.1 r9979" GEOS="3.3.5-CAPI-1.7.5" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.1, released 2012/05/15" LIBXML="2.7.8" LIBJSON="UNKNOWN" TOPOLOGY RASTER

Também detalho todo o processo de instalação que usei para configurar o postgis, incluindo a criação da própria VM .

Também aumentei a memória compartilhada dos 24 MB padrão para 6 GB no arquivo conf e executei os seguintes comandos para permitir a execução do postgres:

sudo sysctl -w kernel.shmmax=7516192768 (I know this setting is deleted every time you restart the OS)
sudo /etc/init.d/postgresql restart

Até onde eu sei, isso não faz absolutamente nada perceptível em termos de desempenho.

Aqui estão os links para os dados que usei para este teste:

  1. Parcelas: tcad_parcels_06142012.shp.zip de City of Austin, TX
  2. Jurisdições: Limites Jurisdicionais de City of Austin, TX

Aqui estão as etapas que tomei para processar os dados:

ArcGIS

  1. Adicionar conjuntos de dados ao ArcMap
  2. Defina o sistema de coordenadas como central texas feet (srid 2277)
  3. Use a ferramenta de interseção no menu suspenso

Postgis

Importe parcelas usando:

shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "tcad_parcels_06142012.shp" "public"."tcad_parcels_06142012" |psql -d postgis_testing -U postgres -h local_ip -p 5432

Importar jurisdições usando:

shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "jurisdictions.shp" "public"."jurisdictions" |psql -d postgis_testing -U postgres -h local_ip -p 5432

Geometria inválida limpa em parcelas:

DROP TABLE IF EXISTS valid_parcels;
CREATE TABLE valid_parcels(
  gid serial PRIMARY KEY,
  orig_gid integer,
  geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_parcels USING gist (geom);
INSERT INTO valid_parcels(orig_gid,geom)
  SELECT 
    gid 
    orig_gid,
    st_multi(st_makevalid(geom)) 
  FROM 
    tcad_parcels_06142012;
CLUSTER valid_parcels USING valid_parcels_geom_idx;

Geometria inválida limpa nas jurisdições:

DROP TABLE IF EXISTS valid_jurisdictions;
CREATE TABLE valid_jurisdictions(
  gid serial PRIMARY KEY,
  orig_gid integer,
  geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_jurisdictions USING gist (geom);
INSERT INTO valid_jurisdictions(orig_gid,geom)
  SELECT 
    gid 
    orig_gid,
    st_multi(st_makevalid(geom)) 
  FROM 
    jurisdictions;
CLUSTER valid_jurisdictions USING valid_jurisdictions_geom_idx;

Execute o cluster:

cluster;

Execute a análise de vácuo:

vacuum analyze;

Execute a interseção em tabelas limpas:

CREATE TABLE parcel_jurisdictions(
  gid serial primary key,
  parcel_gid integer,
  jurisdiction_gid integer,
  isect_geom geometry(multipolygon,2277)
);
CREATE INDEX ON parcel_jurisdictions using gist (isect_geom);

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
  SELECT
    a.orig_gid parcel_gid,
    b.orig_gid jurisdiction_gid,
    st_multi(st_intersection(a.geom,b.geom))
  FROM
    valid_parcels a, valid_jurisdictions b
  WHERE
    st_intersects(a.geom,b.geom);

Explicar Analisar a consulta de interseção:

Total runtime: 3446860.731 ms
        Index Cond: (geom && b.geom)
  ->  Index Scan using valid_parcels_geom_idx on valid_parcels a  (cost=0.00..11.66 rows=2 width=1592) (actual time=0.030..4.596 rows=1366 loops=525)
  ->  Seq Scan on valid_jurisdictions b  (cost=0.00..113.25 rows=525 width=22621) (actual time=0.009..0.755 rows=525 loops=1)
Nested Loop  (cost=0.00..61428.74 rows=217501 width=24213) (actual time=2.625..3445946.889 rows=329152 loops=1)
  Join Filter: _st_intersects(a.geom, b.geom)

De tudo o que li, minha consulta de interseção é eficiente e não faço a menor idéia do que estou fazendo de errado para que a consulta demore 56 minutos em geometria limpa!

THX1138
fonte
2
É uma linguagem comum no PostGIS adicionar uma verificação de interseção de caixa delimitadora para acelerar as coisas. Tente adicionar 'AND a.geom && b.geom' à sua cláusula WHERE e veja quanta diferença isso faz.
21412 Sean
2
st_intersects () inclui uma consulta de caixa delimitadora antes de executar qualquer teste de interseção no postgis 2.x, infelizmente, isso não economiza tempo.
THX1138
11
Você pode executar sua consulta usando EXPLAIN ANALYZE e postar os resultados #
Nathan W
11
você também deve estar ciente de que está executando conjuntos de dados diferentes no postgis vs arcgis, uma vez que diz que precisa fazer com que eles sejam válidos para serem aceitos pelo postgis.
Nicklas Avén
2
É possível obter os conjuntos de dados para dar uma olhada?
Nicklas Avén 13/08/2012

Respostas:

87

Abordagem diferente. Saber que a dor está no ST_Intersection e que os testes true / false são rápidos, tentar minimizar a quantidade de geometria que passa pela interseção pode acelerar as coisas. Por exemplo, as parcelas totalmente contidas em uma jurisdição não precisam ser cortadas, mas ST_Intersection provavelmente ainda terá o trabalho de criar parte da sobreposição de interseção antes de perceber que não precisa gerar nenhuma nova geometria. Então, é isso

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,

  st_multi(st_intersection(a.geom,b.geom)) AS geom
FROM
  valid_parcels a, valid_jurisdictions b
WHERE
  st_intersects(a.geom, b.geom) and not st_within(a.geom, b.geom)
UNION ALL
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,
  a.geom AS geom
FROM
  valid_parcels a, valid_jurisdictions b
WHERE
  st_within(a.geom, b.geom);

Ou mesmo terser

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,
  CASE 
     WHEN ST_Within(a.geom,b.geom) 
     THEN a.geom
     ELSE ST_Multi(ST_Intersection(a.geom,b.geom)) 
  END AS geom
FROM valid_parcels a
JOIN valid_jurisdictions b
ON ST_Intersects(a.geom, b.geom)

Pode até ser mais rápido sem a UNIÃO.

Paul Ramsey
fonte
13
Obrigado que me reduz a 3,63 minutos! Eu nunca pensei que um sindicato seria mais rápido. Essa resposta realmente me fará repensar a maneira como faço consultas a partir de agora.
THX1138
2
Isso é muito legal. Eu tive um caso no trabalho onde minha consulta ST_Intersection foi 30mins take + e agora eu sei como posso evitar que :)
Nathan W
11
esta pergunta me fez aprender Postgis! i vai dormir ombro bem hoje vendo Postgis executar a ombro com o ArcGIS :-)
Vinayan
2
Mais um aprimoramento de Martin Davis, você poderia incluir o "in ou out?" pergunta no SELECT usando uma instrução CASE e evite a UNION dessa maneira.
Paul Ramsey
2
UNIONelimina linhas duplicadas das duas consultas, mas essas duas consultas não podem ter a mesma linha no conjunto de resultados. Portanto UNION ALL, o que ignora a verificação duplicada, seria apropriado aqui. (Eu não uso UNIONmuito, mas eu descobrir que geralmente fora das vezes eu faço, eu muito mais freqüentemente usam UNION ALL.)
jpmc26
4

O que aconteceria se você omitir a "st_multi(st_intersection(a.geom,b.geom))"parte?

A consulta abaixo não significa a mesma coisa sem ela? Eu executei nos dados que você forneceu.

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
  SELECT
    a.orig_gid parcel_gid,
    b.orig_gid jurisdiction_gid,
    a.geom
  FROM
    valid_parcels a, valid_jurisdictions b
  WHERE
    st_intersects(a.geom,b.geom);

Configuração

Processor: AMD Athlon II X4 635 2.9 GHz 
Memory: 4 GB
Platform: Windows 7 Professional
Potgres Version: 8.4
Postgis Version: "POSTGIS="2.0.1 r9979" GEOS="3.3.5-CAPI-1.7.5" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.1, released 2012/05/15" LIBXML="2.7.8" LIBJSON="UNKNOWN" TOPOLOGY RASTER"

Analisar resultados

"Nested Loop  (cost=0.00..7505.18 rows=217489 width=1580) (actual time=1.994..248405.616 rows=329150 loops=1)"
"  Join Filter: _st_intersects(a.geom, b.geom)"
"  ->  Seq Scan on valid_jurisdictions b  (cost=0.00..37.25 rows=525 width=22621) (actual time=0.054..1.732 rows=525 loops=1)"
"  ->  Index Scan using valid_parcels_index on valid_parcels a  (cost=0.00..11.63 rows=2 width=1576) (actual time=0.068..6.423 rows=1366 loops=525)"
"        Index Cond: (a.geom && b.geom)"
"Total runtime: 280087.497 ms"
vinayan
fonte
Não, ele deseja os polígonos de interseção resultantes, mas sua consulta demonstra muito bem que toda a dor está na geração de interseções, não na parte binária de teste verdadeiro / falso da consulta. E isso é bastante esperado, já que o código verdadeiro / falso é altamente otimizado, enquanto a geração de interseção não é.
Paul Ramsey