infrapy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infrapy/__init__.py +12 -0
- infrapy/config.py +131 -0
- infrapy/cores.py +120 -0
- infrapy/excel.py +147 -0
- infrapy/export.py +42 -0
- infrapy/geo.py +569 -0
- infrapy/interface.py +30 -0
- infrapy/mapas.py +85 -0
- infrapy/sharepoint.py +76 -0
- infrapy-0.1.0.dist-info/METADATA +100 -0
- infrapy-0.1.0.dist-info/RECORD +12 -0
- infrapy-0.1.0.dist-info/WHEEL +4 -0
infrapy/__init__.py
ADDED
infrapy/config.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
config.py - ponto unico onde o pacote le configuracao/segredos do ambiente.
|
|
3
|
+
|
|
4
|
+
Nenhuma credencial ou caminho pessoal fica escrito aqui. Tudo vem do .env
|
|
5
|
+
(carregado no __init__.py). Estruturas compostas (ex.: o dicionario de
|
|
6
|
+
credenciais do certificado) sao MONTADAS aqui a partir das variaveis
|
|
7
|
+
simples lidas do .env.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
if True: # Bibliotecas
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if True: # Funcoes que leem o .env
|
|
15
|
+
def _get(nome, obrigatorio=True, default=None) -> str | None:
|
|
16
|
+
"""
|
|
17
|
+
Le uma variavel de ambiente (vinda do .env).
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
nome : str
|
|
22
|
+
Nome da variavel no .env.
|
|
23
|
+
obrigatorio : bool, optional
|
|
24
|
+
Se True, levanta erro quando a variavel esta ausente. Padrao True.
|
|
25
|
+
default : str, optional
|
|
26
|
+
Valor retornado quando a variavel nao existe e nao e obrigatoria.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
str or None
|
|
31
|
+
Valor da variavel, ou o default.
|
|
32
|
+
|
|
33
|
+
Raises
|
|
34
|
+
------
|
|
35
|
+
RuntimeError
|
|
36
|
+
Se a variavel for obrigatoria e nao estiver definida.
|
|
37
|
+
"""
|
|
38
|
+
valor = os.environ.get(nome, default)
|
|
39
|
+
if obrigatorio and not valor:
|
|
40
|
+
raise RuntimeError(
|
|
41
|
+
f"[infrapy] variavel '{nome}' nao encontrada. "
|
|
42
|
+
f"Adicione-a ao seu .env. Veja .env.example."
|
|
43
|
+
)
|
|
44
|
+
return valor
|
|
45
|
+
|
|
46
|
+
# ---- Caminhos padrao ----
|
|
47
|
+
def PATH_ACC() -> str | None:
|
|
48
|
+
"""Caminho base do projeto ACC. Retorna str ou None."""
|
|
49
|
+
return _get("ACC_PATH", obrigatorio=False)
|
|
50
|
+
|
|
51
|
+
def PATH_SHAREPOINT() -> str | None:
|
|
52
|
+
"""Caminho local ate a pasta do SharePoint. Retorna str ou None."""
|
|
53
|
+
return _get("SHAREPOINT_PATH", obrigatorio=False)
|
|
54
|
+
|
|
55
|
+
def PATH_REDE() -> str | None:
|
|
56
|
+
"""Caminho de rede padrao do projeto na Infra. Retorna str ou None."""
|
|
57
|
+
return _get("REDE_PATH", obrigatorio=False)
|
|
58
|
+
|
|
59
|
+
def PATH_BASE_ALN() -> str | None:
|
|
60
|
+
"""Caminho padrão da base de alinhamentos. Retorna str ou None."""
|
|
61
|
+
return _get("BASE_ALINHAMENTOS", obrigatorio=False)
|
|
62
|
+
|
|
63
|
+
# ---- SharePoint ----
|
|
64
|
+
def SHAREPOINT_USER() -> str:
|
|
65
|
+
"""E-mail/usuario do SharePoint. Retorna str."""
|
|
66
|
+
return _get("INFRA_USER")
|
|
67
|
+
|
|
68
|
+
def SHAREPOINT_PASSWORD() -> str:
|
|
69
|
+
"""Senha do SharePoint. Retorna str."""
|
|
70
|
+
return _get("INFRA_SENHA")
|
|
71
|
+
|
|
72
|
+
def SHAREPOINT_SITE() -> str:
|
|
73
|
+
"""URL do site do SharePoint. Retorna str."""
|
|
74
|
+
return _get("INFRA_SITE")
|
|
75
|
+
|
|
76
|
+
def SHAREPOINT_URL() -> str:
|
|
77
|
+
"""URL relativa base dos documentos no SharePoint. Retorna str."""
|
|
78
|
+
return _get("INFRA_URL", obrigatorio=False)
|
|
79
|
+
|
|
80
|
+
# ---- Azure / certificado ---- APENAS TI PODE GERAR/FORNECER
|
|
81
|
+
def SHAREPOINT_TENANT_ID() -> str:
|
|
82
|
+
"""ID do Tenant(SITE) do sharepoint. Retorna str."""
|
|
83
|
+
return _get("INFRA_AZURE_TENANT_ID", obrigatorio=False)
|
|
84
|
+
|
|
85
|
+
def SHAREPOINT_CLIENT_ID() -> str:
|
|
86
|
+
"""ID da aplicação do Azure. Retorna str."""
|
|
87
|
+
return _get("INFRA_AZURE_CLIENT_ID", obrigatorio=False)
|
|
88
|
+
|
|
89
|
+
def SHAREPOINT_CERTIFICATE_THUMBPRINT() -> str:
|
|
90
|
+
"""Certificado da aplicação do Azure. Retorna str."""
|
|
91
|
+
return _get("CERT_THUMBPRINT", obrigatorio=False)
|
|
92
|
+
|
|
93
|
+
def SHAREPOINT_CERTIFICATE_KEY() -> str:
|
|
94
|
+
"""Caminho da "key.pem" do Azure. Retorna str."""
|
|
95
|
+
return _get("CERT_KEY", obrigatorio=False)
|
|
96
|
+
|
|
97
|
+
def SHAREPOINT_CERTIFICATE() -> dict:
|
|
98
|
+
"""
|
|
99
|
+
Monta o dicionario de credenciais de app do Azure (autenticacao por
|
|
100
|
+
certificado), a partir das variaveis simples do .env.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
dict
|
|
105
|
+
Dicionario com as chaves tenant, client_id, thumbprint e cert_path,
|
|
106
|
+
no formato esperado pelo Office365-REST-Python-Client.
|
|
107
|
+
"""
|
|
108
|
+
return {
|
|
109
|
+
"tenant": _get("INFRA_AZURE_TENANT_ID"),
|
|
110
|
+
"client_id": _get("INFRA_AZURE_CLIENT_ID"),
|
|
111
|
+
"thumbprint": _get("CERT_THUMBPRINT"),
|
|
112
|
+
"cert_path": _get("CERT_KEY"),
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# ---- DBeaver / PostgreSQL ----
|
|
116
|
+
def DBEAVER_CONFIG() -> dict:
|
|
117
|
+
"""
|
|
118
|
+
Retorna as credenciais de conexao ao banco (DBeaver/PostgreSQL).
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
dict
|
|
123
|
+
Dicionario com user, password, host, port e database.
|
|
124
|
+
"""
|
|
125
|
+
return {
|
|
126
|
+
"user": _get("DBEAVER_USER"),
|
|
127
|
+
"password": _get("DBEAVER_PASSWORD"),
|
|
128
|
+
"host": _get("DBEAVER_HOST"),
|
|
129
|
+
"port": _get("DBEAVER_PORT"),
|
|
130
|
+
"database": _get("DBEAVER_DATABASE"),
|
|
131
|
+
}
|
infrapy/cores.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""cores.py - geracao de cores aleatorias e exibicao no Jupyter."""
|
|
2
|
+
|
|
3
|
+
if True: # Bibliotecas
|
|
4
|
+
from IPython.display import HTML, display
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
import random
|
|
7
|
+
|
|
8
|
+
if True: # Funcoes
|
|
9
|
+
def cor_aleatoria() -> str:
|
|
10
|
+
"""
|
|
11
|
+
Gera uma cor hexadecimal aleatória.
|
|
12
|
+
|
|
13
|
+
A cor é gerada sorteando um valor inteiro entre ``0x000000`` e
|
|
14
|
+
``0xFFFFFF`` e convertendo-o para o formato hexadecimal utilizado em CSS.
|
|
15
|
+
|
|
16
|
+
Returns
|
|
17
|
+
-------
|
|
18
|
+
str
|
|
19
|
+
Código hexadecimal da cor no formato ``"#RRGGBB"``.
|
|
20
|
+
|
|
21
|
+
Examples
|
|
22
|
+
--------
|
|
23
|
+
>>> cor_aleatoria()
|
|
24
|
+
'#3fa9d2'
|
|
25
|
+
"""
|
|
26
|
+
return f"#{random.randint(0, 0xFFFFFF):06x}"
|
|
27
|
+
|
|
28
|
+
def cor_aleatoria_variada() -> str:
|
|
29
|
+
"""
|
|
30
|
+
Gera uma cor hexadecimal aleatória com maior saturação.
|
|
31
|
+
|
|
32
|
+
A função limita um dos canais RGB a zero e sorteia os outros dois
|
|
33
|
+
entre 60 e 255, produzindo predominantemente cores mais vibrantes
|
|
34
|
+
e evitando tons muito escuros ou acinzentados.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
str
|
|
39
|
+
Código hexadecimal da cor no formato ``"#RRGGBB"``.
|
|
40
|
+
|
|
41
|
+
Examples
|
|
42
|
+
--------
|
|
43
|
+
>>> cor_aleatoria_variada()
|
|
44
|
+
'#ff7a00'
|
|
45
|
+
"""
|
|
46
|
+
valores_1 = [(60, 255), (0, 0), (60, 255)]
|
|
47
|
+
valores_2 = [(0, 0), (60, 255), (60, 255)]
|
|
48
|
+
valores_3 = [(60, 255), (60, 255), (0, 0)]
|
|
49
|
+
|
|
50
|
+
valores = random.choice([valores_1, valores_2, valores_3])
|
|
51
|
+
|
|
52
|
+
R = random.randint(valores[0][0], valores[0][1])
|
|
53
|
+
G = random.randint(valores[1][0], valores[1][1])
|
|
54
|
+
B = random.randint(valores[2][0], valores[2][1])
|
|
55
|
+
|
|
56
|
+
return f"#{R:02x}{G:02x}{B:02x}"
|
|
57
|
+
|
|
58
|
+
def cor_aleatoria_predef() -> str:
|
|
59
|
+
"""
|
|
60
|
+
Retorna uma cor escolhida aleatoriamente de uma paleta predefinida.
|
|
61
|
+
|
|
62
|
+
A paleta contém cores previamente selecionadas, incluindo tons de
|
|
63
|
+
azul, roxo, ciano, magenta, vermelho, amarelo, verde e cores neutras.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
str
|
|
68
|
+
Código hexadecimal da cor selecionada no formato ``"#RRGGBB"``.
|
|
69
|
+
|
|
70
|
+
Examples
|
|
71
|
+
--------
|
|
72
|
+
>>> cor_aleatoria_predef()
|
|
73
|
+
'#1f7ee6'
|
|
74
|
+
"""
|
|
75
|
+
cores_predefinidas = [
|
|
76
|
+
# Tons de azul e roxo
|
|
77
|
+
"#500090", "#3e23b4", "#cb99e1", "#1f7ee6", "#005987", "#55aaff",
|
|
78
|
+
# Tons de ciano
|
|
79
|
+
"#00ffff", "#00b7c7",
|
|
80
|
+
# Tons de magenta
|
|
81
|
+
"#ff00ff", "#ff007f", "#a300a5", "#9600f4", "#aa007f", "#860044", "#ffaaff",
|
|
82
|
+
# Tons de vermelho
|
|
83
|
+
"#ff0000", "#f94305", "#ad2a0d", "#dd4160", "#ff8ba5", "#ffaa7f",
|
|
84
|
+
# Tons de amarelo
|
|
85
|
+
"#ffff00", "#afdb00", "#deec00", "#c5c800", "#c6b200", "#bd9033",
|
|
86
|
+
# Tons de verde
|
|
87
|
+
"#00ff00", "#00ff7f", "#47c700", "#01ed9d", "#00f63d",
|
|
88
|
+
# Tons neutros
|
|
89
|
+
"#ffffff", "#b4f3db", "#add3b1", "#d2af9f", "#979768", "#1a2b35"
|
|
90
|
+
]
|
|
91
|
+
return random.choice(cores_predefinidas)
|
|
92
|
+
|
|
93
|
+
def exibir_blocos_cores_ipynb(cores: Iterable[str]) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Exibe uma paleta de cores em um notebook Jupyter.
|
|
96
|
+
|
|
97
|
+
Cria um conjunto de retângulos coloridos utilizando HTML, onde cada
|
|
98
|
+
retângulo possui a cor especificada e exibe seu respectivo nome ou
|
|
99
|
+
código hexadecimal.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
cores : Iterable[str]
|
|
104
|
+
Iterável contendo nomes de cores CSS ou códigos de cores
|
|
105
|
+
(ex.: "#FF0000", "red", "rgb(0, 128, 255)", (f"#{i:06X}" for i in range(10)) ).
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
None
|
|
110
|
+
A função apenas renderiza o HTML no notebook.
|
|
111
|
+
|
|
112
|
+
Examples
|
|
113
|
+
--------
|
|
114
|
+
>>> exibir_blocos_cores_ipynb(["#FF0000", "#00FF00", "#0000FF", "gold", "royalblue"])
|
|
115
|
+
"""
|
|
116
|
+
html = "<div style='display: flex; flex-wrap: wrap;'>"
|
|
117
|
+
for cor in cores:
|
|
118
|
+
html += f"<div style='width: 100px; height: 50px; background-color: {cor}; margin: 5px; text-align: center; line-height: 50px; color: #fff;'>{cor}</div>"
|
|
119
|
+
html += "</div>"
|
|
120
|
+
display(HTML(html))
|
infrapy/excel.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""excel.py - leitura de planilhas (xlwings) e exportacao para xlsx."""
|
|
2
|
+
|
|
3
|
+
if True: # Bibliotecas
|
|
4
|
+
import os
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import geopandas as gpd
|
|
7
|
+
import xlwings as xw
|
|
8
|
+
|
|
9
|
+
from . import config
|
|
10
|
+
|
|
11
|
+
if True: # Funcoes
|
|
12
|
+
|
|
13
|
+
def abrir_ou_conectar_excel(caminho: str) -> xw.Book:
|
|
14
|
+
"""
|
|
15
|
+
Retorna um objeto Book do xlwings, reaproveitando o arquivo se ele ja
|
|
16
|
+
estiver aberto no Excel, ou abrindo-o do disco caso contrario.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
caminho : str
|
|
21
|
+
Caminho completo do arquivo .xlsx/.xlsb a abrir.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
xlwings.Book
|
|
26
|
+
O livro correspondente ao arquivo, ja aberto e pronto para uso.
|
|
27
|
+
|
|
28
|
+
Raises
|
|
29
|
+
------
|
|
30
|
+
FileNotFoundError
|
|
31
|
+
Se o arquivo nao existir no disco e tambem nao estiver aberto.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
nome_alvo = os.path.basename(caminho).lower()
|
|
35
|
+
|
|
36
|
+
for app in xw.apps:
|
|
37
|
+
for livro in app.books:
|
|
38
|
+
if livro.name.lower() == nome_alvo:
|
|
39
|
+
return livro
|
|
40
|
+
|
|
41
|
+
if not os.path.exists(caminho):
|
|
42
|
+
raise FileNotFoundError(
|
|
43
|
+
f"Arquivo nao encontrado e nao esta aberto no Excel: {caminho}"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return xw.Book(caminho)
|
|
47
|
+
|
|
48
|
+
def ler_excel(caminho: str, aba: str | int, celula: str, CRS: str | None = None, expandir: bool = True) -> tuple[xw.Sheet, pd.DataFrame | gpd.GeoDataFrame]:
|
|
49
|
+
"""
|
|
50
|
+
Le uma tabela de um arquivo Excel via xlwings.
|
|
51
|
+
|
|
52
|
+
Reaproveita o arquivo se ele ja estiver aberto no Excel, evitando conflitos de instancia.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
caminho : str
|
|
57
|
+
Caminho completo do arquivo Excel.
|
|
58
|
+
|
|
59
|
+
aba : str or int
|
|
60
|
+
Nome ou indice da planilha (aba).
|
|
61
|
+
|
|
62
|
+
celula : str
|
|
63
|
+
Ponto de leitura. Se ``expandir=True``, e a celula ancora onde a
|
|
64
|
+
tabela comeca (ex.: "A1"). Se ``expandir=False``, e o intervalo
|
|
65
|
+
exato a ler (ex.: "A1:F20").
|
|
66
|
+
|
|
67
|
+
CRS : str, optional
|
|
68
|
+
Se informado (ex.: "EPSG:4326"), retorna um GeoDataFrame, criando a
|
|
69
|
+
coluna 'geometry' vazia se ela nao existir. Padrao None (DataFrame).
|
|
70
|
+
|
|
71
|
+
expandir : bool, optional
|
|
72
|
+
Se True (padrao), expande a partir da celula ancora para toda a
|
|
73
|
+
regiao de dados contigua. Se False, le exatamente o intervalo
|
|
74
|
+
informado em ``celula``.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
tuple of (xlwings.Sheet, pandas.DataFrame or geopandas.GeoDataFrame)
|
|
79
|
+
A planilha (para manipulacao posterior) e os dados lidos. O segundo
|
|
80
|
+
elemento e um GeoDataFrame quando ``CRS`` e informado, senao um
|
|
81
|
+
DataFrame comum.
|
|
82
|
+
|
|
83
|
+
Examples
|
|
84
|
+
--------
|
|
85
|
+
Expandir a partir de uma celula ancora (comportamento padrao):
|
|
86
|
+
|
|
87
|
+
>>> ws, df = ler_excel("dados.xlsx", "Plan1", "A1")
|
|
88
|
+
|
|
89
|
+
Ler um intervalo exato, sem expandir:
|
|
90
|
+
|
|
91
|
+
>>> ws, df = ler_excel("dados.xlsx", "Plan1", "A1:F20", expandir=False)
|
|
92
|
+
|
|
93
|
+
Ler como dado geografico:
|
|
94
|
+
|
|
95
|
+
>>> ws, gdf = ler_excel("snv.xlsx", "SNV", "A1", CRS="EPSG:4326")
|
|
96
|
+
"""
|
|
97
|
+
wb = abrir_ou_conectar_excel(caminho)
|
|
98
|
+
ws = wb.sheets(aba)
|
|
99
|
+
|
|
100
|
+
rng = ws.range(celula)
|
|
101
|
+
if expandir:
|
|
102
|
+
rng = rng.expand(mode='table')
|
|
103
|
+
|
|
104
|
+
df = rng.options(pd.DataFrame, index=False, header=1).value
|
|
105
|
+
|
|
106
|
+
if CRS is not None:
|
|
107
|
+
if 'geometry' not in df.columns:
|
|
108
|
+
df['geometry'] = None
|
|
109
|
+
df = gpd.GeoDataFrame(df, crs=CRS, geometry='geometry')
|
|
110
|
+
|
|
111
|
+
return ws, df
|
|
112
|
+
|
|
113
|
+
def exportar_abas(caminho: str, abas: list[str], output: str) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Le abas especificas de um arquivo Excel e salva cada uma como um .xlsx
|
|
116
|
+
separado na pasta de saida.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
caminho : str
|
|
121
|
+
Caminho do arquivo Excel de origem.
|
|
122
|
+
|
|
123
|
+
abas : list of str
|
|
124
|
+
Nomes das abas a exportar.
|
|
125
|
+
|
|
126
|
+
output : str
|
|
127
|
+
Pasta de destino. Criada automaticamente se nao existir.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
None
|
|
132
|
+
|
|
133
|
+
Examples
|
|
134
|
+
--------
|
|
135
|
+
>>> exportar_abas("origem.xlsx", ["Jan", "Fev"], "saida/")
|
|
136
|
+
"""
|
|
137
|
+
if not os.path.exists(output):
|
|
138
|
+
os.makedirs(output)
|
|
139
|
+
|
|
140
|
+
for aba in abas:
|
|
141
|
+
df = pd.read_excel(caminho, sheet_name=aba)
|
|
142
|
+
df.dropna(subset=["COD"], inplace=True)
|
|
143
|
+
nome_arquivo = os.path.join(output, f"{aba}.xlsx")
|
|
144
|
+
if os.path.exists(nome_arquivo):
|
|
145
|
+
os.remove(nome_arquivo)
|
|
146
|
+
with pd.ExcelWriter(nome_arquivo, engine='xlsxwriter') as writer:
|
|
147
|
+
df.to_excel(writer, sheet_name=aba, index=False)
|
infrapy/export.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""export.py - exportacao de DataFrames/GeoDataFrames para xlsx e shapefile."""
|
|
2
|
+
|
|
3
|
+
if True: # Bibliotecas
|
|
4
|
+
import os
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
import geopandas as gpd
|
|
8
|
+
from shapely.geometry import shape
|
|
9
|
+
|
|
10
|
+
from . import config
|
|
11
|
+
|
|
12
|
+
if True: # Funcoes
|
|
13
|
+
|
|
14
|
+
def JSON_para_SHP(caminho: str, arquivo: str) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Le um GeoJSON e salva como shapefile na mesma pasta.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
caminho : str
|
|
21
|
+
Pasta onde esta o arquivo .json (e onde o .shp sera salvo).
|
|
22
|
+
arquivo : str
|
|
23
|
+
Nome do arquivo (sem extensao).
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
None
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
--------
|
|
31
|
+
>>> JSON_para_SHP("dados/", "vetores")
|
|
32
|
+
"""
|
|
33
|
+
# Load the JSON file
|
|
34
|
+
with open(fr'{caminho}/{arquivo}.json') as f:
|
|
35
|
+
data = json.load(f)
|
|
36
|
+
|
|
37
|
+
# Convert the JSON data to a GeoDataFrame
|
|
38
|
+
geoms = [shape(item['geometry']) for item in data['features']]
|
|
39
|
+
gdf = gpd.GeoDataFrame(data['features'], geometry=geoms, crs="EPSG:4326")
|
|
40
|
+
|
|
41
|
+
# Save the GeoDataFrame as a shapefile
|
|
42
|
+
gdf.to_file(fr'{caminho}/{arquivo}')
|
infrapy/geo.py
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
"""geo.py - geoprocessamento: leitura de shapes, conversao de coordenadas,
|
|
2
|
+
calculo de km em alinhamentos/SNV, cortes e utilitarios geometricos."""
|
|
3
|
+
|
|
4
|
+
if True: # Bibliotecas
|
|
5
|
+
from typing import Tuple
|
|
6
|
+
|
|
7
|
+
import math
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
import geopandas as gpd
|
|
11
|
+
import pandas as pd
|
|
12
|
+
|
|
13
|
+
import shapely
|
|
14
|
+
from shapely import ops
|
|
15
|
+
from shapely.ops import substring
|
|
16
|
+
from shapely.geometry import Point, LineString
|
|
17
|
+
import shapely.geometry as geom
|
|
18
|
+
from shapely.errors import ShapelyDeprecationWarning
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
|
|
22
|
+
from . import config
|
|
23
|
+
|
|
24
|
+
if True: # Funcoes
|
|
25
|
+
|
|
26
|
+
# ------ Auxiliares -----
|
|
27
|
+
def closest_geometry(gdf: gpd.GeoDataFrame, point: shapely.geometry.point.Point | tuple) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Retorna a geometria mais próxima de um ponto.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
gdf : geopandas.GeoDataFrame
|
|
34
|
+
GeoDataFrame contendo uma coluna de geometria.
|
|
35
|
+
point : shapely.geometry.point.Point | tuple[float, float]
|
|
36
|
+
Ponto de referência ou tupla (x, y).
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
tuple[int, BaseGeometry, float]
|
|
41
|
+
Índice da geometria, geometria encontrada e distância.
|
|
42
|
+
|
|
43
|
+
Raises
|
|
44
|
+
------
|
|
45
|
+
RuntimeError
|
|
46
|
+
Se o geodataframe utilizado está vazio.
|
|
47
|
+
|
|
48
|
+
Examples
|
|
49
|
+
--------
|
|
50
|
+
>>> closest_geometry(gdf, Point(x, y))
|
|
51
|
+
>>> closest_geometry(gdf, (x, y))
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
if gdf.empty:
|
|
55
|
+
raise ValueError("GeoDataFrame is Empty.")
|
|
56
|
+
|
|
57
|
+
index_found = 0
|
|
58
|
+
smallest_dist = float("inf")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
for index, geom in gdf.geometry.items():
|
|
62
|
+
dist = geom.distance(point)
|
|
63
|
+
|
|
64
|
+
if dist < smallest_dist:
|
|
65
|
+
smallest_dist = dist
|
|
66
|
+
index_found = index
|
|
67
|
+
geometry_found = geom
|
|
68
|
+
|
|
69
|
+
return index_found, geometry_found
|
|
70
|
+
|
|
71
|
+
def linestring_ini_fim_coordinates(line: LineString) -> tuple[float, float, float, float]:
|
|
72
|
+
"""
|
|
73
|
+
Extrai as coordenadas inicial e final de uma linha em Lat/Long.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
line : shapely.geometry.LineString
|
|
78
|
+
Alinhamento do qual extrair os extremos.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
tuple of float
|
|
83
|
+
(start_lat, start_lon, end_lat, end_lon).
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
start_point = Point(line.coords[0])
|
|
87
|
+
start_lat = start_point.y
|
|
88
|
+
start_lon = start_point.x
|
|
89
|
+
|
|
90
|
+
end_point = Point(line.coords[-1])
|
|
91
|
+
end_lat = end_point.y
|
|
92
|
+
end_lon = end_point.x
|
|
93
|
+
|
|
94
|
+
return start_lat, start_lon, end_lat, end_lon
|
|
95
|
+
|
|
96
|
+
# ----- Bases -----
|
|
97
|
+
def EPSG_SIRGAS(zona: int) -> str | None:
|
|
98
|
+
"""
|
|
99
|
+
Retorna o codigo EPSG (SIRGAS 2000 / UTM) correspondente a uma zona.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
zona : int
|
|
104
|
+
Zona UTM (17 a 25) ou 84 para WGS84 geografico.
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
str or None
|
|
109
|
+
Codigo EPSG no formato "EPSG:XXXXX", ou None se a zona nao for
|
|
110
|
+
reconhecida.
|
|
111
|
+
|
|
112
|
+
Examples
|
|
113
|
+
--------
|
|
114
|
+
>>> EPSG_SIRGAS(23)
|
|
115
|
+
'EPSG:31983'
|
|
116
|
+
"""
|
|
117
|
+
tabela = {
|
|
118
|
+
17: "EPSG:31977",
|
|
119
|
+
18: "EPSG:31978",
|
|
120
|
+
19: "EPSG:31979",
|
|
121
|
+
20: "EPSG:31980",
|
|
122
|
+
21: "EPSG:31981",
|
|
123
|
+
22: "EPSG:31982",
|
|
124
|
+
23: "EPSG:31983",
|
|
125
|
+
24: "EPSG:31984",
|
|
126
|
+
25: "EPSG:31985",
|
|
127
|
+
84: "EPSG:4326",
|
|
128
|
+
}
|
|
129
|
+
return tabela.get(zona)
|
|
130
|
+
|
|
131
|
+
def EPSG_SAD(zona: int) -> str | None:
|
|
132
|
+
"""
|
|
133
|
+
Retorna o codigo EPSG (SAD69 / UTM) correspondente a uma zona.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
zona : int
|
|
138
|
+
Zona UTM (17 a 25) ou 84 para WGS84 geografico.
|
|
139
|
+
|
|
140
|
+
Returns
|
|
141
|
+
-------
|
|
142
|
+
str or None
|
|
143
|
+
Codigo EPSG no formato "EPSG:XXXXX", ou None se a zona nao for
|
|
144
|
+
reconhecida.
|
|
145
|
+
|
|
146
|
+
Examples
|
|
147
|
+
--------
|
|
148
|
+
>>> EPSG_SAD(23)
|
|
149
|
+
'EPSG:31983'
|
|
150
|
+
"""
|
|
151
|
+
tabela = {
|
|
152
|
+
17: "EPSG:29187",
|
|
153
|
+
18: "EPSG:29188",
|
|
154
|
+
19: "EPSG:29189",
|
|
155
|
+
20: "EPSG:29190",
|
|
156
|
+
21: "EPSG:29191",
|
|
157
|
+
22: "EPSG:29192",
|
|
158
|
+
23: "EPSG:29193",
|
|
159
|
+
24: "EPSG:29194",
|
|
160
|
+
25: "EPSG:29195",
|
|
161
|
+
84: "EPSG:4326",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return tabela.get(zona)
|
|
165
|
+
|
|
166
|
+
def read_shape(caminho: str | None = None, epsg: str | None = None) -> gpd.GeoDataFrame:
|
|
167
|
+
"""
|
|
168
|
+
Le um shapefile como GeoDataFrame.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
caminho : str, optional
|
|
173
|
+
Caminho do arquivo .shp. Se None, usa ALN_PADRAO do .env.
|
|
174
|
+
epsg : str, optional
|
|
175
|
+
CRS a atribuir caso o shapefile nao tenha um definido
|
|
176
|
+
(ex.: "EPSG:4326"). Padrao None.
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
geopandas.GeoDataFrame
|
|
181
|
+
Os dados lidos.
|
|
182
|
+
|
|
183
|
+
Raises
|
|
184
|
+
------
|
|
185
|
+
RuntimeError
|
|
186
|
+
Se caminho for None e ALN_PADRAO nao estiver definido no .env.
|
|
187
|
+
|
|
188
|
+
Examples
|
|
189
|
+
--------
|
|
190
|
+
>>> gdf = read_shape() # usa ALN_PADRAO do .env
|
|
191
|
+
>>> gdf = read_shape("aln.shp", "EPSG:4326")
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
#warnings.filterwarnings("ignore", category=ShapelyDeprecationWarning)
|
|
195
|
+
|
|
196
|
+
if caminho is None:
|
|
197
|
+
caminho = config.PATH_BASE_ALN()
|
|
198
|
+
if caminho is None:
|
|
199
|
+
raise RuntimeError("read_shape() sem caminho e sem ALN_PADRAO no .env.")
|
|
200
|
+
|
|
201
|
+
df = gpd.read_file(caminho)
|
|
202
|
+
if df.crs is None and epsg is None:
|
|
203
|
+
return df
|
|
204
|
+
elif df.crs is None and epsg is not None:
|
|
205
|
+
return gpd.GeoDataFrame(df, crs=epsg, geometry="geometry")
|
|
206
|
+
else:
|
|
207
|
+
return df
|
|
208
|
+
|
|
209
|
+
def load_SNV() -> gpd.GeoDataFrame:
|
|
210
|
+
"""
|
|
211
|
+
Carrega e padroniza os SNVs (rede federal + cide) a partir dos
|
|
212
|
+
caminhos definidos no .env (SNV_FED e SNV_CIDE).
|
|
213
|
+
|
|
214
|
+
Junta as duas fontes, remove SNVs duplicados (preferindo a rede
|
|
215
|
+
federal), adiciona colunas de lat/long inicial e final e retorna
|
|
216
|
+
tudo em WGS84.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
geopandas.GeoDataFrame
|
|
221
|
+
SNVs padronizados em EPSG:4326.
|
|
222
|
+
|
|
223
|
+
Raises
|
|
224
|
+
------
|
|
225
|
+
RuntimeError
|
|
226
|
+
Se SNV_FED ou SNV_CIDE nao estiverem definidos no .env.
|
|
227
|
+
|
|
228
|
+
Examples
|
|
229
|
+
--------
|
|
230
|
+
>>> snv = load_SNV()
|
|
231
|
+
|
|
232
|
+
"""
|
|
233
|
+
fed = config.caminho_snv_fed()
|
|
234
|
+
cide = config.caminho_snv_cide()
|
|
235
|
+
if not fed or not cide:
|
|
236
|
+
raise RuntimeError("load_SNV() requer SNV_FED e SNV_CIDE no .env.")
|
|
237
|
+
|
|
238
|
+
VGEO_fed_df = gpd.read_file(fed)
|
|
239
|
+
VGEO_std_df = gpd.read_file(cide)
|
|
240
|
+
|
|
241
|
+
filt_fed = ['Codigo_SNV','Codigo_BR','Unidade_Fe','Local_Inic','Quilometra','Local_Fim','Quilometr0','Extensao','Superficie','versao_snv','geometry']
|
|
242
|
+
filt_std = ['Codigo_SNV','Codigo_Rod','Unidade_Fe','Local_Inic','Quilometra','Local_Fim','Quilometr0','Extensao','Superficie','Referencia','geometry']
|
|
243
|
+
col_padrao = ['SNV','ROD','UF','inicio','km i','fim','km f','ext','perfil','ref','geometry']
|
|
244
|
+
|
|
245
|
+
VGEO_fed_df = VGEO_fed_df[filt_fed]
|
|
246
|
+
VGEO_fed_df.columns = col_padrao
|
|
247
|
+
VGEO_fed_df.insert(7, 'fonte', 'vgeo_snv')
|
|
248
|
+
|
|
249
|
+
VGEO_std_df = VGEO_std_df[filt_std]
|
|
250
|
+
VGEO_std_df.columns = col_padrao
|
|
251
|
+
VGEO_std_df.insert(7, 'fonte', 'vgeo_cide')
|
|
252
|
+
|
|
253
|
+
VGEO_fed_df = VGEO_fed_df.to_crs('EPSG:4326')
|
|
254
|
+
VGEO_std_df = VGEO_std_df.to_crs('EPSG:4326')
|
|
255
|
+
|
|
256
|
+
vgeo_df = pd.concat([VGEO_fed_df, VGEO_std_df])
|
|
257
|
+
vgeo_df = vgeo_df.drop_duplicates(subset='SNV', keep='first').reset_index(drop=True)
|
|
258
|
+
vgeo_df[['lat i', 'long i', 'lat f', 'long f']] = vgeo_df['geometry'].apply(
|
|
259
|
+
lambda x: pd.Series(linestring_ini_fim_coordinates(x)))
|
|
260
|
+
geometry = vgeo_df.pop('geometry')
|
|
261
|
+
vgeo_df.insert(len(vgeo_df.columns), 'geometry', geometry)
|
|
262
|
+
return vgeo_df.to_crs('EPSG:4326')
|
|
263
|
+
|
|
264
|
+
# ------ Aligments -----
|
|
265
|
+
def point_to_km(gdf: gpd.GeoDataFrame, point, rod=None):
|
|
266
|
+
"""
|
|
267
|
+
Retorna o km ate um ponto no alinhamento mais proximo (via UTM).
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
aln_gdf : geopandas.GeoDataFrame
|
|
272
|
+
Alinhamentos, com colunas ZONA, KM I, NOME e geometry.
|
|
273
|
+
ponto : shapely.geometry.Point or tuple
|
|
274
|
+
Ponto de referencia (lon, lat) ou Point.
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
tuple
|
|
279
|
+
(km, nome_do_alinhamento). Retorna ('Erro', ...) quando o ponto
|
|
280
|
+
esta distante (>300 m) ou em zona UTM incompativel.
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
if type(point) != shapely.geometry.point.Point:
|
|
284
|
+
Point(point)
|
|
285
|
+
|
|
286
|
+
closest_index, closest_aln = closest_geometry(gdf, point)
|
|
287
|
+
|
|
288
|
+
ALN = gdf.loc[closest_index,'ALN']
|
|
289
|
+
|
|
290
|
+
# alinhamento final
|
|
291
|
+
EPSG = gdf.loc[closest_index,'EPSG']
|
|
292
|
+
|
|
293
|
+
gdf_utm = gdf.to_crs(EPSG)
|
|
294
|
+
aln = gdf_utm.loc[closest_index,'geometry']
|
|
295
|
+
|
|
296
|
+
# calcular km
|
|
297
|
+
(lon,lat) = point.coords[0]
|
|
298
|
+
|
|
299
|
+
try:
|
|
300
|
+
point_gdf = gpd.GeoDataFrame(geometry=[point], crs='EPSG:4326')
|
|
301
|
+
point_gdf_utm = point_gdf.to_crs(EPSG)
|
|
302
|
+
utm_point = point_gdf_utm.loc[0,'geometry']
|
|
303
|
+
km = aln.project(utm_point)/1000 + gdf.loc[closest_index, 'km_i']# m
|
|
304
|
+
KM = float(round(km,3))
|
|
305
|
+
|
|
306
|
+
except:
|
|
307
|
+
km = "Erro"
|
|
308
|
+
ALN = "Erro"
|
|
309
|
+
|
|
310
|
+
return (KM,ALN)
|
|
311
|
+
|
|
312
|
+
def km_to_point(aln_gdf: gpd.GeoDataFrame, km: float, rod: str) -> tuple:
|
|
313
|
+
"""
|
|
314
|
+
Dado um km e uma rodovia, retorna as coordenadas (lat, long) do ponto.
|
|
315
|
+
|
|
316
|
+
Parameters
|
|
317
|
+
----------
|
|
318
|
+
aln_gdf : geopandas.GeoDataFrame
|
|
319
|
+
Alinhamentos, com colunas ROD, km_i, km_f, EPSG e geometry.
|
|
320
|
+
km : float
|
|
321
|
+
Quilometragem desejada.
|
|
322
|
+
rod : str
|
|
323
|
+
Rodovia a filtrar.
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
tuple of float or None
|
|
328
|
+
(lat, long) do ponto, ou None se o km/rodovia nao resolver para
|
|
329
|
+
um unico alinhamento.
|
|
330
|
+
"""
|
|
331
|
+
# Filtrar dataframe para rodovia desejada
|
|
332
|
+
gdf = aln_gdf[aln_gdf.ROD == rod]
|
|
333
|
+
|
|
334
|
+
# Encontrar SNV desejado
|
|
335
|
+
gdf = gdf[(gdf.km_i <= km)&(gdf.km_f > km)]
|
|
336
|
+
|
|
337
|
+
resultados_lat = []
|
|
338
|
+
resultados_long = []
|
|
339
|
+
resultados_aln = []
|
|
340
|
+
|
|
341
|
+
if (len(gdf) == 0):
|
|
342
|
+
return "Erro, não achamos este KM nesta rodovia"
|
|
343
|
+
|
|
344
|
+
elif len(gdf) != 1:
|
|
345
|
+
for index, row in gdf.iterrows():
|
|
346
|
+
km_i = row['km_i']
|
|
347
|
+
line = row['geometry']
|
|
348
|
+
epsg = row['EPSG']
|
|
349
|
+
aln = row["ALN"]
|
|
350
|
+
# Cálculo
|
|
351
|
+
line_df = gpd.GeoDataFrame(geometry=[line], crs="EPSG:4326")
|
|
352
|
+
line_df = line_df.to_crs(epsg)
|
|
353
|
+
line_utm = line_df.loc[0, 'geometry']
|
|
354
|
+
|
|
355
|
+
ponto = line_utm.interpolate((km - km_i) * 1000)
|
|
356
|
+
ponto_df = gpd.GeoDataFrame(geometry=[ponto], crs=epsg)
|
|
357
|
+
ponto_df = ponto_df.to_crs("EPSG:4326")
|
|
358
|
+
long, lat = ponto_df.loc[0, 'geometry'].coords[0]
|
|
359
|
+
|
|
360
|
+
resultados_lat.append(str(round(lat, 8)).replace('.', ','))
|
|
361
|
+
resultados_long.append(str(round(long, 8)).replace('.', ','))
|
|
362
|
+
resultados_aln.append(aln)
|
|
363
|
+
|
|
364
|
+
return ', '.join(resultados_lat), ', '.join(resultados_long), ', '.join(resultados_aln)
|
|
365
|
+
|
|
366
|
+
else:
|
|
367
|
+
# Entradas
|
|
368
|
+
aln_id = int(gdf.index[0])
|
|
369
|
+
km_i = gdf.loc[aln_id,'km_i']
|
|
370
|
+
line = gdf.loc[aln_id,'geometry']
|
|
371
|
+
epsg = gdf.loc[aln_id,'EPSG']
|
|
372
|
+
aln = gdf.loc[aln_id, "ALN"]
|
|
373
|
+
|
|
374
|
+
# Cálculo
|
|
375
|
+
line_df = gpd.GeoDataFrame(geometry=[line],crs="EPSG:4326")
|
|
376
|
+
line_df = line_df.to_crs(epsg)
|
|
377
|
+
line_utm = line_df.loc[0,'geometry']
|
|
378
|
+
|
|
379
|
+
ponto = line_utm.interpolate((km-km_i)*1000)
|
|
380
|
+
ponto_df = gpd.GeoDataFrame(geometry=[ponto],crs = epsg)
|
|
381
|
+
ponto_df = ponto_df.to_crs("EPSG:4326")
|
|
382
|
+
long,lat = ponto_df.loc[0,'geometry'].coords[0]
|
|
383
|
+
|
|
384
|
+
# Retornar coordenadas
|
|
385
|
+
return round(lat,8),round(long,8), aln
|
|
386
|
+
|
|
387
|
+
# ----- SNVs ------
|
|
388
|
+
def km_snv(snv_gdf: gpd.GeoDataFrame, ponto):
|
|
389
|
+
"""
|
|
390
|
+
Dado um ponto e SNVs, retorna o km ate o ponto e o SNV mais proximo.
|
|
391
|
+
|
|
392
|
+
Parameters
|
|
393
|
+
----------
|
|
394
|
+
snv_gdf : geopandas.GeoDataFrame
|
|
395
|
+
SNVs, com colunas 'km i', 'ext', 'SNV' e geometry.
|
|
396
|
+
ponto : shapely.geometry.Point or tuple
|
|
397
|
+
Ponto de referencia (lon, lat) ou Point.
|
|
398
|
+
|
|
399
|
+
Returns
|
|
400
|
+
-------
|
|
401
|
+
tuple
|
|
402
|
+
(km, codigo_SNV).
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
#Transformar coordenadas para ponto
|
|
406
|
+
if type(ponto) != shapely.geometry.point.Point:
|
|
407
|
+
Point(ponto)
|
|
408
|
+
|
|
409
|
+
# Correr a lista de SNVs e determinar qual o mais próximo do ponto desejado
|
|
410
|
+
snv_gdf_id = closest_geometry(snv_gdf, ponto)
|
|
411
|
+
|
|
412
|
+
snv_gdf_id_geometry = snv_gdf.iloc[snv_gdf_id]['geometry']
|
|
413
|
+
|
|
414
|
+
# Calcular a distância ao longo do SNV encontrado até o ponto
|
|
415
|
+
dist = snv_gdf_id_geometry.project(ponto) # Procura a distancia até o ponto
|
|
416
|
+
nearest_point = snv_gdf_id_geometry.interpolate(dist) # Gera um ponto mais procimo a distancia
|
|
417
|
+
|
|
418
|
+
# Dividir a extensão proporcionalmente e obter a quilometragem (sem cálculo direto)
|
|
419
|
+
propini = snv_gdf_id_geometry.project(nearest_point, normalized=True) # Pega a proporção da distancia do snv_gdf até o ponto mais proximo
|
|
420
|
+
km = snv_gdf.loc[snv_gdf_id, 'km i']+(snv_gdf.loc[snv_gdf_id, 'ext']*propini) # Acha o KM com base na tabela multiplicado pela proporção encontrada até o ponto
|
|
421
|
+
|
|
422
|
+
return km,snv_gdf.loc[snv_gdf_id]['SNV']
|
|
423
|
+
|
|
424
|
+
def km_snv2(snv_gdf: gpd.GeoDataFrame, ponto, data: pd.DataFrame | None = None):
|
|
425
|
+
"""
|
|
426
|
+
Variante de ``km_snv`` que aceita um DataFrame auxiliar com nomes de
|
|
427
|
+
SNVs.
|
|
428
|
+
|
|
429
|
+
Parameters
|
|
430
|
+
----------
|
|
431
|
+
snv_gdf : geopandas.GeoDataFrame
|
|
432
|
+
SNVs, com colunas 'km i', 'ext', 'SNV' e geometry.
|
|
433
|
+
ponto : shapely.geometry.Point or tuple
|
|
434
|
+
Ponto de referencia (lon, lat) ou Point.
|
|
435
|
+
data : pandas.DataFrame, optional
|
|
436
|
+
Se informado, deve conter as colunas 'longitude', 'latitude' e
|
|
437
|
+
'name' para localizar o SNV pelo nome. Padrao None.
|
|
438
|
+
|
|
439
|
+
Returns
|
|
440
|
+
-------
|
|
441
|
+
tuple
|
|
442
|
+
(km, codigo_SNV).
|
|
443
|
+
|
|
444
|
+
"""
|
|
445
|
+
# Caso haja algum banco de dados que tenha o nome dos SNVs:
|
|
446
|
+
if data is not None:
|
|
447
|
+
for index2,linha in data.iterrows():
|
|
448
|
+
ponto = (linha['longitude'],linha['latitude'])
|
|
449
|
+
name = data.iloc[index2]["name"]
|
|
450
|
+
snv_gdf_id = snv_gdf.index[snv_gdf["SNV"] == name][0]
|
|
451
|
+
snv_gdf_id_geometry = snv_gdf.iloc[snv_gdf_id]['geometry']
|
|
452
|
+
|
|
453
|
+
# Calcular a distância ao longo do SNV encontrado até o ponto
|
|
454
|
+
dist = snv_gdf_id_geometry.project(ponto) # Procura a distancia até o ponto
|
|
455
|
+
nearest_point = snv_gdf_id_geometry.interpolate(dist) # Gera um ponto mais proximo a distancia
|
|
456
|
+
|
|
457
|
+
# Dividir a extensão proporcionalmente e obter a quilometragem (sem cálculo direto)
|
|
458
|
+
propini = snv_gdf_id_geometry.project(nearest_point, normalized=True) # Pega a proporção da distancia do snv_gdf até o ponto mais proximo
|
|
459
|
+
km = snv_gdf.loc[snv_gdf_id, 'km i']+(snv_gdf.loc[snv_gdf_id, 'ext']*propini) # Acha o KM com base na tabela multiplicado pela proporção encontrada até o ponto
|
|
460
|
+
|
|
461
|
+
# Caso contrario:
|
|
462
|
+
else:
|
|
463
|
+
#Transformar coordenadas para ponto
|
|
464
|
+
if type(ponto) != shapely.geometry.point.Point:
|
|
465
|
+
ponto = Point(ponto)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
# Correr a lista de SNVs e determinar qual o mais próximo do ponto desejado
|
|
469
|
+
snv_gdf_id = closest_geometry(snv_gdf, ponto)
|
|
470
|
+
|
|
471
|
+
snv_gdf_id_geometry = snv_gdf.iloc[snv_gdf_id]['geometry']
|
|
472
|
+
|
|
473
|
+
# Calcular a distância ao longo do SNV encontrado até o ponto
|
|
474
|
+
dist = snv_gdf_id_geometry.project(ponto) # Procura a distancia até o ponto
|
|
475
|
+
nearest_point = snv_gdf_id_geometry.interpolate(dist) # Gera um ponto mais proximo a distancia
|
|
476
|
+
|
|
477
|
+
# Dividir a extensão proporcionalmente e obter a quilometragem (sem cálculo direto)
|
|
478
|
+
propini = snv_gdf_id_geometry.project(nearest_point, normalized=True) # Pega a proporção da distancia do snv_gdf até o ponto mais proximo
|
|
479
|
+
km = snv_gdf.loc[snv_gdf_id, 'km i']+(snv_gdf.loc[snv_gdf_id, 'ext']*propini) # Acha o KM com base na tabela multiplicado pela proporção encontrada até o ponto
|
|
480
|
+
|
|
481
|
+
return km,snv_gdf.loc[snv_gdf_id]['SNV']
|
|
482
|
+
|
|
483
|
+
# ----- Shapely ------
|
|
484
|
+
def cut_linestring(km: float, line: LineString) -> tuple[LineString, LineString]:
|
|
485
|
+
"""
|
|
486
|
+
Corta um alinhamento em um km especifico, gerando duas linhas.
|
|
487
|
+
|
|
488
|
+
Parameters
|
|
489
|
+
----------
|
|
490
|
+
km : float
|
|
491
|
+
Quilometragem onde cortar (a partir do inicio da linha).
|
|
492
|
+
line : shapely.geometry.LineString
|
|
493
|
+
Alinhamento a cortar.
|
|
494
|
+
|
|
495
|
+
Returns
|
|
496
|
+
-------
|
|
497
|
+
tuple of shapely.geometry.LineString
|
|
498
|
+
(lin1, lin2), onde lin1 + lin2 reconstroem a linha original e o
|
|
499
|
+
ponto de divisao e o km informado.
|
|
500
|
+
"""
|
|
501
|
+
comprimento = line.length
|
|
502
|
+
m = km * 1000
|
|
503
|
+
corte = line.interpolate(m)
|
|
504
|
+
projecao = line.project(corte)
|
|
505
|
+
|
|
506
|
+
lin1 = substring(line, 0, projecao)
|
|
507
|
+
lin2 = substring(line, projecao, comprimento)
|
|
508
|
+
|
|
509
|
+
return lin1, lin2 # onde lin1+lin2 é line, e o ponto de divisão é o km informado
|
|
510
|
+
|
|
511
|
+
def cut_linestring_with_points(aln_gdf: gpd.GeoDataFrame, rod, ponto_a, ponto_b):
|
|
512
|
+
"""
|
|
513
|
+
Recorta um alinhamento entre dois pontos.
|
|
514
|
+
|
|
515
|
+
Converte cada ponto no km correspondente e delega o corte para
|
|
516
|
+
``cortar``.
|
|
517
|
+
|
|
518
|
+
Parameters
|
|
519
|
+
----------
|
|
520
|
+
aln_gdf : geopandas.GeoDataFrame
|
|
521
|
+
Alinhamentos, com coluna ROD e geometry.
|
|
522
|
+
rod : str
|
|
523
|
+
Rodovia a filtrar.
|
|
524
|
+
ponto_a : shapely.geometry.Point or tuple
|
|
525
|
+
Ponto inicial do corte.
|
|
526
|
+
ponto_b : shapely.geometry.Point or tuple
|
|
527
|
+
Ponto final do corte.
|
|
528
|
+
|
|
529
|
+
Returns
|
|
530
|
+
-------
|
|
531
|
+
shapely.geometry.LineString
|
|
532
|
+
Trecho recortado entre os dois pontos.
|
|
533
|
+
"""
|
|
534
|
+
# Filtrar dataframe para rodovia desejada (ordenando para o caso de não estar ordenado)
|
|
535
|
+
line_gdf = aln_gdf[aln_gdf.ROD == rod]
|
|
536
|
+
|
|
537
|
+
# Ajustar ordem (possível apenas por ser a mesma rodovia)
|
|
538
|
+
km_a = point_to_km(line_gdf, ponto_a, rod)[0][1]
|
|
539
|
+
km_b = point_to_km(line_gdf, ponto_b, rod)[0][1]
|
|
540
|
+
|
|
541
|
+
# Gerar resultado
|
|
542
|
+
return cut_linestring(line_gdf, rod, km_a, km_b)
|
|
543
|
+
|
|
544
|
+
# ----- Randoms -----
|
|
545
|
+
def gms2gd(s: str) -> float:
|
|
546
|
+
"""
|
|
547
|
+
Converte coordenada de graus-minutos-segundos para graus decimais.
|
|
548
|
+
|
|
549
|
+
Parameters
|
|
550
|
+
----------
|
|
551
|
+
s : str
|
|
552
|
+
Coordenada em GMS, ex.: ``"0°51\'56.29\"S"``. Aceita virgula
|
|
553
|
+
como separador decimal.
|
|
554
|
+
|
|
555
|
+
Returns
|
|
556
|
+
-------
|
|
557
|
+
float
|
|
558
|
+
Valor em graus decimais (negativo para direcoes S, W ou O).
|
|
559
|
+
"""
|
|
560
|
+
s = s.replace(" ","")
|
|
561
|
+
|
|
562
|
+
if "," in s:
|
|
563
|
+
s = s.replace(",",".")
|
|
564
|
+
# example: s = "0°51'56.29"S"
|
|
565
|
+
degrees, minutes, seconds, direction = re.split('[°\'"]+', s)
|
|
566
|
+
dd = float(degrees) + float(minutes)/60 + float(seconds)/(60*60);
|
|
567
|
+
if direction in ('S','W', "O"):
|
|
568
|
+
dd *= -1
|
|
569
|
+
return dd
|
infrapy/interface.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""interface.py - Interfaces gráficas."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import tkinter as tk
|
|
5
|
+
from tkinter import filedialog
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def tkinter_select_file() -> str | None:
|
|
9
|
+
"""
|
|
10
|
+
Abre uma janela para seleção de um arquivo.
|
|
11
|
+
|
|
12
|
+
Returns
|
|
13
|
+
-------
|
|
14
|
+
str | None
|
|
15
|
+
Caminho do arquivo selecionado. Retorna ``None`` caso o usuário
|
|
16
|
+
cancele a seleção.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
root = tk.Tk()
|
|
20
|
+
root.withdraw()
|
|
21
|
+
root.attributes("-topmost", True)
|
|
22
|
+
|
|
23
|
+
file_path = filedialog.askopenfilename()
|
|
24
|
+
|
|
25
|
+
root.destroy()
|
|
26
|
+
|
|
27
|
+
if not file_path:
|
|
28
|
+
raise "Usuário não escolheu um arquivo."
|
|
29
|
+
|
|
30
|
+
return file_path
|
infrapy/mapas.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""mapas.py - geracao de mapas interativos com Folium."""
|
|
2
|
+
|
|
3
|
+
if True: # Bibliotecas
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import geopandas as gpd
|
|
7
|
+
import folium
|
|
8
|
+
|
|
9
|
+
from .cores import cor_aleatoria, cor_aleatoria_variada, cor_aleatoria_predef
|
|
10
|
+
|
|
11
|
+
if True: # Funcoes
|
|
12
|
+
|
|
13
|
+
def mapa_gdf(*gdfs: gpd.GeoDataFrame) -> folium.Map:
|
|
14
|
+
"""
|
|
15
|
+
Gera um mapa Folium a partir de um ou mais GeoDataFrames.
|
|
16
|
+
|
|
17
|
+
Cada GeoDataFrame e desenhado com uma cor aleatoria e tooltips
|
|
18
|
+
contendo todas as colunas (exceto geometry). O mapa usa imagem de
|
|
19
|
+
satelite (Esri World Imagery) como base.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
*gdfs : geopandas.GeoDataFrame
|
|
24
|
+
Um ou mais GeoDataFrames a plotar.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
folium.Map
|
|
29
|
+
Mapa pronto para exibicao no notebook.
|
|
30
|
+
|
|
31
|
+
Examples
|
|
32
|
+
--------
|
|
33
|
+
>>> mapa_gdf(aln_gdf)
|
|
34
|
+
>>> mapa_gdf(aln_gdf, snv_gdf)
|
|
35
|
+
"""
|
|
36
|
+
map = folium.Map(location=[-15.83, -47.86], width='100%', height='100%', left='0%', top='0%', position='relative',
|
|
37
|
+
tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
|
38
|
+
attr='Esri', min_zoom=0, max_zoom=18, zoom_start=4, min_lat=-90, max_lat=90, min_lon=-180, max_lon=180,
|
|
39
|
+
max_bounds=False, control_scale=False, prefer_canvas=False, no_touch=False, disable_3d=False,
|
|
40
|
+
png_enabled=False, zoom_control=True)
|
|
41
|
+
|
|
42
|
+
for gdf in gdfs:
|
|
43
|
+
JSON = json.loads(gdf.to_json())
|
|
44
|
+
colunas = [coluna for coluna in gdf.columns if coluna != 'geometry']
|
|
45
|
+
folium.GeoJson(
|
|
46
|
+
JSON,
|
|
47
|
+
tooltip=folium.features.GeoJsonTooltip(fields=colunas, labels=True),
|
|
48
|
+
style_function=lambda x: {'color': cor_aleatoria_variada()}
|
|
49
|
+
).add_to(map)
|
|
50
|
+
|
|
51
|
+
return map
|
|
52
|
+
|
|
53
|
+
def mapa_shapely(shapely_geometries: list, epsg) -> folium.Map:
|
|
54
|
+
"""
|
|
55
|
+
Gera um mapa Folium a partir de uma lista de geometrias Shapely.
|
|
56
|
+
|
|
57
|
+
As geometrias sao convertidas para WGS84 e desenhadas com cores
|
|
58
|
+
aleatorias, cada uma com um popup identificando sua ordem.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
shapely_geometries : list of shapely geometry
|
|
63
|
+
Geometrias a plotar.
|
|
64
|
+
epsg : int or str
|
|
65
|
+
CRS de origem das geometrias (ex.: 4326 ou "EPSG:31983").
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
folium.Map
|
|
70
|
+
Mapa pronto para exibicao no notebook.
|
|
71
|
+
"""
|
|
72
|
+
gdf = gpd.GeoDataFrame(geometry=shapely_geometries, crs=epsg)
|
|
73
|
+
if gdf.crs.to_string() != 'EPSG:4326':
|
|
74
|
+
gdf = gdf.to_crs(epsg=4326)
|
|
75
|
+
|
|
76
|
+
m = folium.Map(location=[-15.83, -47.86], width='100%', height='100%', left='0%', top='0%', position='relative', tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attr='Esri', min_zoom=0, max_zoom=18, zoom_start=4, min_lat=-90, max_lat=90, min_lon=-180, max_lon=180, max_bounds=False, control_scale=False, prefer_canvas=False, no_touch=False, disable_3d=False, png_enabled=False, zoom_control=True)
|
|
77
|
+
|
|
78
|
+
for idx, geometry in enumerate(gdf['geometry']):
|
|
79
|
+
cor = cor_aleatoria_variada()
|
|
80
|
+
nome_geometria = f'Geometria {idx + 1}'
|
|
81
|
+
popup = folium.Popup(nome_geometria)
|
|
82
|
+
style_function = lambda x, cor=cor: {'fillColor': cor, 'color': cor}
|
|
83
|
+
folium.GeoJson(geometry, style_function=style_function, popup=popup).add_to(m)
|
|
84
|
+
|
|
85
|
+
return m
|
infrapy/sharepoint.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""sharepoint.py - integracao com SharePoint / Office365.
|
|
2
|
+
|
|
3
|
+
Credenciais NAO ficam no codigo: sao lidas do .env via infrapy.config.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
if True: # Bibliotecas
|
|
7
|
+
import os
|
|
8
|
+
import tempfile
|
|
9
|
+
|
|
10
|
+
import xlwings as xw
|
|
11
|
+
|
|
12
|
+
from office365.runtime.auth.authentication_context import AuthenticationContext
|
|
13
|
+
from office365.runtime.auth.client_credential import ClientCredential
|
|
14
|
+
from office365.runtime.auth.user_credential import UserCredential
|
|
15
|
+
from office365.sharepoint.client_context import ClientContext
|
|
16
|
+
from office365.sharepoint.files.file import File
|
|
17
|
+
from office365.sharepoint.folders.folder import Folder
|
|
18
|
+
from office365.runtime.auth.token_response import TokenResponse
|
|
19
|
+
from office365.sharepoint.files.system_object_type import FileSystemObjectType
|
|
20
|
+
from office365.sharepoint.listitems.listitem import ListItem
|
|
21
|
+
|
|
22
|
+
import msal
|
|
23
|
+
|
|
24
|
+
from . import config
|
|
25
|
+
|
|
26
|
+
if True: # Funcoes
|
|
27
|
+
|
|
28
|
+
def _contexto() -> ClientContext:
|
|
29
|
+
"""
|
|
30
|
+
Cria um ClientContext autenticado com as credenciais do .env.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
office365.sharepoint.client_context.ClientContext
|
|
35
|
+
Contexto pronto para consultas ao SharePoint.
|
|
36
|
+
"""
|
|
37
|
+
site = config.SHAREPOINT_SITE()
|
|
38
|
+
cert = config.SHAREPOINT_CERTIFICATE()
|
|
39
|
+
|
|
40
|
+
return ClientContext(site).with_client_certificate(**cert)
|
|
41
|
+
|
|
42
|
+
def buscar_arquivos_recursivamente(ctx: ClientContext, directory_url: str):
|
|
43
|
+
"""
|
|
44
|
+
Gera (yield) todos os arquivos de uma pasta e suas subpastas.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
ctx : office365.sharepoint.client_context.ClientContext
|
|
49
|
+
Contexto autenticado do SharePoint.
|
|
50
|
+
directory_url : str
|
|
51
|
+
URL relativa da pasta raiz da busca.
|
|
52
|
+
|
|
53
|
+
Yields
|
|
54
|
+
------
|
|
55
|
+
office365.sharepoint.files.file.File
|
|
56
|
+
Cada arquivo encontrado, recursivamente.
|
|
57
|
+
"""
|
|
58
|
+
folder = ctx.web.get_folder_by_server_relative_url(directory_url)
|
|
59
|
+
ctx.load(folder)
|
|
60
|
+
ctx.execute_query()
|
|
61
|
+
|
|
62
|
+
# Buscar arquivos na pasta atual
|
|
63
|
+
files = folder.files
|
|
64
|
+
ctx.load(files)
|
|
65
|
+
ctx.execute_query()
|
|
66
|
+
|
|
67
|
+
for file in files:
|
|
68
|
+
yield file
|
|
69
|
+
|
|
70
|
+
# Buscar arquivos nas subpastas
|
|
71
|
+
folders = folder.folders
|
|
72
|
+
ctx.load(folders)
|
|
73
|
+
ctx.execute_query()
|
|
74
|
+
|
|
75
|
+
for subfolder in folders:
|
|
76
|
+
yield from buscar_arquivos_recursivamente(ctx, subfolder.properties['ServerRelativeUrl'])
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: infrapy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Biblioteca de funcoes de engenharia civil / rodovias (SNV, alinhamentos, geoprocessamento, Excel, SharePoint)
|
|
5
|
+
Author: Eduardo Abel
|
|
6
|
+
Keywords: engenharia civil,excel,geoprocessamento,rodovias,sharepoint
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: easygui
|
|
9
|
+
Requires-Dist: folium
|
|
10
|
+
Requires-Dist: geopandas
|
|
11
|
+
Requires-Dist: ipython
|
|
12
|
+
Requires-Dist: mapclassify
|
|
13
|
+
Requires-Dist: matplotlib
|
|
14
|
+
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: office365-rest-python-client
|
|
16
|
+
Requires-Dist: openpyxl
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: pyproj
|
|
19
|
+
Requires-Dist: python-dotenv
|
|
20
|
+
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: rtree
|
|
22
|
+
Requires-Dist: scipy
|
|
23
|
+
Requires-Dist: shapely
|
|
24
|
+
Requires-Dist: simplekml
|
|
25
|
+
Requires-Dist: utm
|
|
26
|
+
Requires-Dist: xlsxwriter
|
|
27
|
+
Requires-Dist: xlwings
|
|
28
|
+
Requires-Dist: xyzservices
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# InfraPy
|
|
32
|
+
|
|
33
|
+
Biblioteca de funcoes de engenharia / rodovias: leitura de shapefiles,
|
|
34
|
+
calculo de km em alinhamentos e SNV, cortes, leitura de Excel (xlwings),
|
|
35
|
+
geracao de mapas Folium, integracao com SharePoint.
|
|
36
|
+
|
|
37
|
+
## Instalacao (na sua maquina, modo editavel)
|
|
38
|
+
|
|
39
|
+
Dentro da pasta do projeto (onde esta o `pyproject.toml`):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install -e .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
O `-e` aponta para o codigo-fonte: qualquer alteracao em `src/infrapy/*.py`
|
|
46
|
+
vale na hora (basta reiniciar o kernel), sem reinstalar.
|
|
47
|
+
|
|
48
|
+
## Instalacao em outra maquina (via GitHub)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install git+https://github.com/SrBlacky/InfraPy.git
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configuracao (.env)
|
|
55
|
+
|
|
56
|
+
Segredos e caminhos pessoais NAO ficam no codigo. Eles vem de um arquivo
|
|
57
|
+
`.env` fora do repositorio.
|
|
58
|
+
|
|
59
|
+
1. Copie `.env.example` para um `.env` numa pasta segura (fora do OneDrive,
|
|
60
|
+
ex.: `C:\Users\SEU_USUARIO\.infrapy\.env`).
|
|
61
|
+
2. Preencha os valores.
|
|
62
|
+
3. Ajuste o caminho do `.env` em `src/infrapy/__init__.py` (variavel
|
|
63
|
+
`_ENV_PATH`) OU defina a variavel de ambiente do Windows `INFRAPY_ENV`
|
|
64
|
+
apontando para o arquivo.
|
|
65
|
+
|
|
66
|
+
## Uso
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from infrapy import read_shape, ler_excel, ponto_para_km
|
|
70
|
+
|
|
71
|
+
aln = read_shape() # usa ALN_PADRAO do .env
|
|
72
|
+
ws, df = ler_excel("dados.xlsx", "Plan1", "A1")
|
|
73
|
+
km = ponto_para_km(aln, (-47.9, -15.8))
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Ou por modulo:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from infrapy.geo import cortar, km_para_ponto
|
|
80
|
+
from infrapy.sharepoint import abrir_mef
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Estrutura
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
InfraPy/
|
|
87
|
+
├── pyproject.toml identidade e dependencias do pacote
|
|
88
|
+
├── .env.example modelo de configuracao (versionavel)
|
|
89
|
+
├── .gitignore
|
|
90
|
+
└── src/
|
|
91
|
+
└── infrapy/
|
|
92
|
+
├── __init__.py carrega o .env e expoe atalhos
|
|
93
|
+
├── config.py le variaveis do ambiente
|
|
94
|
+
├── geo.py geoprocessamento (o grosso das funcoes)
|
|
95
|
+
├── excel.py leitura/escrita de Excel
|
|
96
|
+
├── export.py exportar shp / json->shp
|
|
97
|
+
├── mapas.py mapas Folium
|
|
98
|
+
├── cores.py cores aleatorias
|
|
99
|
+
└── sharepoint.py integracao SharePoint
|
|
100
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
infrapy/__init__.py,sha256=oABTbhSRLkGnlND4R7ICMHma1fabaVuU8qO93l9los4,229
|
|
2
|
+
infrapy/config.py,sha256=MNGQwJLnfvFeJw6C_QinlcaT8Idiw7MK8Wx5o295NXA,4592
|
|
3
|
+
infrapy/cores.py,sha256=KyaC2oRBLfGha03JOUQdqvWeJi2D0dqzlln3P2o6B04,4236
|
|
4
|
+
infrapy/excel.py,sha256=pdLxRsKM76apmLn6EA2VrRJddlMflbx5uvBgE497l3Q,4930
|
|
5
|
+
infrapy/export.py,sha256=4OPlVFfA_GZTNZeUuZSJguWhuEtiFafuO1dTLYbqIzI,1192
|
|
6
|
+
infrapy/geo.py,sha256=tQ_7U7mHTo7gIKOfzWS108FDiBq45nKMcmdf0YTv8Ks,20287
|
|
7
|
+
infrapy/interface.py,sha256=-xgh_K1-8d02cKevujKSTm98IZ74FjoWdFqOFMxZtFA,664
|
|
8
|
+
infrapy/mapas.py,sha256=3q1VEKNDVYfHwFIUqWK2PJcUvOSBcd-uS_q63EoGkuQ,3654
|
|
9
|
+
infrapy/sharepoint.py,sha256=U1nntfrp9wluHTc3cwUPpRXNloZXjVbbDo8Frw6gfec,2531
|
|
10
|
+
infrapy-0.1.0.dist-info/METADATA,sha256=Qqwnn_aQTgBwZG2pFW7RZEHRR7dzaiDSvdUfz2DCALU,2932
|
|
11
|
+
infrapy-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
12
|
+
infrapy-0.1.0.dist-info/RECORD,,
|