Blog Geo.NET Geoprocessamento, SIG e Sensoriamento Remoto

12Jul/100

GeoInfo 2010

Recebi recentemente uma email falando sobre o GeoInfo 2010 - o simpósio brasileiro de GeoInformática.

O GeoInfo é um congresso voltado para desenvolvimento e soluções em geotecnologias, abrangendo diversas áreas. Este ano será realizado em Campos do Jordão, entre 28 de novembro à 01 de dezembro.

Me parece que é um congresso que vale a pena comparecer e publicar. As áreas para publicação são as seguintes:

- Bancos de dados espaciais e espaço-temporais; - Modelagem de dados espaciais e espaço-temporais;
- Mineração de dados espaciais e espaço-temporais;
- Data warehouses espacias e espaço-temporais;
- Múltiplas representações em bancos de dados espaciais;
- Análise de dados sobre objetos móveis; - Métodos de acesso espaciais, espaço-temporais e multidimensionais;
- Processamento e otimização de consultas espaciais;
- Estruturas de dados espaciais;
- Geometria computacional aplicada;
- Visualização de dados espaciais;
- Ontologias para dados espaciais;
- Interoperabilidade e padrões para SIG;
- Metadados espaciais;
- Propagação de erros e controle de qualidade;
- Geoestatística;
- Análise espacial e estatística espacial;
- Modelagem dinâmica e aplicações em SIG de autômatas celulares;
- Sistemas espaciais de apoio à decisão;
- Bibliotecas digitais para SIG;
- Projeto e implementação de SIG;
- Novas aplicações de tecnologia de geoprocessamento;
- Fusão de dados;
- Mapeamento e sensoriamento remoto;
- Integração sensoriamento remoto e SIG;
- Recuperação de imagens baseada em conteúdo;
- Cartografia digital;
- Interfaces web para SIG;
- Realidade virtual e SIG 3D; - Serviços de informação geográfica móveis e distribuídos.
Bastante coisa não? Com certeza conseguiremos encaixar um projeto em algum dos temas.

Confiram o site. É uma boa oportunidade para conhecer mais o pessoal da área, conhecer Campos do Jordão e publicar um artigo!

Fica a dica.

Abraços

George

30Jun/100

Brincando com ArcObjects #2

No último post sobre ArcObjects, falamos como construir pontos de uma maneira simples. Hoje vamos brincar com alguns polígonos e polilinhas. Qual é a diferença? Bem, como explicado anteriormente, um polígono é uma coleção de anéis (rings), formados por pontos ou por segmentos. As polilinhas funcionam quase da mesma maneira, só que ao invés de anéis, as polilinhas são formadas por uma coleção de caminhos (paths).

Não podemos criar estas geometrias de alto nível, sem criar antes as geometrias de baixo nível que as compõe. É a maneira na qual o ArcGIS trabalha, e é a mais sã, do ponto de vista de desenvolvimento.

Primeiro vamos construir um polígono. Podemos construir os anéis separadamente, de pontos ou de segmentos e logo depois, adicionar à uma coleção de geometrias, na qual o próprio ArcGIS irá entender como um polígono.

public static IPolygon ConstruirPolígono()
    {
        object _missing = Type.Missing;

        IGeometryCollection poligonoFinal = new PolygonClass();

        // vamos criar primeiro nosso anel exterior
        // perceba com estamos utilizando uma interface IPointCollection

        IPointCollection anelExterior = new RingClass();

        // lembram da função BuildPoint do post anterior?

        anelExterior.AddPoint(BuildPoint(0,0,0),ref _missing, ref _missing);
        anelExterior.AddPoint(BuildPoint(0,1,0),ref _missing, ref _missing);
        anelExterior.AddPoint(BuildPoint(1,1,0),ref _missing, ref _missing);
        anelExterior.AddPoint(BuildPoint(1,0,0),ref _missing, ref _missing);

        // o último ponto deve ser sempre igual ao primeiro - somente assim
        // o polígono está fechado
        // podemos utilizar também uma outra técnica para usar o primeiro ponto
        // sem duplicar o ponto inicial - muito útil se você não sabe de antemão qual é o primeiro ponto.
        // anelExterior.AddPoint(anelExterior.get_Point(0),ref _missing, ref _missing);

        anelExterior.AddPoint(BuildPoint(0,0,0));

        poligono.AddGeometry(anelExterior as IGeometry, ref _missing, ref _missing);

        // definir a referência espacial - sem a referência espacial o ArcGis não realiza operações espaciais direitinho!

        return poligono as IPolygon;
}
Polígono construído com o código anterior

Polígono construído com o código anterior


Este é um código simples para a criação de um polígono igualmente simples. Vamos aumentar um pouco a dificuldade inserindo outros anéis.

public static IPolygon ConstruirPolígono()
    {
        object _missing = Type.Missing;

        IGeometryCollection poligonoFinal = new PolygonClass();

        // vamos criar primeiro nosso anel exterior
        // perceba com estamos utilizando uma interface IPointCollection

        IPointCollection anelExterior = new RingClass();

        // lembram da função BuildPoint do post anterior?

        anelExterior.AddPoint(BuildPoint(0,0,0),ref _missing, ref _missing);
        anelExterior.AddPoint(BuildPoint(0,10,0),ref _missing, ref _missing);
        anelExterior.AddPoint(BuildPoint(10,10,0),ref _missing, ref _missing);
        anelExterior.AddPoint(BuildPoint(10,0,0),ref _missing, ref _missing);

        // o último ponto deve ser sempre igual ao primeiro - somente assim
        // o polígono está fechado
        // podemos utilizar também uma outra técnica para usar o primeiro ponto
        // sem duplicar o ponto inicial - muito útil se você não sabe de antemão qual é o primeiro ponto.
        anelExterior.AddPoint(anelExterior.get_Point(0),ref _missing, ref _missing);

        IPointCollection anelInterior = new RingClass();
        anelInterior.AddPoint(BuildPoint(1,1,0), ref _missing, ref _missing);
        anelInterior.AddPoint(BuildPoint(1,2,0), ref _missing, ref _missing);
        anelInterior.AddPoint(BuildPoint(2,2,0), ref _missing, ref _missing);
        anelInterior.AddPoint(BuildPoint(2,1,0), ref _missing, ref _missing);
        anelInterior.AddPoint(anelInterior.get_Point(0),ref _missing, ref _missing);

        // nao é permitido sobrepor anéis, à menos que eles tenham orientações contrárias
        // nosso anel exterior tem orientação horária - todos os anéis exteriores devem ter orientação horária!
        // anéis interiores, que descrevem buracos em nossos polígonos devem ter orientação anti-horária!
        ICurve curva = anelInterior as ICurve;
        curva.ReverseOrientation();

        poligono.AddGeometry(anelExterior as IGeometry, ref _missing, ref _missing);
        poligono.AddGeometry(anelInterior as IGeometry, ref _missing, ref _missing);

        // definir a referência espacial - sem a referência espacial o ArcGis não realiza operações espaciais direitinho!

        return poligono as IPolygon;
}

Polígono simples criado pelo código acima

Polígono simples criado pelo código acima (fora de escala)

Bem, é fácil trabalhar com geometrias da ESRI. Claro, quanto mais complexa a geometria, mais complexo deverá ser o código para cuidar da mesma. Mas não é nenhum bixo de 7 cabeças. Um outro detalhe interessante é que revertemos a orientação da geometria interna usando uma outra interface, e adicionamos nosso anel original. Por que funciona? A biblioteca ArcObjects fica responsável por notificar a geometria original de que certa operação ocorreu e a realiza de acordo.

Em geral, os passos para se trabalhar com geometrias complexas é construir anéis ou segmentos, criados através da interface IPointCollection, pois assim podemos ir adicionando os pontos que temos interesse e depois ajeitar a orientação de cada anel para nos dar o resultado desejado.

