Blog Geo.NET Geoprocessamento, SIG e Sensoriamento Remoto

29jul/113

Diquinhas de ArcGIS #3

Olá pessoal,

Continuando a série de diquinhas de ArcGIS!

Algumas pessoas fazem bastante uso de ferramentas de geoprocessamento, especialmente para análise espacial. Neste quesito, a ferramenta mais utilizada do ArcMap é o ArcToolbox.

Existem processos, que invariavelmente são: rodar ferramenta de geoprocessing, conferir resultados, refinar análise, etc. São tantos arquivos gerados que podemos ficar facilmente perdidos.

Uma das coisas legais que temos no ArcToolbox (acho que a partir da versão 9.3) é o histórico de ferramentas (tanto no ArcMap, quanto no ArcCatalog). Podemos precisar de um processo de foi rodado à algumas horas e onde está o tal arquivo?

Bem, para acabar com os problemas, o histórico vem ao resgate.

Aba Results no ArcToolbox

Aba Results no ArcToolbox

Para cada processo que rodamos, ele guarda um registro da entrada, dos parâmetros e da saída. Através disso é possível re-rodar o processo ou então arrastar o resultado (desde que não tenha sido fisicamente deletado do PC!) par ao mapa novamente e pronto. Está lá denovo.

O post de hoje foi patrocionado pelo...silêncio.

Abraços

George Rodrigues da Cunha Silva

Compartilhe:
27jul/113

Diquinhas de ArcGIS #2

Novas diquinhas frescas para você!

Boa tarde pessoal, uma coisa legal que o ArcMap tem de MONTE são atalhos. Atalhos, são...atalhos. Faça o que você precisa fazer, mas mais rápido. Isso não é bom? Ah, estas dicas são para versão 9.3.1;

Portanto, hoje vou falar de alguns, pois existem muitos:

Atalhos comuns à todas as ferramentas de edição

  • Z (Zoom Mais);
  • X (Zoom Menos);
  • C (Navegação);
  • B (Navegação contínua);
  • V (Mostrar vértices);
  • Esc (Cancela operação);
  • Ctrl+Z (desfazer);
  • Ctrl+Y (refazer);
  • Segurar Barra de Espaço (suspende snapping);

Os mais demais são os em negrito. Mãozassa na roda.

Post de hoje foi patrocinado por The Prodigy e Sublime.

Abraços

George R. C. Silva

Compartilhe:
26jul/112

Diquinhas de ArcGIS #1

Olá pessoal,

Bem vindos ao primeiro post da (espero que longa) série de dicas de ArcGIS. O objetivo é mostrar algo que salva a vida do peão em pouco tempo.

Hoje vamos falar de uma opção bem escondida do Editor que força ao usuário inserir os dados em uma nova feição antes que o ArcGIS a crie de verdade.

O fluxo comum é:

  • Criar feição;
  • Feição vai para o banco de dados (ou shapefile);
  • Editar atributos;

Desta forma, podemos esquecer alguma coisa, ou se existirem campos que são obrigatórios, mas não tem valor default, causar um erro.

No ArcMap é possível pedir para "promptar" o usuário antes de armazenar a feição em nosso repositório de dados

O fluxo alternativo, portanto:

  • Criar feição;
  • Receber entrada de dados básica;
  • Feição vai para o banco de dados (ou shapefile);

Perceba que a entrada de dados foi realizada anteriormente à ida da feição ao banco de dados. Isto ajuda a economizar até um cadinho de banda.

Para habilitar:

Editor > Options > Aba Attributes

Diquinha de ArcGIS #1 - Editar feição

Diquinha de ArcGIS #1 - Editar feição

Agora é só editar!

É bastante útil em projetos de edição em que se precisa estar atento o tempo todo.

Post de hoje patrocinado por Velhas Virgens e The Offspring.

Abraços

George R. C. Silva

Compartilhe:
11jul/110

