Mapeando Arvores Isoladas (ou qualquer outra feição pontual)

Em um trabalho recente o objetivo era fazer o levantamento de centenas de arvores em um determinado local. Por fim, além de um mapa, era necessário gerar um relatório contendo informações e foto de cada arvore.

Fazer todo o processo manualmente e elaborar o relatório em um documento comum do Word seria uma tarefa extremamente demorada e caso fosse necessário fazer alguma mudança, como no layout do documento por exemplo, o trabalho de alteração seria enorme. Assim resolvi automatizar o processo no seguinte fluxo de trabalho:

Coleta de dados -> Importar dados (Python) -> Banco de dados (PostgreSQL) -> Gerar Relatório (iReports) -> Gerar Mapa (Qgis)

O foco deste post é demonstrar a modelagem do banco de banco de dados e a importação dos dos dados, preparando o terreno para o uso do iReports e do Qgis, os quais não vou abordar aqui.

Este modelo foi utilizado para o caso das árvores, mas com pequenas alterações pode ser extrapolado para qualquer outro tipo de objeto pontual.

1) A primeira etapa (após a coleta de dados) é definir o modelo de banco de dados, após alguns rascunhos no papel cheguei ao modelo que segue:

Basicamente, cada árvore mapeada possui:
-Uma Espécie.
-Diversos Atributos.
-Apenas um ponto.
-Diversas fotos.

2)Codificar o modelo no SqlAlchemy.

Importar dados em um banco de dados relacional pode ser um processo complicado devido a presença de chaves externas, para facilitar a tarefa utilizei o ORM do SqlAlchemy e eliminei dois processos em um só:

1)O SqlAlchemy criou a estrutura do banco de dados para mim.

2)O banco de dados foi mapeado em objetos Python para que os dados sejam importados facilmente.

O código a seguir é a definição do modelo da imagem acima para ser interpretado pelo SqlAlchemy:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy import Column, Integer, String,Sequence,Boolean,ForeignKey,Numeric
from geoalchemy import *

#configurar o banco de dados aqui
engine = create_engine("postgresql+psycopg2://postgres:omoomo@localhost/postgis", echo=False)
Session=sessionmaker(bind=engine)
Base=declarative_base()

class Arvore(Base):
    __tablename__='arvores'
    __table_args__ = {'schema':'public'}
    id=Column(Integer,Sequence('arvores_id_seq'), primary_key=True)
    plaqueta=Column(Integer)
    especie_id = Column(Integer, ForeignKey('public.especies.id'))
    especie = relationship("Especie")
    foto = relationship("Foto")
    fitossanidade=Column(String)
    hfuste=Column(Numeric)
    dap=Column(Numeric)
    volume=Column(Numeric)
    ponto_id=Column(Integer, ForeignKey('public.pontos.id'))
    ponto=relationship("Ponto")

class Ponto(Base):
    __tablename__='pontos'
    __table_args__ = {'schema':'public'}
    id=Column(Integer, primary_key=True)
    the_geom = GeometryColumn(Point(dimension=2,srid=32723))

class Especie(Base):
    __tablename__='especies'
    __table_args__ = {'schema':'public'}
    id=Column(Integer,Sequence('especies_id_seq'), primary_key=True)
    especie=Column(String)
    nomepopular=Column(String)
    nativa=Column(Boolean)
    ameacada=Column(String)
    repouso=Column(String)

class Foto(Base):
    __tablename__='fotos'
    __table_args__ = {'schema':'public'}
    id=Column(Integer,Sequence('fotos_id_seq'), primary_key=True)
    arvore_id = Column(Integer, ForeignKey('public.arvores.id'))
    foto=Column(String)

GeometryDDL(Ponto.__table__)

if __name__=="__main__":
    Base.metadata.create_all(engine)

Não é necessário criar as tabelas no banco de dados, o comando

Base.metadata.create_all(engine)

cuida disto automaticament, e como este módulo será chamado diversas vezes mais para frente, este comando foi colocado dentro da condicional if __name__==”__main__” para evitar que quando for chamado ele tente criar as tabelas repetidamente.

3) A próxima etapa é importar os dados.

Para importar o arquivo gpx com os pontos utilizei a API do OGR (biblioteca que lê e escreve inúmeros formatos de arquivos de geoprocessamento).
Para importar os demais dados exportei as tabelas em formato de texto delimitado por tabulações e no Python lí os arquivos com a ajuda da biblioteca CSV.