Certo, mas e um polígono que contenha curvas? O formato shapefile não suporta(va) o desenho delas e estão disponíveis à partir dos geodatabases (as curvas são convertidas em diversos segmentos, aproximando o resultado. Mas podemos criar um polígon com curvas via código? Sim, claro! O segredo, neste caso, é separar cada segmento em um anel, mesmo que ele, no final, seja parte de uma coisa só.

Vamos criar dois anéis, um para nosso segmento curvo e o outro para nossos segmentos retos. Juntaremos os dois em apenas um anel e depois adicionaremos este anel ao polígono.

Para construir segmentos curvos, existem diversas interfaces do tipo IConstruct* (IConstructCircularArc, IConstructBezierCurve, etc.), cada uma com suas particularidades.

object _missing = Type.Missing;

        IGeometryCollection polygon = new PolygonClass();

        // building circular arc

        IPoint p1 = PointPolygonBuilder.BuildPoint(1, 2);
        IPoint p2 = PointPolygonBuilder.BuildPoint(1, 1);
        IPoint p3 = PointPolygonBuilder.BuildPoint(1, 0);
        IPoint p4 = PointPolygonBuilder.BuildPoint(0, 0);
        IPoint p5 = PointPolygonBuilder.BuildPoint(0, 1);

        IConstructCircularArc constructCircularArc = new CircularArcClass();
        constructCircularArc.ConstructThreePoints(p5, p2, p1, true);
        ICircularArc circularArc = constructCircularArc as ICircularArc;
        // end circular Arc

        ISegmentCollection finalRing = new RingClass();
        ISegmentCollection ring1 = new RingClass();
        IPointCollection ring2 = new RingClass();

        ring1.AddSegment(circularArc as ISegment, ref _missing, ref _missing);
        ring2.AddPoint(p1, ref _missing, ref _missing);
        ring2.AddPoint(p3, ref _missing, ref _missing);
        ring2.AddPoint(p4, ref _missing, ref _missing);
        ring2.AddPoint(p5, ref _missing, ref _missing);

        finalRing.AddSegmentCollection(ring1);
        finalRing.AddSegmentCollection(ring2 as ISegmentCollection);
        polygon.AddGeometry(finalRing as IGeometry, ref _missing, ref _missing);
Polígono resultante do código acima

Polígono resultante do código acima

Note que deu um trabalhinho à mais para montar este segmento curvo. É bom deixar claro também, que o ponto (1,1) representado na figura, não entra no anel feito de segmentos retos, ele é apenas utilizado para construir o segmento curvo.

Outra coisa legal é ver que para acessar alguns métodos, você tem que instanciar certas interfaces. Notem que o finalRing e ring1 possuem propriedades semelhantes, pois têm a mesma interface. Já ring2 possui outros membros, pois é de interface diferente. Classes iguais, interfaces diferentes = propriedades diferentes.

E aí, o que acharam?

Abraços pessoal!

George R. C. Silva

27Jun/100

Usando Python e o Geoprocessing Framework #1

Boa noite pessoal,

Hoje quero falar um pouquinho do framework de geoprocessamento do ArcGIS, disponível em Python. Python, como sabem, é uma linguagem de alto nível, orientada à objetos e muito - mas muito fácil de se aprender.

Existem tarefas extremamente repetitivas que podem ser facilmente automatizadas com um pouquinho de Python e o tal Geoprocessing. O ArcGIS, basicamente faz uso do Python em toda a command-line e em algumas ferramentas da toolbox. Tudo que é feito no ModelBuilder é convertido em código Python.

Bem, vamos à proposta: imagine que você tenha necessidade de atualizar dados disponibilizados como shapefiles, por um órgão do governo, de forma periódica e em um banco de dados (ArcSDE, Personal GDB, File GDB, etc.). Será possível?

Bem, isto é possível pois o Python conta com milhares de bibliotecas para acesso à páginas da web, compressão/descompressão de arquivos .zip, entre outras. Este foi um requerimento real de um trabalho antigo.

Bem, imaginemos o site do DNPM, que disponibiliza shapefiles dos direitos minerários, de tempos em tempos (o DNPM também publica um serviço web, mas nem sempre é um bom caminho). No site SIGMINE você pode puxar separado por estados o shape atualizado e alimentar um banco. Mas é muito trampo para ser feito toda semana. Ainda mais se tua base não for centralizada. Todo mundo tem atualizar milhares de mxds, mapas, etc.

Quais são nossos passos?

  • Fazer o download do shapefile em .zip
  • Descompactar nosso arquivo em disco
  • Atualizar o banco de dados

Podemos realizar o passo número 3 de várias formas, deletando a Feature Class antiga ou então atualizando registro por registro. Para nosso exercício, vamos deletar todos os registros e inseri-los novamente. Em ambientes mais controlados, com outras necessidades, talvez isto não seja o ideal.

Para contemplar nosso objetivo, iremos criar algumas classes:

  • leecherHandler - responsável pelo download de arquivos da web;
  • folderHandler - responsável pela criação/deleção de pastas;
  • zipHandler - responsável pela compressão/descompressão de arquivos .zip;
  • logger - responsável por guardar mensagens importantes para controle do que está acontecendo;
  • geoprocessor - responsável por realizar as operações e interfaces com o ArcGIS;

Vamos começar pela classe leecherHandler, que é o coração de nosso pequeno sistema:

import sys, urllib2, logHandler

class leecherHandlerClass():

    def __init__(self,webAddress,tempFolder):
        self.logs = logHandler.logHandlerClass()
        # Startup the log object.

        self.webAddress = webAddress
        self.tempFolder = tempFolder
        self.localFile = self.buildLocalFile(self.downloadWebFile())

    def buildFileName(self,webAddress):
        fileName = "\\" + webAddress.split(r"/")[-1]
        return fileName

    def downloadWebFile(self):
        try:
            self.logs.newLogMessage(self,"Starting download...","Information")
            # @eventoLogged: Start download.
            webFile = urllib2.urlopen(self.webAddress)
            # Opening the URL chosen.
            self.logs.newLogMessage(self,"Finished download.","Information")
            # @eventLogged: Download finished. Return a file object type.
            return webFile
        except:
            self.logs.newLogMessage(self,"It was not possible to download file.\n" + str( sys.exc_info()[0]),"Error")
            # @eventLogged: Error downloading file from URL

    def buildLocalFile(self,webFile):
        try:
            self.logs.newLogMessage(self,"Writing file to disk...","Information")
            # @eventLogged: Start writing to a local file.
            fileName = self.buildFileName(self.webAddress)
            localFile = open(self.tempFolder + fileName,"wb")
            # Get the filename and open a localfile.

            localFile.write(webFile.read())
            # Write to brand new file.
            # @todo: find a better way to write the file to disk

            webFile.close()
            localFile.close()
            # Close both files. Clean-up action.

Esta classe é bastante simples. Ela usa o módulo urllib2 para fazer os downloads, além de construir um novo arquivo em disco, na pasta especificada. Vejam que o código da função downloadWebFile é muito simples. Apenas precisamos apontar qual é o arquivo que queremos puxar e ela já o constrói em disco, com um nome alterado com data - para não nos perdemos.

Você não precisa fazer mais nada, pois no construtor da classe, ela já dispara todas as ações de download e construção de arquivo em disco. Pode levar um tempo para puxar os arquivos, mas em geral é bastante rápido. Caso seja necessário, você terá de montar e inicializar um proxy - que também será mostrado.

A forma de uso é bastante simples:

    shapeLeecher = leecherHandler.leecherHandlerClass('endereco do arquivo web','pasta temporaria de destino')
    # para acessarmos o arquivo local em disco, utilzamos a seguinte sintaxe:
    # shapeLeecher.localFile

Teremos agora um arquivo .zip em disco. Precisamos descompactá-lo. Crie uma nova classe, com a seguinte definição:

import sys, zipfile, logHandler

class zipHandlerClass():

    def __init__(self,tempFolder,zipFilePathname,watchFileFormat="shp"):
        self.logs = logHandler.logHandlerClass()
        # Startup the log object

        self.tempFolder = tempFolder
        self.zipFilePathname = zipFilePathname
        self.watchFileFormat = watchFileFormat
        # Basic properties

        self.containedFiles = []
        # Generated file list inside zip archive
        self.outputWatchFile = watchFileFormat
        # outputInformation

    def testZipFile(self):
        if self.zipFilePathname == None or zipfile.is_zipfile == False:
            self.logs.newLogMessage(self,"File pointed is not a valid zipfile" + str(sys.exc_info()[0]),"Error")
            return False
        else:
            return True

    def readContainedFiles(self,zipFile):
            return zipFile.namelist()

    def extractFiles(self):
        try:
            if self.testZipFile()== True:
                extractingFile = zipfile.ZipFile(self.zipFilePathname,"r")
                self.containedFiles = self.readContainedFiles(extractingFile)
                # Defines a zipFile object using pathname for further manipulation. Read the files inside archive.
                for zippedFile in self.containedFiles:
                    unpackedFile = open(self.tempFolder + "\\" + zippedFile, "wb")
                    unpackedFile.write(extractingFile.read(zippedFile))
                    unpackedFile.close()
                    # Unpack and write file to disk
                    self.logs.newLogMessage(self,"File " + zippedFile + " unpacked and written to disk.","Information")
                    # @eventLogged: Zip file unpacked and written in disk with success.

                    if zippedFile[-3:] == self.watchFileFormat:
                        self.outputWatchFile = zippedFile
                    else:
                        self.outputWatchFile = self.tempFolder
                    # Test to see if any of these files is of type watched.
                    # @todo: make this classe output a list of watched files.
                extractingFile.close()
                # close extracted file. CleanUp.
                return True
            else:
                return False
            # Test to see if it is a valid zipfile. If not, return false.
        except zipfile.BadZipfile:
            self.logs.newLogMessage(self,"Corrupted zipfile. Please download it again.","Error")
            return False

Esta classe é um pouco mais complicada. Temos de monitorar um arquivo mestre (em nosso caso, um shapefile) para que possamos iniciar os próximos passos. Olhem a definição da classe - ela pede uma pasta temporária (a mesma que você usou com o leecher), uma localização do arquivo .zip e um formato. Este formato, tem como default a extensão .shp, mas você pode especificar outro.

    # a idéia é que se use os resultados armazenados na classe leecher para alimentar
    # o construtor da classe zipHandler, criando um processo encadeado
    zipH = zipHandler.zipHandlerClass('pasta temporaria','arquivo.zip');
    zipH.extractFiles()

No frigir dos ovos, esta classe testa o arquivo zip, confere se o mesmo é válido, extrai os arquivos para a pasta selecionada, e guarda uma referência ao arquivo com a extensão escolhida.

Vamos mostrar agora a clase folderHandler. Ela irá criar nossas pastas para nós:

import sys, os,datetime, shutil, logHandler

class folderHandlerClass():
    def __init__(self,tempFolder):
        self.logs = logHandler.logHandlerClass()
        self.tempFolder = tempFolder

    def generateTempFolderName(self):
        dataHora = datetime.date.today()
        folderName = self.tempZipFile[:-4] + "_" + str(dataHora)
        return folderName

    def createTempFolder(self):
        try:
            if os.path.exists(self.tempFolder):
                if os.path.isdir(self.tempFolder):
                    return True
                else:
                    self.logs.newLogMessage(self,"The specified folder is not a valid folder.","Error")
                    # @eventLogged: Folder is not valid.
                    return False
            else:
                os.mkdir(self.tempFolder,222)
                self.logs.newLogMessage(self,"Folder " + self.tempFolder + " created with success.","Information")
                # @eventLogged: Folder create with success.
                return True
        except:
            self.logs.newLogMessage(self,"An unexpected error occurred while creating the specified folder.\n" + str(sys.exc_info()[0]),"Error")
            # @eventLogged: Error while creating folder. More info on sys.exc_info()

    def deleteTempFolder(self):
        try:
            shutil.rmtree(self.tempFolder)
        except:
            self.logs.newLogMessage(self,"An unexpected error occurred while deleting the specified folder.\n" + str(sys.exc_info()[0]),"Error")
            # @eventLogged: Error while deleting folder. More info on sys.exc_info()

Esta classe tem dois métodos principais: um para criar pastas e outro para deletá-las. A classe executa uma verificação básica para saber se a pasta existe ou é uma pasta. Em caso de positivo a pasta não é criada, apenas usada como destino dos arquivos. Caso ela não exista, a classe tenta criá-la. Isto é feito para economizarmos uma operação e evitar de criar uma pasta com um nome já existente, o que nos daria um erro.

O método que deleta a pasta é radical. Ele remove tudo que existe dentro da pasta. Sub-pastas, arquivos, não importa. Ele irá deletar tudo. Cuidado ao usá-lo com outros arquivos. O ideal para se trabalhar com este script é uma pasta separada só para ele. Para usar esta classe, faça assim:

    # como usar:
    folderH = folderHandler.folderHandlerClass('pasta temporaria')
    folderH.createTempFolder()
    # ele irá tentar criar a pasta
    folderH.deleteTempFolder()
    # ele irá tentar deletar a pasta e seu conteúdo

Bem, com estas classes conseguimos construir um pequeno framework para atualizarmos os dados em um banco do ArcGIS. São coisas simples, mas que ajudam bastante. No próximo post, teremos a classe que cuida das operações de geoprocessamento, interfaceando com o ArcGIS - e uma classe "mestra" que faz todas estas classes conversarem.

Como utilizar as três classes juntas?

import sys,folderHandler,zipHandler,leecherHandler

    # primeiro tentamos acessar/criar a pasta que queremos
    # lembrem-se que um r antes de uma string, significa raw
    # não sendo necessário escapar os caracteres \
    folderH = folderHandler.folderHandlerClass(r"C:\temporario")
    folderH.createTempFolder()

    # é só inicializar o leecher Handler. seu construtor cuida do restante para nós
    shpLeecher = leecherHandler.leecherHandlerClass("ftp://sigmine.dnpm.gov.br/Brasil.zip",folderH.tempFolder)

    # podemos inicializar o zipHandler de duas maneiras
    # com uma chamada explícita
    zipH = zipHandler.zipHandlerClass(folderH.tempFolder,shpLeecher.localFile,"shp")

    # ou implicita, caso a extensao seja shapefile - pois é um parametro com uma entrada
    # default. se nada for especificado, .shp irá cair em seu lugar
    # zipH = zipHandler.zipHandlerClass(folderH.tempFolder,shpLeecher.localFile)
    zipH.extractFiles()

Com o código acima e as três classes descritas,  já conseguimos puxar um arquivo da web e descompactá-lo para uma pasta qualquer.

Vocês podem notar que existem outras classes envolvidas aqui, especialmente o tal de LogHandler. Irei disponibilizar todo o projeto no próximo post ;)

