PGST-LIB
Introdução
A biblioteca PGST-LIB contém uma série de funcionalidades implementadas em Python que são comuns aos módulos PGST-PORTAL, PGST-GATEWAY e PGST-CONSUMER. A ideia dessa biblioteca é permitir a reutilização de código nos diversos módulos do GeoProcess.
A seguir será descrita uma visão geral da biblioteca PGST-LIB.
Visão Geral
A estrutura com as pastas e arquivos principais do projeto PGST-LIB está destacada a seguir.

Na raiz da biblioteca PGST-LIB, há um módulo Python chamado main.py, utilizado para realizar testes de integração e testes de carga.
Na raiz do projeto tem também uma pasta chamada src em que ficam localizados os módulos do Python. Dentro desta pasta é importante destacar as subpastas baseconsumer e util.
Na pasta baseconsumer existem dois importantes módulos Python que são: worker.py e controller.py.
Na pasta util, encontram-se importantes módulos Python, como answer.py e contract.py.
Na pasta util/postgis existem alguns importantes módulos Python que são: models.py e postgis.py.
Por fim, há outras pastas, como integration, load e testserver, que contêm módulos Python de menor importância.
A seguir, detalharemos alguns dos principais módulos desta biblioteca.
Módulo baseconsumer/worker.py
A classe Worker definida em baseconsumer/worker.py é uma classe abstrata que será herdada nos consumidores. Essa classe contém os elementos comuns a todos os consumidores. Analise o diagrama de classes a seguir.
classDiagram
ABC <|-- Worker : Herança
class Worker {
+Logger : logger
+boolean : testMode
+PostGis : postGis
+dict : questionsInfo
+Answer : answer
+str : dbName
+str : dbUser
+str : dbPassword
+str : dbHost
+str : dbPort
+str : gsUser
+str : gsPassword
+str : gsHost
+Worker()
+getVersion()*
+getName()*
+startDB()
+confGeoServer()
+getPostGis()
+createAnswer()
+setQuestions(data)
+parametersToView(questionId, data)
+process(contract)
+test()
}
A classe Worker é responsável por iniciar a conexão com o banco de dados PostGis e com o Geoserver, caso o consumidor utilize esses recursos. Essa classe pode criar objetos do tipo Answer, pode definir Questões e realizar o processamento do consumidor. Essa classe possui dois métodos abstratos (getVersion e getName) que devem ser implementados no consumidor que a herdar. Essa classe possui um construtor sem parâmetros.
Módulo baseconsumer/controller.py
A classe Controller definida em baseconsumer/controller.py é uma classe concreta responsável pela inicialização e execução de um consumidor. O diagrama de classes da Controller é destacado a seguir com os principais atributos e métodos. Analise o diagrama de classes a seguir.
classDiagram
class Controller {
+Logger : logger
+Worker : worker
+BlockingConnection : connection
+BlockingConnection : channel
+str : consumerCode
+str : mainQueue
+str: queueOut
+str: queueIn
+str: questionsData
+dict: questionsInfo
+str : mode
+str: rabbit_host
+int: rabbit_port
+str: rabbit_user
+str: rabbit_password
+bool : docker
+bool : ready
+Controller(worker)
+startConsuming()
+sendQuestions(data)
+sendMessage(message)
+sendAnswer(data, channel, method)
+answerRequest(data, channel, method)
+verifyQuestionVersion(questionId, version)
+verifyVersion(data, channel, method)
+callback(channel, method, properties, body)
+start()
}
A classe Controller é responsável por fazer a comunicação entre o Consumidor e o Gateway. Essa comunicação se dá através da troca de mensagens, perguntas, respostas. Essa classe possui um construtor que recebe um objeto do tipo Worker como parâmetros.
Módulo util/answer.py
O módulo util/answer.py contém os as seguintes classes definidas internamente: Color, ChartLayout, ChartType, ChartLib, FileType, MessageType, Message e Answer. A seguir é apresentado um breve diagrama de classe de cada uma dessas classes.
classDiagram
class Color {
+str : RED
+str : GREEN
+str : BLUE
+str : ORANGE
+str : PURPLE
+str : YELLOW
+str : BROWN
}
class ChartLayout {
+str : FULL
+str : DEFAULT
}
class ChartType {
+str : BAR
+str : LINE
+str : PIE
+str : POLAR_AREA
}
class ChartLib {
+str : CHART_JS
+str : PLOTLY
}
classDiagram
class FileType{
+str : PDF
+str : KML
+str : EXCEEDED_SIZE
}
class MessageType {
+str : SUCCESS
+str : INFO
+str : WARNING
+str : DANGER
}
class Message {
+str : code
+str : title
+str : text
+str : type
+Message(code, title, text, type)
}
classDiagram
class Answer {
+Logger : logger
+dict : data
+str : gsHost
+str : gsUser
+str : gsPassword
+Answer()
+addMapLayer(title, layerLabel, geoJson, geoWkb, group, color, colorPin, tip, pinType)
+addMapLines(title, layerLabel, lines, color, arrow_color, group)
+addMapCircles(title, layerLabel, circles, color, group)
+addTable(title, description, head, data, identifier)
+addChart(title, label, labels, data, type, layout, color)
+addChartPlotly(title, data, identifier)
+addChartLineDataSet(title, titles, labels, values, colors, layout)
+addWMSLayer(worker, workspace, id, title, file, style)
+setGeoServerData(gsUser, gsPassword, gsHost)
+isError()
+getUserInfo()
+addMessage(message)
+addSysMessage(code, title, text, type)
+addTemplate(data)
+addFile(description, file, type)
+setManualFit(area)
+setAlert(message, triggered, date, level)
+tipToText(tip)
}
As classes Color, ChartLayout, ChartType, ChartLib, FileType e MessageType contém apenas constantes definidas para simplificar a utilização no código. A classe Message contém basicamente uma estrutura de dados dos elementos constituintes de uma mensagem. A classe Message contém um construtor com quatro parâmetros para definir o mensagem. Por fim, temos a classe Answer que é a mais importante desse módulo e contém os principais métodos que podem ser chamados para formar uma resposta para uma pergunta. Os consumidores implementados devem criar objetos dessa classe para modelar a resposta a uma determinada pergunta.
A classe Answer herda a classe Contract. O diagrama de classes a seguir destaca a relação de herança e os métodos das classes Contract, Answer e AnswerRequest.
classDiagram
Contract <|-- Answer : Herança
Contract <|-- AnswerRequest : Herança
class Contract {
+dict : data
+str : consumerCode
+Contract()
+loadJson(data)
+getType()
+setType(type)
+loadContent(data)
+hasPropertie(propertie)
+getPropertie(propertie)
+setPropertie(propertie, value)
+getConfPropertie(propertie)
+getCurrentChainConf()
+addNext(consumerCode, conf)
+getNextConsumer()
+cleanNext()
+setConf(conf)
+toJson()
+contentToJson()
+setCurrentDate()
}
class Answer {
+Answer()
}
class AnswerRequest {
+AnswerRequest()
}
A classe Answer será melhor detalhada dos módulos consumidores.
Módulo util/postgis/models.py
O diagrama de classe a seguir contém a estrutura de duas classes que foram modeladas no módulo models.py definido na subpasta util/postgis.
classDiagram
class Property {
+int : id
+str : geometry
+dict : geojson
+Property(id, geometry, geojson)
}
class City {
+int : id
+str : geometry
+dict : geojson
+City(id, geometry, geojson)
}
Repare que no módulo models.py existem duas classes. Uma chamada Property (Propriedade) e a outra City (Cidade), ambas com atributos de mesmo nome e mesmo tipo. A classe City será utilizada para definirmos a entidade de uma cidade. A classe Property será utilizada para definirmos a entidade de uma propriedade. Cada um dessas classe possui um construtor com os parâmetros id, geometry e geojson para organizar a estrutura de dados.
O atributo geometry é do tipo string, mas os dados representam de fato um código em hexadecimal com as coordenadas da geometria da propriedade ou da cidade. Veja um exemplo de como é a informação do atributo geometry:
O atributo geojson é do tipo dicionário e possui o seguinte formato:
{
'type': 'MultiPolygon',
'crs': {
'type': 'name',
'properties': {
'name': 'EPSG:4674'
}
},
'coordinates':
[[[[-52.566412045, -27.198236418],
[-52.565110699, -27.199583449],
[-52.574959585, -27.207013026],
[-52.576173296, -27.205790115],
[-52.566412045, -27.198236418]]]]
}
No dicionário exemplo acima, temos as seguintes chaves: type, crs e coordinates. Em que, type indica o tipo do geojson que nesse caso é MultiPolygon. Em que, CRS indica o sistema de referência de coordenadas (coordinate reference system) note que nesse exemplo estamos utilizando EPSG 4674 (SIRGAS 2000). Por fim, coordinates destaca as coordenadas geográficas da propriedade ou da cidade.
Módulo util/postgis/postgis.py
O diagrama de classe a seguir contém a classe PostGis que foi modelada no módulo postgis.py definido na subpasta util/postgis.
classDiagram
class PostGis {
+Logger : logger
+str : dbName
+str : dbUser
+str : dbPassword
+str : dbHost
+str : dbPort
+PostGis(dbName, dbUser, dbPassword, dbHost, dbPort)
+connected()
+connect()
+close()
+createMultiPolygon(region)
+crossLayerGeom(geom, layer_table, proportion, columns)
+crossLayerMultipolygon(multipolygon, layer_table, proportion, columns, conditions)
+getPropertyFromCode(code)
+getCityFromCode(code)
+executeSQL(sql, params, fechall)
+verifyMaxArea(polygon, max_area_search)
}
A ideia dessa classe é fornecer um conjunto de métodos comuns sobre dados geoespaciais. Essa classe pode abrir ou fechar a comunicação com os dados do Banco de Dados PostGis. Assim, o construtor dessa classe contém todas as informações para o acesso a esse banco de dados.
O método createMultiPolygon recebe como parâmetro uma região ou área selecionada em um mapa. A região selecionada é do tipo dicionário e possui a seguinte estrutura geral:
{
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'properties': {},
'geometry': {
'coordinates': [
[[-52.79853429987142, -26.668723440791226],
[-52.79853429987142, -27.722008785135223],
[-50.72010819942622, -27.722008785135223],
[-50.72010819942622, -26.668723440791226],
[-52.79853429987142, -26.668723440791226]]
],
'type': 'Polygon'
}
}
]
}
A seguir temos um exemplo de utilização do método createMultiPolygon.
A variável answer_request é um parâmetro do método processN do consumidor implementado e esse trecho de código foi suprimido para melhor entendimento. O método getPropertie("map") retorna uma região baseado na propriedade mapa que está definido em algum arquivo json, oculto para melhor entendimento. O método getPostGis está definido na classe Worker e retorna uma instância para a classe PostGis. A variável de saída multipolygon é um dicionário que possui a seguinte estrutura geral.
{
'type': 'MultiPolygon',
'coordinates': [
[[[-52.79853429987142, -26.668723440791226],
[-52.79853429987142, -27.722008785135223],
[-50.72010819942622, -27.722008785135223],
[-50.72010819942622, -26.668723440791226],
[-52.79853429987142, -26.668723440791226]]]
],
'crs': {
'type': 'name',
'properties': {
'name': 'EPSG:4674'
}
}
}
O objetivo do método createMultiPolygon é definir uma estrutura do tipo MultiPolygon que possa ser utilizada como entrada para outros métodos.
O método crossLayerMultipolygon recebe como parâmetro de entrada uma região do tipo multipolygon, o nome de uma tabela que deve estar armazenada no BD Postgres e dois parâmetros opcionais que são: tem proporção do tipo booleano por padrão seu valor é False e colunas selecionadas do tipo lista por padrão seu valor é None.
O objetivo do método crossLayerMultipolygon é retornar todas as geometrias da tabela (camada) passada como argumento que intersectam (cruzam) com o multipolygon passado como argumento. No exemplo acima, entitulado "Exemplo: sem proporção e sem colunas", é retornado todas as geometrias da tabela áreas de imóvel (area_imovel) que intersectam com a área especificada (definida na variável multipolygon). No exemplo acima, entitulado "Exemplo: com proporção e com colunas", é retornado todas as geometrias e também as colunas gid, cod_imovel e proportion da tabela áreas de imóvel (area_imovel) que intersectam com a área especificada (definida na variável multipolygon). As colunas gid e cod_imovel são selecionadas, pois foi solicitado via parâmetro columns. A coluna proportion é retornada, pois foi solicitada via parâmetro proportion igual a True.
O método getPropertyFromCode recebe como parâmetro um código de uma propriedade e retorna um objeto do tipo Property correspondente aquele código. Caso não encontre a propriedade no banco de dados o valor None é retornado.
Atenção
Esta propriedade é buscada na Tabela area_imovel. Dessa maneira, a Tabela area_imovel deve estar disponível no BD Postgres.
Exemplo de utilização:
O método getCityFromCode recebe como parâmetro um código de uma cidade e retorna um objeto do tipo City correspondente aquele código. Caso não encontre a cidade no banco de dados o valor None é retornado.
Atenção
Esta propriedade é buscada na Tabela br_municipios_2022. Dessa maneira, a Tabela br_municipios_2022 deve estar disponível no BD Postgres.
Exemplo de utilização:
O método crossLayerGeom recebe como parâmetro de entrada uma geometria, o nome de uma tabela que deve estar armazenada no BD Postgres e dois parâmetros opcionais que são: tem proporção do tipo booleano por padrão seu valor é False e colunas selecionadas do tipo lista por padrão seu valor é None.
cod_movel = answer_request.getPropertie("cod_imovel")
property = self.getPostGis().getPropertyFromCode(cod_movel)
geom = property.geometry
layer_table = "terras_indigenas"
col = ["uf_sigla", "municipio_", "etnia_nome"]
layer, col_data = self.getPostGis().crossLayerGeom(geom, layer_table, proportion=True, columns=col)
O objetivo do método crossLayerGeom é retornar todas as geometrias das camadas (layer) da tabela (layer_table) passada como argumento que intersectam (cruzam) com a geometria (geom) passada como argumento. No exemplo acima, entitulado "Exemplo: sem proporção e sem colunas", é retornado todas as geometrias da tabela "area_imovel" que intersectam com a geometria da cidade passada (código 3138203 que representa a cidade de Lavras-MG). No exemplo acima, entitulado "Exemplo: com proporção e com colunas", é retornado todas as geometrias e também as colunas uf_sigla, municipio_ e etnia_nome da tabela "terras_indigenas" que intersectam com a propriedade recebida como parâmetro (definida na property). As colunas uf_sigla, municipio_ e etnia_nome são selecionadas, pois foi solicitado via parâmetro columns. A coluna proportion é retornada, pois foi solicitada via parâmetro proportion igual a True.
O método executeSQL é um método mais genérico e recebe como parâmetros uma linha de código SQL, os parâmetros dessa SQL e um parâmetro opcional indicando se irá retornar todos os resultado da busca ou apenas um. Exemplo de utilização:
A função ST_AsGeoJSON das querys acima retornam uma geometria GeoJSON. A query SQL acima retorna o código gid e a geometria da tabela area_imovel onde o código do imóvel é igual ao código passado. A primeira query retorna todos os resultados e a segunda query retorna apenas o primeiro resultado.
Para mais informações sobre a classe PostGis veja sua utilização nos módulos consumidores.
Módulo util/tests.py
O módulo util/tests.py possui duas funções úteis que são getConfPropertie(propertie) e loadDataForm(). A função getConfPropertie obtém o valor da propriedade do arquivo env.conf passada como argumento. Analise o exemplo mostrado a seguir em que o valor do nome do BD é armazenado na variável db_name.
Instalação
Pré-Requisitos
Instalações: São necessárias as seguintes instalações.
- Python
- Pip
- Git
Clonando a Aplicação
Primeiramente, clone o projeto pgst-lib utilizando o comando:
Observação
Esse projeto deve ser clonado dentro de uma pasta geoprocess, conforme a organização do projeto.
Configurando a Aplicação
Em seguida, copie o arquivo de configuração do ambiente utilizando o comando:
Em seguida, abra o arquivo env.conf e faça as atualizações necessárias conforme a configuração do seu ambiente de trabalho.
Instale o ambiente virtual venv para isolar as dependências do Python. Utilize o comando abaixo:
Ative o ambiente virtual no seu computador utilizando o comando abaixo:
Instale as dependências do projeto utilizando o comando abaixo:
Github do Projeto
A seguir temos o link para o github do projeto.