12
/ 32 Receitas com GeoTools Desenhando Mapas a partir de Shapefiles GeoTools é um conjunto de ferramentas e APIs que provê uma enorme quantidade de classes para representação e manipulação de dados geo- gráficos/geoespaciais. Neste artigo, veremos algumas receitas simples para leitura e visualização de dados geográficos na forma de shapefiles. I nformações georeferenciadas são aquelas que têm algum componente geográfico associado (geral- mente associáveis a mapas). Este tipo de informação tem inúmeras aplicações no dia-a-dia: mapas temá- ticos, rotas, pontos de interesse, geotagging etc. Informações georeferenciadas são aquelas que são associadas a algum componente espacial (geral- mente mapas e/ou coordenadas geográficas). Este tipo de informação é amplamente utilizado no dia- -a-dia: jornais imprimem mapas com informações sobrepostas a eles, usamos aplicativos como Google Maps e Google Earth para várias finalidades, alguns modelos de celulares e câmeras associam as figuras a coordenadas geográficas, GPSs permitem a coleta e extração de coordenadas geográficas etc. Apesar da existência de sistemas dedicados para processamen- to de informações georeferenciadas, para algumas finalidades pode ser interessante desenvolver apli- cações específicas. O toolkit GeoTools é um conjunto de APIs que permite a representação, entrada e saída, manipula- ção e exibição gráfica de informações georeferencia- das. É um toolkit maduro, com muitas funcionalida- des, e razoavelmente documentado. O conjunto de APIs do toolkit GeoTools é bas- tante extenso, portanto é impossível, em um artigo, cobrir todas as suas funcionalidades. Neste artigo veremos somente receitas básicas para leitura, in- vestigação de dados e exibição gráfica de arquivos do tipo shapefile em aplicações em Java. Receitas básicas As receitas nesta seção servem para preparar o ambiente para desenvolvimento com GeoTools e para familiarizar o leitor com alguns conceitos. Este artigo apresenta alguns exemplos de aplicações que permitem a leitura, exploração e visualização de dados georeferenciados na forma de shapefiles, usando o toolkit GeoTools. geotools_

geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

Embed Size (px)

Citation preview

Page 1: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

/ 32

Receitas com

GeoToolsDesenhando Mapas a partir de Shapefiles

GeoTools é um conjunto de ferramentas e APIs que provê uma enorme quantidade de classes para representação e manipulação de dados geo-

gráficos/geoespaciais. Neste artigo, veremos algumas receitas simples para leitura e visualização de dados geográficos na forma de shapefiles.

Informações georeferenciadas são aquelas que têm algum componente geográfico associado (geral-

mente associáveis a mapas). Este tipo de informação tem inúmeras aplicações no dia-a-dia: mapas temá-ticos, rotas, pontos de interesse, geotagging etc.

Informações georeferenciadas são aquelas que são associadas a algum componente espacial (geral-mente mapas e/ou coordenadas geográficas). Este tipo de informação é amplamente utilizado no dia--a-dia: jornais imprimem mapas com informações sobrepostas a eles, usamos aplicativos como Google Maps e Google Earth para várias finalidades, alguns modelos de celulares e câmeras associam as figuras a coordenadas geográficas, GPSs permitem a coleta e extração de coordenadas geográficas etc. Apesar da existência de sistemas dedicados para processamen-to de informações georeferenciadas, para algumas finalidades pode ser interessante desenvolver apli-

cações específicas. O toolkit GeoTools é um conjunto de APIs que

permite a representação, entrada e saída, manipula-ção e exibição gráfica de informações georeferencia-das. É um toolkit maduro, com muitas funcionalida-des, e razoavelmente documentado.

O conjunto de APIs do toolkit GeoTools é bas-tante extenso, portanto é impossível, em um artigo, cobrir todas as suas funcionalidades. Neste artigo veremos somente receitas básicas para leitura, in-vestigação de dados e exibição gráfica de arquivos do tipo shapefile em aplicações em Java.

Receitas básicas As receitas nesta seção servem para preparar

o ambiente para desenvolvimento com GeoTools e para familiarizar o leitor com alguns conceitos.

Este artigo apresenta alguns exemplos de aplicações que permitem a leitura, exploração e visualização de dados georeferenciados na forma de shapefiles, usando o toolkit GeoTools.

geotools_

Page 2: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

33 \

Rafael Santos | [email protected] em Inteligência Artificial pelo Instituto Tecnológico de Kyushu, Japão. É tecnologista do Instituto Nacional de Pesquisas

Espaciais, atuando em pesquisa e desenvolvimento de aplicações e técnicas de inteligência artificial aplicada, mineração de dados e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e

tutoriais sobre Java e desenvolvimento de aplicações científicas.

Criando um projeto GeoTools com Netbeans e Maven