Quaisquer dúvidas, estou à disposição!

Abraços

George

24Jun/100

Brincando com ArcObjects

Boa tarde pessoal,

ArcObjects é algo relativamente difícil. É complicado pois temos muitas formas de se fazer o que queremos, a documentação - apesar de razoável, não é excelente (existem detalhes importantes que podem estar escondidos em outras páginas) e não existe uma comunidade forte que trate destas questões.

Gostaria de mostrar um pouco de ArcObjects aos iniciantes, em especial um namespace complicadinho: ESRI.ArcGIS.Geometry. Não tenho domínio do namespace suficiente para dizer que sou um expert, mas consigo me virar.

O namespace Geometry é o responsável por cuidar de todas as operações com as geometrias, sejam elas de quaisquer tipos. Primeiramente gostaria de apresentaar à vocês aos tipos de geometria de alto-nível. Isto é importante pois algumas funções ou interfaces, só estão disponíveis nas geometrias de alto nível, outras somente nas de baixo nível.

Todas as geometrias de alto nível tem à sua disposição um set teórico de operações possíveis, tais como interseção, união, diferença, diferença simétrica, entre outros. Outras interfaces/classes do namespace ESRI.ArcGIS.Geometry são responsáveis por estas operações.

Geometrias de Alto Nível

As geometrias de alto nível são:

  • Points;
  • Multipoints;
  • Polylines;
  • Polygons;
  • MultiPatches;

Ponto é o tipo de geometria mais simples que podemos encontrar no modelo do ArcGIS. É basicamente composta de uma coordenada X e uma coordenada Y. Opcionalmente os pontos podem ser IdAware, ZAware e MAware, ou seja: podem ter um Id, Z e M. Todas os tipos de geometrias podem ter estes atributos em seus vértices, que em suma, são pontos.

Multipontos é um tipo de geometria representado por uma coleção de pontos. Da mesma maneira que um ponto, cada ponto desta única geometria tem seus atributos.

Polilinhas são composta pela união de diversos paths (uma geometria de nível mais baixo) que são compostos por sua vez de diversos segments, que podem ser dos tipos: linha, arco circular, curva bezier ou arco elíptico.

Polígonos são geometrias compostas por rings, que são compostos por segments, dos tipos citados acima. A ordem de cada ring e o sentido de construção (horário/ante-horário) determinar o comportamento de cada ring. Exemplo: todos os rings externos são ordenados em sentido horário, indicando que o interior dele é o polígono. Caso um ring seja ordenado em anti-horário você está dizendo ao ArcGIS que ele é hole - com área negativa.

No caso dos polígonos, isso pode ocorrer indefinidamente, sendo determinado pela ordem em que cada ring aparece na coleção.

Ilustração do funcionamento de rings e polígonos - Fonte: ESRI

Ilustração do funcionamento de rings e polígonos - Fonte: ESRI

Multipatches são geometrias compostas e são em três dimensões. São geometrias que podem ter múltiplas superfícies, com textura. É a forma como o ArcGIS representa objetos em três dimensões. São compostas de Triangles, TriangleStrips e TriangleFans.

Todas as geometrias de nível mais baixo são construídas necessáriamente por pontos. É o bloco de construção do ArcGIS.

Geometrias de Baixo Nível

  • Paths
  • Rings
  • Segments
  • TriangleStrips
  • TriangleFans
  • Triangles
Diagrama mostrando a estrutura de Polyline - Fonte: ESRI

Diagrama mostrando a estrutura de Polyline - Fonte: ESRI

Diagrama mostrando à estrutura de Polygon - Fonte: ESRI

Diagrama mostrando à estrutura de Polygon - Fonte: ESRI

Pela estrutura que temos aí dá pra entender melhor não?

Vamos começar brincando com os pontinhos, já que são as estruturas básicas de trabalho.

Classes e Interfaces

Para simplificar para os iniciantes, classe é um projeto de um objeto. Quando criamos um novo, aquele projeto é "materializado" na memória do computador.

Interface é um contrato. Quando dizemos que uma classe implementa uma interface, queremos dizer que aquela classe assinou um contrato de funcionalidade com aquela interface - ou seja, tudo que realiza operações na interface ou em algum membro da interface, está também disponível na classe, sendo possível utilizar aquela classe em alguma função que requer a interface.

PointClass

Esta classe implementa diversas interfaces (ou contratos) do namespace Geometry. As interfaces definem as funcionalidades que a PointClass terá. Caso você tenha o Help de desenvolvimento do ArcGIS instalado, abra o mesmo e procure esta URL ms-help://ESRI.EDNv9.3/esriGeometry/html/Point.htm .

Caso não tenha o help instalado

Como criamos um novo ponto? É bem simples, vejam só.

    IPoint ponto = new PointClass();
    ponto.PutCoords(10,10);