ArcGIS Online recebe grande atualização

Boa tarde pessoal,

Pegando o embalo da ESRI User Conference que está sendo realizada esta semana, em San Diego, o site ArcGIS Online recebeu uma grande atualização de funcionalidades.

Agora é possível construir seus mapas utilizando webservices WMS, renomear camadas, importar KMLs e outras delícias mais. Estas eram funcionalidades há muito esperadas!

Para quem não sabe, ArcGIS Online é uma plataforma na web, que permite que o usuário envie seus dados ou colete dados de outros usuários para montar mapas e mini-SIGs de maneira extremamente fácil e intuitiva. E o melhor, seu mapa pode ser compartilhado com usuários, clientes, etc. É uma plataforma amigável para publicação de informações geográficas.

Nunca testou? Corra até o site e teste! Acredito que ficarão surpreendidos.

Abraços,

George R. C. Silva

Compartilhe:
11dez/102

ArcObjects e Class Extensions #1

Boa noite senhores e senhoras,

Class Extensions é um assunto relativamente antigo. Quem já desenvolveu para ArcObjects às conhecem razoavelmente bem e são implementadas em larga escala como forma de customização avançada dos famosos geodatabases (qualquer um deles, seja Personal, File ou ArcSDE).

Bem, as class extensions são implementadas através de algumas interfaces especiais e devem ser registradas em uma categoria apropriada (o assunto das categorias de objetos ESRI merece um post sozinho!), GeoObjectClassExtensions para funcionarem e além disso devem ser aplicadas à uma Feature Class ou Table para rodarem apropriadamente. E outra coisa: elas só funcionam com quem possui o código cliente (da Class Extension) instalado na máquina. Para quem não possui o cliente elas se comportam de maneira comum.

Com Class Extensions é possível customizar o inspetor de objetos (aquele carinha utilizado para editar os objetos), customizar a renderização de objetos (isso é poderoso!) e customizar a validação realizada pelo ArcGIS antes da feature receber uma chamada no método Store() (ou seja, antes de ir para o banco);

Existem diversas coisas legais que podemos fazer com estas class extensions e não existe muita dificuldade de implementação (claro, quanto mais requisitos ou quanto mais complexos, mais complicado é de implementar e manter uma CE.).

As CEs podem existes para os seguintes tipos:

  • Features
  • Rows
  • Quaisquer tipos derivados de IRow

O que isto quer dizer? Bem, tanto tabelas, quanto feature classes e qualquer objeto derivado disto podem ter CEs.

Os requerimentos/limitações para Class Extensions são:

  • Feature classes e tabelas só podem ter uma class extension;
  • A CE precisa ser registrada na categoria apropriada (GeoObjectClassExtensions)
  • A funcionalidade não está presente no repositório de dados e sim no cliente. Sempre será necessário realizar o deploy de todo o código das CEs em todos os clientes;

Não são requerimentos/limitações muito pesadas, portanto é uma alternativa viável para customizações mais complexas do procedimento de edição/criação/deleção de objetos.

Obrigatoriamente, o desenvolvedor precisa implementar as seguintes interfaces: IObjectClassExtension (e IFeatureClassExtension, caso seja uma extensão para feature classes). Neste ponto, nossa CE não tem nenhuma funcionalidade. Ela apenas existe.

Para adicionar funcionalidade, podemos implementar outras interfaces, listadas à seguir:

  • IObjectClassEvents
  • IObjectClassValidation
  • IObjectClassInfo
  • IRelatedObjectClassEvents
  • IConfirmSendRelatedObjectEvents
  • IFeatureClassEdit
  • IFeatureClassDraw
  • IFeatureClassCreation
  • IObjectInspector

Estas interfaces são opcionais, cada uma oferencendo um pouquinho de funcionalidade customizada. Hoje falaremos sobre IObjectClassEvents, que é uma das interfaces mais comum de se implementar. Ao longo de uma pequena série tentarei demonstrar a funcionalidade de algumas destas interfaces.

