quarta-feira, 16 de setembro de 2015

OpenGIS + Base do IBGE com PostgreSQL (PostGIS)

Existe disponível no IBGE a base de dados Cartográfica Contínua do Brasil na escala de 1:250.000, chamada de BC250. Estes dados são de 2013.


A Base Cartográfica Contínua serve como referência cartográfica para as ações de planejamento, monitoramento e gestão territorial do país, e está modelada conforme as Especificações Técnicas para Estruturação de Dados Geoespaciais Vetoriais na versão 2.1 (ET-EDGV v2.1) (IBGE).

A BC250 foi disponibilizada pelo IBGE em dois formatos:
  • Em um formato de banco de dados proprietário (*.mdb), usado pelo ArcMap;
  • E em PostGIS;
Os arquivos compactados com zip são no formado mdb, que nesse caso, lido pelo Access da Microsoft, e pode ser importada ou migrada para outras bases.
O que vai interessar aqui é o dump da base do PostGIS. Para tanto, vou colocar aqui os primeiros passos para instalação do postgres, principalmente no MacOS, que foi onde instalei, mas os passos são os mesmos, seja linux, ou windows. o que vai mudar, é claro, são alguns comandos ou procedimentos referentes, principalmente, à instalação do postgres, mas os links valem para todos os SO's.

Instalação do Postgres com PostGIS

O PostGIS, PostgreSQL + PostGIs podem ser encontrados nesse link http://postgis.net/install , recomendo baixar a versão binária, seja para Windows, Mac ou Linux.
Para instalar no seu SO, basta seguir as informações no link acima, normalmente, a instalação é bem automatizada.
No MacOs não vem por padrão com uma IDE de acesso ao Postgres, para tanto podem ser baixadas em http://postgresapp.com/documentation/gui-tools.html, pessoalmente recomendo mesmo a PgAdmin. No Windows, ela já vem com o pacote de instalação, se não me engano. No linux também tem que ser instalada a parte.
O PostGIS já vem instalado com o Postgres, mas não vem habilitado. Para habilitá-lo deve-se em cada base de dados criada, executar os comandos abaixo:
-- Enable PostGIS (includes raster)
CREATE EXTENSION postgis;
-- Enable Topology
CREATE EXTENSION postgis_topology;
-- fuzzy matching needed for Tiger
CREATE EXTENSION fuzzystrmatch;
-- Enable US Tiger Geocoder
CREATE EXTENSION postgis_tiger_geocoder;

Após executar esses comandos no banco de dados recém criado, o PostGIS estará habilitado para ser usado. Lembrando novamente, para cada nova base de dados, deve-se executar os comandos acima.
Exemplo básico de uso do SQL Spartial, mais informações podem ser encontradas em http://postgis.net/documentation ou http://postgis.net/docs/manual-2.1/
-- Create table with spatial column
CREATE TABLE mytable ( 
  id SERIAL PRIMARY KEY,
  geom GEOMETRY(Point, 26910),
  name VARCHAR(128)
); 
 
-- Add a spatial index
CREATE INDEX mytable_gix
  ON mytable 
  USING GIST (geom); 
 
-- Add a point
INSERT INTO mytable (geom) VALUES (
  ST_GeomFromText('POINT(0 0)', 26910)
);
 
-- Query for nearby points
SELECT id, name
FROM mytable
WHERE ST_DWithin(
  geom, 
  ST_GeomFromText('POINT(0 0)', 26910),
  1000
); 

Bom, instalado o PostGIS e habilitado em uma nova base, vamos agora fazer o download do dump da base do IBGE, a BC250.

Obtendo os Dados

Os arquivos de dados do IBGE podem ser baixados no endereço abaixo,

Acima uma prévia do deve ser encontrado no ftp do IBGE. O arquivo que vai interessar é o bc250_pgis20_dump.zip. No caso ele possui cerca de 1.1Gb compactado.
Façam o download e descompactem.

Restaurando a base de dados do IBGE - BC250