Vamos criar uma novo projeto C# do tipo ArcGIS>Console Application. Adicione as referências:

  • ESRI.ArcGIS.Geometry;
  • ESRI.ArcGIS.Framework;
  • ESRI.ArcGIS.esriSystem;

Você verá que o Visual Studio já criou algumas linhas código para nós. Este é o código para buscar a licença na máquina. Em qualquer tipo de aplicação que você for construir, você deve ter um código similar para buscar uma licença, senão ele é automaticamente desligado.

namespace DesktopConsoleApplication1
{
    class Program
    {
        private static LicenseInitializer m_AOLicenseInitializer = new DesktopConsoleApplication1.LicenseInitializer();

        [STAThread()]
        static void Main(string[] args)
        {
            //ESRI License Initializer generated code.
            m_AOLicenseInitializer.InitializeApplication(new esriLicenseProductCode[] { esriLicenseProductCode.esriLicenseProductCodeArcView },
            new esriLicenseExtensionCode[] { });
            //ESRI License Initializer generated code.
            //Do not make any call to ArcObjects after ShutDownApplication()
            m_AOLicenseInitializer.ShutdownApplication();
        }
    }
}

Este é como meu código se parece. O método Main é o que executa o programa de verdade. Para demonstrar o funcionamento da interface IPoint e da PointClass, vamos escrever algo bem simples, para ilustrar.

Acima do namespace, adicione as seguintes linhas:

using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Framework;

Elas são necessárias para utilizarmos os objetos contidos nestes namespaces sem qualificá-los totalmente, ex: ESRI.ArcGIS.Geometry.IPoint. Após a adição destas linhas, podemos utilizar somente IPoint, diretamente, pois estes objetos estão à nossa disposição.

Crie um novo método dentro desta classe (isto não é o ideal em código de produção - estude orientação à objetos. este é apenas um exemplo) chamado BuildPoint. Este método deve ser estático (não queremos ter de criar uma outra classe Program para acessá-lo), ou seja, pode ser acessado no "projeto" da classe (ao invés de funcionar no objeto). Neste método, queremos construir um ponto, designar suas coordenadas x, y e z e queremos que esta função nos devolva este ponto.

Ficaria assim:

        static IPoint BuildPoint(double x, double y, double z)
        {
            IPoint ponto = new PointClass();
            ponto.PutCoords(x, y);
            ponto.Z = z;

            return ponto;
        }

Sugiro que você não façam somente um Ctrl-C + Ctrl-V dos códigos. Teste isto no Visual Studio. O Intelllisense irá lhe mostrar algumas outras coisinhas interessantes. Não esqueça de consultar a documentação para saber mais sobre cada um dos membros e métodos.

Bem, o que este método faz? Ele simplesmente instancia um novo ponto, atualiza as coordenadas conformes passadas nos argumentos da função e nos devolve o ponto. Para que fazer uma função assim? Bem, são somente 4 linhas de código, mas imagine se você precisa criar 80000 pontos? Certo, então temos nossa primeira função pronta.

O que faremos com ela? Vamos modificar nosso método Main, para ler diversas coordenadas e criar um monte de pontos para nós. Depois iremos pedir para nosso método Main nos mostrar os pontos criados. Além de modificar o Main, vamos construir um segundo método para nos ajudar a criar os pontos.

Vamos lá:

        [STAThread()]
        static void Main(string[] args)
        {
            //ESRI License Initializer generated code.
            m_AOLicenseInitializer.InitializeApplication(new esriLicenseProductCode[] { esriLicenseProductCode.esriLicenseProductCodeArcView },
            new esriLicenseExtensionCode[] { });
            //ESRI License Initializer generated code.
            //Do not make any call to ArcObjects after ShutDownApplication()
            m_AOLicenseInitializer.ShutdownApplication();

            // após a inicialização da licença, vamos brincar
            string finaliza = "s";
            string leitura;

            // aqui guardaremos nossos pontos
            List listaDePontos = new List();

            // enquanto finaliza for diferente de n, repita:
            while (finaliza.Substring(0,1).ToLower() != "n")
            {
                // vamos dizer ao usuário o que precisamos.
                Console.WriteLine("Digite as coordenadas de seu ponto. Utilize vírgulas para separá-las");
                Console.WriteLine("X,Y,Z");

                // leitura guardará o resultado que nosso usuário digitar!
                leitura = Console.ReadLine();

                // temos uma string. precisamos de um método para quebrá-la
                // e nos devolver um ponto - que será inserido na listaDePontos
                listaDePontos.Add(TraduzirStringPonto(leitura));

                // será que o usuário quer digitar mais pontos?
                Console.WriteLine("Você deseja criar mais pontos? n para não");
                // vamos alterar o valor de finaliza, que será testa mais à frente.
                // note que esta rotina não cobre TODAS AS POSSIBILIDADES
                finaliza = Console.ReadLine();
            }

            // agora que o usuário terminou de criar seus pontos,
            // vamos mostrá-los à ele.
            Console.WriteLine();
            Console.WriteLine("Lista de Pontos:\n");

            // para cada variável do tipo IPoint em listaDePontos, faça
            foreach (IPoint p in listaDePontos)
            {
                // \t é caractere de tabulação.
                Console.Write("ID " + p.ID.ToString() + "\t");
                Console.Write("X " + p.X.ToString() + "\t");
                Console.Write("Y " + p.Y.ToString() + "\t");
                Console.Write("Z " + p.Z.ToString() + "\t");
                Console.WriteLine();
            }
            // vamos esperar o usuário ler os dados e assim que ele teclar denovo
            // finalizamos o programa
            Console.WriteLine();
            Console.WriteLine("Aperte qualquer botão do teclado para finalizar...");
            Console.ReadLine();
        }

No meio da função está a lógica do programa. Queremos ler dados, até o usuário dizer não. Note que este programa é bastante simples, não realiza verificação de erros nem os trata. Não deve ser feito assim na vida real!

Mostramos para o usuário algumas propriedades da classse IPoint, algumas setadas por nós, outras não. Viram que todos os IDs são iguais à zero?

Agora vou lhes mostrar a função TraduzirStringPonto. Ela envolve mais programação comum, do que ArcObjects. Vou mostrá-la só para não ficarem no escuro :P

        static IPoint TraduzirStringPonto(string s)
        {
            // recebemos nesta função uma string e queremos transformá-la em números
            // vamos usar o método Split, que tem como argumentos um array de caracteres
            // delimitados como as strings, com a exceção de que usamos aspas simples!
            string[] arrayDeStrings = s.Split(new Char[]{','},3);

            // vamos criar uma array com três posições para receber nossos números!
            double[] arrayDeDoubles = new double[3];

            // para i = 0 até i = 2
            for(int i = 0; i <= 2; i++)
            {
                // assinale o valor da posição i em arrayDeDoubles
                // para ser igual ao valor da posição i em arrayDeStrings,
                // convertendo-os para um objeto do tipo Double
                arrayDeDoubles[i] = Double.Parse(arrayDeStrings[i]);
            }

            // vamos criar nosso ponto. lembra de BuildPoint?
            IPoint ponto = BuildPoint(arrayDeDoubles[0],arrayDeDoubles[1],arrayDeDoubles[2]);

            // nos devolva nosso ponto para que possamos inseri-lo na lista
            return ponto;
        }

Novamente um aviso: esta função também não tem identificação de erros ou tratamento dos mesmos. Recomendo ao interessado em desenvolver seu método de validação da entrada de dados, pois se inserirmos em nosso programa os valores:

10,eitcha,100

ele irá simplesmente travar - no momento da conversão de string para double.

Esta foi uma gentil introdução à manipulação de geometrias em ArcObjects. Os próximos passos são um pouco mais cabeludos, mas nada que seja impossível.

O que acharam?

18Jun/100

Configure o TerraLib 3.3 no Visual Studio 2003

Boa noite pessoal,

Este é o primeiro post de uma série sobre como utilizar a biblioteca TerraLib para desenvolver suas próprias aplicações. Espero que gostem!

O tutorial apresentado é um complemento da documentação disponibilizada pelo INPE , que ensina como configurar a biblioteca TerraLib 3.3.1 no Visual Studio 2003.

1 - Requisitos para executar os exemplos

  • Visual Studio C++ 2003;
  • MySQL Administrator 5.o;
  • Driver MySQL para Windows;
  • Banco de Dados MySQL 5.1;
  • Biblioteca TerraLib para Windows, 3.3 ou superior;

2 - Instruções gerais de instalação

O primeiro software a instalar é o Visual Studio 2003. Utilize a instalação personalizada e desmarque o suporte a todas as outras linguagens diferentes do C++, por exemplo: C# e VB. A seguir, instale o MySQL, Driver do MySQL e o MySQL Administrator.

Após instalar com sucesso os softwares descritos acima, crie uma pasta no disco C, por exemplo C:/TerraDir e descompacte todo o conteúdo do arquivo TerraLib_win_v_3_3_1.zip. Após descompactar, a árvore de pastas deve seguir o formato da figura 1.

Árvore de pastas do TerraLib

Árvore de pastas do TerraLib

3 - Compilando a biblioteca TerraLib

