CNEFE

De Documentação
Revisão de 11h11min de 1 de junho de 2024 por Peter (discussão | contribs) (→‎Pontos)

CNEFE - Cadastro Nacional de Endereços para Fins Estatísticos

Ver página oficial do IBGE/CNEFE.

Faces de Quadra

Pegar em 4314902_faces_de_logradouros_2021.prj o SRID da projeção usada.

Fonte em IBGE/base faces de quadra/acesso ao produto. Descritivo em Sobre a publicação.

Para POA utilizamos esses originais, com download de maio de 2024:

3ad3031f5503a4404af825262ee8232cc04d4ea6683d42c5dd0a2f2a27ac9824  4314902_faces_de_logradouros_2021.cpg
e55572718b6bc70f3f32bc58c81cecaf48aa1aebeec313ae20e567e81f3d235d  4314902_faces_de_logradouros_2021.dbf
c9b61f7c5c0d268153118af4183b06d943c1652e94e1201f513a6dcf0ab06567  4314902_faces_de_logradouros_2021.prj
de05db0b048dab811c900c28f6bde2c77f9e3d277011c03801390d20ef838a51  4314902_faces_de_logradouros_2021.shp
2c83a1e2f6c69e1ea331d59166dc2ff612206e42e7511a5bddc2d1fe2df9d07f  4314902_faces_de_logradouros_2021.shx

Falta conferir se usamos mesmo a fonte mais atual no DL Digital-guard. Conferir com https://www.ibge.gov.br/geociencias/organizacao-do-territorio/malhas-territoriais/28971-base-de-faces-de-logradouros-do-brasil.html Resumo das explicações na aba "Sobre".

mkdir /tmp/POA_FACES/

cd /tmp/POA_FACES

wget https://dl.digital-guard.org/5d5daa06e048ad8c83c909babee040e26fd8a54a29ed4cef2d2d220a3bd17b84.zip

zip -T 5d5daa06e048ad8c83c909babee040e26fd8a54a29ed4cef2d2d220a3bd17b84.zip

cd '/tmp/5d5daa06e048ad8c83c909babee040e26fd8a54a29ed4cef2d2d220a3bd17b84/rs_faces_de_logradouros_2021/'
cp 4314902* /tmp/POA_FACES

Supor arquivos logradouro em log:

create database poa;
\c poa
create extension postgis;
-----
shp2pgsql  log | psql -q postgres://postgres@localhost/poa

Melhorando os dados:

drop view if exists vw01_log ;
create view vw01_log as select *, regexp_replace( coalesce(nm_tip_log,'') || ' '||  coalesce(nm_tit_log,'') ||' '|| coalesce(nm_log,''), '\s+', ' ', 'g') as via_name from log;

select gid, cd_setor, cd_quadra, cd_face, via_name, tot_res from vw01_log limit 100;

Pontos

Por cidade temos Censo_Demografico_2022/Arquivos_CNEFE/Municipio, direto latLong em srid=4326.

Esta página dá acesso ao Excel de descrição, "Dicionário". Pelo cabeçalho podemos copiar para uma tabela texto, depois testar se a algumas converções de tipo são válidas.

CREATE TABLE ibge_cnefe2022 (
COD_UNICO_ENDERECO text,
COD_UF text,
COD_MUNICIPIO text,
COD_DISTRITO text,
COD_SUBDISTRITO text,
COD_SETOR text,
NUM_QUADRA text,
NUM_FACE text,
CEP text,
DSC_LOCALIDADE text,
NOM_TIPO_SEGLOGR text,
NOM_TITULO_SEGLOGR text,
NOM_SEGLOGR text,
NUM_ENDERECO text,
DSC_MODIFICADOR text,
NOM_COMP_ELEM1 text,
VAL_COMP_ELEM1 text,
NOM_COMP_ELEM2 text,
VAL_COMP_ELEM2 text,
NOM_COMP_ELEM3 text,
VAL_COMP_ELEM3 text,
NOM_COMP_ELEM4 text,
VAL_COMP_ELEM4 text,
NOM_COMP_ELEM5 text,
VAL_COMP_ELEM5 text,
LATITUDE real,
LONGITUDE real,
NV_GEO_COORD text, -- nivel de confiança na coordenada
COD_ESPECIE text,
DSC_ESTABELECIMENTO text,
COD_INDICADOR_ESTAB_ENDERECO text,
COD_INDICADOR_CONST_ENDERECO text,
COD_INDICADOR_FINALIDADE_CONST text,
COD_TIPO_ESPECI text
);
copy ibge_cnefe2022 from '/tmp/POA_FACES/4314902_PORTO_ALEGRE.csv' with  (format CSV, delimiter ';', header);

