CNEFE
CNEFE - Cadastro Nacional de Endereços para Fins Estatísticos
Ver página oficial do IBGE/CNEFE.
Para a visualização em diferentes mapas de fundo, ver https://gis.stackexchange.com/a/217670/7505
Pendente revisar e simplificar passo a passo.
Ver também CNEFE/Pontos basicos para tratamento global final OpenAddresses
PS: o nome do aparelho de coleta de dados do Cernso é Dispositivo Móvel de Coleta (DMC) (repassado para o Ministério da Saúde).
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. aula do IBGE para os agentes usando celular. Mais uma boa aula Tanaka.
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