IObjectClassEvents

Esta interface é responsável por nos fornecer métodos para lidar com os eventos de criação, atualização e deleção de um registro. Pense em uma trigger de um banco de dados. Sempre que o evento disparar, determinado método será disparado em sequência.

Os métodos que devem ser implementados para total aderência ao contrato (a interface :P ) são:

  • OnCreate(IObject obj);
  • OnChange(IObject obj);
  • OnDelete(IObject obj);

Um exemplo de implementação de uma CE com a interface IObjectClassEvents. Note que esta implementação não é completa, ou seja, faltam métodos para o registro da CE, inicialização correta, etc;

Imagine que temos uma tabela log de operação. Nela salvamos os registros relativos às operações realizadas por todos operadores durante o dia. Sua estrutura é a seguinte (em SQL, mas devemos considerar que a tabela está presente em um ambiente ArcGIS - Geodatabases de qualquer tipo);

CREATE TABLE LOG_OPERACAO(
OBJECTID serial NOT NULL,
NOME_TABELA VARCHAR(100) NOT NULL,
NOME_USUARIO VARCHAR(100) NOT NULL,
TIPO_OPERACAO VARCHAR(20) NOT NULL,
OID_ORIGINAL integer NOT NULL,
DATA_HORA_OPERACAO timestamp NOT NULL default now()
CONSTRAINT LOG_OPERACAO_PK PRIMARY KEY (OBJECTID)
);
    public class LogUsuario:IObjectClassExtension,IObjectClassEvents
    {
        public void OnCreate(IObject obj)
        {
            // vamos adicionar um registro à uma tabela de log de operações
            // precisamos de nosso workspace para abrir a tabela
            IFeatureWorkspace workspace = ((IDataset)obj.Class).Workspace;
            ITable logTable = workspace.OpenTable("LOG_OPERACAO");

            // só vamos tentar adicionar se a tabela existir no geodatabase!
            if (logTable != null)
            {
                // ok, a tabela existe. caso não existisse, ela seria nula
                // portanto, vamos criar um novo registro
                IRow novoRegistroLog = logTable.CreateRow();

                novoRegistroLog.set_Value(1,((IDataset)obj.Class).Name); // nome da tabela que estamos modificando
                novoRegistroLog.set_Value(1,Environment.UserName); // nome do usuario
                novoRegistroLog.set_Value(2,"INSERCAO"); // tipo da operacao
                novoRegistroLog.set_Value(3,obj.OID); // id no geodatabase
                novoRegistroLog.set_Value(4,DateTime.Now); // data e hora
                novoRegistroLog.Store();
           }
     }

        public void OnChange(IObject obj)
        {
            // vamos adicionar um registro à uma tabela de log de operações
            // precisamos de nosso workspace para abrir a tabela
            IFeatureWorkspace workspace = ((IDataset)obj.Class).Workspace;
            ITable logTable = workspace.OpenTable("LOG_OPERACAO");

            // só vamos tentar adicionar se a tabela existir no geodatabase!
            if (logTable != null)
            {
                // ok, a tabela existe. caso não existisse, ela seria nula
                // portanto, vamos criar um novo registro
                IRow novoRegistroLog = logTable.CreateRow();

                novoRegistroLog.set_Value(1,((IDataset)obj.Class).Name); // nome da tabela que estamos modificando
                novoRegistroLog.set_Value(1,Environment.UserName); // nome do usuario
                novoRegistroLog.set_Value(2,"ATUALIZACAO"); // tipo da operacao
                novoRegistroLog.set_Value(3,obj.OID); // id no geodatabase
                novoRegistroLog.set_Value(4,DateTime.Now); // data e hora
                novoRegistroLog.Store();
           }
     }

        public void OnDelete(IObject obj)
        {
            // vamos adicionar um registro à uma tabela de log de operações
            // precisamos de nosso workspace para abrir a tabela
            IFeatureWorkspace workspace = ((IDataset)obj.Class).Workspace;
            ITable logTable = workspace.OpenTable("LOG_OPERACAO");

            // só vamos tentar adicionar se a tabela existir no geodatabase!
            if (logTable != null)
            {
                // ok, a tabela existe. caso não existisse, ela seria nula
                // portanto, vamos criar um novo registro
                IRow novoRegistroLog = logTable.CreateRow();

                novoRegistroLog.set_Value(1,((IDataset)obj.Class).Name); // nome da tabela que estamos modificando
                novoRegistroLog.set_Value(1,Environment.UserName); // nome do usuario
                novoRegistroLog.set_Value(2,"INSERCAO"); // tipo da operacao
                novoRegistroLog.set_Value(3,obj.OID); // id no geodatabase
                novoRegistroLog.set_Value(4,DateTime.Now); // data e hora
                novoRegistroLog.Store();
           }
     }
}