----
drop view if exists vw02_ibge_cnefe2022;
create view vw02_ibge_cnefe2022 as
 select *, ST_GeoHash(geom,9) as ghs9, ST_GeoHash(geom,10) as ghs10
 from (
  select *, regexp_replace( coalesce(nom_tipo_seglogr,'') ||' '|| coalesce(nom_titulo_seglogr,'') ||' '|| coalesce(nom_seglogr,''), '\s+',' ','g') as via_name,
         ST_SetSRID(ST_MakePoint(longitude::float, latitude::float),4326) as geom
  from ibge_cnefe2022
 ) t
;
-- teste de hipotese de ponto e endereço únicos:
-- select count(*) from (select distinct ghs9, cod_setor, num_quadra, num_face, via_name, num_endereco from vw02_ibge_cnefe2022) t; -- 379957. -- same where geom is not null and via_name>'' and num_endereco>'' ) t;
-- select 379957.0/762239; -- 49,847% = 50%

Avaliação pelo qualificador do IBGE, nv_geo_coord:

-- Por domicílio
with t as (select count(*) as nt from ibge_cnefe2022) select nv_geo_coord, count(*) n, round(100.0*count(*)/t.nt,2)::text||'%' as perc  from t,vw02_ibge_cnefe2022 group by 1,nt order by 1;

Os dois primeiros são válidos, os demais podem ser descartados:


Contagens gerais

select count(*) n_dist from (select distinct * from ibge_cnefe2022) t
; -- 762239
select count(*) n, count(distinct cod_unico_endereco) n_cod_uend, count(distinct ghs) n_ghs,
count(distinct latitude||','||longitude) n_latlon
from vw02_ibge_cnefe2022
; -- 762239 |     757512 | 350635 | 389028
select avg(n) avg,  stddev_pop(n) dev, count(*) tmultiplos, sum(n) as tot from (
 select cod_unico_endereco, count(*) n from ibge_cnefe2022 group by 1 having count(*)>1
) t; --  2.0045 | 0.07 | 4706 | 9433.  Conclusão: 2.01±0.07 , maioria dos 9433 com 1 duplicidade. 
select count(*) n_ghs9_nv from (select distinct nv_geo_coord, ghs9 from vw02_ibge_cnefe2022) t
; -- 358000
select count(*) n_ghs10_nv from (select distinct nv_geo_coord, ghs10 from vw02_ibge_cnefe2022) t
; -- 393565
select sqrt(avg(st_area(ST_GeomFromGeoHash(ghs9),true))) as ghs9_precisao,
       sqrt(avg(st_area(ST_GeomFromGeoHash(ghs10),true))) as ghs10_precisao
from vw02_ibge_cnefe2022
; -- ghs9_precisao = 4.437 ~ 5 metros de precisão no Geohash de 9 dígitos. 
  -- ghs10_precisao = 0.7844 ~ 1 metro no Geohash de 10 dígitos.
-- select round(100.0*x/762239) perc_x_of_n;

select compl1, count(*) n_casos FROM (
  select ghs9, array_agg(distinct nom_comp_elem1) compl1 from vw02_ibge_cnefe2022 group by 1 having count(*)>1
) t group by 1 order by 2 desc, 1;
select compl, count(*) n_casos FROM (
  select ghs10, array_agg(distinct coalesce(nom_comp_elem1,'')||coalesce(' '||nom_comp_elem2,'')) compl from vw02_ibge_cnefe2022 group by 1 having count(*)>1
) t group by 1 order by 2 desc, 1limit  10;
         compl         | n_casos 