A restauração da base não é simplesmente executar o pg_dump. Precisamos configurar algumas coisas antes, vamos lá:
1. Os dados podem ser restaurados para qualquer base de dados, sendo assim, deve-se criar uma base de dados nova no PostGIS. Não esqueçam de habilitar o PostGIS, como descrito no início desse artigo, que no meu caso chamei de gisibge.
2. Obrigatoriamente, deve-se criar um schema nessa base de dados nova, chamada de bc250. Deve-se também criar um usuário chamado ccar. Abaixo os comandos, lembrando de trocar o nome da base de gisibge para o nome do seu database :
1
2
3
4
5
6
7
8
9
--Logando como superusuário
sudo su
su postgres
--Logando à base de dados
psql gisibge
-- Criando o shema
CREATE SCHEMA bc250;
--Criando o usuário
CREATE USER ccar;

3. Editar o arquivo restore.sql que foi descompactado e substituir a variável “$$PATH$$” para o caminho onde os arquivos foram descompactados. Nesse caso recomendo o localizar e substituir. Esse passo é necessário pois os dados de cada tabela estão em arquivos separados, ou seja os insert's. 

Outra coisa que foi necessária alterar no arquivo restore.sql foi o tamanho dos campos que são varchar(3) pois dava erro na importação, sendo assim, fiz uma substituição de character varying(3) para character varying(30), e com isso não deu mais erro.

4. Executar o comando de importar os dados para o Postgres.
1
psql -d gisibge -f restore.sql
A restore vai demorar um pouco devido ao tamanho e quantidade de informações existentes.
No FTP existe um arquivo, pdf que possui as informações e detalhes da organização do Base do IBGE.

Criando novas tabelas e importando dados

O objetivo de ter estudado o PostGIS e importado a base do IBGE é para o Mestrado que estou fazendo, em uma disciplina. Para tanto, abaixo vou colocar o que foi feito para o trabalho, como forma de exemplificar o seu uso, e entender o que pode ser feito a título de exemplos de seu uso.
Foram criadas novas tabelas tblcidades, tblestados, tblrios, tblcidade_localizacao.
A diferença entre tblcidades e tblcidade_localização é que a primeira possui a geometria da cidade, e a segunda somente a coordenada de localização e a informação se a cidade é capital, e foi utilizada de forma temporária somente.


CREATE TABLE public.tblcidade_localizacao
(
  intcidadeid integer,
  bolcapital integer,
  strcidade character varying(80),
  gislocalizacao geometry
)
WITH (
  OIDS=FALSE
);