O primeiro passo é compilar toda biblioteca TerrLib. Para isso, abra o Visual Studio, clique em File/Open Project / e escolha a solution, referente à biblioteca que será executada. No exemplo abaixo será compilado a solution createDatabase.sln.

1) File/Open project/ ou Ctrl+Shift+O
C:\TerraDir\examples\createDatabase\createDatabase.sln. Certifique-se que o arquivo escolhido possui a extensão.sln, porque na pasta createDatabase, também possui um arquivo chamado createDatabase.dsw.

2) Build/Build Solution ou Ctrl+Shitf+B
Build Sucessfull -> Essa mensagem é indício que está indo tudo muito bem, parabéns!

Faça este mesmo procedimento para todas as bibliotecas abaixo na ordem que estão listadas.

  • createLayer;
  • createTable;
  • importMIDMIF;
  • importShape;
  • importDBF;
  • importJPEG;
  • copyLayer;
  • importGeoTab;
  • convertCoordinates;
  • databaseQuery;
  • databaseSQLQuery;
  • spatialQuery;
  • addGeomRepresentation;
  • createTheme;
  • themeGrouping;
  • createSTElementSet;
  • mosaicTIFFImages;
  • importCSV;
  • importGridData;
  • rasterSlicing;
  • querierFromTheme;
  • proxMatrixAndSpatialStatistics;
  • spatialQueryAndBuffer;
  • querierFromLayer;
  • createSTElementSetFromLayer;
  • image_processing.

Após compilar todas as bibliotecas acima sem erros, pode fechar o Visual Studio. A próxima etapa consiste em codificar alguns trechos de códigos para iniciar o aprendizado na biblioteca TerraLib. No tutorial do INPE  existem diversas páginas com trechos de código.

4 - Configurar o projeto

Este tutorial ficará limitado em explicar detalhadamente apenas os passos necessários para criar um projeto.

O primeiro passo é criar um novo projeto, para isso abra o Visual Studio. Escolha File/New/Project, conforme a figura 2 e coloque o nome no projeto de Primeiro Exemplo.

Criar um novo projeto

Criar um novo projeto

 Escolha o template Console Application (.NET), conforme imagem 3.

Figura 3 - Escolher Template

Figura 3 - Escolher Template

Após criar o projeto PrimeiroExemplo, é necessário várias configurações. A primeira é adicionar os projetos existentes do TerraLib. Para isso clique na solution e escolha add e selecione Existing Project. Veja nas figuras 4, 5 e 6 como adicionamos o projeto terralib.

Figura 04  - Adicionar projeto existente

Figura 04 - Adicionar projeto existente

Figura 05 - Adicionar projeto existente  (2° passo)

Figura 05 - Adicionar projeto existente (2° passo)

Figura 06 - Adicionar projeto existente (3° passo)

Figura 06 - Adicionar projeto existente (3° passo)

Figura 07 - Adicionar projeto existente (4° passo)

Figura 07 - Adicionar projeto existente (4° passo)

Além do projeto terralib, é necessário adicionar os projetos libjpeg e tiff, que estão respectivamente nos caminhos C:\TerraDir\terralibw\libjpeg  e C:\TerraDir\terralibw\tiff. Após adicionar os três projetos, faça um Build na solution.

Além desses três projetos que foram incorporados ao projeto PrimeiroExemplo, outros projetos do TerraLib poderão ser agregados conforme a necessidade.

O procedimento a seguir objetiva configurar o projeto PrimeiroExemplo. Clique nas propriedades do projeto, conforme na figura 08.

Figura 08 - Propriedades do Projeto

Figura 08 - Propriedades do Projeto

Insira o caminho para os diversos diretórios adicionais do projeto. Para realizar essa operação, copie e cole toda a linha a seguir no campo Additional Include Directories. A figura 09 detalha o que precisa ser feito.

.;..\..\src\terralib\kernel;..\..\src\terralib\drivers\MySQL;..\..\src\terralib\drivers\MySQL\include

Figura 09 - Adicionar Diretórios Adicionais

Figura 09 - Adicionar Diretórios Adicionais

Desabilite o Precompiled headers. Não feche a janela de propriedades. Configure o Linker do projeto PrimeiroExemplo. Esse procedimento é necessário para que o projeto criado consiga referenciar as classes do TerraLib. Inclua as dependências adicionais conforme ilustra a figura 10. Para isso copie as linhas abaixo e cole em Additional Dependencies.

Figura 10 - Desabilite os precompiled headers

Figura 10 - Desabilite os precompiled headers

Figura 11 - Additional dependencies

Figura 11 - Additional dependencies

../../Debug/terralib/terralib.lib ../../Debug/libjpeg/libjpeg.lib ../../Debug/tiff/tiff.lib

../../terralibw/zlib/zlibdll.lib ../../terralibw/MySQL/libMySQL.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib imm32.lib winmm.lib wsock32.lib winspool.lib delayimp.lib

Após realizar todos os procedimentos acima, faça um build do projeto. Caso ocorrer algum erro, tente remover e copiar novamente as linhas que foram inseridas nas propriedades do projeto. Atenção ao copiar essas linhas, espaços não podem ser retirados ou inseridos.

Os procedimentos abaixo consistem em adicionar itens existentes ao projeto. Para o projeto PrimeiroProjeto, será adicionado as bibliotecas TeMySQL.h e TeMySQL.cpp. As figuras de 12 14 demonstram como adicionar o item TeMySQL.cpp ao Source File do Projeto.

Figura 12 - Adicionar itens existentes do 1° passo

Figura 12 - Adicionar itens existentes do 1° passo

Figura 13 - Adicionar Itens existentes (2° passo)

Figura 13 - Adicionar Itens existentes (2° passo)

 A figura 13 apresenta os dois arquivos da biblioteca TerraLib que deverão ser inseridos ao projeto. Inicialmente escolha apenas TeMySQL.cpp a selecione Open.

Figura 14 - Arquivos existentes da biblioteca

Figura 14 - Arquivos existentes da biblioteca

Para adicionar o arquivo TeMySQL.h ao projeto, click sobre Header Files do projeto PrimeiroProjeto. A figura 14 demonstra como fazer isso. Os próximos passos para adicionar TeMySQL.h são os mesmos das figuras 12 e 13, porém escolha TeMySQL.h e pressione open.

Ao final de todos esses procedimentos, realize um build de todo o projeto PrimeiroProjeto para certificarmos que todos as configurações estão corretas.

5 – Codificar o primeiro exemplo

Para iniciar a codificação do primeiro exemplo, click duas vezes sobre a classe PrimeiroExemplo.cpp, conforme a imagem 15 e adicione as bibliotecas e os métodos criarBancoDeDados e conectar. Após a figura 15 tem o código completo da classe PrimeiroExemplo.cpp. Após codificar a classe, realize um build no projeto e execute-o.

Figura 15 - Alterando a classe PrimeiroExemplo.cpp

Figura 15 - Alterando a classe PrimeiroExemplo.cpp

#include "stdafx.h"
#include "conio.h"

#include "TeMySQL.h"
#include "TeDriverMIDMIF.h"

#using 

using namespace System;

//Declaração dos métodos
int conectar();
int criarBancoDeDados();

int _tmain()
{
    // TODO: Please replace the sample code below with your own.
    Console::WriteLine(S"Hello World");
	conectar();
	criarBancoDeDados();
	getch();
	return 0;
}

int criarBancoDeDados ()
{
	// Parâmetros do servidor de bancos de dados
	string host = "localhost";
	string dbname = "TerraTeste";
	string user = "root";
	string password = ""; 

	// Cria um novo banco
	TeDatabase* db = new TeMySQL();
	if (!db->newDatabase(dbname,user, password, host))
	{
	cout << "Erro: " << db->errorMessage() << endl;
	return 1;
	} 

	cout << "O banco de dados \"" << dbname;
	cout <<"\" foi criado com sucesso no servidor MySQL localizado em\""
		<< host;
	cout << "\" para o usuario \"" << user << "\"!"  << endl;  	// Fecha o banco de dados  	db->close();
	delete db;
	return 0;
}

int conectar()
{
	// Cria uma conexão a um servidor de banco de dados
	TeDatabase* db = new TeMySQL();
	// Parametros do servidor de bancos de dados
	string host ="localhost";
	string dbname = "TerraTeste";
	string user = "root";
	string password = "";
	if (!db->connect(host,user, password,dbname,0))
	{
	cout<< "Erro: " << db->errorMessage() << endl;
	return 0;
	}
	cout << "Aberta conexão ao banco: " << dbname;
             db->close();
	delete db;
  }

É isso aí pessoal! O que acharam? Abraços e comentem!

João Tácio Silva

Tagged as: , , , No Comments
25May/102

Funções PostGIS #3

Boa tarde pessoal!

Vamos falar um pouco hoje das funções de análise espacial e medições. Antes disso, o blog alcançou +50 postagens essa semana. Parabéns para nós!

ST_Area()

Esta função é bastante simples. Ela retorna a área de um polígono ou multipolígono.

-- assinatura
-- SELECT ST_Area(geometria)

SELECT ST_Area(
   ST_GeomFromText(('POLYGON((0 0, 1 0,1 1,0 1,0 0))')))