-----------------------+---------
 {APARTAMENTO}         |   15620
 {""}                  |    6945
 {CASA}                |    5642
 {FRENTE,FUNDOS}       |    4616
 {"BLOCO APARTAMENTO"} |    3694
 {"",FUNDOS}           |    1285
 {"",CASA}             |    1231
 {FUNDOS}              |     877
 {SOBRADO,TERREO}      |     821
 {"",FRENTE}           |     349
                total  |   41080 = 5.4% de 762239; ou 10.6% de 389028.
Rótulo select count(criterio?)
from ibge_cnefe2022
n_doms perc n
n
= n_dist
* 762239 100%
n_cod_uend distinct cod_unico_endereco 757512 99.4%
n_latlon distinct latitude||','||longitude 389028 51%
n_ghs10 distinct ghs10 386857 51%
n_ghs9 distinct ghs9 350635 46%

Conclusões sobre o perfil de algumas contagens:

  • Identificadores únicos: era esperada a unicidade em 100% dos registros, ou seja, que n=n_cod_uend. Houve, todavia, uma diferença de 0,6% (100% - 99,4%), devido a casos de repetição. O valor de contagens superiores a 1 resultou em média de 2,01±0,07; portanto maioria dos 9433 identificadores problemáticos tem apenas mais 1 outro domicílio apresentando mesmo identificador.
  • A contagem de n_latlon, com apenas 51% ao invés de 100%, mostra a intensão do IBGE em incluir nos registros os casos de endereço horizontais iguais porém com complemento diferenciando, o que se confirma pelas contagens no perfil dos complementos (vide amostragem compl1 e compl2 em ~11% dos casos de n_latlon). A rigor o IBGE registrou "endereços domicíliares", não apenas "endereços de rua". Neste relatório denominaremos o primeiro de domicílio (dom) e o segundo de endereço de fato (end) ou ponto.
  • A escolha entre Geohashes de 9 ou 10 metros dependeria da precisão média dos pontos do IBGE. Como demonstra a nuvem de pontos, é bastante
  • A contagem de n_ghs9 fez uso de Geohashes de 9 dígitos, garantindo a precisão da ordem de 5 metros, ou seja, superior ao erro de GPS da núvem de pontos. A contagem n_ghs9 resultou em 46%, bem longe dos 51% de n_latlon, gerando talvez duplicidade maior do que esperada. Como a precisão é variável (depende dos satélites GPS e do tempo de espera do agente), sugere-se usar ghs10 ou manter o ponto original e só depois do translado para a face de quadra (que aumenta a precisão) fazer a contagem de Geohashes ghs10.
nv_geo_coord n_doms perc
1 = Endereço - coordenada original do Censo 2022 630603 82.73%
2 = Endereço - coordenada modificada (apartamentos em um mesmo número no logradouro). 119942 15.74%
3 = Endereço - coordenada estimada (endereços originalmente sem coordenadas ou coordenadas inválidas). 8900 1.17%
4 = Face de quadra. 2753 0.36%
5 = Localidade 4 0.00%
6 = Setor censitário 37 0.00%
-- por pontos únicos em ghs9
with t as (select count(distinct ghs9||nv_geo_coord) ntpt  from vw02_ibge_cnefe2022) select nv_geo_coord, count(distinct ghs9) n_ghs9s, round(100.0*count(distinct ghs9)/t.ntpt,2)::text||'%' as perc  from t,vw02_ibge_cnefe2022 group by 1,ntpt order by 1;
nv_geo_coord n_ghs9s perc
1 = Endereço - coordenada original do Censo 2022 343098 95.84%
2 = Endereço - coordenada modificada (apartamentos em um mesmo número no logradouro). 11224 3.14%
3 = Endereço - coordenada estimada (endereços originalmente sem coordenadas ou coordenadas inválidas). 2865 0.80%
4 = Face de quadra. 798 0.22%
5 = Localidade 3 0.00%
6 = Setor censitário 12 0.00%

Conclusão: como queremos filtrar até o nível de pontos únicos, há garantia de 99,9% dos endereços em condição adequada (tipos 1 ou 2).