CREATE TABLE public.tblcidades
(
  intcidadeid integer NOT NULL,
  bolcapital integer NOT NULL DEFAULT 0,
  strcidade character varying(80),
  gislocalizacao geometry NOT NULL,
  CONSTRAINT tblcidades_pkey PRIMARY KEY (intcidadeid)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE public.tblcidades
  OWNER TO thiago;

-- Index: public.idx_cidades_001

-- DROP INDEX public.idx_cidades_001;

CREATE INDEX idx_cidades_001
  ON public.tblcidades
  USING gist
  (gislocalizacao);

-- Index: public.idx_cidades_002

-- DROP INDEX public.idx_cidades_002;

CREATE INDEX idx_cidades_002
  ON public.tblcidades
  USING btree
  (strcidade COLLATE pg_catalog."default");

-- Index: public.idx_cidades_003

-- DROP INDEX public.idx_cidades_003;

CREATE INDEX idx_cidades_003
  ON public.tblcidades
  USING btree
  (bolcapital);



CREATE TABLE public.tblestados
(
  intestadoid integer NOT NULL,
  strestado character varying(80),
  strsigla character(2),
  intgeocodigoid integer,
  gislocalizacao geometry NOT NULL,
  CONSTRAINT tblestados_pkey PRIMARY KEY (intestadoid)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE public.tblestados
  OWNER TO thiago;

-- Index: public.idx_estados_001

-- DROP INDEX public.idx_estados_001;

CREATE INDEX idx_estados_001
  ON public.tblestados
  USING btree
  (strestado COLLATE pg_catalog."default");

-- Index: public.idx_estados_002

-- DROP INDEX public.idx_estados_002;

CREATE INDEX idx_estados_002
  ON public.tblestados
  USING gist
  (gislocalizacao);

-- Index: public.idx_estados_003

-- DROP INDEX public.idx_estados_003;

CREATE INDEX idx_estados_003
  ON public.tblestados
  USING btree
  (strsigla COLLATE pg_catalog."default");

-- Index: public.idx_estados_004

-- DROP INDEX public.idx_estados_004;

CREATE INDEX idx_estados_004
  ON public.tblestados
  USING btree
  (intgeocodigoid);


CREATE TABLE public.tblrios
(
  intrioid integer NOT NULL,
  strrio character varying(80),
  strsalinidade character varying(30),
  strregime character varying(30),
  strtipotrechomassa character varying(30),
  gislocalizacao geometry NOT NULL,
  CONSTRAINT tblrios_pkey PRIMARY KEY (intrioid)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE public.tblrios
  OWNER TO thiago;

-- Index: public.idx_rios_001

-- DROP INDEX public.idx_rios_001;

CREATE INDEX idx_rios_001
  ON public.tblrios
  USING btree
  (strrio COLLATE pg_catalog."default");

-- Index: public.idx_rios_002

-- DROP INDEX public.idx_rios_002;

CREATE INDEX idx_rios_002
  ON public.tblrios
  USING gist
  (gislocalizacao);

-- Index: public.idx_rios_003

-- DROP INDEX public.idx_rios_003;

CREATE INDEX idx_rios_003
  ON public.tblrios
  USING btree
  (strsalinidade COLLATE pg_catalog."default");

-- Index: public.idx_rios_004

-- DROP INDEX public.idx_rios_004;

CREATE INDEX idx_rios_004
  ON public.tblrios
  USING btree
  (strregime COLLATE pg_catalog."default");

-- Index: public.idx_rios_005

-- DROP INDEX public.idx_rios_005;

CREATE INDEX idx_rios_005
  ON public.tblrios
  USING btree
  (strtipotrechomassa COLLATE pg_catalog."default");


Após a criação das tabelas, foi feita a importação dos dados diretamente da base do IBGE importada.

 


insert into tblcidade_localizacao  (intcidadeid, strcidade, gislocalizacao, bolcapital)
select id_objeto, nome, geom, 1 as bolcapital from bc250.loc_capital_p
union
select id_objeto, nome, geom, 0 as bolcapital from bc250.loc_cidade_p;


insert into tblcidades  (intcidadeid, strcidade, gislocalizacao, bolcapital)
select id_objeto, nome, geom, 0 as bolcapital from bc250.lim_municipio_a ;


insert into tblestados  (intestadoid, strestado, strsigla, intgeocodigoid, gislocalizacao)
select id_objeto, nome, sigla,  geocodigo::int, geom from bc250.lim_unidade_federacao_a ;

insert into tblrios(intrioid, strrio, strsalinidade, strtipotrechomassa, strregime, gislocalizacao)

select id_objeto, nome, salinidade, tipotrechomassa, regime, geom from bc250.hid_trecho_massa_dagua_a ;




Foi feito um update para atualizar o campo bolcapital 

update tblcidades set bolcapital = 1
where
ST_Contains(tblcidades.gislocalizacao, (select gislocalizacao from tblcidade_localizacao where bolcapital =1
and replace(strcidade, '''', '')  = tblcidades.strcidade) );

Nesse caso foi utilizada a função ST_Contains para pegar qual a capital que pertente, está contida, ao estado. Aqui foi onde foi utilizada a tabela tblcidade_localizacao.


Foram criadas as seguintes estruturas: 1 view e 2 funções como forma de explorar um pouco as funcionalidades do postgres.

Abaixo os códigos das mesmas:


CREATE OR REPLACE VIEW public.viewcidades AS 
 SELECT tblcidades.intcidadeid,
    tblcidades.bolcapital,
    tblcidades.strcidade,
    tblcidades.gislocalizacao,
    tblestados.intestadoid,
    tblestados.strestado,
    tblestados.strsigla,
    tblestados.gislocalizacao AS gisestado,
    tblcapitais.strcidade AS strcapital,
    tblcapitais.gislocalizacao AS giscapital,
    st_distance_sphere(st_centroid(tblcapitais.gislocalizacao), st_centroid(tblcidades.gislocalizacao)) / 1000::double precision AS numdistanciacapital,
    st_astext(st_centroid(tblcidades.gislocalizacao)) AS numcoordenadas,
    st_astext(st_centroid(tblcapitais.gislocalizacao)) AS numcoordenadascapital,
    st_astext(st_centroid(tblestados.gislocalizacao)) AS numcoordenadas_estado,
    st_area(tblcidades.gislocalizacao) AS numareacidade,
    st_area(tblcapitais.gislocalizacao) AS numareacapital,
    st_area(tblestados.gislocalizacao) AS numareaestado
   FROM tblcidades
     JOIN tblestados ON st_within(tblcidades.gislocalizacao, tblestados.gislocalizacao)
     JOIN tblcidades tblcapitais ON st_within(tblcapitais.gislocalizacao, tblestados.gislocalizacao) AND tblcapitais.bolcapital = 1;



Nesse caso, foram usadas as funções ST_Within para pegar quais cidades pertencem ao estado e qual capital pertence ao estado.  Além dessas as seguintes:
st_distance_sphere: Calcular a distância, nesse caso, da cidade até a capital do estado.
st_centroid: Utilizar o centro das cidades para calcular a distância.
st_astext: Converter a geometria salva, em formato binário, para formato texto e assim, identificar qual o tipo de geometria: Ponto, Poligono e etc. É bom ver a documentação no link mais acima.
st_area: Calcular a área da cidade, capital e do estado.


A função abaixo, fn_estadosvizinhos, serve para mostrar quais são os estados vizinhos ao desejado, e para isso foi utilizada a função st_Intersects.

Sua chamada é da seguinte forma: 
select * from fn_estadosvizinhos('MA')

CREATE OR REPLACE FUNCTION public.fn_estadosvizinhos(sigla character)
  RETURNS SETOF tblestados AS
$BODY$
BEGIN
RETURN QUERY select tbloutros.* from tblestados 
inner join tblestados tbloutros on st_Intersects(tblestados.gislocalizacao, tbloutros.gislocalizacao) and
tbloutros.intestadoid <> tblestados.intestadoid
where
tblestados.strsigla =  $1; 
RETURN ; 
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100
  ROWS 1000;



A função abaixo, fn_quaisrios, serve para mostrar quais são os  rios que passam por uma cidade ou por um estado, e para isso foi utilizada a função st_Intersects.

Sua chamada é da seguinte forma: 
select * from fn_quaisrios('MA') -- para o estado

select * from fn_quaisrios('Rosário') -- para uma cidade

CREATE OR REPLACE FUNCTION public.fn_quaisrios(local character varying)
  RETURNS SETOF tblrios AS
$BODY$
BEGIN
RETURN QUERY select tblrios.* from tblrios 
inner join tblestados on ST_Intersects(tblestados.gislocalizacao, tblrios.gislocalizacao)
where
strsigla =  local 
and
strrio is not null
union
select tblrios.* from tblrios 
inner join tblcidades on ST_Intersects(tblcidades.gislocalizacao, tblrios.gislocalizacao)
where
strcidade =  local
and
strrio is not null
order by
strrio; 
RETURN ; 
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100
  ROWS 1000;


um outro select interessante é calcular a área do rio que passa em uma cidade, por exemplo.


select tblrios.intrioid, strrio, strsalinidade, strregime, st_area(ST_Intersection(tblrios.gislocalizacao, tblcidades.gislocalizacao)) as arearionacidade  from tblrios 
inner join tblcidades on ST_Intersects(tblcidades.gislocalizacao, tblrios.gislocalizacao)
where
strcidade =  'Rosário' 
and
strrio is not null



Quem quiser fazer o download do esquema completo das tabelas criadas, views, funções e dados importados, faça aqui.







Nenhum comentário:

Postar um comentário