Uma das coisas mais chatas que eu me recordo trabalhando com geoprocessamento eram os procedimentos para fazer upload e depois a leitura dos arquivos de geometrias ‘shapefiles’ no Java.
Basicamente nós tínhamos que informar cada coluna desejada e jogá-las em algum lugar, passando para um setAlgumaCoisa de alguma classe. Isso lembra o ResultSet, da especificação do JDBC…
[java]String s = resultSet.getString(“nome_do_campo”);[/java]
No Geotools, nós temos que fazer uma leitura de uma tabela de informações que vem junto do arquivo DBF, assim, nós fazemos algo assim:
[java]
SimpleFeature feat = (SimpleFeature) this.getSimpleFeatureIterator().next();
String nomeEstado = (String) feat.getAttribute(“nome_pais”);
[/java]
Se dermos uma olhada rápida, o código não é tão feio assim… só que ele poderia ser melhorado um pouco não é? Vamos olhar mais uma vez, só que agora atribuindo os valores dentro de um objeto Estado.
1 2 3 4 5 6 |
SimpleFeature feature = (SimpleFeature) this.getSimpleFeatureIterator().next(); Estado estado = new Estado(); estado.setNome( feature.getAttribute("nome_estado") ); estado.setAreaGeografica( feature.getAttribute("the_geom") ); //Pega a geometria do registro no shapefile. estado.setTotalPopulacao( feature.getAttribute("total_populacao") ); estado.setUltimoCenso( feature.getAttribute("ultimo_censo") ); // Data do último censo realizado. |
Ah, o código continua legal, não está tão feio assim.
Bom, vamos imaginar que na nossa aplicação iríamos ter que fazer o upload de arquivos shapefiles para um monte de informações, como estados, países, cidades, hidrografias, rios, arruamentos, propriedades, glebas, etc. Imaginem que teríamos cuidar de instanciar e setar cada valor dentro destes objetos, isso não iria dar muito trabalho ?
Então, utilizando as quatro ferramentas (VRaptor, Mirror, Geotools e ShapeFileReader) combinadas é possível fazer de maneira fácil.
A jogada é abstrair todo esse procedimento de leitura ao máximo, a mesma idéia do Hibernate com SQL. Com a biblioteca ShapeFileReader nós conseguimos fazer a leitura do nosso shapefile utilizando uma sintaxe alternativa ao modo moroso de utilizar ‘setAlgumaCoisa’, basicamente eu consigo falar a ligação entre coluna e atributo de uma classe de forma verbal! Assim…
1 2 3 4 |
Definition def = new Definition(); def.from("nome_estado").to("nome") .and().from("total_populacao").to("totalPopulacao") .and().from("the_geom").to("areaGeografica"); |
Está feito, agora eu já sei que as colunas do meu arquivo shapefile devem ir para esses destinos “atributos de uma classe“, legal não é?! O que eu preciso fazer agora é informar qual a classe de destino e qual é o shapefile (arquivo de origem).
Primeiro vamos saber da origem do arquivo. Pelo VRaptor eu fiz um pequeno formulário que me enviava esses arquivos por upload, no final eu tenho um método onde eu tenho os arquivos SHP, SHX e DBF, assim eu preciso apenas instanciar a classe que sabe processar esse arquivo(s).
1 2 |
File shp = algumArquivo; ShapeFileReader<Estado> reader = new ShapeFileReader<Estado>(Estado.class, shp, def); |
A classe ShapeFileReader recebe no seu construtor as dependências diretas dele, que são:
- A classe que deverá ser gerada com base nos dados do arquivo (Estado, Pais, etc.);
- o arquivo .SHP de origem;
- As definições que escrevemos antes (Definition);
Mais simples até agora né pessoal? O que me falta fazer é pegar a lista de dados da tabela do arquivo shapefile transformado em uma lista de classes que eu queira…
1 2 3 4 5 6 |
Collection<Estado> estados = reader.getRecords(); for (Estado estado: estados) { // Alguma coisa de útil aqui... } |
Bom, você já tem a sua lista de objetos desejada, mais simples não? Vou deixando a solução completa aqui…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// Classe Java para o formulário HTML para o VRaptor public class UploadForm implements Serializable { private UploadedFile shx; private UploadedFile shp; private UploadedFile dbf; // getters e setters... } // Método que recebe o post do VRaptor com os inputs do tipo File... @Post("/salvar/") public void salvar (UploadForm upload, ServletContext context) throws IOException{ // Caminho para guardar os arquivos e um nome padrão baseado em um timestamp... String caminho = context.getRealPath("/") + "/WEB-INF/arquivos/"; String prefixo = Calendar.getInstance().getTimeInMillis() + ""; // Copia o arquivo SHP String arquivoDestinoShp = prefixo + "." + FilenameUtils.getExtension(upload.getShp().getFileName()); IOUtils.copyLarge(upload.getShp().getFile(), new FileOutputStream(new File(caminho, arquivoDestinoShp))); File shp = new File(caminho + arquivoDestinoShp); // Copia o arquivo SHX String arquivoDestinoShx = prefixo + "." + FilenameUtils.getExtension(upload.getShx().getFileName()); IOUtils.copyLarge(upload.getShx().getFile(), new FileOutputStream(new File(caminho, arquivoDestinoShx))); // Copia o arquivo DBF String arquivoDestinoDbf = prefixo + "." + FilenameUtils.getExtension(upload.getDbf().getFileName()); IOUtils.copyLarge(upload.getDbf().getFile(), new FileOutputStream(new File(caminho, arquivoDestinoDbf))); // Leitura do arquivo SHP. Definition def = new Definition(4326); def.from("LEVEL_4_CO").to("sigla") .and().from("LEVEL_4_NA").to("nome") .and().from("the_geom").to("limite"); ShapeFileReader<Estado> reader = new ShapeFileReader<Estado>(Estado.class, shp, def); Collection<Estado> estados = reader.getRecords(); // Itera a coleção e salvamos no banco :) for (Estado estado: estados) { dao.salva(estado); } } |
Referências
ShapeFileReader – ferramenta com a abstração do Geotools.
Geotools – Framework para manipulação de geometrias.
Mirror – bibilioteca para trabalhar com reflection no Java.
VRaptor – framework para desenvolvimento WEB.
Arquivos de shapefile do exemplo estão aqui
Abraços e até a próxima.
Dúvidas usem a área de comentários abaixo.