Bem, este é um exemplo de implementação da interface IObjectClassEvents. Poderiamos ter deixado o código muito mais limpo (escrevendo uma função à parte para a criação de um registro na tabela de log, por exemplo) e implementado outras funcionalidades. O que importa é que você já sabe como implementar algumas funcionalidades extras dentro do ArcGIS. Lembrando que para esta CE funcionar precisamos aplicar a mesma à uma feature class. Para aplicar uma class extension programaticamente, preciso utilizar algo similar à isto:

public void ChangeClassExtension(IObjectClass objectClass, String extensionUID,IPropertySet extensionProperties)
{
    ISchemaLock schemaLock = (ISchemaLock)objectClass;

    try
    {
        // Attempt to get an exclusive schema lock.
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);

        // Cast the object class to the IClassSchemaEdit2 interface.
        IClassSchemaEdit2 classSchemaEdit = (IClassSchemaEdit2)objectClass;

        if (!extensionUID.Equals(""))
        {
            // Create a unique identifier (UID) object and change the extension.
            UID extUID = new UIDClass();
            extUID.Value = extensionUID;
            classSchemaEdit.AlterClassExtensionCLSID(extUID, extensionProperties);
        }
        else
        {
            // Clear the class extension.
            classSchemaEdit.AlterClassExtensionCLSID(null, null);
        }
    }
    catch (COMException comExc)
    {
        throw new Exception("Could not change class extension.", comExc);
    }
    finally
    {
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
}