#encoding:utf-8
#importa arquivos delimitados por tabulações

import model
import csv
from osgeo import ogr
from geoalchemy import WKTSpatialElement

session=model.Session()

#Arquivo de pontos GPX
pontos = ogr.Open( "./dados/pontos.gpx" )
camada=pontos.GetLayerByName('waypoints')
camada.ResetReading()
for ponto in camada:
    nome=ponto.GetField('name')
    geometria=ponto.GetGeometryRef()
    print geometria.ExportToWkt()
    try:
        nome=int(nome)
        itemponto=model.Ponto(id=nome,the_geom=WKTSpatialElement(geometria.ExportToWkt()))
        session.add(itemponto)
    except: pass

#arquivo de espécies
especies = csv.reader(open('./dados/especies.txt', 'rb'),dialect='excel-tab')
for especie in especies:
    itemespecie=model.Especie(id=float(especie[0].strip()),especie=especie[1])
    session.add(itemespecie)

#arquivo de arvores
arvores = csv.reader(open('./dados/arvorestab.txt', 'rb'),dialect='excel-tab')
listaplaquetas=[]
for arvore in arvores:
    try:hfuste=float(arvore[3].replace(',','.'))
    except:hfuste=0
    try:dap=float(arvore[4].replace(',','.'))
    except:dap=0
    if arvore[1]=='':especie_id = None
    else:especie_id=arvore[1]
    if arvore[5]=='':ponto_id=None
    else:ponto_id=arvore[5]
    itemarvore=model.Arvore(id=arvore[0],plaqueta=arvore[0],
                            especie_id=especie_id,fitossanidade=arvore[2],
                            hfuste=hfuste,dap=dap,ponto_id=ponto_id)
    listaplaquetas.append(arvore[0])
    session.add(itemarvore)

#fotos
for i in listaplaquetas:
    itemfoto=model.Foto(arvore_id=i,foto="\\fotos\\%s.jpg"%(i))
    session.add(itemfoto)

session.commit()

5) Gerar o Relatório.

Para gerar os campos no iReports é necessária uma SQL Query, eu uso o pgAdmin para facilitar o processo, crio parte da busca no editor visual e depois faço pequenas alterações no código. Na figura ao lado está a busca como fica no editor e abaixo o código gerado.

SELECT
  arvores.plaqueta,
  especies.especie,
  especies.nomepopular,
  especies.nativa,
  especies.ameacada,
  especies.repouso,
  arvores.fitossanidade,
  arvores.hfuste,
  arvores.dap,
  arvores.volume,
  ST_X(pontos.the_geom),
  ST_Y(pontos.the_geom),
  fotos.foto
FROM
  public.arvores,
  public.pontos,
  public.fotos,
  public.especies
WHERE
  arvores.especie_id = especies.id AND
  arvores.ponto_id = pontos.id AND
  fotos.arvore_id = arvores.id;

Para quem não conhece a ferramenta, o iReports é um gerador de relatórios com uma interface gráfica excelente, nele você adiciona os campos resultantes da busca SQL em um layout e com um comando ele gera o relatório em diversos formatos, incluindo PDF, Word e HTML.

De posse da busca SQL resta apenas desenhar o layout do relatório no iReports, mas este é um tema que vou abordar em um próximo post. Att.

Anúncios

Fast SQL Layer – Plugin para o QGIS

Para quem trabalha com banco de dados espaciais e camadas Postgis, utilizar os plugins “Add PostGIS/SpatiaLite Layer” e “RT Sql Layer” começa a ficar irritante pois toda a vez que quiser fazer uma mudança na camada, seja a menor que for, é necessário clicar em vários menus e digitar ou colar novamente o código.

É neste ponto que o plugin Fast SQL Layer otimiza o processo permitindo que camadas SQL sejam adicionadas rapidamente.

Continuar lendo

Índice de Desenvolvimento de Reflorestamentos

Nos reflorestamentos com espécies nativas encontra-se uma grande dificuldade em se quantificar o desenvolvimento dos plantios, principalmente devido a heterogeneidade dos mesmos, devido a esta característica percebemos que um dos fatores chave é obter uma grande quantidade de amostras. Mas como amostrar grandes áreas de forma prática? Continuar lendo