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.
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
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
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;
}
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;
}
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);
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
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
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.
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
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
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?
ArcGIS.com lançado
Durante a semana passada foi lançado o site ArcGIS.com.
É um site da ESRI voltado basicamente para a publicação de mapas e serviços de mapas, direto no site.
Existe a possibilidade de criar seu arquivo .mxd, construir um pacote de layers (Layer Package) e enviar tudo para o site, para colaboração remota, publicação de dados e cartografia temática.
São milhares de pessoas publicando informação em um único lugar. Sabe aquele layer difícil de se encontrar (ex: cidades da Indía, dados de meio ambiente na Europa)? É bem possível que algum usuário tenha publicado um mapa com o mesmo conteúdo, que está disponível para o usuário.
Com um registro gratuito você pode criar e salvar mapas interativos, publicar mapas (você pode deixá-los privados, sem distribuir conteúdo) e usar até 2 Gb de espaço da ESRI para guardar suas coisinhas.
É uma iniciativa bacana, especialmente quando temos diversos softwares Open Source respirando no pescoço da ESRI. É a chance do pessoal que trabalha com ESRI publicar mapas rapidamente.
O site ficou bonito, é bastante intuitivo e fácil de navegar.
Fica a dica pessoal.
Abraços
George R. C. Silva
Hello World, ArcGIS style!
Boa noite pessoal,
Conforme prometi, vamos começar a brincar de ArcObjects.
Se vocês instalaram o Visual Studio 2005 (pode ser o Express, disponível gratuitamente) e depois instalaram o software development kit vocês não teram problema para acompanhar este simples guia.
Notas importantes
- Não irei focar no código. Não irei explicar detalhadamente o que cada função faz. Isto é trabalho de vocês.
- Irei ajudar na parte mais difícil: como começar. Existem algumas coisinhas que são chatíssimas quando programamos para ArcGIS, mas ainda bem que o próprio Visual Studio resolve algumas delas para nós.
Início
Primeiramente, abra o Visual Studio e peça um New Project. Selecione um Empty Project e dê um nome para seu projeto.
Depois de isso feito, termos algo mais ou menos assim:
O Visual Studio é extremamente personalizável, portanto não precisa se concentar em deixar sua tela exatamente igual a minha. Quero que você veja o Solution Explorer que fica à direita.
Vamos para o rock. O que iremos fazer?
Vamos criar uma simples barrinha de ferramentas que nos avisa quantas camadas temos em um determinado MXD. Simples né? Besta né? Mas isso vai ser o fundamento, coisas mais complexas virão.
Então vamos lá. Vá na raiz do projeto (escrito HelloWorldArcGIS) e clique com o botão direito. Clique em Add ArcGIS Reference.
O que isto exatamente faz? Bem, o ArcGIS como diversos softwares, são muito grandes, e não podemos/devemos referenciar todo o código junto. Para que adicionar referências que não iremos utilizar?
Certo, quando você clicar no botãozinho, irá aparecer uma janelinha. Vá na opção Desktop ArcMap e adicione as seguintes assemblies:
- ESRI.ArcGIS.ArcMapUI;
- ESRI.ArcGIS.ArcMap;
- ESRI.ArcGIS.Display;
- ESRI.ArcGIS.Framework;
- ESRI.ArcGIS.Geodatabase;
- ESRI.ArcGIS.System;
- ESRI.ArcGIS.Carto;
Clique em Finish.
Você verá que a pasta References foi atualizada. Vamos adicionar algumas assemblies do Windows e do .NET.
Siga o mesmo passo acima, mas ao invés de escolher Add ArcGIS Reference, escolha Add Reference. Uma caixinha irá se abrir, portanto em .NET, seleciona as seguintes assemblies:
- System
- System.Drawing
- System.Windows.Forms
Clique ok. Estamos good to go.
Nossas referências estão no lugar. Isto significa que poderemos utilizar código pronto contido nestas assemblies. Isto é importante e interessante que se aprenda cedo, senão algo pode deixar de funcionar simplesmente porque você nao colocou aquela referência...
Vamos agora criar uma barra de ferramentas para colocar nossas coisinhas nela.
Clique com o botão direito na raiz do projeto e clique em Add > Add New Item.
Selecione Base Toolbar e dê um nome à esta toolbar. Algo como HelloWorldToolbar, e clique em Add. Um tela irá aparecer perguntando qual tipo de barra de ferramentas você quer. Selecione ArcMap e boa.
Olhe suas referências. Elas foram atualizadas e adicionados alguns assemblies extras. Não se preocupe com eles. O Visual Studio é até inteligente para colocar algumas que você pode ter esquecido, mas só pelos canais oficiais (como este de adicionar uma toolbar). Se você criar uma classe e tentar herdar de BaseToolbar você provavelmente ganhará um erro.
Ele abriu um arquivo de código com a extensão .cs que contém diversas coisas, uns números muito doidos e por aí vai. Vamos explicar:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.ADF.BaseClasses;
namespace HelloWorldArcGIS
{
///
/// Summary description for HelloWorldToolbar.
///
[Guid("cc7ed839-de6c-46a7-9817-f4a3756cc57c")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("HelloWorldArcGIS.HelloWorldToolbar")]
public sealed class HelloWorldToolbar : BaseToolbar
{
#region COM Registration Function(s)
[ComRegisterFunction()]
[ComVisible(false)]
static void RegisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryRegistration(registerType);
//
// TODO: Add any COM registration code here
//
}
[ComUnregisterFunction()]
[ComVisible(false)]
static void UnregisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryUnregistration(registerType);
//
// TODO: Add any COM unregistration code here
//
}
#region ArcGIS Component Category Registrar generated code
///
/// Required method for ArcGIS Component Category registration -
/// Do not modify the contents of this method with the code editor.
///
private static void ArcGISCategoryRegistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommandBars.Register(regKey);
}
///
/// Required method for ArcGIS Component Category unregistration -
/// Do not modify the contents of this method with the code editor.
///
private static void ArcGISCategoryUnregistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommandBars.Unregister(regKey);
}
#endregion
#endregion
public HelloWorldToolbar()
{
//
// TODO: Define your toolbar here by adding items
//
//AddItem("esriArcMapUI.ZoomInTool");
//BeginGroup(); //Separator
//AddItem("{FBF8C3FB-0480-11D2-8D21-080009EE4E51}", 1); //undo command
//AddItem(new Guid("FBF8C3FB-0480-11D2-8D21-080009EE4E51"), 2); //redo command
}
public override string Caption
{
get
{
//TODO: Replace bar caption
return "My C# Toolbar";
}
}
public override string Name
{
get
{
//TODO: Replace bar ID
return "HelloWorldToolbar";
}
}
}
}
Vamos lá:
O que merece nota imediata:
- O construtor da barra de ferramentas. O construtor é o método responsável por instanciar um novo objeto do tipo HelloWorldToolbar. Veja que existem um monte de comentários ensinando como podemos inserir ferramentas ou botões nele. À seguir utilizaremos eles. Mas dê uma lida nos comments com carinho.
- A propriedade Caption. Nela temos "My C# Toolbar" como caption. Altere para o que achar melhor. Este nome é o nome que irá aparecer dentro do ArcGIS.
- A propriedade Name. Deve ser único. Portanto, é melhor nem mexer com isto, por enquanto.
- O restante são funções para registro/desregistro da barra de ferramentas. Isto é um pré-requisito, pois estamos trabalhando com tecnologia COM, se lembram? Não altere nada.
Nossa barra de ferramentas está criada, mas uma barra de ferramentas sozinha não me adianta de nada. Vamos criar umas ferramentinhas. Siga o mesmo procedimento que utilizou para criar a barra de ferramentas, mas desta vez, escolha BaseCommand e nomeie ele como achar melhor. No meu caso, vou dar o nome de ComandoHelloWorld.
Mais uma vez um prompt irá aparecer e te perguntar qual é o tipo de comando você quer criar. Vejamos...escolha Desktop ArcMap Command e boa.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.ArcMapUI;
namespace HelloWorldArcGIS
{
///
/// Summary description for ComandoHelloWorld.
///
// preste atenção neste valor aqui!
[Guid("2423ecdf-4f14-4993-a165-df9d79167f4d")]
// preste atenção neste valor aqui!
[ClassInterface(ClassInterfaceType.None)]
[ProgId("HelloWorldArcGIS.ComandoHelloWorld")]
public sealed class ComandoHelloWorld : BaseCommand
{
#region COM Registration Function(s)
[ComRegisterFunction()]
[ComVisible(false)]
static void RegisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryRegistration(registerType);
//
// TODO: Add any COM registration code here
//
}
[ComUnregisterFunction()]
[ComVisible(false)]
static void UnregisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryUnregistration(registerType);
//
// TODO: Add any COM unregistration code here
//
}
#region ArcGIS Component Category Registrar generated code
///
/// Required method for ArcGIS Component Category registration -
/// Do not modify the contents of this method with the code editor.
///
private static void ArcGISCategoryRegistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommands.Register(regKey);
}
///
/// Required method for ArcGIS Component Category unregistration -
/// Do not modify the contents of this method with the code editor.
///
private static void ArcGISCategoryUnregistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommands.Unregister(regKey);
}
#endregion
#endregion
private IApplication m_application;
public ComandoHelloWorld()
{
//
// TODO: Define values for the public properties
//
base.m_category = ""; //localizable text
base.m_caption = ""; //localizable text
base.m_message = ""; //localizable text
base.m_toolTip = ""; //localizable text
base.m_name = ""; //unique id, non-localizable (e.g. "MyCategory_ArcMapCommand")
try
{
//
// TODO: change bitmap name if necessary
//
string bitmapResourceName = GetType().Name + ".bmp";
base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
}
}
#region Overriden Class Methods
///
/// Occurs when this command is created
///
///
Instance of the application
public override void OnCreate(object hook)
{
if (hook == null)
return;
m_application = hook as IApplication;
//Disable if it is not ArcMap
if (hook is IMxApplication)
base.m_enabled = true;
else
base.m_enabled = false;
// TODO: Add other initialization code
}
///
/// Occurs when this command is clicked
///
public override void OnClick()
{
// TODO: Add ComandoHelloWorld.OnClick implementation
}
#endregion
}
}
Pontos merecedores de notas:
- Novamente o construtor do comando. Ele tem diversas informações que são mostradas ao usuário no ArcGIS. Não irei explicar uma à uma, brinque com elas e teste.
- Um campo (field) importantissímo: m_application. Ele guarda uma referência de memória à sua aplicação que está rodando. Sem o software não teria como chamar coisas que estão lá dentro.
- Método OnClick(). Este método é que irá rodar quando o usuário clicar no comando. Nós implementaremos as coisinhas todas ali. Capiche?
Vamos adicionar nosso comando à barra de ferramentas. Lembram do construtor da barra de ferramentas, que contém alguns exemplos de Add?
Vá no seu comando e procure pelo valor GUID dele. O meu é 2423ecdf-4f14-4993-a165-df9d79167f4d. Digite isto (claro, com o seu valor GUID) no construtor da barra de ferramentas.
AddItem(new Guid("2423ecdf-4f14-4993-a165-df9d79167f4d"), 0);
Vamos tentar compilar. Aperte F6.
Um erro deve aparecer. Ainda não configuramos nossa aplicação para abrir com o ArcMap. Vá na raiz do projeto, clique com o botão direito e vá em Properties.
Você irá ver um monte de coisas.
Na parte de Application procure Output Type e escolha Class Library.
Em Build marque a última opção, Register for COM Interop. Lembra que tudo que fazemos com ArcObjects temos que fazer com COM? Ficou estranho isso, mas válá.
Vá em DEBUG e procure a seção Start Action. Marque Start External Program e localize o arquivo ArcMap.exe no seu computador. O meu fica em C:\Program Files\ArcGIS\bin\ArcMap.exe.
Tente compilar novamente. Aperte F6. Você não deve ver nenhum erro. O ArcGIS irá abrir sozinho.
Nossa barrinha de ferramentas esta lá!
Agora vamos implementar a nossa super-ultra-mega-complexa funcionalidade. Feche o ArcMap. Retorne ao Visual Studio.
Abra o arquivo de código do nosso comando. Suba até o topo de nosso arquivo.
Você verá um monte de coisas como using System.Drawing entre outros. Aqui nós dizemos ao Visual Studio quais assemblies este arquivo poderá acessar. Faltam duas importantes para nós aí.
Insira a assembly ESRI.ArcGIS.Carto e a assembly System.Windows.Forms. Lembre-se de colocar cada uma em uma linha, e não se esqueça do ponto-e-vírgula.
public override void OnClick()
{
// pegue uma referência ao documento que está rodando
// note o uso do campo m_application
IMxDocument documento = (IMxDocument)this.m_application.Document;
// pegue uma referência ao mapa atual
// note que caso tenha múltiplos data-frames isto pode não funcionar como esperado...
IMap mapa = documento.FocusMap;
// vamos contar quantas camadas temos no nosso mapa?
Int32 numeroCamadas = mapa.LayerCount;
// me diga quantas camadas eu tenho!
MessageBox.Show("Temos neste mapa " + numeroCamadas.ToString() + " camadas!");
}
Altere sua função OnClick nestes termos. Não copie e cole. Tente entender o que está acontencendo dentro do código. Digite linha por linha. Porque? Porque faz bem e o Visual Studio ainda irá mostrar para vocês a jóia de sua coroa, o Intellisense. Ele te sugere o que você pode estar precisando. Você conseguirá enxergar diversos atributos e propriedades de cada uma destas classes.
Tem bastante coisa interessante só nessas três classezinhas. Navegue. Use o EDN. Use o Help (F1). E poste suas dúvidas.
Bem, agora é com vocês. Me digam o que acharam. Foi difícil? À princípio vai ser difícil sim! Como meu bom e velho avô diz: rapadura é doce mas não é mole não!
Estou no aguardo das dúvidas, comentários e blasfêmias!
Espero que tenham gostado,
Um abraço,
George R. C. Silva
ArcObjects #1 – introdução
Boa tarde pessoal,
Mais uma vez venho a vocês com uma futura série de pequenos artigos, desta vez sobre ArcObjects, a API da ESRI para o desenvolvimento de funções que levam em conta o espaço e dados espaciais.
Bem, primeiramente devemos começar dizendo que ArcObjects não é um bicho de sete cabeças, é um bicho só de cinco. Não é nada complicado, quando você entende o que quer fazer e onde procurar.
Para quem está perdido, API é um conjunto de rotinas e funcionalidades já escritas que pode-se extender, através de programação. Ou seja, ArcObjects é essencialmente os blocos construtivos do software ArcGIS. Os caras na ESRI desenvolvem o ArcObjects e depois o usam para montar o ArcMap, por exemplo.
E realmente são muitos blocos. A API é composta (hoje, na versão 9.3) por quatro mil classes, cinco mil interfaces, mais de mil enumerações e cinquenta structs, isso sem contar os tipos escondidos e restritos!
Certo e o que mais precisamos saber sobre a API ArcObjects para começar a programar para o ArcGIS? Nada, na realidade, mas existem alguns conceitos que devem ser conhecidos. Toda a API foi desenvolvida seguindo o modelo COM (Component Object Model), o padrão para distribuição de bibliotecas binárias em ambiente desktop, desenvolvido pela Microsoft.
E isto muda tudo. A tecnologia COM estabelece padrões e exige alguns requisitos para que um software seja COM-compatible. Finalmente, devemos entender que COM é uma arquitetura, uma forma de desenvolver software.
A arquitetura COM é baseada em servidores e clientes. O servidor, ou o objeto, dá uma funcionalidade e o cliente a utiliza. Para facilitar ainda mais, um servidor pode ser um cliente e vice-versa. A arquitetura COM facilita a comunicação entre estes dois processos (servidor/cliente). Existem muitas particularidades da tecnologia, que realmente não cabem no escopo deste post, mas todos devem ficar de olho nisto, pois software mal desenvolvido que utiliza COM é software que um dia irá explodir. Esta arquitetura tem sérios problemas de perfomance e coleta de lixo.
Para saber mais, visite esta página e esta página.
Certo, o que podemos fazer com ArcObjects? Tudo o que podemos fazer dentro do ArcGIS, podemos fazer utilizando ArcObjects. Tudo e muito mais, claro. Como o próprio ArcGIS foi construído sobre ArcObjects, estamos na realidade falando de uma coisa só!
O que preciso para desenvolver em ArcObjects?
Bem, a API está implementada em uma porção de linguagens, sendo possível utilizar qualquer uma e realizar as mesmas tarefas. As linguagens suportadas são: VC++ (Visual C++), C# (minha favorita), VB.NET, Java e até VBA (cuidado, o suporte para VBA ACABOU!).
Não existe melhor ou pior, apenas diferente
. Caso você tenha experiência com uma ou outra, sugiro que comece pela linguagem que tem maior familiaridade, mas um aviso: existem tendências e a tendência é .NET (C# principalmente).
Após escolher sua linguagem de preferência, instale um IDE (Integrated Development Environment - como o Visual Studio, no caso de .NET) e instale as bibliotecas que veêm com o ArcGIS. Note que as bibliotecas já estão no CD de instalação do ArcGIS Desktop (as bibliotecas de programação para Desktop, claro).
Primeiro instale o ambiente de desenvolvimento e depois a biblioteca. Nunca o contrário.
Depois que tudo estiver instalado, sugiro que passe um tempo se familiarizando com cada IDE e com a referência oficial da ESRI. Ache um pequeno problema que lhe incomoda no ArcGIS (algo que poderia ser mais fácil, ou poderia ser diferente e facilitaria seu trabalho - imagino que existem diversas coisas) e tente criar alguma coisa em ArcObjects para isto.
Como são muitas classes e interfaces, não se preocupe em conhecer todas, se preocupe em conhecer como achar na referência oficial dados sobre determinada classe/interface.
Aviso aos navegantes: é muito comum em ArcObjects você ter de instaciar dois, três ou quatro objetos para fazer uma coisinha simples (como é o caso das interfaces IFields e IFieldsEdit, IField e IFieldEdit, entre outras).
Os namespaces mais utilizados, provavelmente são esriSystem, ArcMap, ArcMapUI, Geometry e Geodatabase. Todos tem sua própria página na ESRI, incluindo um diagrama completo do mesmo.
Caso tenham dúvidas, estamos aqui! Próximo post: Hello World, ArcGIS style!
Abraços
George
#Python – o que é?
Olá pessoal,
Primeiro gostaria de falar sobre a Geo Rede. GeoRede é um projeto do site Geo.NET, um agregador de feeds de diversos blogs sobre Geoprocessamento/Sensoriamento Remoto em geral.
Estou lá também! Depois passem lá e deêm uma olhada. Se você não conhece o Geo.NET é mais uma coisa bacana. Fórum, seção de downloads e muito mais.
Bem, hoje falo para os usuários de ArcGIS. Vocês já devem ter notado, que durante a instalação do ArcGIS ele te pergunta/pede para instalar uma linguagem de programação chamada Python. Bem, se algum de vocês já abriram a aba dela no menu Iniciar do windows devem ter se deparado com algo bastante simples, a IDLE. A IDLE é um shell que intepreta comandos em Python. Algo como o DOS.
Bem, não dá pra fazer muita coisa pela IDLE, já que estamos limitados a "programas" de uma linha. Mas o porque estou falando de Python? O ArcGIS, felizmente, tem um ambiente customizável e permite ao usuário mais avançado a desenvolver suas próprias ferramentinhas, para executar uma infinidade de ações. Desde tarefas de "geoprocessamento" (geoprocessing) à customizar suas layers e representações.
Existem duas maneiras de se estender o ArcGIS. Uma delas é através de uma linguagem compilada .NET (ou VBA) e através do Python.
De acordo com a própria ESRI, a linguagem recomendada para se trabalhar com geoprocessing é Python. O .NET é mais fácil caso você precise estender a GUI (Graphic User Interface) e montar coisinhas mais complexas. O Python é recomendado para geoprocessing e a criação de ferramentas mais simples justamente pelo Python ser simples. A linguagem Python, criada em 1991, é considerada VHLL (Very High Level Language) e traz enormes facilidades para desenvolvedores iniciantes.
Primeiramente, ela é dinamicamente tipada (what the hell?!). Bem isso significa que não é necessário declarar com QUE tipos de variáveis queremos trabalhar antecipadamente. Você coloca alguma coisa na variável e o Python identifica o que ela é pra você. Ou seja, não existe muita confusão no momento de falar que A = 3 ou A = "spam". (neste exemplo, A = 3, representa automaticamente que A é uma variável do tipo inteiro e que em segundo momento, ela é do tipo string)
Segundamente, Python não é uma linguagem compilada. Não é necessário desenvolver um arquivo executável ou algo do tipo, ela é interpretada conforme o Python lê o código digitado. Isso traz uma perda de velocidade (nada muito importante) mas o ganho em agilidade no desenvolvimento compensa.
Python tem tipos muito flexíveis, como listas e dicionários. Não entrarei em detalhes, mas você pode com uma única expressão, criar uma lista com valores que você esteja trabalhando e de forma mais fácil ainda, ler os mesmos.
Um exemplo de lista, que sempre estão entre COLCHETES:
ListaTeste = ['spam','geoprocessamento',2,['arcgis','envi','arcsde']]
Temos na lista acima os items 'spam', 'geoprocessamento',2 e uma outra lista! aninhada, com os objetos 'arcgis','envi' e 'arcsde'. Listas são o carro-chefe do Python e devem ser aprendidas o quanto antes. Através delas podemos fazer o capeta com muito pouco código.
Agora, como integro ArcGIS com Python? A ESRI disponibiliza uma "API" própria para manipulação (de seus) objetos geográficos, como Feature Classes, Layers, Datasets, Geodatabases entres outros.
tudo o que tiver após # são considerados comentários, e não são interpretados pelo Python.
Um exemplinho simples de um script em Python (embora não muito útil) manipulando dados do ArcGIS seria:
import os, sys, arcgisscripting
gp = arcgisscripting.create(9.3) #vamos criar um objeto do tipo geoprocessing, versão 9.3
ListaCamadas = gp.ListDatasets(r"C:\teste python\geodatabase.gdb")
#pega uma lista dos featuredatasets no geodatabase.gdb
for camada in ListaCamadas: #para cada camada em ListaCamadas
print camada #imprima camada
#a resposta do Python vai ser algo assim.
>>>>['Hidrografia','Altimetria','Geologia','Transportes']
Bem, vejam como é fácil andar pelas listas e realizar operações em série. Os loops em Python são descomplicadíssimos.
Sugiro que agora, corram ao site da ESRI, e deem uma olhada nos geoprocessing Objects. Eles podem facilitar muito a vida de um utilizador de SIG.
Um abraço










