Joshua Sundance Bailey
commited on
Commit
•
4b5e485
0
Parent(s):
initial commit
Browse files- .dockerignore +14 -0
- .github/ISSUE_TEMPLATE/bug_report.md +38 -0
- .github/ISSUE_TEMPLATE/feature_request.md +17 -0
- .github/dependabot.yml +11 -0
- .github/pull_request_template.md +12 -0
- .gitignore +91 -0
- .pre-commit-config.yaml +63 -0
- Dockerfile +20 -0
- LICENSE +9 -0
- README.md +0 -0
- bumpver.toml +18 -0
- docker-compose.yml +15 -0
- geospatial-data-converter/.streamlit/config.toml +6 -0
- geospatial-data-converter/app.py +66 -0
- geospatial-data-converter/utils.py +66 -0
- kubernetes/deploy.sh +6 -0
- kubernetes/resources.yaml +61 -0
- requirements.txt +3 -0
.dockerignore
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.env
|
2 |
+
.env-example
|
3 |
+
.git/
|
4 |
+
.github
|
5 |
+
.gitignore
|
6 |
+
.idea
|
7 |
+
.mypy_cache
|
8 |
+
.pre-commit-config.yaml
|
9 |
+
.ruff_cache
|
10 |
+
Dockerfile
|
11 |
+
kubernetes
|
12 |
+
docker-compose.yml
|
13 |
+
junk/
|
14 |
+
kubernetes/
|
.github/ISSUE_TEMPLATE/bug_report.md
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Bug report
|
3 |
+
about: Create a report to help us improve
|
4 |
+
title: ''
|
5 |
+
labels: bug
|
6 |
+
assignees: ''
|
7 |
+
|
8 |
+
---
|
9 |
+
|
10 |
+
**Describe the bug**
|
11 |
+
A clear and concise description of what the bug is.
|
12 |
+
|
13 |
+
**To Reproduce**
|
14 |
+
Steps to reproduce the behavior:
|
15 |
+
1. Go to '...'
|
16 |
+
2. Click on '....'
|
17 |
+
3. Scroll down to '....'
|
18 |
+
4. See error
|
19 |
+
|
20 |
+
**Expected behavior**
|
21 |
+
A clear and concise description of what you expected to happen.
|
22 |
+
|
23 |
+
**Screenshots**
|
24 |
+
If applicable, add screenshots to help explain your problem.
|
25 |
+
|
26 |
+
**Desktop (please complete the following information):**
|
27 |
+
- OS: [e.g. iOS]
|
28 |
+
- Browser [e.g. chrome, safari]
|
29 |
+
- Version [e.g. 22]
|
30 |
+
|
31 |
+
**Smartphone (please complete the following information):**
|
32 |
+
- Device: [e.g. iPhone6]
|
33 |
+
- OS: [e.g. iOS8.1]
|
34 |
+
- Browser [e.g. stock browser, safari]
|
35 |
+
- Version [e.g. 22]
|
36 |
+
|
37 |
+
**Additional context**
|
38 |
+
Add any other context about the problem here.
|
.github/ISSUE_TEMPLATE/feature_request.md
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Feature request
|
3 |
+
about: Suggest an idea for this project
|
4 |
+
title: ''
|
5 |
+
labels: enhancement
|
6 |
+
assignees: ''
|
7 |
+
|
8 |
+
---
|
9 |
+
|
10 |
+
**Describe the solution you'd like**
|
11 |
+
A clear and concise description of what you want to happen.
|
12 |
+
|
13 |
+
**Describe alternatives you've considered**
|
14 |
+
A clear and concise description of any alternative solutions or features you've considered.
|
15 |
+
|
16 |
+
**Additional context**
|
17 |
+
Add any other context or screenshots about the feature request here.
|
.github/dependabot.yml
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# To get started with Dependabot version updates, you'll need to specify which
|
2 |
+
# package ecosystems to update and where the package manifests are located.
|
3 |
+
# Please see the documentation for all configuration options:
|
4 |
+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5 |
+
|
6 |
+
version: 2
|
7 |
+
updates:
|
8 |
+
- package-ecosystem: "pip" # See documentation for possible values
|
9 |
+
directory: "/" # Location of package manifests
|
10 |
+
schedule:
|
11 |
+
interval: "weekly"
|
.github/pull_request_template.md
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Thank you for contributing!
|
2 |
+
Before submitting this PR, please make sure:
|
3 |
+
|
4 |
+
- [ ] Your code builds clean without any errors or warnings
|
5 |
+
- [ ] Your code doesn't break anything we can't fix
|
6 |
+
- [ ] You have added appropriate tests
|
7 |
+
|
8 |
+
Please check one or more of the following to describe the nature of this PR:
|
9 |
+
- [ ] New feature
|
10 |
+
- [ ] Bug fix
|
11 |
+
- [ ] Documentation
|
12 |
+
- [ ] Other
|
.gitignore
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*$py.class
|
2 |
+
*.chainlit
|
3 |
+
*.chroma
|
4 |
+
*.cover
|
5 |
+
*.egg
|
6 |
+
*.egg-info/
|
7 |
+
*.env
|
8 |
+
*.langchain.db
|
9 |
+
*.log
|
10 |
+
*.manifest
|
11 |
+
*.mo
|
12 |
+
*.pot
|
13 |
+
*.py,cover
|
14 |
+
*.py[cod]
|
15 |
+
*.sage.py
|
16 |
+
*.so
|
17 |
+
*.spec
|
18 |
+
.DS_STORE
|
19 |
+
.Python
|
20 |
+
.cache
|
21 |
+
.coverage
|
22 |
+
.coverage.*
|
23 |
+
.dmypy.json
|
24 |
+
.eggs/
|
25 |
+
.env
|
26 |
+
.hypothesis/
|
27 |
+
.idea/
|
28 |
+
.installed.cfg
|
29 |
+
.ipynb_checkpoints
|
30 |
+
.mypy_cache/
|
31 |
+
.nox/
|
32 |
+
.pyre/
|
33 |
+
.pytest_cache/
|
34 |
+
.python-version
|
35 |
+
.ropeproject
|
36 |
+
.ruff_cache/
|
37 |
+
.scrapy
|
38 |
+
.spyderproject
|
39 |
+
.spyproject
|
40 |
+
.tox/
|
41 |
+
.venv
|
42 |
+
.vscode
|
43 |
+
.webassets-cache
|
44 |
+
/site
|
45 |
+
ENV/
|
46 |
+
MANIFEST
|
47 |
+
__pycache__
|
48 |
+
__pycache__/
|
49 |
+
__pypackages__/
|
50 |
+
build/
|
51 |
+
celerybeat-schedule
|
52 |
+
celerybeat.pid
|
53 |
+
coverage.xml
|
54 |
+
credentials.json
|
55 |
+
data/
|
56 |
+
db.sqlite3
|
57 |
+
db.sqlite3-journal
|
58 |
+
develop-eggs/
|
59 |
+
dist/
|
60 |
+
dmypy.json
|
61 |
+
docs/_build/
|
62 |
+
downloads/
|
63 |
+
eggs/
|
64 |
+
env.bak/
|
65 |
+
env/
|
66 |
+
fly.toml
|
67 |
+
htmlcov/
|
68 |
+
instance/
|
69 |
+
ipython_config.py
|
70 |
+
junk/
|
71 |
+
lib/
|
72 |
+
lib64/
|
73 |
+
local_settings.py
|
74 |
+
models/*.bin
|
75 |
+
nosetests.xml
|
76 |
+
notebooks/scratch/
|
77 |
+
parts/
|
78 |
+
pip-delete-this-directory.txt
|
79 |
+
pip-log.txt
|
80 |
+
pip-wheel-metadata/
|
81 |
+
profile_default/
|
82 |
+
sdist/
|
83 |
+
share/python-wheels/
|
84 |
+
storage
|
85 |
+
target/
|
86 |
+
token.json
|
87 |
+
var/
|
88 |
+
venv
|
89 |
+
venv.bak/
|
90 |
+
venv/
|
91 |
+
wheels/
|
.pre-commit-config.yaml
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Don't know what this file is? See https://pre-commit.com/
|
2 |
+
# pip install pre-commit
|
3 |
+
# pre-commit install
|
4 |
+
# pre-commit autoupdate
|
5 |
+
# Apply to all files without commiting:
|
6 |
+
# pre-commit run --all-files
|
7 |
+
# I recommend running this until you pass all checks, and then commit.
|
8 |
+
# Fix what you need to and then let the pre-commit hooks resolve their conflicts.
|
9 |
+
# You may need to git add -u between runs.
|
10 |
+
exclude: "AI_CHANGELOG.md"
|
11 |
+
repos:
|
12 |
+
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
13 |
+
rev: "v0.0.292"
|
14 |
+
hooks:
|
15 |
+
- id: ruff
|
16 |
+
args: [--fix, --exit-non-zero-on-fix, --ignore, E501]
|
17 |
+
- repo: https://github.com/koalaman/shellcheck-precommit
|
18 |
+
rev: v0.9.0
|
19 |
+
hooks:
|
20 |
+
- id: shellcheck
|
21 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
22 |
+
rev: v4.5.0
|
23 |
+
hooks:
|
24 |
+
- id: check-ast
|
25 |
+
- id: check-builtin-literals
|
26 |
+
- id: check-merge-conflict
|
27 |
+
- id: check-symlinks
|
28 |
+
- id: check-toml
|
29 |
+
- id: check-xml
|
30 |
+
- id: debug-statements
|
31 |
+
- id: check-case-conflict
|
32 |
+
- id: check-docstring-first
|
33 |
+
- id: check-executables-have-shebangs
|
34 |
+
- id: check-json
|
35 |
+
# - id: check-yaml
|
36 |
+
- id: debug-statements
|
37 |
+
- id: fix-byte-order-marker
|
38 |
+
- id: detect-private-key
|
39 |
+
- id: end-of-file-fixer
|
40 |
+
- id: trailing-whitespace
|
41 |
+
- id: mixed-line-ending
|
42 |
+
- id: requirements-txt-fixer
|
43 |
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
44 |
+
rev: v1.6.0
|
45 |
+
hooks:
|
46 |
+
- id: mypy
|
47 |
+
- repo: https://github.com/asottile/add-trailing-comma
|
48 |
+
rev: v3.1.0
|
49 |
+
hooks:
|
50 |
+
- id: add-trailing-comma
|
51 |
+
- repo: https://github.com/dannysepler/rm_unneeded_f_str
|
52 |
+
rev: v0.2.0
|
53 |
+
hooks:
|
54 |
+
- id: rm-unneeded-f-str
|
55 |
+
- repo: https://github.com/psf/black
|
56 |
+
rev: 23.9.1
|
57 |
+
hooks:
|
58 |
+
- id: black
|
59 |
+
- repo: https://github.com/PyCQA/bandit
|
60 |
+
rev: 1.7.5
|
61 |
+
hooks:
|
62 |
+
- id: bandit
|
63 |
+
args: ["-x", "tests/*.py"]
|
Dockerfile
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11-slim-bookworm
|
2 |
+
|
3 |
+
RUN adduser --uid 1000 --disabled-password --gecos '' appuser
|
4 |
+
USER 1000
|
5 |
+
|
6 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
7 |
+
PYTHONUNBUFFERED=1 \
|
8 |
+
PATH="/home/appuser/.local/bin:$PATH"
|
9 |
+
|
10 |
+
RUN pip install --user --no-cache-dir --upgrade pip
|
11 |
+
COPY ./requirements.txt /home/appuser/requirements.txt
|
12 |
+
RUN pip install --user --no-cache-dir --upgrade -r /home/appuser/requirements.txt
|
13 |
+
|
14 |
+
COPY geospatial-data-converter/ /home/appuser/geospatial-data-converter/
|
15 |
+
|
16 |
+
WORKDIR /workspace
|
17 |
+
EXPOSE 7860
|
18 |
+
|
19 |
+
CMD ["streamlit", "run", "/home/appuser/geospatial-data-converter/app.py", "--server.port", "7860", "--server.address", "0.0.0.0"]
|
20 |
+
#CMD ["/bin/bash"]
|
LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2023 Joshua Sundance Bailey
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6 |
+
|
7 |
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8 |
+
|
9 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
README.md
ADDED
File without changes
|
bumpver.toml
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[bumpver]
|
2 |
+
current_version = "0.0.1"
|
3 |
+
version_pattern = "MAJOR.MINOR.PATCH"
|
4 |
+
commit_message = "bump version {old_version} -> {new_version}"
|
5 |
+
tag_message = "{new_version}"
|
6 |
+
tag_scope = "default"
|
7 |
+
pre_commit_hook = ""
|
8 |
+
post_commit_hook = ""
|
9 |
+
commit = true
|
10 |
+
tag = true
|
11 |
+
push = true
|
12 |
+
|
13 |
+
[bumpver.file_patterns]
|
14 |
+
"bumpver.toml" = [
|
15 |
+
'current_version = "{version}"',
|
16 |
+
]
|
17 |
+
"geospatial-data-converter/app.py" = ['__version__ = "{version}"']
|
18 |
+
"kubernetes/resources.yaml" = [' image: joshuasundance/geospatial-data-converter:{version}']
|
docker-compose.yml
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '3.8'
|
2 |
+
|
3 |
+
services:
|
4 |
+
geospatial-data-converter:
|
5 |
+
image: geospatial-data-converter:latest
|
6 |
+
build: .
|
7 |
+
ports:
|
8 |
+
- "${APP_PORT:-7860}:${APP_PORT:-7860}"
|
9 |
+
working_dir: /workspace
|
10 |
+
command: [
|
11 |
+
"streamlit", "run",
|
12 |
+
"/home/appuser/geospatial-data-converter/app.py",
|
13 |
+
"--server.port", "${APP_PORT:-7860}",
|
14 |
+
"--server.address", "0.0.0.0"
|
15 |
+
]
|
geospatial-data-converter/.streamlit/config.toml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[theme]
|
2 |
+
primaryColor="#F63366"
|
3 |
+
backgroundColor="#FFFFFF"
|
4 |
+
secondaryBackgroundColor="#F0F2F6"
|
5 |
+
textColor="#262730"
|
6 |
+
font="sans serif"
|
geospatial-data-converter/app.py
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
import streamlit as st
|
4 |
+
|
5 |
+
from utils import read_file, convert, output_format_dict
|
6 |
+
|
7 |
+
__version__ = "0.0.1"
|
8 |
+
|
9 |
+
# --- Initialization ---
|
10 |
+
st.set_page_config(
|
11 |
+
page_title=f"geospatial-data-converter v{__version__}",
|
12 |
+
page_icon="🌎",
|
13 |
+
)
|
14 |
+
|
15 |
+
|
16 |
+
# Upload the file
|
17 |
+
st.file_uploader(
|
18 |
+
"Choose a geospatial file",
|
19 |
+
key="uploaded_file",
|
20 |
+
type=["kml", "kmz", "geojson", "zip"],
|
21 |
+
)
|
22 |
+
|
23 |
+
|
24 |
+
if st.session_state.uploaded_file is not None:
|
25 |
+
fn_without_extension, _ = os.path.splitext(
|
26 |
+
os.path.basename(st.session_state.uploaded_file.name),
|
27 |
+
)
|
28 |
+
|
29 |
+
st.session_state.gdf = read_file(st.session_state.uploaded_file)
|
30 |
+
|
31 |
+
st.selectbox(
|
32 |
+
"Select output format",
|
33 |
+
output_format_dict.keys(),
|
34 |
+
key="output_format",
|
35 |
+
index=0,
|
36 |
+
)
|
37 |
+
|
38 |
+
if st.button("Convert"):
|
39 |
+
file_ext, dl_ext, mimetype = output_format_dict[st.session_state.output_format]
|
40 |
+
output_fn = f"{fn_without_extension}.{file_ext}"
|
41 |
+
dl_fn = f"{fn_without_extension}.{dl_ext}"
|
42 |
+
|
43 |
+
st.session_state.converted_data = convert(
|
44 |
+
gdf=st.session_state.gdf,
|
45 |
+
output_name=output_fn,
|
46 |
+
output_format=st.session_state.output_format,
|
47 |
+
)
|
48 |
+
|
49 |
+
st.download_button(
|
50 |
+
label="Download",
|
51 |
+
data=st.session_state.converted_data,
|
52 |
+
file_name=dl_fn,
|
53 |
+
mime=mimetype,
|
54 |
+
)
|
55 |
+
|
56 |
+
st.markdown(
|
57 |
+
"---\n"
|
58 |
+
f"## {fn_without_extension}\n"
|
59 |
+
f"### CRS: *{st.session_state.gdf.crs}*\n"
|
60 |
+
f"### Shape: *{st.session_state.gdf.shape}*\n"
|
61 |
+
"*(geometry omitted for display purposes)*",
|
62 |
+
)
|
63 |
+
|
64 |
+
display_df = st.session_state.gdf.drop(columns=["geometry"])
|
65 |
+
|
66 |
+
st.dataframe(display_df)
|
geospatial-data-converter/utils.py
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import io
|
2 |
+
import os
|
3 |
+
import zipfile
|
4 |
+
from tempfile import TemporaryDirectory
|
5 |
+
from typing import BinaryIO
|
6 |
+
|
7 |
+
import geopandas as gpd
|
8 |
+
|
9 |
+
output_format_dict = {
|
10 |
+
"ESRI Shapefile": ("shp", "zip", "application/zip"), # must be zipped
|
11 |
+
"OpenFileGDB": ("gdb", "zip", "application/zip"), # must be zipped
|
12 |
+
"GeoJSON": ("geojson", "geojson", "application/geo+json"),
|
13 |
+
"CSV": ("csv", "csv", "text/csv"),
|
14 |
+
"KML": ("kml", "kml", "application/vnd.google-earth.kml+xml"),
|
15 |
+
}
|
16 |
+
|
17 |
+
|
18 |
+
def read_file(file: BinaryIO, *args, **kwargs) -> gpd.GeoDataFrame:
|
19 |
+
"""Read a file and return a GeoDataFrame"""
|
20 |
+
if file.name.lower().endswith(".zip"):
|
21 |
+
with TemporaryDirectory() as tmp_dir:
|
22 |
+
tmp_file_path = os.path.join(tmp_dir, file.name)
|
23 |
+
with open(tmp_file_path, "wb") as tmp_file:
|
24 |
+
tmp_file.write(file.read())
|
25 |
+
return gpd.read_file(
|
26 |
+
f"zip://{tmp_file_path}",
|
27 |
+
*args,
|
28 |
+
engine="pyogrio",
|
29 |
+
**kwargs,
|
30 |
+
)
|
31 |
+
return gpd.read_file(file, *args, engine="pyogrio", **kwargs)
|
32 |
+
|
33 |
+
|
34 |
+
def zip_dir(directory: str) -> bytes:
|
35 |
+
"""Zip a directory and return the bytes"""
|
36 |
+
zip_buffer = io.BytesIO()
|
37 |
+
|
38 |
+
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zipf:
|
39 |
+
for root, dirs, files in os.walk(directory):
|
40 |
+
for file in files:
|
41 |
+
new_member = os.path.join(root, file)
|
42 |
+
zipf.write(
|
43 |
+
new_member,
|
44 |
+
os.path.relpath(new_member, directory),
|
45 |
+
)
|
46 |
+
|
47 |
+
return zip_buffer.getvalue()
|
48 |
+
|
49 |
+
|
50 |
+
def convert(gdf: gpd.GeoDataFrame, output_name: str, output_format: str) -> bytes:
|
51 |
+
"""Convert a GeoDataFrame to the specified format"""
|
52 |
+
with TemporaryDirectory() as tmpdir:
|
53 |
+
out_path = os.path.join(tmpdir, output_name)
|
54 |
+
|
55 |
+
if output_format == "CSV":
|
56 |
+
gdf.to_csv(out_path)
|
57 |
+
else:
|
58 |
+
gdf.to_file(out_path, driver=output_format, engine="pyogrio")
|
59 |
+
|
60 |
+
if output_format in ("ESRI Shapefile", "OpenFileGDB"):
|
61 |
+
output_bytes = zip_dir(tmpdir)
|
62 |
+
else:
|
63 |
+
with open(out_path, "rb") as f:
|
64 |
+
output_bytes = f.read()
|
65 |
+
|
66 |
+
return output_bytes
|
kubernetes/deploy.sh
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
set -euo pipefail
|
3 |
+
IFS=$'\n\t'
|
4 |
+
|
5 |
+
# Deploy to Kubernetes
|
6 |
+
kubectl apply -f kubernetes/resources.yaml
|
kubernetes/resources.yaml
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
apiVersion: apps/v1
|
2 |
+
kind: Deployment
|
3 |
+
metadata:
|
4 |
+
name: geospatial-data-converter-deployment
|
5 |
+
spec:
|
6 |
+
replicas: 1
|
7 |
+
selector:
|
8 |
+
matchLabels:
|
9 |
+
app: geospatial-data-converter
|
10 |
+
template:
|
11 |
+
metadata:
|
12 |
+
labels:
|
13 |
+
app: geospatial-data-converter
|
14 |
+
spec:
|
15 |
+
containers:
|
16 |
+
- name: geospatial-data-converter
|
17 |
+
image: joshuasundance/geospatial-data-converter:0.0.1
|
18 |
+
imagePullPolicy: Always
|
19 |
+
resources:
|
20 |
+
requests:
|
21 |
+
cpu: "100m"
|
22 |
+
memory: "200Mi"
|
23 |
+
limits:
|
24 |
+
cpu: "500m"
|
25 |
+
memory: "500Mi"
|
26 |
+
securityContext:
|
27 |
+
runAsNonRoot: true
|
28 |
+
---
|
29 |
+
apiVersion: v1
|
30 |
+
kind: Service
|
31 |
+
metadata:
|
32 |
+
name: geospatial-data-converter-service
|
33 |
+
# configure on Azure and uncomment below to use a vnet
|
34 |
+
# annotations:
|
35 |
+
# service.beta.kubernetes.io/azure-load-balancer-internal: "true"
|
36 |
+
# service.beta.kubernetes.io/azure-load-balancer-ipv4: vnet.ip.goes.here
|
37 |
+
# service.beta.kubernetes.io/azure-dns-label-name: "geospatial-data-converter"
|
38 |
+
spec:
|
39 |
+
selector:
|
40 |
+
app: geospatial-data-converter
|
41 |
+
ports:
|
42 |
+
- protocol: TCP
|
43 |
+
port: 80
|
44 |
+
targetPort: 7860
|
45 |
+
type: LoadBalancer
|
46 |
+
---
|
47 |
+
apiVersion: networking.k8s.io/v1
|
48 |
+
kind: NetworkPolicy
|
49 |
+
metadata:
|
50 |
+
name: geospatial-data-converter-network-policy
|
51 |
+
spec:
|
52 |
+
podSelector:
|
53 |
+
matchLabels:
|
54 |
+
app: geospatial-data-converter
|
55 |
+
policyTypes:
|
56 |
+
- Ingress
|
57 |
+
ingress:
|
58 |
+
- from: [] # An empty array here means it will allow traffic from all sources.
|
59 |
+
ports:
|
60 |
+
- protocol: TCP
|
61 |
+
port: 7860
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
geopandas==0.14.0
|
2 |
+
pyogrio==0.6.0
|
3 |
+
streamlit==1.27.2
|