ST_Perimeter()

Outra função bastante simples, de um único argumento. O argumento deve ser uma geometria do tipo polígono. Se você passar uma LINESTRING, o PostGIS irá retornar 0. Veja ST_Length() para LINESTRINGs.

-- assinatura
-- SELECT ST_Area(geometria)

SELECT ST_Perimeter(
   ST_GeomFromText(('POLYGON((0 0, 1 0,1 1,0 1,0 0))')));

-- só funciona com polígonos. user ST_Length()
SELECT ST_Perimeter(
   ST_GeomFromText('LINESTRING(0 0, 1 0, 2 1, 2 2, 3 2)'));

ST_Perimeter2D()

Calcula o perímetro considerando apenas as coordenadas X e Y de um polígono.

-- assinatura
-- SELECT ST_Perimeter2D(geometria)
-- note que nosso polígono é 3D, possui Z.
SELECT ST_Perimeter2D(
   ST_GeomFromText(('POLYGON((0 0 1, 1 0 1,1 1 2,0 1 3,0 0 1))')));

ST_Perimeter3D()

Calcula o perímetro de um polígono levando em conta as coordenadas Z. Veja que nosso polígono anterior, com a coordenada Z, ST_Perimeter3D() nos dá um retorno diferente.

-- assinatura
-- SELECT ST_Perimeter3D(geometria)
-- note que o polígono é 3D, possui Z.
SELECT ST_Perimeter3d(
   ST_GeomFromText(('POLYGON((0 0 1, 1 0 1,1 1 2,0 1 3,0 0 1))')));

ST_Length()

Calcula o comprimento de uma LINESTRING. Simples e direto :D . Esta função calcula o comprimento de uma linha em duas dimensões.

-- assinatura
-- SELECT ST_Length(geometria)

SELECT ST_Length(
    ST_GeomFromText('LINESTRING(0 0, 1 0, 2 1, 3 3)'));

ST_Length2D()

Exatamente igual à função acima. É apenas um apelido para ST_Length().

ST_Length3D()

Esta função funciona exatamente como ST_Perimeter3D. Mas para LINESTRINGs. Esta função leva em conta as coordenadas no eixo Z de uma geometria.

-- assinatura
-- SELECT ST_Length3D(geometria)

SELECT ST_Length3D(
    ST_GeomFromText('LINESTRING(0 0 1, 0 0 0, 1 0 2, 2 3 1)'));

ST_Centroid()

Esta função calcula o centro de massa de uma geometria. Para pontos é apenas uma média aritmética de seus conjuntos de coordenadas. Para LINESTRINGS, é o resultado de uma média ponderada do comprimento de seus segmentos e para polígonos o centro de massa do mesmo.

É possível aplicar esta função em qualquer tipo de geometria e em coleções genéricas (GEOMETRY_COLLECTIONs - conjuntos de geometrias de diversos tipos), sendo o resultado equivalente ao centróide do conjunto de geometrias de maior dimensionalidade (pontos são 0-dimensionais, linhas unidimensionais, polígonos bidimensionais). Ou seja, se temos um conjunto de pontos, linhas e polígonos, o centróide será igual ao centróide do conjunto de polígonos.

Esta função é importante. Diversas vezes a maneira mais prática e fácil de se transformar dados é através do centróide e preparar dados para estatísticas espaciais.

-- assinatura
-- SELECT ST_Centroid(geometria)

SELECT ST_AsText(
	ST_CENTROID(
	ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))')));

SELECT ST_AsText(
	ST_CENTROID(
	ST_GeomFromText('LINESTRING(0 0, 1 0, 1 1)')));

SELECT ST_AsText(
	ST_CENTROID(
	ST_GeomFromText('MULTIPOINT((0 0),(5 8),(10 1))')));

Aqui estão os resultados das chamadas acima.

Resultado da função ST_Centroid utilizando um polígono como argumento

Resultado da função ST_Centroid utilizando um polígono como argumento

Resultado da função ST_Centroid utilizando uma linestring como argumento

Resultado da função ST_Centroid utilizando uma linestring como argumento

Resultado da função ST_Centroid utilizando um multipoint como argumento

Resultado da função ST_Centroid utilizando um multipoint como argumento

ST_Intersects()

Outra função importante. Esta função simplesmente teste se geometria A interseciona geometria B. Esta função não retorna a real interseção das duas partes, mas somente se elas se intersecionam ou não, true ou false. ST_Intersects pode ser utilizado com qualquer par de geometrias.

-- assinatura
-- SELECT ST_Intersercts(geometria_A,geometria_B);

-- retorna true
SELECT ST_Intersects(
	ST_GeomFromText('POLYGON((0 0, 1 1, 1 0, 0 1, 0 0))'),ST_GeomFromText('LINESTRING(0 0, 2 2, 1 1)'));

-- retorna false
SELECT ST_Intersects(
	ST_GeomFromText('POLYGON((0 0, 1 1, 1 0, 0 1, 0 0))'),ST_GeomFromText('LINESTRING(10 10, 20 10)'));

ST_Intersection()

Esta função é parecida com a ST_Intersects, mas esta retorna de fato a interseção entre as duas geometrias. Ambas as funções são computacionalmente intensivas, portanto cuidado ao utilizá-la em um sistema em produção, em views e outras coisas deste tipo. Esta função tem uma particularidade: podemos usar como parametro qualquer tipo de geometria, mas o resultado sempre virá no formato de geometria com menor dimensionalidade.

Exemplo: se computarmos a interseção entre dois polígonos, teremos como resultado um polígono (ou nulo). Se computarmos a interseção entre um polígono e diversas linhas, o resultado será uma LINESTRING obrigatoriamente. Porque? É matemático, a interseção de um polígono com um ponto sempre terá como resultado um ponto. Os resultados estão em verde.

Veja:

Resultado gráfico da interseção de duas LINESTRINGs

Resultado gráfico da interseção de duas LINESTRINGs

Podemos ter como resultado da interseção entre duas LINESTRINGs um conjunto de pontos ou uma LINESTRING (quando dois segmentos são coincidentes).

Resultado gráfico da interseção de um conjunto de pontos e um polígono

Resultado gráfico da interseção de um conjunto de pontos e um polígono

Resultado gráfico de ST_Intersection entre um conjunto de LINESTRINGs e um polígono

Resultado gráfico de ST_Intersection entre um conjunto de LINESTRINGs e um polígono

Resultado gráfico de ST_Intersection entre dois polígonos

Resultado gráfico de ST_Intersection entre dois polígonos

Vamos à assinatura da função:

-- assinatura
-- SELECT ST_Intersection(geometria_A,geometria_B);

-- POLYGON e LINESTRING
SELECT ST_AsText(ST_Intersection(
	ST_GeomFromText('POLYGON((0 0,1 0, 1 1,0 1,0 0))'),
        ST_GeomFromText('LINESTRING(0 0,.5 .5,2 2,3 3)')));

-- POLYGON E PONTO
SELECT ST_AsText(ST_Intersection(
	ST_GeomFromText('POLYGON((0 0,1 0, 1 1,0 1,0 0))'),
        ST_GeomFromText('POINT(.2 .2)')))

-- POLYGON e POLYGON
SELECT ST_AsText(ST_Intersection(
	ST_GeomFromText('POLYGON((0 0,1 0, 1 1,0 1,0 0))'),
	ST_GeomFromText('POLYGON((.5 .5,1.5 .5,1.5 1.5,.5 1.5,.5 .5))')))

-- LINESTRING e LINESTRING
SELECT ST_AsText(ST_Intersection(
	ST_GeomFromText('LINESTRING(0 0,1 1,3 2,6 6)'),
	ST_GeomFromText('LINESTRING(-1 -1,.5 .5,3 4,5 6)')))

Os resultados em formato texto são os seguintes:

Resultado textual de ST_Intersection entre um polígono e uma linestring

Resultado textual de ST_Intersection entre um polígono e uma linestring

Resultado textual de ST_Intersection entre um polígono e um ponto

Resultado textual de ST_Intersection entre um polígono e um ponto

Resultado textual de ST_Intersection entre dois polígonos

Resultado textual de ST_Intersection entre dois polígonos

Resultado textual de ST_Intersection entre duas LINESTRINGs

Resultado textual de ST_Intersection entre duas LINESTRINGs

Uma nota importante: pontos não se intersecionam. Ou eles são coincidentes ou não.

ST_Overlaps

Esta função é parecida em funcionamento com a ST_Intersects. Na verdade ela realiza a mesma operação que ST_Intersects, mas leva em consideração se um objeto não está contido plenamente dentro do outro. Esta função opera com qualquer tipo de geometria, mas presta atenção na dimensionalidade de cada uma: uma geometria de menor dimensionalidade está sempre contida na geometria de maior dimensionalidade, portanto, não sobrepõe a de maior dimensionalidade.

Exemplo: geometria_a é um ponto e geometria_b é um polígono. Se perguntado se ST_Overlaps(geometria_a,geometria_b), o PostGIS vai retornar falso, pois não são da mesma dimensionalidade. Se geometria_a é um polígono e geometria_b também, a operação pode retornar verdadeiro, caso a sobreposição ocorra.