Este snippet de código tirado da documentação da EDN (ESRI Developer Network), instalado junto com o SDK faz as seguintes operações:

  1. Tenta assumir um lock exclusivo sobre a ObjectClass (veja a interface IObjectClass);
  2. Converte a object class para um class schema edit (veja IClassSchemaEdit2), para poder acessar os membros (métodos e propriedades) de edição;
  3. Acha a extensão através do código GUID (é o carinho que se vê como um atributo de classe, localizado acima da declaração comum de classe, no seu projeto do Visual Studio;
  4. Altera a class extension;
  5. Libera o lock exclusivo (feito na chamada finally para termos garantia que o lock não ficará por lá, atoa);

O que acharam pessoal? Não é exatamente um bixo de sete cabeças, mas precisamos aprofundar nos ArcObjects para ter sucesso na implementação de class extensions úteis. O exemplo fornecido é bobo, pode ter utilidade, mas existem milhares de possibilidades com esses diabinhos. Vários produtos de nível corporativo (soluções de energia, da Miner&Miner por exemplo), apostam pesado na customização de feature classes como saída para problemas complexos.

Tentem brincar um pouquinho com as class extensions. É bem divertido e rende um trabalho interessante.

Vou ficar por aqui!

Um abraço,

George R. C. Silva

Compartilhe:
4out/1017

VideoAula – ArcGIS 9.3 – Elementos de mapas e layout

Hoje instalei um software para capturar a tela de trabalho e fiz uma vídeo aula para vocês.

Nesse vídeo abordei ferramentas básicas de edição de mapas tais como: legenda, grid, operações gráficas, orientação de página, etc.

Para pedidos de tutorial favor deixar um comentário.

Abraços, Alex Tinoco.

Compartilhe:
4out/1010

Integração CAD/GIS

Boa noite pessoal,

Faz um tempo que não apareço, mas é por um bom motivo. Estou completamente atolado de trabalho e tenho passado a maior parte de meus dias em Manaus. É uma ótima cidade, apesar do calor que vem fazendo e das constantes quedas de energia que sofremos no escritório. Esquenta, ligam mais ar-condicionados, a rede não aguenta e todo mundo fica sem luz e sem ar. É divertido ver como essas coisas funcionam :P .

Hoje gostaria de falar um pouquinho sobre as dificuldades da integração de produtos CAD com SIGs ou GISs. É um assunto, como sempre, extenso, mas como está me tomando um bocado de tempo, achei que seria bastante apropriado comentar alguma coisa sobre isso.

Gostaria de começar explicitando que o CAD é um excelente programa: para o que ele foi construído. Ele não foi desenvolvido para construção ou armazenamento de feições geográficas. O CAD é um programa de desenho e apenas isso. Ele entende o básico, pontos, linhas, polígonos e anotações. Não tem a menor idéia de semântica. Quando digo semântica, ele não compreende o que cada linha significa ou o que pode ser. O GIS também não é muito esperto neste ponto, mas o número de regras de negócio que podem ser representadas é maior e mais consistente.

Então imaginem, um projeto enorme, que requer que se levante uma área extensa, tudo feito em CAD. O CAD dá produtividade, é mais fácil de arrumar mão de obra, por aí vai. Tudo bem, mas quando chegamos na parte de conversão de dados para o ArcGIS ou qualquer outro software, ficamos em uma saia justíssima. Porque?

  • As plantas em CAD em geral são mal construídas. Isto significa que veremos muitos polígonos não fechados, vértices duplicados em uma mesma linha, self-intersections (é quando dentro de um polígono ou uma linha, temos uma auto-inteseção, a geometria se cruza - gerando um erro difícil de detectar e causador de inúmeros problemas, como no cálculo de área);
  • Plantas em CAD sempre terão inúmeras versões. Mas muitas mesmo. Não estou falando de três ou quatro. São três ou quatro - por dia. Pelo menos uma por usuário trabalhando na mesma área geográfica.
  • Os objetos CAD (pontos, linhas e polígonos) não possuem atributos. É difícil identificá-los unicamente num mar de geometrias. Dependemos unicamente de labels estrategicamente posicionados e milhares de joins espaciais para tentar identificar um objeto corretamente.

Bem, estes são alguns dos problemas que existem em ambientes mistos, mas temos de tolerá-los, correto?

A melhor maneira de lidar com este workflow é a criação de um modelbuilder (quando estamos falando de ArcGIS, claro) ou algum script esperto em Python para cuidar dos detalhes chatos.

Foi o que fizemos, mas outros problemas de gerenciamento de dados e workflow nos deram bastante trabalho. Mas está tudo mudando - existe uma salvação! ArcSDE ao resgate. Apesar do que dizem, que geodatabases são lentos, difíceis de se trabalhar, etc. montamos um mini ambiente em menos de um dia e conseguimos colocar todo mundo trabalhando em cima de uma pequena máquina DELL - que já conseguiu seu próprio no-break e vou pedir mais alguns GB de RAM nesta semana.

Mudou de ambiente, mudou de problema, mas pelo menos é um problema que eu consigo resolver com trabalho de máquina :P , ao invés de trabalho "braçal".

Até comecei um pequeno novo projeto para ajudar nas (infinitas) correções dos problemas CAD. Estou montando uma pequena extensão do ArcMap para gerenciar usuários e correções, com áreas assinaladas à usuários, prazos, entre outras coisinhas mais para facilitar a (nossa) vida. Por ser um projeto da empresa, não posso comentar, mas vou dando pequenos detalhes assim que possível (o ArcMap já possui uma extensão oficial da ESRI para cuidar disso, é a PLTS - Production Line Tool Set - mas como não preciso de todos os recursos e não tenho tempo ($) para isso, vou criar uma coisinha mais simples).

Bem pessoal, vou nessa. Amanhã é preto na folhinha.

Abraços,

George R. C. Silva

Compartilhe:
7jul/101

Usando Python e o geoprocessing framework #2

Boa tarde pessoal!

Continuando naquele nosso projetinho, hoje vamos falar sobre a classe em Python que atualiza nossos dados em um determinado banco de dados. Temos algumas particularidades quando trabalhamos com ArcSDE, (registro de camadas como versionadas/não versionadas), portanto mostrarei como trabalhar com um Geodatabase local. A alteração para ArcSDE não é tão grande, e com um cadinho de pesquisa vocês conseguem fazer.

Lembre-se das outras classes que precisamos, mostradas no post anterior.

Construiremos duas classes, uma chamada GeoprocessorWrapper, um "embrulho" do objeto geoprocessor da ESRI. Isto não necessário, mas facilita, já que facilitamos algumas operações através deste "embrulho". A outra será uma tarefa, que juntará todos os dados e resultados das classes anteriores para atualizar nosso banco de dados.

Vamos começar com a mais simples, geoprocessorWrapper:

import arcgisscripting, logHandler

__toolboxAliases = [
            "analysis",
            "management",
            "3d",
            "cartography",
            "arc",
            "interop",
            "geocoding",
            "ga",
            "lr",
            "md",
            "na",
            "samples",
            "sa",
            "stats"]

class geoprocessorWrapperClass():
    def __init__(self,workspace,toolboxList,overwriteOutput=False):
        self.logs = logHandler.logHandlerClass()
        # Startup logging object
        self.gp = arcgisscripting.create(9.3)
        # Startup ESRI geoprocessing object

        self.gp.Workspace = workspace
        self.gp.OverwriteOutput = overwriteOutput
        # Define overwrite output. Default is to FALSE.

        #lista de toolboxes disponiveis
        self.toolboxList = []
        self.toolboxList += toolboxList

        self.buildGeoprocessorOptions(toolboxList)

    def buildGeoprocessorOptions(self):
        for x in self.toolboxList:
            try:
                if x in __toolboxAliases:
                    self.gp.AddToolbox(x)
            except:
                self.logs.newLogMessage(self, "Error in adding " + x + " toolbox to geoprocessor object. Are you sure this toolbox exists?" + self.gp.GetMessages(), "Geoprocessing Error")
    # adds all toolboxes to current geoprocessing object.

    def changeWorkspace(self,workspace):
        self.gp.Workspace = workspace;
    # changes the current workspace in geoprocessing object

    def removeToolboxes(self,toolboxList):
        for x in self.toolboxList:
            try:
                self.gp.RemoveToolbox(x)
                self.toolboxList.remove(x)
                self.logs.newLogMessage(self,"Removed " + x + "toolbox from geoprocessing object.","Information")
            except:
                self.logs.newLogMessage(self,self.gp.GetMessages(),"Geoprocessing Error")
    # removes unneeded toolboxes from current geoprocessing object

Vamos começar com alguns detalhes desta classe:

Em primeiro lugar, temos os tradicionais import x. Eles são responsáveis por disponibilizar outras bibliotecas ao nosso código. Note que importamos o módulo arcgisscripting.

Logo depois, temos uma lista dos aliases que cada toolbox possui no Python. É necessário adicionar uma toolbox ao objeto geoprocessing para que se tenha acesso às ferramentas da mesma - o que pode ser feito de dois jeitos, pelo caminho da toolbox (C:\Program Files\...\ArcGis\Toolbox.tbx) ou pelo seu apelido. A primeira maneira é útil para adicionar toolboxes customizadas, que não tem apelido. A segunda maneira é mais prática para adicionar as toolboxes tradicionais.

O construtor da classe toma dois parâmetros obrigatórios e um opcional para inicializar a classe. O workspace é a pasta onde iremos trabalhar - ele poderia ser também um geodatabase, mas neste caso, uma pasta. toolboxList é uma lista com os apelidos das toolboxes que queremos adicionar ao nosso objeto de geoprocessing. O parâmetro adicional é se você deseja que os resultados do objeto geoprocessing sejam escritos por cima dos anteriores (caso tenham o mesmo nome) e é por default, falso.

O construtor é bastante simples. Ele cria um objeto geoprocessing, utilizando a versão 9.3 como opção (ajuste para sua versão caso necessário - mas não garanto que funcione na 9.2 - já que vários métodos como ListDatasets, têm uma resposta diferente do que
na versão 9.3), define qual é o workspace inicial e adiciona as toolboxes relevantes.

A forma de uso é a seguinte:

g = geoprocessorWrapper.geoprocessorWrapperClass(r"C:\",["analysis","management","lr"])
#ou
g = geoprocessorWrapper.geoprocessorWrapperClass(r"C:\",["analysis","management","lr"],true)

# para acessar o geoprocessor do nosso wrapper, use:

listaDeFeatureClasses = g.gp.ListFeatureClasses()

for x in listaDeFeatureClasses:
    print x.FeatureType

Bem simples né? Temos um método também para mudar de workspace, sem necessitar de criarmos uma nova ferramenta. O outro método disponível é para remover toolboxes que não precisamos. Um método aqui poderia ser facilmente criado para adicionar novas toolboxes ao objeto. Alguém se arrisca?

Com esta classe podemos fazer quaisquer operações, mas utilizaremos uma outra classe para realizar todo o trabalho sujo. Lembra-se do código do post passado? Temos disponível em nossa máquina um arquivo .zip extraído em uma pasta qualquer, correto?

Vamos à classe geodatabaseUpdateTask.

import os, sys

class geodatabaseOperationClass():
    def __init__(self,geoprocessor,inputShapefile,outputGeodatabase,outputFeatureDataset,outputFeatureClass):

        self.geoprocessorWrapperClass = geoprocessor

        self.inputShapefile = inputShapefile

        self.outputGeodatabase = outputGeodatabase
        self.outputFeatureDataset = outputFeatureDataset
        self.outputFeatureClass = outputFeatureClass

        self.processStage = 0
        self.stages = ["Testes de conformidade",
                       "Deleçãoo de Feature Class",
                       "Cópia de Shapefile",
                       "Atualização Completa"]

    def getProcessStage(self):
        return self.stages[self.processStage]

    def testGeodatabase(self):
        #workspace definido pelo geoprocessor
        return self.outputGeodatabase in self.geoprocessorWrapperClass.gp.ListWorkspaces(self.outputGeodatabase,"ALL")

    def testFeatureDataset(self):
        #adaptando workspace
        self.geoprocessorWrapperClass.gp.Workspace += "\\" + self.outputGeodatabase
        return self.outputFeatureDataset in self.geoprocessorWrapperClass.gp.ListDatasets(self.outputFeatureDataset,"ALL")

    def testFeatureClass(self):
        #adaptando workpace
        self.geoprocessorWrapperClass.gp.Workspace += "\\" + self.outputFeatureDataset
        return self.outputFeatureClass.gp.Workspace in self.geoprocessorWrapperClass.gp.ListFeatureClasses(self.outputFeatureClass,"ALL")

    def testAllObjetcs(self):
        if self.testGeodatabase() and self.testFeatureDataset() and self.testFeatureClass():
            self.processStage = 1
            return True
        else:
            self.processStage = 0
            return False

    def deleteFeatureClass(self,featureClass):
        try:
            self.geoprocessorWrapperClass.gp.Delete_management(featureClass)
        except:
            print self.geoprocessorWrapperClass.gp.getmessages()

    def copyFeatures(self,inputShapefile,outputFeatureClass):
        try:
            self.geoprocessorWrapperClass.gp.CopyFeatures_management(inputShapefile,outputFeatureClass)
        except:
            print self.geoprocessorWrapperClass.gp.getmessages()

    def updateFeatureClass(self):
        try:
            if self.testAllObjects:
                self.processStage += 1
                print self.getProcessStage()

                self.deleteFeatureClass(self.outputFeatureClass)
                self.processStage += 1
                print self.getProcessStage()

                self.copyFeatures(self.inputShapefile,self.outputFeatureClass)
                self.processStage += 1
                print self.getProcessStage()
        except:
            print self.geoprocessorWrapperClass.gp.getmessages()

Temos os tradicionais import no cabeçalho de nosso arquivo e logo depois o construtor da classe.

Nosso construtor tem os seguintes parâmetros: geoprocessor (um objeto do tipo geoprocessorWrapper), o caminho para o shapefile à ser carregado no banco de dados, o geodatabase de destino, o featureDataset de destino e a FeatureClass de destino.

Você pode ver que construí uma lista de estágios que temos de passar antes de atualizar a FeatureClass. O estágio testes de conformidade vão garantir que os objetos existam no geodatabase e não ocorram erros. Esta etapa pode ser customizada, caso a FeatureClass não exista, crie a mesma, somente importando os dados - também deixo isto para o usuário interessado! Lembre-se que temos de passar um objeto geoprocessorWrapper com um workspace, uma pasta. Neste caso em específico, deve-se passar apenas pastas, pois definimos o geodatabase em outras áreas.

Os testes cuidam da mudança de workspace e asseguram que a featureClass exista. Perceba que todo o trabalho é realizado dentro desta classe, nas funções:

  • deleteFeatureClass()
  • copyFeatureClass()

Uma função resume todo o processo: updateFeatureClass(). Veja que ela não tem parâmetros, pois usa os parâmetros setados no construtor. À medida que a mesma progride, atualiza o status do processo e informa ao usuário.

Forma de uso:

gp = geoprocessorWrapper.geoprocessorWrapperClass(r"C:\",['management'])
geoOperation = geodatabaseOperation.geodatabaseOperationClass(gp,r"C:\shapefile.shp","teste.mdb","dnpm","dnpm_brasil")
geoOperation.updateFeatureClass()

Lembrem-se que o restante do código (buscar o arquivo na net, dezipar, etc) está no post anterior. Como prometi, aqui vai a classe LogHandler.

LogHandler Class

E ae pessoal? Dúvidas?

Podem ver que o Python dá muito poder ao framework de geoprocessing do ArcGIS e é muito simples.

Um abraço

George

Compartilhe:
1jul/100

ArcGIS 10 disponível para venda

Boa noite pessoal!

O ArcGIS 10 finalmente foi lançado para venda oficialmente durante esta semana. Nos Estados Unidos, pelo menos.

O novo ArcGIS promete muita coisa, ArcCatalog integrado com ArcMap, capacidade de edição em três dimensões (dentro do ArcMap!), entre outras coisinhas bastante interessantes.

Temos agora uma nova API para programação em Python, a ArcPy, que pelo visto é bastante poderosa. Criaram novas classes, interfaces e funcionalidades, incluindo a capacidade de automatizar a produção de mapas com Python - o que é uma excelente notícia.

A ESRI já até mudou a cara do site deles para o anúncio. Aqui temos um link com as novas funcionalidades.

George

Compartilhe:
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

Compartilhe:
Get Adobe Flash playerPlugin by wpburn.com wordpress themes