Esta receita foi adaptada para o Netbeans ver-são 7.1.2 e é baseada nos passos mostrados na pá-gina Netbeans Quickstart (http://docs.geotools.org/latest/userguide/tutorial/quickstart/netbeans.html).

» No Netbeans, selecione as opções File/New Project. Escolha no menu a opção Java Appli-cation na categoria Maven. Clique em Next.

» Escolha um nome adequado para o projeto (modificando, se desejado, o campo Group Id) e clique em Finish.

» Na lista de arquivos de projeto (lado esquer-do da tela do Netbeans), selecione o projeto recém-criado, clique com o botão direito do mouse e selecione Set as Main Project.

» Selecione novamente o projeto recém-criado e clique na lista de Project Files. Selecione o arquivo pom.xml (que foi gerado automatica-mente com valores default) e clique duas vezes para editá-lo.

» No arquivo pom.xml devemos indicar a versão do GeoTools que será usada no projeto. Para as receitas neste artigo é sugerida usar a versão 8.0-M4. Devemos também incluir os módulos do GeoTools que serão usados, na forma de dependências, e os repositórios que contêm os arquivos .jar necessários para a compilação e execução das aplicações. Estas modificações são mostradas na Listagem 1 (os trechos que devem ser incluídos são mostrados em negrito e itálico).

Listagem 1. Arquivo pom.xml, modificado para podermos desenvolver aplicações com o GeoTools.

<project xmlns=”http://maven.apache.org/POM/4.0.0” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”> <modelVersion>4.0.0</modelVersion> <groupId>mundojava.geotools</groupId> <artifactId>MundoJavaGeoTools</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging>

<name>MundoJavaGeoTools</name> <url>http://maven.apache.org</url>

O que são shapefiles?Shapefile é o nome comum dado a arquivos que con-têm pequenas bases de dados com atributos veto-riais ou geométricos e atributos não-geométricos associados – em resumo, shapefiles podem ser usa-dos para representar mapas e dados associados aos elementos gráficos dos mapas. Dados vetoriais como linhas, pontos e polígonos podem ser representados em shapefiles, e a cada dado vetorial podemos ter vários dados não-vetoriais associados. Shapefiles podem ser comparados com uma tabela de um ban-co de dados, onde um campo da tabela corresponde a um atributo geométrico e os outros aos atributos não-geométricos associados a este. Por exemplo, um shapefile pode conter polígonos corresponden-tes aos municípios do Brasil e, para cada um destes polígonos, informações não-vetoriais, como nome, sede, população, área etc.

O padrão shapefile foi desenvolvido pela empre-sa ESRI (Environmental Systems Research Institu-te) que desenvolve aplicativos de bancos de dados georelacionados e sistemas de informações geográ-ficas. Shapefiles seguem uma especificação aberta, possibilitando o desenvolvimento de aplicações que usam este padrão sem necessidade de licenciamen-to. Vários sistemas de informações geográficas (SIGs ou GIS) usam este formato ou são capazes de impor-tá-lo ou exportá-lo.

Informações contidas em shapefiles são geral-mente armazenadas em três ou mais arquivos, com o mesmo nome e extensões diferentes. Arquivos com a extensão .shp contêm os dados vetoriais em si, arquivos .shx contém índices geométricos que agilizam a leitura dos arquivos .shp, e arquivos com a extensão .dbf contêm os dados não-vetoriais asso-ciados. Outros arquivos (com o mesmo nome-base) podem representar índices adicionais, projeções e sistemas de coordenadas etc. A maioria das apli-cações (inclusive as desenvolvidas neste artigo) só precisa do nome-base do arquivo com extensão .shp para abrir todos os arquivos necessários para pro-cessamento.

<properties> <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding> <geotools.version>8.0-M4</geotools.version> </properties>

Page 3: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

/ 34

<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-shapefile</artifactId> <version>${geotools.version}</version> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-swing</artifactId> <version>${geotools.version}</version> </dependency> </dependencies> <repositories> <repository> <id>maven2-repository.dev.java.net</id> <name>Java.net repository</name> <url>http://download.java.net/maven/2</url> </repository> <repository> <id>osgeo</id> <name>Open Source Geospatial Foundation Repository</name> <url>http://download.osgeo.org/webdav/geotools/</url> </repository> </repositories></project>

Com estes passos podemos criar aplicações que usam as APIs do GeoTools para processar e exibir shapefiles. Até este momento não precisamos baixar nada dos repositórios, isto será feito automaticamen-te pelo Maven quando criarmos as aplicações.

Mostrando (no terminal) o conteúdo de um shapefile

Como primeiro exemplo prático vamos abrir um arquivo shapefile e mostrar de forma resumida parte de seu conteúdo. Para a execução desta receita é pre-ciso ter o projeto criado corretamente, como mostra-do na receita “Criando um projeto GeoTools com Ne-tbeans e Maven”. Antes de prosseguir, leia os boxes “O que são shapefiles?” e “Onde conseguir arquivos shapefile?”.

Para mostrar (como texto) o conteúdo de um sha-pefile precisamos primeiro entender como os dados são organizados pelas APIs do GeoTools que podem ser usadas para representá-los. Algumas das classes e interfaces relevantes para nossos exemplos são:

» FileDataStore: classes que implementam esta

interface representam conjuntos de dados geo-referenciados de uma só fonte (apesar de dados em shapefiles terem vários arquivos associa-dos, serão considerados como uma só fonte de dados). Instâncias de classes que implementam esta interface podem ser obtidas com o método estático FileDataStoreFinder.getDataStore(), que recebe como parâmetro uma instância de uRL ou File que aponta para um arquivo .shp.

» SimpleFeatureSource: classes que implemen-tam esta interface provêem acesso aos atribu-tos (“features”) representados pelo FileDataS-tore.

» SimpleFeatureCollection: instâncias de clas-ses que implementam esta interface permitem acesso aos dados em si, incluindo acesso ao esquema de representação destes dados (equi-valente ao descritor da tabela de atributos dos dados georeferenciados).

» PropertyDescriptor: classes que implementam esta interface descrevem os tipos de atributos (nomes, classes usadas para a representação, outros). Podemos recuperar a coleção de Pro-pertyDescriptors de uma base de dados geo-referenciada com o método getSchema().ge-tDescriptors() de uma instância de classe que implementa a interface SimpleFeatureCollec-tion.

» FeatureIterator: instâncias desta classe com-portam-se como um Iterator que pode ser usa-do para acessar cada registro em uma base de dados georeferenciada. O método next() desta interface retorna uma instância de classe que implementa a interface Feature, e a partir desta podemos obter cada valor (instância de classe que implementa Property) para os atributos daquele objeto.

Parece complicado, mas não é: esta multiplicida-de de interfaces permite que o mesmo código gené-rico processe dados representados originariamente em diferentes formatos. No nosso exemplo criaremos uma referência a um shapefile, recuperaremos a co-leção de atributos e mostraremos os tipos de dados armazenados (PropertyDescriptor) e algumas infor-mações sobre cada uma das Features armazenada. O exemplo é mostrado no código na Listagem 2 (classe ListaShapefile.java), que contém apenas métodos es-táticos para simplificar a compreensão.

Listagem 2. Classe ListaShapefile, que lista parte do conteúdo de um shapefile.

package receitas;

import java.io.*;import java.util.Collection;import org.geotools.data.*;a

Page 4: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

35 \

import org.geotools.data.simple.*;import org.geotools.feature.*;import org.opengis.feature.*;import org.opengis.feature.type.PropertyDescriptor;public class ListaShapefile { public static void main(String[] args) throws IOException { File file = new File(“Dados/LM_UF.shp”); FileDataStore dataStore = FileDataStoreFinder.getDataStore(file); SimpleFeatureSource featureSource = dataStore.getFeatureSource(); SimpleFeatureCollection features = featureSource.getFeatures(); mostraEstrutura(features); mostraAtributos(features); } private static void mostraEstrutura(FeatureCollection features) { Collection<PropertyDescriptor> props = features.getSchema().getDescriptors(); for(PropertyDescriptor pd:props) { System.out.printf(“Nome: %-11s”,pd.getName()); System.out.print(“Classe “ +pd.getType().getBinding()); System.out.println(); } } private static void mostraAtributos(FeatureCollection features) { System.out.println(“Temos “ + features.size() + “ objetos.”); FeatureIterator iterator = features.features();

while (iterator.hasNext()) { Feature feature = (Feature) iterator.next(); System.out.printf(“%3s “,feature.getProperty(“UF”).getValue()); System.out.printf(“%14.5f”,feature.getProperty( “area_ofici”).getValue()); System.out.println(); } }}

A classe mostrada na Listagem 2 é realmente sim-ples: o método main cria, a partir de um arquivo .shp, a instância de classe que implementa simple-featurecollection. Esta instância é usada por um método estático que imprime o cabeçalho (coleção de propertyDescriptors que descrevem os dados no shapefile), e por outro método estático que imprime os valores de algumas propriedades do shapefile.

Como os nomes das propriedades são codificadas diretamente, a aplicação só funcionará com o sha-

pefile LM_uF.shp (veja como obtê-lo no box “Onde conseguir arquivos shapefile?”). Este shapefile con-tém polígonos e alguns dados sobre as 27 unidades federativas do Brasil. Duas de suas propriedades são “uF” e “área_oficial”, e o método mostraAtributos imprime os valores destas propriedades para as 27 entradas na base de dados. Neste método, nesta par-te, usamos diretamente os nomes das propriedades que queremos acessar, mas seria possível recuperar todos os atributos através da chamada ao método getproperties() que retornaria uma instância de collection<property>.

uma das linhas mostradas como resultado da execução da classe na Listagem 2 mostra uma pro-priedade chamada the_geom cuja classe é com.vivi-dsolutions.jts.geom.MultiPolygon. Esta é a proprie-dade daquele shapefile que representa os polígonos correspondentes aos limites geográficos da unidade federativa. Em outras receitas veremos como é sim-ples criar uma aplicação gráfica que mostre estas pro-priedades geométricas.

Mostrando (em uma janela gráfica) o conteúdo de um shapefile

Para visualizar o conteúdo geométrico do shape-file também usaremos classes das APIs contidas no Geotools. A classe JMapPane herda de JPanel e per-mite a exibição de uma ou mais coleções de atributos geométricos. Para criar uma instância de JMapPane precisaremos de uma instância de MapContent, que contém referências a classes que representam os da-dos que devem ser mostrados e como devem ser mos-trados no painel.

uma instância de MapContent pode conter vá-rias camadas ou layers, cada um correspondente a um conjunto de polígonos e tendo um estilo diferente para visualização. Por exemplo, se tivermos conjun-tos de dados geográficos sobre rodovias (linhas), li-mites de estados (polígonos) e capitais (pontos), cada um representado em um shapefile, podemos criar três camadas no nosso mapa para visualização, cada uma com um estilo diferente.

A Listagem 3 mostra uma aplicação gráfica em Java que exibe um único shapefile em uma janela. A receita é simples: a partir de um shapefile obtemos o seu conjunto de atributos (FeatureSource), e o usa-mos para criar uma instância de FeatureLayer, que será adicionada à instância de MapContent, que será exibida pela instância de JMapPane.

Listagem 3. Classe MostraShapefile, que exibe um shapefile em uma aplicação gráfica.

package receitas;

Page 5: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

/ 36

import java.io.*;import javax.swing.JFrame;import org.geotools.data.*;import org.geotools.data.simple.SimpleFeatureSource;import org.geotools.map.*;import org.geotools.styling.*;import org.geotools.swing.JMapPane;

public class MostraShapefi le extends JFrame { public MostraShapefi le(MapContent map) throws IOException { super(“Mapa”); JMapPane mapPane = new JMapPane(map); getContentPane().add(mapPane); setVisible(true); setSize(800, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) throws IOException { MapContent map = new MapContent(); map.setTitle(“”); File fi le = new File(“Dados/LM_UF.shp”); FileDataStore store = FileDataStoreFinder.getDataStore(fi le); SimpleFeatureSource featureSource = store.getFeatureSource(); Style style = SLD.createSimpleStyle( featureSource.getSchema()); Layer layer = new FeatureLayer( featureSource,style); map.addLayer(layer); new MostraShapefi le(map); } }

Na Listagem 3 a instância de MapContent é criada no método main, e contém as chamadas a métodos que criam uma instância de FeatureLayer (camada), a partir do FeatureSource obtido de um shapefi le, usando um estilo genérico (criado a partir do método SLD.createSimpleStyle). Este estilo é bem simples: objetos geométricos serão desenhados com linhas pretas fi nas e sem preenchimento (no caso de ponto e polígonos). O resultado da aplicação é mostrado na fi gura 1.

Modifi cando a aparência de objetos gráfi cos de um shapefi le para visualização

Para demonstrar melhor as capacidade das clas-ses FeatureLayer e MapContent vamos adicionar mais shapefi les ao nosso mapa e usar estilos diferen-tes para os mesmos. A classe MostraShapefi le2, na Listagem 4, usa três shapefi les do conjunto que pode ser obtido no site do IBGE (veja box “Onde conseguir

arquivos shapefi le?”): um com os limites estaduais (polígonos), um com a malha viária (linhas) e outro com aeródromos (pontos). Para facilitar a leitura do código cada uma destas camadas será gerada por um método estático diferente.

Listagem 4. Classe MostraShapefi le2, que exibe três shapefi les em uma aplicação gráfi ca.

package receitas;import java.awt.Color;import java.io.*;import javax.swing.JFrame;import org.geotools.data.*;import org.geotools.data.simple.SimpleFeatureSource;import org.geotools.factory.CommonFactoryFinder;import org.geotools.map.*;import org.geotools.styling.*;import org.geotools.swing.JMapPane;import org.opengis.fi lter.FilterFactory;public class MostraShapefi le2 extends JFrame { private static StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(); private static FilterFactory fi lterFactory = CommonFactoryFinder.getFilterFactory(); public MostraShapefi le2(MapContent map) throws IOException { super(“Mapa”); JMapPane mapPane = new JMapPane(map); getContentPane().add(mapPane); setVisible(true); setSize(800, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) throws IOException { MapContent map = new MapContent(); map.setTitle(“”); map.addLayer(criaLayerRE()); map.addLayer(criaLayerROD()); map.addLayer(criaLayerAERO()); new MostraShapefi le2(map); }

Figura 1. Execução da classe MostraShapefi le (Listagem 3).

Page 6: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

37 \

private static Layer criaLayerRE() throws IOException { // Shapefile com limites estaduais. File file = new File(“Dados/LM_UF.shp”); FileDataStore store = FileDataStoreFinder.getDataStore(file);SimpleFeatureSource featureSource = store.getFeatureSource(); // Estilo para este shapefile: linhas azuis grossas, // preenchimento amarelo bem claro. Stroke stroke = styleFactory.createStroke( filterFactory.literal(Color.BLUE), filterFactory.literal(2), filterFactory.literal(1.)); Fill fill = styleFactory.createFill( filterFactory.literal(new Color(255, 255, 220)), filterFactory.literal(1)); PolygonSymbolizer sym = styleFactory.createPolygonSymbolizer(stroke, fill, null); Rule rule = styleFactory.createRule(); rule.symbolizers().add(sym); FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule }); Style style = styleFactory.createStyle(); style.featureTypeStyles().add(fts); // Criamos e retornamos o layer. Layer layer = new FeatureLayer(featureSource, style); return layer; } private static Layer criaLayerROD() throws IOException { // Shapefile com rodovias. File file = new File(“Dados/ST_RODOVIA.shp”); FileDataStore store = FileDataStoreFinder.getDataStore(file); SimpleFeatureSource featureSource = store.getFeatureSource(); // Estilo para este shapefile: linhas vermelhas finas, // sem preenchimento. Stroke stroke = styleFactory.createStroke( filterFactory.literal(Color.RED), filterFactory.literal(1), filterFactory.literal(1.)); LineSymbolizer sym = styleFactory.createLineSymbolizer(stroke, null); Rule rule = styleFactory.createRule(); rule.symbolizers().add(sym);

FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule });

Style style = styleFactory.createStyle(); style.featureTypeStyles().add(fts); // Criamos e retornamos o layer. Layer layer = new FeatureLayer(featureSource, style); return layer; } private static Layer criaLayerAERO() throws

IOException { // Shapefile com aeródromos. File file = new File(“Dados/ST_AERODROMO.shp”); FileDataStore store = FileDataStoreFinder.getDataStore(file);

SimpleFeatureSource featureSource = store.getFeatureSource(); // Estilo para este shapefile: estrelas como // símbolos, comlinhas verdes e preenchimento // azul. Graphic gr = styleFactory.createDefaultGraphic(); Mark mark = styleFactory.getStarMark(); mark.setStroke(styleFactory.createStroke( filterFactory.literal(Color.GREEN), filterFactory.literal(1))); mark.setFill(styleFactory.createFill(filterFactory. literal(Color.BLUE))); gr.graphicalSymbols().clear(); gr.graphicalSymbols().add(mark); gr.setSize(filterFactory.literal(8)); PointSymbolizer sym = styleFactory.createPointSymbolizer(gr, null); Rule rule = styleFactory.createRule(); rule.symbolizers().add(sym); FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule }); Style style = styleFactory.createStyle(); style.featureTypeStyles().add(fts); // Criamos e retornamos o layer. Layer layer = new FeatureLayer(featureSource, style); return layer; }}

Para criar os estilos de desenho e preenchimento dos diversos objetos geométricos é preciso definir o es-tilo e cor de linha, o estilo e cor de preenchimento e o tipo de símbolo a ser usado, dependendo do tipo de objeto geométrico (linhas, por exemplo, não têm preenchimento nem símbolos). A classe MostraSha-pefile2 (Listagem 4) usa algumas classes e interfaces para definir os estilos que serão usados para desenhar os objetos geométricos. Estas classes e interfaces são:

Page 7: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

/ 38

» StyleFactory: classes que implementam esta interface são fábricas que provêem métodos para a criação de instâncias que representam diversos elementos que serão usados nas re-presentações gráficas, como tipos de linha, preenchimentos, símbolos gráficos etc. uma instância desta fábrica pode ser criada com a chamada ao método CommonFactoryFinder.getStyleFactory().

» FilterFactory: classes que implementam esta interface permitem a criação de instâncias que implementam as interfaces Filter e Expression. Estas duas interfaces representam, respectiva-mente, filtros (condições para comparação de valores de atributos) e expressões para com-binar os filtros. Neste exemplo, usaremos ape-nas valores “constantes”, ou seja, filtros cria-dos com a chamada ao método FilterFactory.literal(). uma instância desta fábrica pode ser criada com a chamada ao método CommonFac-toryFinder.getFilterFactory().

» Stroke: classes que implementam esta inter-face representam um tipo de padrão usado para desenhar linhas. Instâncias podem ser construídas com o método StyleFactory.crea-teStroke(), que na forma mais simples recebe três argumentos: a cor da linha, sua espessura e opacidade (que pode ser omitida). Todos os parâmetros devem ser instâncias de classes que implementam Filter (geralmente criadas atra-vés de chamadas a FilterFactory.literal()). Não devemos confundir esta interface com a inter-face java.awt.Stroke.

» Fill: classes que implementam esta interface representam o tipo de preenchimento que será usado para objetos geométricos que podem ser preenchidos (polígonos e representação gráfi-ca de pontos). Instâncias são construídas com chamadas a StyleFactory.createFill(), que na sua forma mais simples aceita uma cor e uma opacidade, ambas instâncias de classes que herdam de Filter e que podem ser construídas com FilterFactory.literal().

» PolygonSymbolizer: classes que implementam

esta interface descrevem como polígonos de-vem ser desenhados. Em sua forma mais sim-ples, criamos instâncias com uma chamada a StyleFactory.createPolygonSymbolizer(), pas-sando como argumentos o Stroke, o Fill e um nome de propriedade (null em casos mais sim-ples, como o mostrado nos exemplos).

» LineSymbolizer: classes que implementam esta interface descrevem como linhas devem ser desenhadas. Instâncias podem ser construí-das com uma chamada a StyleFactory.createLi-neSymbolizer(), que recebe como argumentos uma instância de classe que implementa Stroke e um nome de propriedade (null em casos mais simples, como o mostrado nos exemplos).

» PointSymbolizer: classes que implementam esta interface descrevem como pontos devem ser desenhados. Pontos combinam aspectos de polígonos (por poder usar linhas e preenchi-mentos), mas sua forma é geralmente deriva-da de uma instância de classe que implementa Graphic (descrita a seguir), passada como pri-meiro argumento para o método StyleFactory.createPointSymbolizer(). O segundo argumen-to para este método é um nome de propriedade (null em casos mais simples, como o mostrado nos exemplos).

» Mark: classes que implementam esta interfa-ce correspondem a símbolos predefinidos que podem ter estilos de linha e preenchimento associados a ele. Para criar um desenho que será usado para representar pontos em um mapa devemos criar instâncias de classes que implementam Mark com um dos métodos está-ticos da fábrica StyleFactory (por exemplo, ge-tCircleMark(), getStarMark(), getCrossMark(), getTriangleMark() etc.). Podemos determinar o tipo de linha e preenchimento a ser usado nesta marca chamando os métodos setStroke e setFill da instância de classe que implementa Mark, como mostrado no método criaLayerAE-RO() da Listagem 4.

» Graphic: instâncias de classes que implemen-tam esta interface podem ser consideradas símbolos que serão desenhados em uma deter-minada coordenada. Estes símbolos podem ser compostos de combinação de outros símbolos, que serão instâncias de classes que implemen-tam a interface Mark. A forma mais simples de criar um símbolo gráfico é criamos uma instân-cia de classe que implementa Graphic através da chamada a StyleFactory.createDefaultGra-phic(), eliminamos o gráfico padrão criado com a chamada ao método Graphic.graphicalSym-bols().clear() e adicionamos uma ou mais ins-tâncias de classes que implementam Mark com

Informações sobre shapefiles, incluindo links para o documento com a descrição técnica do formato, podem ser encontradas em http://en.wikipedia.org/wiki/Shapefile.

A documentação do Geotools contém tutoriais interessantes e os javadocs das APIs, e pode ser acessada em docs.geotools.org.

/para saber mais

Page 8: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

39 \

Graphic.graphicalSymbols().add(). » Rule: instâncias de classes que implementam

Rule determinam como os descritores de sím-bolos (PolygonSymbolizer, PointSymbolizer, LineSymbolizer) serão interpretados de acor-do com os valores dos atributos dos dados que queremos desenhar. Nos exemplos mais sim-ples, como o mostrado na Listagem 4, criare-mos uma instância com o método StyleFactory.createRule(), adicionaremos as instâncias dos descritores de símbolos e usaremos a instância para criar um FeatureTypeStyle, descrito a se-guir.

» FeatureTypeStyle: esta interface contém méto-dos convenientes para defi nir estilos baseados em regras. Instâncias são criadas com uma cha-mada a StyleFactory.createFeatureTypeStyle(), que recebe como argumento um array de Rules. Alternativamente podemos criar a instância e adicionar regras posteriormente com chama-das a FeatureTypeStyle.rules.add() passando uma instância de Rule como argumento.

» Style: interface que defi ne como todo um con-junto de dados geográfi co será exibido grafi ca-mente. Instâncias são criadas com a execução de StyleFactory.createStyle(), e instâncias de FeatureTypeStyle são adicionadas com chama-das a Style.featureTypeStyles().add(). uma ins-tância básica de Style, contendo valores default para linhas, pontos e preenchimentos, pode ser criada com SLD.createSimpleStyle() como mostrado na Listagem 3. Com a instância de Style podemos criar a instância de Layer usan-do como parâmetros o FeatureSource (obtido do shapefi le) e o Style recém-criado.

A API de defi nição de estilos de desenho é bas-tante complexa, e composta de mais classes do que as mostradas. Esta complexidade permite a abstração dos conceitos, usando os mesmos métodos e técnicas para criar representações visuais a partir de dados de diversas fontes e com diversas fi nalidades. As classes e interfaces comentadas foram escolhidas por permi-tirem a criação de receitas simples para defi nir a apa-rência dos objetos a serem desenhados, como mos-trado nos métodos criaLayerRE(), criaLayerROD() e criaLayerAERO() da classe MostraShapefi le2.

O resultado da execução da classe MostraShape-fi le 2 (Listagem 4) é mostrado na fi gura 2.

Usando fi ltros e regras para determinar a aparência de objetos gráfi cos em um shapefi le

Como visto na Listagem 4, a aparência gráfi ca dos elementos de um shapefi le pode ser mudada com a criação de estilos, que para serem construídos usam regras e fi ltros (instâncias de classes que implemen-

tam Rule e Filter). No exemplo mostrado na classe MostraShapefi le2 usamos somente regras e fi ltros simples, que modifi cavam o estilo usado para os ele-mentos do shapefi le independentemente dos atribu-tos destes.

Filtros e regras podem ser combinados e usados para defi nir estilos gráfi cos diferentes para cada re-gistro em um shapefi le, usando os valores do próprio registro. Para exemplifi car, vamos considerar a visu-alização de um mapa com os limites municipais ao fundo e os aeródromos superpostos. Os municípios que forem dos Estados do Maranhão, Pará e Amazo-nas terão preenchimento de cor diferente dos outros, e os aeródromos terão suas cores e tamanhos de sím-bolos diferenciados de acordo com o tipo.

A base de dados com os limites municipais (LM_MuNICIPIO_2007.shp) contém um atributo uF que pode ser comparado com valores literais (“PA”, “MA” e “AM”) para determinar o preenchimento especial. Se as comparações falharem o preenchimento default será usado. Os passos para implementar esta capaci-dade são:

» Criamos uma instância de Stroke que será usa-da pelos dois estilos de desenho dos polígonos – também seria possível criar um Stroke para cada tipo de estilo.

» Criamos uma instância de Fill para cada estilo de preenchimento.

» Criamos uma instância de PolygonSymbolizer para cada estilo de desenho, usando o Stroke e Fill correspondentes.

» Criamos uma instância de Rule para determi-nar o estilo dos municípios pertencentes aos estados determinados e outra instância para todos os outros municípios.

» Criamos uma coleção de instâncias de classes que implementam Filter. Esta coleção conterá

Figura 2. Execução da classe MostraShapefi le2 (Listagem 4).

Page 9: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

/ 40

instâncias de Filter criadas com o método Fil-terFactory.equals que recebe como argumentos o nome da propriedade (atributo) e o valor que queremos comparar para igualdade.

» Indicamos que a regra para preenchimento dos polígonos nos estados determinados usará esta coleção de filtros, combinados de forma que qualquer um deles seja aceito. Isto é feito com uma chamada ao método FilterFactory.or como parâmetro ao método Rule.setFilter().

» Indicamos que a regra para preenchimento de todos os outros polígonos será feita com o ou-tro estilo. Isto é feito chamando o método Rule.setElseFilter() com o argumento true.

» Criamos o FeatureTypeStyle e adicionamos as duas instâncias de Rule, e finalmente usamos este FeatureTypeStyle para criar o Style e o Layer com os dados do shapefile.

Destes passos os mais críticos são os de 4 a 7: ne-les determinamos os estilos que serão associados às regras e os filtros que serão usados para determinar o uso destes estilos.

A classe MostraShapefileComRegras, mostrada na Listagem 5, demonstra duas formas de executar estes passos, nos métodos criaLayerMun() e criaLaye-rAERO(). O método criaLayerMun() usa uma combi-nação de filtros que verifica o atributo uF dos muni-cípios para determinar qual dos dois tipos de desenho e preenchimento dos polígonos será usado: as regras podem ser lidas como “se uF for AM ou PA ou MA use o primeiro estilo; senão use o segundo”. O método criaLayerAERO() usa o atributo cd_uso dos aeródro-mos para determinar a cor e tamanho dos símbolos que será usada para desenho (usando um método es-tático auxiliar), e implementa uma estrutura parecida com um switch: “caso cd_uso seja igual a PuB use a regra 0, caso seja igual a MIL use a regra 1, caso seja igual a PRIV use a regra 2, em outros casos use a re-gra 3”. Neste caso a regra 3 é marcada como um filtro “else”.

Listagem 5. Classe MostraShapefileComRegras, que usa filtros e regras para determinar como elementos serão desenhados.

package receitas;

import java.awt.Color;import java.io.*;import java.util.ArrayList;import java.util.List;import javax.swing.JFrame;import org.geotools.data.*;import org.geotools.data.simple.SimpleFeatureSource;import org.geotools.factory.CommonFactoryFinder;import org.geotools.map.*;import org.geotools.styling.*;

import org.geotools.swing.JMapPane;import org.opengis.filter.*;import org.opengis.filter.expression.Expression;public class MostraShapefilesComRegras extends JFrame { private static StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(); private static FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(); public MostraShapefilesComRegras(MapContent map) throws IOException { super(“Mapa”); JMapPane mapPane = new JMapPane(map); getContentPane().add(mapPane); setVisible(true); setSize(800, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) throws IOException { MapContent map = new MapContent(); map.setTitle(“”); map.addLayer(criaLayerMUN()); map.addLayer(criaLayerAERO()); new MostraShapefilesComRegras(map); } // Usaremos o mesmo stroke para todos os // municípios. Stroke stroke = styleFactory.createStroke( filterFactory.literal(Color.LIGHT_GRAY), filterFactory.literal(2), filterFactory.literal(1.)); // Usaremos uma regra para o preenchimento. Se o // município for em um dos estados predefinidos o // preenchimento será em amarelo forte, senão em // amarelo pálido. private static Layer criaLayerMUN() throws IOException { // Shapefile com limites municipais. File file = new File(“Dados/LM_MUNICIPIO_2007.shp”); FileDataStore store = FileDataStoreFinder.getDataStore(file); SimpleFeatureSource featureSource = store.getFeatureSource();

Fill fillPredef = styleFactory.createFill( filterFactory.literal(new Color(255, 255, 0)), filterFactory.literal(1)); PolygonSymbolizer symPredef = styleFactory.createPolygonSymbolizer(stroke, fillPredef, null); Fill fillOutros = styleFactory.createFill( filterFactory.literal(new Color(255, 255, 220)), filterFactory.literal(1));

Page 10: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

41 \

PolygonSymbolizer symOutros = styleFactory.createPolygonSymbolizer(stroke, fi llOutros, null); // Criamos as duas regras. Rule rulePredef = styleFactory.createRule(); rulePredef.symbolizers().add(symPredef); Rule ruleOutros = styleFactory.createRule(); ruleOutros.symbolizers().add(symOutros); // Para a primeira regra precisamos de um conjunto // de fi ltros baseados na propriedade UF. List<Filter> fi ltros = new ArrayList<Filter>(); fi ltros.add(fi lterFactory.equals( fi lterFactory.property(“UF”), fi lterFactory.literal(“PA”))); fi ltros.add(fi lterFactory.equals( fi lterFactory.property(“UF”), fi lterFactory.literal(“MA”))); fi ltros.add(fi lterFactory.equals( fi lterFactory.property(“UF”), fi lterFactory.literal(“AM”))); rulePredef.setFilter(fi lterFactory.or(fi ltros)); // Determinamos que a segunda regra será ativada //para tudo que não for coberto pela primeira regra. ruleOutros.setElseFilter(true);

// Adicionamos as regras ao FeatureTypeStyle. FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(); fts.rules().add(rulePredef); fts.rules().add(ruleOutros); Style style = styleFactory.createStyle(); style.featureTypeStyles().add(fts); // Criamos e retornamos o layer. Layer layer = new FeatureLayer(featureSource, style); return layer; } private static Layer criaLayerAERO() throws IOException { // Shapefi le com aeródromos. File fi le = new File(“Dados/ST_AERODROMO.shp”); FileDataStore store = FileDataStoreFinder.getDataStore(fi le); SimpleFeatureSource featureSource = store.getFeatureSource(); // Preenchimento e tamanho dos símbolos será em // função do tipo do aeródromo. FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(); // Criamos o array de regras. Rule[] rules = new Rule[4]; for (int r = 0; r < 4; r++) rules[r] = styleFactory.createRule(); rules[0].symbolizers().add(criaPonto( Color.GREEN,6)); rules[0].setFilter(fi lterFactory.equals( fi lterFactory.property(“cd_uso”), fi lterFactory.literal(“PUB”))); rules[1].symbolizers().add(criaPonto( Color.BLUE,6));

rules[1].setFilter(fi lterFactory.equals( fi lterFactory.property(“cd_uso”), fi lterFactory.literal(“MIL”))); rules[2].symbolizers().add(criaPonto(Color.RED,6)); rules[2].setFilter(fi lterFactory.equals( fi lterFactory.property(“cd_uso”), fi lterFactory.literal(“PRIV”))); // Todos os outros serão marcados em cinza com // tamanho maior. rules[3].symbolizers().add(criaPonto( Color.DARK_GRAY,8)); rules[3].setElseFilter(true); for (int r = 0; r < 4; r++) fts.rules().add(rules[r]); Style style = styleFactory.createStyle(); style.featureTypeStyles().add(fts); // Criamos e retornamos o layer. Layer layer = new FeatureLayer(featureSource, style); return layer; }

// Método utilitário para criar um PointSymbolizer. private static PointSymbolizer criaPonto(Color cor, int tamanho) { Graphic gr = styleFactory.createDefaultGraphic(); Mark mark = styleFactory.getCircleMark(); mark.setStroke(styleFactory.createStroke( fi lterFactory.literal(Color.GRAY), fi lterFactory.literal(1))); mark.setFill(styleFactory.createFill( fi lterFactory.literal(cor))); gr.graphicalSymbols().clear(); gr.graphicalSymbols().add(mark); gr.setSize(fi lterFactory.literal(tamanho));

PointSymbolizer sym = styleFactory.createPointSymbolizer(gr, null); return sym; }}

Figura 3. Execução da classe MostraShapefi leComRegras (Lista-gem 5).

Page 11: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

/ 42

A fi gura 3 mostra o resultado da execução da classe MostraShapefi leComRegras.

Classes que implementam FilterFactory provêem vários métodos interessantes para a criação de fi ltros para regras: fi ltros que comparam por igualdade e diferença, operadores geométricos que verifi cam se objetos se tocam ou são contidos, operadores lógicos (and, or, not) etc. A lista de métodos é muito extensa para comentar neste artigo, os leitores devem procu-rar o javadoc no site docs.geotools.org.

Modifi cando a área visível (viewport) do JMapPane

Nos exemplos de visualização criamos um com-ponente gráfi co (JFrame) que contém a instância de JMapPane que exibe o mapa baseado nos shapefi les. O tamanho do componente é fi xo no código, e a esca-la do mapa é ajustada para que todos os elementos do shapefi le sejam visíveis.

É possível modifi car a área visível (viewport) do JMapPane para selecionar uma região diferente para visualização. Isto pode ser feito recuperando o viewport do JMapPane (instância de MapViewPort), modifi cando o seu retângulo envolvente (método ReferencedEnvelope.expandBy()) e usando este novo retângulo envolvente. O trecho de código exibido na Listagem 6 mostra o construtor da classe MostraSha-pefi leComRegras, modifi cado para que o mapa seja desenhado com algum espaço nas bordas.

Listagem 6. Construtor para a classe MostraShapefi leComRegras, mostrando como modifi car a área do componente usada para desenho.

public MostraShapefi lesComRegras(MapContent map) throws IOException { super(“Mapa”); JMapPane mapPane = new JMapPane(map); MapViewport vp = mapPane.getRenderer(). getMapContent().getViewport();ReferencedEnvelope env = vp.getBounds(); env.expandBy(10); vp.setBounds(env); getContentPane().add(mapPane); setVisible(true); setSize(800, 600);

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}

Figura 4. Componente JMapFrame com o MapContent gerado em outros exemplos.

Page 12: geotools - univale.com.br · e processamento de imagens. É autor do livro “Introdução à Programação Orientada a Objetos usando Java” e de várias palestras e tutoriais sobre

43 \

Documentação do GeoTools em docs.geotools.org.

/referências

Onde conseguir arquivos shapefi le?O site do IBGE (Instituto Brasileiro de Geografi a e Estatística, http://www.ibge.gov.br/) contém vá-rias bases de dados georeferenciadas na forma de shapefi les (e muitas outras informações interes-santes). A página http://www.ibge.gov.br/home/geociencias/default_prod.shtm mostra vários pro-dutos que podem ser baixados gratuitamente. Para muitos dos produtos existe uma descrição e um ícone de disco para dados que podem ser baixados. Para os exemplos mostrados nestas receitas, baixe o arquivo BCIM_v304_MD5_shp.zip – este ar-quivo pode ser encontrado na página mencionada clicando no ícone de download da entrada “Base Cartográfi ca Contínua – Escala 1:1.000.000”, que levará ao servidor FTP do IBGE. Neste servidor selecione o diretório “2_bcim_v3.04_dados/”, e dentro deste o diretório “shapefi le/” que contém o arquivo indicado. O arquivo é grande – 240 me-gabytes – mas contém muitas informações geore-ferenciadas interessantes para os exemplos deste artigo e para várias outras aplicações. O servidor de FTP do IBGE também contém o diretório “1_documentacao/” que contém arquivos PDF que descrevem as diversas bases de dados desta cole-ção.

Figura 5. Componente JMapFrame com o MapContent gerado em outros exemplos (exemplo de zoom).

Figura 6. Atributos não-geométricos de um ponto clicado no com-ponente.

Usando o componente JMapFrameOs exemplos mostrados até agora usam uma ins-

tância de JMapPane para exibir o conteúdo dos sha-pefi les. Como a classe representa um componente, ela pode ser embutida em qualquer tipo de aplicações gráfi cas baseadas em Swing, mas não provê interati-vidade ou mecanismos de seleção e consulta a dados, que devem também ser desenvolvidos pelos progra-madores.

GeoTools provê um componente mais completo, JMapFrame, que permite a interação com o conteú-do geométrico e dados em geral em uma instância de MapContent. Para usar este componente basta, em um método main, executar JMapFrame.showMap() passando uma instância de MapContent. A fi gura 4 mostra a interface criada através deste componente. Podemos ver que existem algumas ferramentas já dis-poníveis para interação (na barra superior) e algumas informações sobre o conteúdo (na barra inferior).

A fi gura 5 mostra um exemplo de interação com o componente JMapFrame: uma região foi selecionada para zoom. A fi gura 6 mostra uma janela que apare-ce quando usamos a ferramenta de identifi cação de atributos (o quinto ícone na barra de ferramentas): ao clicar em um ponto qualquer do mapa em exibição os atributos não-geométricos são listados.

Considerações fi nais Este artigo apresentou alguns exemplos de códi-

go para ler, mostrar dados sobre e representar visual-mente shapefi les usando o conjunto de APIs GeoTo-ols. Os exemplos demonstrados permitem ao leitor a criação de aplicações simples para visualizar shape-fi les com características gráfi cas para seus atributos determinados de forma programática.