-- assinatura
-- SELECT ST_Overlaps(geometria_a,geometria_b)

-- [1]
-- mesma dimensionalidade: estas geometrias podem se sobrepor.
SELECT ST_Overlaps(
	ST_GeomFromText('LINESTRING(0 0,1 1,3 2,6 6)'),
	ST_GeomFromText('LINESTRING(-1 -1,.5 .5,3 4,5 6)'))

-- [2]
-- mesma dimensionalidade: estas geometrias podem se sobrepor.
SELECT ST_Overlaps(
	ST_GeomFromText('POLYGON((0 0,1 0,1 1,0 1,0 0))'),
	ST_GeomFromText('POLYGON((.5 .5,1.5 .5,1.5 1.5,.5 1.5,.5 .5))'))

-- [3]
-- dimensionalidade diferente: estas geometrias não podem se sobrepor (mesmo que se intersecionem)
SELECT ST_Overlaps(
	ST_GeomFromText('LINESTRING(0 0,1 1,3 2,6 6)'),
	ST_GeomFromText('POINT(0 0)'));

-- [4]
-- dimensionalidade diferente: estas geometrias não podem se sobrepor (mesmo que se intersecionem)
SELECT ST_Overlaps(
	ST_GeomFromText('POLYGON((0 0,1 0,1 1,0 1,0 0))'),
	ST_GeomFromText('POINT(0 0)'));
Resultado da função ST_Overlaps nos exemplos 1 e 2 de ST_Overlaps

Resultado da função ST_Overlaps nos exemplos 1 e 2 de ST_Overlaps

Resultado da função ST_Overlaps nos exemplos 3 e 4 de ST_Overlaps

Resultado da função ST_Overlaps nos exemplos 3 e 4 de ST_Overlaps

ST_Covers()

Esta função retorna verdadeiro caso nenhum ponto da geometria_b se encontre fora da geometria_a.

-- assinatura
-- SELECT ST_Covers(geometria_a,geomtria_b);

-- geometria_a cobre geometria_b? verdadeiro
SELECT ST_Covers(
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'),
	ST_GeomFromText('POLYGON((.5 .5,1.5 .5,1.5 1.5,.5 1.5,.5 .5))'));

-- geometria_a cobre geometria_b? verdadeiro, todos os pontos de geometria_b estão contidos em a
SELECT ST_Covers(
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'),
	ST_GeomFromText('POINT(1 1)'));