Filtragens

As filtragens seguem o seguinte passo a passo:

  • Unicidade de endereço: DISTINCT cod_setor, cep, via_name, num_endereco
  • Unicidade de posição: DISTINCT ghs
  • Filtrando unicidades: primeiro de endereço, porém incluindo array de geohashes distintos. Se forem vizinhos não possuem mesmo prefixo conferir maior distancia entre eles.
CREATE FUNCTION hcodes_sameparent(g text[]) RETURNS boolean AS $f$
  SELECT  bool_and( CASE
     WHEN l1=l2 THEN substr(g1,1,l1-1)=substr(g2,1,l2-1)
     WHEN l1>l2 THEN g1 ~ ('^'||g2)  -- WHEN l1=l2+1
     ELSE g2 ~ ('^'||g1)  -- WHEN l2=l1+1  ELSE false
     END  )
  FROM ( 
    select g1, g2, length(g1) l1, length(g2) l2
    from unnest(g) WITH ORDINALITY AS a(g1,i1) 
    inner join unnest(g) WITH ORDINALITY AS b(g2,i2) 
      on i1<i2
    ) t1
$f$ LANGUAGE SQL;

CREATE FUNCTION geoms_maxdist(g geometry[], use_spheroid boolean default false) RETURNS float AS $f$
    SELECT MAX( ST_Distance(g1,g2,use_spheroid) )
    FROM unnest(g) WITH ORDINALITY AS a(g1,i1) 
    INNER JOIN unnest(g) WITH ORDINALITY AS b(g2,i2) 
      ON i1<i2
$f$ LANGUAGE SQL;

create table tmp_ibge_cnefe2022_uniq1 AS  -- unicidade básica, ainda com repetição de pontos
   SELECT DISTINCT ghs, via_name, num_endereco
   FROM vw02_ibge_cnefe2022
; -- 379228 de 762239 = select round(100.0*379228.0/762239); = 50%

select avg(n) from (select ghs, count(*) n from ibge_cnefe2022_uniq1 group by 1) t
where n=1 -- n>1
;
-- 325993 não-duplicados mais 2.16*24642 duplicados = 325993+2.16*24642 = 379220 ~ 379228.

Só na redução de pontos para 50% já é bem drástica, provavelmente inclui casos que diferenciam apenas no complemento. Desses, com duplicidade posicional apenas ~7,6% (24642 de 325993), sendo também razoável o seu descarte de tmp_ibge_cnefe2022_uniq1.

drop table if exists ibge_cnefe2022_uniq2;
create table ibge_cnefe2022_uniq2 AS  -- remove endereços duplicados em locais diferentes 
 select v.cod_setor, v.num_quadra, v.num_face, v.cep, v.via_name, v.num_endereco,
       array_agg(v.ghs) ghss, array_agg(v.geom) geoms
 from(  
   SELECT DISTINCT ON (ghs) ghs, via_name, num_endereco, count(*) OVER (PARTITION BY ghs) AS ghs_count
   FROM tmp_ibge_cnefe2022_uniq1 
 ) g -- 325993
 INNER JOIN vw02_ibge_cnefe2022 v ON 1=g.ghs_count AND g.ghs=v.ghs
group by 1,2,3,4,5,6
order by 1,2,3,4,5,6
; -- 257387


select count(*) from (select cep, via_name, num_endereco, hcodes_sameparent(ghss) as is_same from ibge_cnefe2022_uniq2 where cardinality(ghss)>1 ) where is_same;-- 49597
select count(*) from (select cep, via_name, num_endereco, hcodes_sameparent(ghss) as is_same from ibge_cnefe2022_uniq2 where cardinality(ghss)>1 ) where not(is_same); -- 22523

-- conclusão: vale a pena manter o same, removendo o último dígito, fornecendo um booleano para usuário decidir se valido ou não.
-- os casos de não same podem ser resgatados por geometria