-- geometria_a cobre geometria_b? falso. geometria_a é menor que geometria_a.
SELECT ST_Covers(
	ST_GeomFromText('POLYGON((.5 .5,1.5 .5,1.5 1.5,.5 1.5,.5 .5))'),
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'));

-- geometria_a cobre geometria_b? falso. geometria_a cobre geometria_b parcialmente (note o segundo ponto, em 100 100)
SELECT ST_Covers(
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'),
	ST_GeomFromText('MULTIPOINT((1 1),(100 100))'));
Resultado da função ST_Covers utilizando com dois polígonos

Resultado da função ST_Covers utilizando com dois polígonos

Resultado da função ST_Covers entre um polígono e multipontos

Resultado da função ST_Covers entre um polígono e multipontos

ST_Within()

Esta função é similar em funcionamento à ST_Covers(). A diferença aqui é que esta retorna positivo se estiver completamente dentro de B.

-- assinatura
-- SELECT ST_Within(geometria_a,geometria_b)

-- [1]
-- geometria_a está completamente dentro de geometria_b? verdadeiro
SELECT ST_Within(
	ST_GeomFromText('POINT(5 5)'),
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'));

-- [2]
-- geometria_a está completamente dentro de geometria_b? verdadeiro
SELECT ST_Within(
	ST_GeomFromText('LINESTRING(5 5, 2 3, 1 0)'),
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'));

-- [3]
-- geometria_a está completamente dentro de geometria_b? verdadeiro
SELECT ST_Within(
	ST_GeomFromText('POLYGON((1 1,2 1,2 2,1 2,1 1))'),
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'));

-- [4]
-- geometria_a está completamente dentro de geometria_b? falso. geometrias de dimensionalidade menor não contém geometrias de maior
-- dimensionalidade
SELECT ST_Within(
	ST_GeomFromText('POLYGON((1 1,2 1,2 2,1 2,1 1))'),
	ST_GeomFromText('MULTIPOINT((0 0),(10 0),(10 10),(0 10),(0 0))'));

-- [5]
-- geometria_a está completamente dentro de geometria_b? falso. A não está inteiramente dentro de B
SELECT ST_Within(
	ST_GeomFromText('MULTIPOINT((5 5),(100 100))'),
	ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'));
Resultado da função ST_Within(), exemplos de 1 à 3

Resultado da função ST_Within(), exemplos de 1 à 3

Resultado da função ST_Within, exemplos 4 e 5

Resultado da função ST_Within, exemplos 4 e 5

Por agora é só pessoal. No próximo post vou tentar explicar melhor sobre a dimensionalidade de cada tipo de geometria e os porquês estas funções se comportam desta maneira.

Espero que tenham gostado.

Abraços

George R. C. Silva

12May/102

Funções PostGIS #2

Boa tarde senhores e senhoras,

Continuando nossa peregrinação pelas funções do PostGIS, irei começar explicar a que usamos no post anterior e não expliquei.

ST_AsText()

Esta função retorna o valor WKT (Well Known Text - especificação OGC) da geometria. Esta função é útil pois lhe mostra algo um pouco mais inteligível do que o WKB (Well Known Byte).

Ela tem como retorno o WKT sem o código SRID da geometria especificada. Se utilizada em um comando SELECT, ela irá realizar esta conversão em todos os objetos válidos da coluna especificada. Ela é uma função de output.

-- assinatura
-- SELECT ST_AsText(the_geom)

SELECT nm_equipamento_urbano, ST_AsText(the_geom);

Vamos falar um pouco das funções construtoras de geometria. Por que elas são importantes? É uma maneira de se converter dados de coordenadas, em objetos geográficos, representados no banco de dados por uma string com o valor WKB. Vamos começar pela mais simples: ST_Point()

ST_Point()

Esta é a mais simples das funções construtoras. Ela tem como entrada apenas um X e Y. Esta função esta de acordo com a norma OGC. Na verdade, o que ela faz é chamar a função ST_MakePoint() passando como entrada apenas os eixos X e Y.

-- assinatura
-- SELECT ST_Point(coordenada_x, coordenada_y);

SELECT ST_Point(-42.5,-19.2);

O resultado é retornado em WKB. Veja:

Função ST_Point() retornando o valor em WKB

Função ST_Point() retornando o valor em WKB

ST_MakePoint()

A função MakePoint é mais completa que a ST_Point, pois permite a utilização de até quatro eixos de coordenadas, X, Y, Z e M. Esta função não está de acordo com a norma OGC mas é uma forma de se trabalhar no PostGIS. O problema do uso da mesma é no momento de integração com outros softwares OGC compliant. Fique atento!

-- assinaturas
-- SELECT ST_MakePoint(x,y);
-- SELECT ST_MakePoint(x,y,z);
-- SELECT ST_MakePoint(x,y,z,m);

SELECT ST_MakePoint(-10,-1,300,1);

O resultado desta função também é trazido em WKB.

Função ST_MakePoint() retornando o valor em WKB

Função ST_MakePoint() retornando o valor em WKB

Existem funções deste tipo para todos os tipos de geometria. Vamos mostrar as outras:

ST_MakeLine()

Vamos complicar um pouquinho. Da mesma maneira que podemos construir pontos, podemos construir linhas. Existem três maneiras de se utilizar esta função: uma é com o resultado agregado de uma consulta, um array de pontos, ou dois pontos. Vou mostrar as três maneiras.

-- assinatura
-- SELECT ST_MakeLine(geometry_set);
-- SELECT ST_MakeLine(ponto1,ponto2);
-- SELECT ST_MakeLine(array_pontos);

-- resultado agregado de consulta
SELECT l.setor, ST_AsText(ST_MakeLine(l.the_geom)) FROM
	(SELECT codsetor as "setor", the_geom FROM equipamento_urbano) as l GROUP BY l.setor;

-- dois pontos
SELECT ST_AsText(ST_MakeLine(ST_MakePoint(-1,-1),ST_MakePoint(-2,-2)))

-- array de pontos
SELECT ST_AsText(ST_MakeLine(ARRAY[ST_MakePoint(-10,-1),ST_MakePoint(-5,0),ST_MakePoint(-10,2),ST_MakePoint(-19,-20),ST_MakePoint(-1,0)]));

-- note que a abertura do array se faz com os colchetes!

Vejamos os resultados, respectivamente: agregado, dois pontos e array de pontos. Uma nota: utilizei ST_AsText nas funções para facilitar a visualização do resultado. Todos os resultados podem ser facilmente testados, com exceção do agregado. Caso alguém tenha interesse, posso lhe enviar a tabela em formato .sql para conferência :P

Resultado de ST_MakeLine com pontos agregados

Resultado de ST_MakeLine com pontos agregados

Resultado de ST_MakeLine com dois pontos

Resultado de ST_MakeLine com dois pontos

Resultado da função ST_MakeLine com um array de pontos

Resultado da função ST_MakeLine com um array de pontos

Vamos complicar mais um pouquinho?

ST_MakePolygon()

Esta função constrói polígonos. Para um polígono ser um polígono ele deve ter uma clara distinção de seu interior com seu exterior. A única maneira de delimitarmos isto é tenho uma LINESTRING fechada, ou seja, o último vértico deve ser igual ao primeiro. Um polígono também pode ter holes ou seja, buracos. Eles também podem ser construídos aqui. Veja só:

-- assinatura
-- SELECT ST_MakePolygon(linestring_fechada);
-- SELECT ST_MakePolygon(linestring_fechada,array_poligonos_interiores);

SELECT ST_AsText(ST_MakePolygon(
	ST_MakeLine(ARRAY[
		ST_MakePoint(0,0),
		ST_MakePoint(1,0),
		ST_MakePoint(1,1),
		ST_MakePoint(0,1),
		ST_MakePoint(0,0)])))

Veja o resultado:

Resultado de ST_MakePolygon

Resultado de ST_MakePolygon

Todas estas funções são extremamente úteis, permitindo o usuário à manipular as geometrias de diversas tabelas para construir objetos temporários ou views para visualização em softwares de SIG. Lembrem-se que você não precisa utilizar ST_MakePoint() para construir polígonos ou linhas. Você pode utilizar a agregados e resultados de consultas como parâmetros de entrada para estas funções.

ST_GeomFromText

Esta é a rainha das funções construtoras de geometria. Com ela é possível construir qualquer geometria, desde que você tenha o WKT das mesmas. Mão na roda. A segunda assinatura lhe permite especificar um SRID.

-- assinatura
-- SELECT ST_GeomFromText('wkt');
-- SELECT ST_GeomFromText('wkt',srid);

SELECT ST_AsText(ST_GeomFromText('LINESTRING(0 0, 1 0, 2 1, 2 2, 3 1, 3 3, 0 1, 0 0)'))

SELECT ST_AsText(ST_GeomFromText('POLYGON((0 0, 1 0,1 1,0 1,0 0))'));
Resultado da função ST_GeomFromText, construindo uma linha.

Resultado da função ST_GeomFromText, construindo uma linha.

Resultado da função ST_GeomFromText, construindo um polígono.

Resultado da função ST_GeomFromText, construindo um polígono.

Agora vem a pergunta: porque utilizamos dois pares de parênteses quando construímos um polígono e apenas um quando construímos uma linestring? Lembra quando construímos um polígono, temos a opção de passar uma array de polígonos internos, os famosos holes? Então, o segundo set de parênteses corresponde à possibilidade de inserir este array de polígonos. Não se esqueça, para polígonos e geometrias multi*, dois sets de parênteses são necessários.

Hoje vimos um pouco sobre as funções construtoras de geometrias no PostGIS. O que acharam?

Abraços

George R. C. Silva

12May/100

Python QuantumGIS Cookbook

Boa tarde pessoal,

A comunidade internacional lançou à poucos dias um livro de receitas de como programar/extender o QuantumGIS. Vale a pena dar uma olhada. Para os interessados é uma mão na roda para ajudar a começar. Check it out.

Tem uma excelente introdução, passando por tópicos sobre como mexer em camadas vetoriais, raster, construção de geometrias, projeções e por fim como construir um plugin.

Python QuantumGIS Cookbook

Abraços

George R. C. Silva

Tagged as: , , No Comments
15Mar/100

Máquina Virtual e Desenvolvimento para ArcObjects

Boa tarde pessoal,

Sejam bem-vindos à mais nova série de postagens do Blog Geo.NET: Congonha's Post.

Bem, ela tem esse nome porque estou postando do aeroporto de Congonhas e hoje vamos falar, rapidinho, sobre máquinas virtuais e o desenvolvimento para ArcObjects. (estava postando do aeroporto de Congonhas, mas meu voô ia sair sem mim, então tive de deixar para hoje :P )

Desenvolver para ArcObjects é complicado. Temos de referenciar diversas bibliotecas, instalar diversos softwares para nos auxiliar e temos, no final de tudo, de fazer o deploy de nossos componentes customizados.

Estes dias anteriores, durante um trabalho em um dos clientes, tive de realizar o deploy de um componente customizado, que cuida de simbologia de algumas camadas (em torno de 200).

Estava me dando um trabalhão. Montava um executável, enviava o executável para um máquina de testes, instalava o software, testava. Deu erro? Desinstala o software e começa tudo denovo.

Aí me sugeriram: pegue uma máquina virtual e monte um ambiente similar ao em que realizaremos o deploy final.

Existem diversos softwares para montarmos máquinas virtuais, mas existe um simples, gratuito e da Microsoft que resolve horrores. Virtual PC. Só fazer o download e instalar o sistema (Windows) que você quiser. Do 95 ao 7 :D . Você pede para criar a máquina virtual, aloca uma certa quantidade de RAM e HD para a mesma, e voilá, você tem um PC dentro do seu PC.

Como isto facilitou minha vida? Ganhei no mínimo, uns quatros dias de trabalho. Instalei a VM (Virtual Machine), abri uma instância do Windows XP, coloquei um ArcGIS 9.3 e fui realizando os ajustes até finalizar.

Agora, o que é legal é que você pode montar diversas instâncias e testar a backwards compatibility (compatibilidade retroativa - ou algo do tipo) com outras versões, de ArcGIS, de ArcServer, enfim, de diversos ambientes diferentes.

Estou com duas máquinas virtuais, as duas com WinXP SP3, uma com ArcGIS 9.3 e a outra com ArcGIS 9.2.

Fica aí a dica.

George R. C. Silva

Tagged as: , , No Comments
11Mar/100

OOP com python – Uma breve introdução.

Olá pessoal, tudo bem?

Tem muita novidade vindo, mas não é hora, nem  lugar, nem o tema para falar sobre. Mas como o show tem que continuar, trago para vocês uma breve introdução sobre Orientação a Objetos com Python. Confesso que já venho estudando Python a algum tempo, mas nunca com tanta intensidade quanto agora, isso por que surgiram projetos para trabalhar com geodjango (uma framework web em python) e a descobri pyqgis.

Deixando de enrolação...

Bem, Python é uma liguagem Orientada a Objetos, mas que permite que você desenvolva aplicativos programando estrutural, é uma liguagem interpretada, que não exige que você indique o tipo da variável (dinamicamente tipada), possui fácil controle de bloco, o que te prende só ao código, sem ter que perder tempo analisando símbolos em casos de erro de sintaxe, e o melhor de tudo, é livre!

Para melhor compreensão, precisamos entender alguns conceitos, como classe, objeto, atributo e método. Para ser diferente (ou não!), começo falando de objetos. Objetos são todas as coisas as quais você deseja abstrair. Bem, você é um objeto! Como assim eu tou louco? É verdade, você é um objeto. Melhor, você alem de ser um objeto, você faz parte de uma classe... Não, esquece a geografia por equanto, que mané classe social... eu estou afirmando que você faz parte de uma classe, a dos Humanos. Legal né? Continuando, seus atributos, de fato são seus atributos (ou características, como queiram.), e os métodos, bem, esses se referem as ações tomadas por você hoje. Acho que você acordou, comeu, andou... Isso são os métodos, as ações tomadas pelos objetos!

E como desenvolver usando OOP com Python?

Para o desenvolvimento em Python usando OOP, é preciso conhecer algumas palavras reservadas, como class, que se refere a uma classe de objetos abstraídos, self que é um parametro que possibilita você fazer referencia a um argumento, substitui o this do Java, dentre outras que veremos conforme a necessidade.

Para exemplificar vou criar aqui um programa bastante simples que pede para você inserir uma mensagem, e esta mensagem é retornada para você. Vejamos:

class Mensagem:
def __init__ (self,pessoa,texto):
self.nome = pessoa
self.opniao = texto

def __str__(self):
saida = self.nome+ ' escreveu: \n' +self.opniao
return saida

nome_saida = raw_input ('Digite seu nome: ')
opniao_saida = raw_input ('O que você achou? \n')
print Mensagem(nome_saida,opniao_saida)


Explicando o código:

No método __init__ da classe Mensagem, foram jogados dois parametros: pessoa e texto. O self, primeiro parametro apresentado é padrao da linguagem, entao é conveniente utilizá-lo.

O trecho self.nome e self.opniao, sao referencias (instanciação)dos objetos antes apresentados: pessoa e texto.

O método __str__(), é mais um método mágico do Python, que determina o que será retornado do objeto.[1]

Perceba que este código sempre que invocado, ira inserir valores setados pelo usuário nos mesmo objetos, o que pra mim, é o segredo da OOP.

Finalizando

Bem, acho que deu para ter noção de como usar OOP em Python. Para maiores referencias sobre o assunto, aconselho visitar a Página da comunidade brasileira

Em breve, outras ferramentas Python... mas isto fica para um próximo post.

P.S.: Agradeço as correções sugeridas pelo amigo George, feitas no código, e ainda os ensinamentos sobre OOP.

Abraço a todos.

Vicente Martins

Get Adobe Flash playerPlugin by wpburn.com wordpress themes