select count(*), avg(dist) from (select geoms_maxdist(geoms,true) dist from (select cep, via_name, num_endereco, hcodes_sameparent(ghss) as is_same, geoms from ibge_cnefe2022_uniq2 where cardinality(ghss)>1 ) where not(is_same)) where dist<=20; 
-- 12193
select count(*), avg(dist) from (select geoms_maxdist(geoms,true) dist from (select cep, via_name, num_endereco, hcodes_sameparent(ghss) as is_same, geoms from ibge_cnefe2022_uniq2 where cardinality(ghss)>1 ) where not(is_same)) where dist<=5; 
-- 1848  (não compensa) 

select count(*), avg(dist) from (select geoms_maxdist(geoms,true) dist from (select cep, via_name, num_endereco, hcodes_sameparent(ghss) as is_same, geoms from ibge_cnefe2022_uniq2 where cardinality(ghss)>1 ) t2 where is_same) t3 
--  49597 | 2.67   (ok distancia pequena e numero expressivo)

select count(*), count(distinct via_name||num_endereco) from (select distinct  cod_setor, via_name, num_endereco, cep
from vw02_ibge_cnefe2022          
) t; -- 288685 | 277160

Tratamentos finais

drop table ibge_cnefe2022_uniq3;
CREATE TABLE  ibge_cnefe2022_uniq3 AS
 SELECT ghss[1] as ghs, cod_setor, num_quadra::int as num_quadra, num_face::int num_face,
        cep, via_name, num_endereco,
        false AS is_centroid
 FROM ibge_cnefe2022_uniq2 WHERE cardinality(ghss)=1

 UNION ALL

 SELECT st_geohash(geoms_centroid(geoms),9) as ghs,
        cod_setor, num_quadra::int, num_face::int, cep, via_name, num_endereco,
        true AS is_centroid
 FROM ibge_cnefe2022_uniq2 
 WHERE cardinality(ghss)>1
       AND (hcodes_sameparent(ghss) OR (geoms_maxdist(geoms,true))[2]<=20)
 ORDER BY 1
;  -- 249834
----
copy ibge_cnefe2022_uniq3 to '/tmp/BR-RS-POA2024_CNEFE-ptsEnderecoUnico.csv' CSV HEADER;
copy (select gid, cd_setor, cd_quadra::int as cd_quadra, cd_face::int as cd_face, via_name, tot_res, geom FROM vw01_log) TO '/tmp/BR-RS-POA-faces.csv' CSV HEADER;
-- COPY 56645
--- CONTAGENS DE JOIN
 With f AS(
 SELECT gid, cd_setor, cd_quadra::int as cd_quadra, cd_face::int as cd_face,
        via_name, tot_res, st_geohash(geom,7) as ghs7
 FROM vw01_log
) 
 SELECT count(*) 
 FROM ibge_cnefe2022_uniq3 u INNER JOIN f
   ON substring(u.cod_setor,1,15)!=f.cd_setor AND substring(u.ghs,1,7)=f.ghs7 AND f.cd_quadra=u.num_quadra AND f.cd_face=u.num_face AND f.via_name=u.via_name 
 ; -- 5893
--  testar se são consistentes ON substring(u.ghs,1,7)=f.ghs7 AND f.cd_quadra=u.num_quadra AND f.cd_face=u.num_face AND f.via_name=u.via_name 

------- final só confiaveis:
drop table ibge_cnefe2022_uniq_onface;
create table ibge_cnefe2022_uniq_onface AS
 WITH f AS (
  SELECT gid, cd_setor, cd_quadra::int as cd_quadra, cd_face::int as cd_face,
        via_name, tot_res, st_geohash(geom,7) as ghs7, geom
  FROM vw01_log
 )
 SELECT u.ghs, u.cep, u.via_name, u.num_endereco, u.is_centroid, substring(u.ghs,1,7)=f.ghs7 as is_nearface,
        ST_ClosestPoint(f.geom, ST_PointFromGeoHash(u.ghs)) as geom 
 FROM ibge_cnefe2022_uniq3 u INNER JOIN f
   ON substring(u.cod_setor,1,15)=f.cd_setor AND f.cd_quadra=u.num_quadra AND f.cd_face=u.num_face AND f.via_name=u.via_name 
   order by 1
 ; -- 198797