diff --git a/Dockerfile b/Dockerfile index 6745360..eed350a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,18 @@ -# app/Dockerfile - FROM python:3.9-slim -WORKDIR /app - -RUN apt-get update && apt-get install -y \ - build-essential \ - curl \ - software-properties-common \ - git \ - && rm -rf /var/lib/apt/lists/* - -#RUN git clone https://github.com/streamlit/streamlit-example.git . - -COPY . /app - -RUN pip3 install --upgrade pip +COPY requirements.txt requirements.txt RUN pip3 install -r requirements.txt +WORKDIR /data +COPY /data /data + +WORKDIR /app +COPY /app /app + +WORKDIR / + EXPOSE 8501 HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health -ENTRYPOINT ["streamlit", "run", "main.py", "--server.port=8501", "--server.address=0.0.0.0"] +ENTRYPOINT ["streamlit", "run", "/app/main.py", "--server.port=8501", "--server.address=0.0.0.0"] diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..dd6b29a --- /dev/null +++ b/app/main.py @@ -0,0 +1,40 @@ +import streamlit as st +from streamlit_folium import st_folium +import folium +import glob +import geopandas as gpd +import pandas as pd +import pathlib + + +if __name__ == "__main__": + + """ + # Lithos Carbon Shapefile Viewer + """ + + # glob shapefiles to load + current_dir = pathlib.Path().resolve() + data_dir = current_dir / "data" + shapefiles_list = sorted(data_dir.glob("*.shp")) + + # user selects which shapefiels to plot + selected_shapefiles = st.pills("Choose shapefiles to view on the map", shapefiles_list, selection_mode="multi") + + # load and merge shapefiles + if selected_shapefiles: + gdf = gpd.GeoDataFrame() + gdf = gpd.GeoDataFrame(pd.concat([gpd.read_file(i) for i in selected_shapefiles], ignore_index=True), crs=gpd.read_file(selected_shapefiles[0]).crs) + + + # Create a Folium map + m = folium.Map(location=[gdf.centroid.y.mean(), gdf.centroid.x.mean()], zoom_start=13) + folium.GeoJson(gdf.to_json()).add_to(m) + + + # Display the map in Streamlit + st_folium(m) + + # Display data in table format + st.write(gdf) + diff --git a/app/readme.md b/app/readme.md new file mode 100644 index 0000000..d2b7b16 --- /dev/null +++ b/app/readme.md @@ -0,0 +1,53 @@ +## Requirements + +Build a simple application using ***existing technologies** (hint: we don’t need you to build a shapefile parser from scratch)* that will allow shapefiles to be visualized on a map. Shapefile data does not need to be persisted into a database, this application should be ephemeral and have the potential to be entirely offline. + +You’re free to use the framework of your choice. We want to understand how you approach this from a product perspective. Remember, we’re chasing ease of use! + +**Please don’t spend more than 1-2 hours on this exercise. Get something working, and we can have a discussion about features that would have followed.** + +--- + +### Running the app via Docker: +``` +chmod u+x build_and_run.sh +./build_and_run_docker.sh +``` +- Visit http://0.0.0.0:8501 +- Ctrl + c to quit + +### Running the app via Python with virtual env: +``` +python3 -m venv venv +. venv/bin/activate +pip install -r requirements +streamlit run main.py --server.port=8501 --server.address=0.0.0.0 +``` +- Visit http://0.0.0.0:8501 +- Ctrl + c to quit + + +--- + +### My dev log: +- streamlit getting started (done) +- display a shapefile + - streamlit plotting doesn't work out of the box for polygons + - geopandas has a geopandas dataframe, dartaframes required for st.map + - st.map is a st.pydeck_chart wrapper + - st.pydeck_chart uses mapbox + - may need mapbox token - *Mapbox requires users to register and provide a token before users can request map tiles. Currently, Streamlit provides this token for you, but this could change at any time.* + - Can I plot polygons or just points? + - test data doesn't plot immediately + - Folium can plot polygons in streamlit via geojson + - requires 3rd party plugin st_folium + - ok I have folium plotting test data + - easiest/least depedencies to convert shapefiles to geojson? + - easy/1-million dependencies: geopands+pandas + - already have that working so will leave it and discuss other options with Evan + - add sidebar for my notes and readme + +- things to add + - add a legend + - better theme + - deterministic map config toi prevent random width/height based on geo data etc \ No newline at end of file diff --git a/streamlit_config.toml b/app/streamlit_config.toml similarity index 100% rename from streamlit_config.toml rename to app/streamlit_config.toml diff --git a/build_and_run.sh b/build_and_run.sh deleted file mode 100755 index 5e10550..0000000 --- a/build_and_run.sh +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t lithos-app . -docker run -p 8501:8501 lithos-app \ No newline at end of file diff --git a/build_and_run_docker.sh b/build_and_run_docker.sh new file mode 100755 index 0000000..708e2c0 --- /dev/null +++ b/build_and_run_docker.sh @@ -0,0 +1,4 @@ +#!/usr/bin/sh + +docker build -t lithos-app . +docker run -p 8501:8501 lithos-app \ No newline at end of file diff --git a/build_and_run_python.sh b/build_and_run_python.sh new file mode 100755 index 0000000..d899d5d --- /dev/null +++ b/build_and_run_python.sh @@ -0,0 +1,7 @@ +#!/usr/bin/sh + +rm -rf venv +python3 -m venv venv +. venv/bin/activate +pip install -r requirements.txt +streamlit run ./app/main.py --server.port=8501 --server.address=0.0.0.0 diff --git a/data/farm_fields.cpg b/data/farm_fields.cpg new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/data/farm_fields.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/data/farm_fields.dbf b/data/farm_fields.dbf new file mode 100644 index 0000000..5b85c12 Binary files /dev/null and b/data/farm_fields.dbf differ diff --git a/data/farm_fields.prj b/data/farm_fields.prj new file mode 100644 index 0000000..f45cbad --- /dev/null +++ b/data/farm_fields.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/data/farm_fields.shp b/data/farm_fields.shp new file mode 100644 index 0000000..d1a5d1a Binary files /dev/null and b/data/farm_fields.shp differ diff --git a/data/farm_fields.shx b/data/farm_fields.shx new file mode 100644 index 0000000..0e532fc Binary files /dev/null and b/data/farm_fields.shx differ diff --git a/data/soil_samples.cpg b/data/soil_samples.cpg new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/data/soil_samples.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/data/soil_samples.dbf b/data/soil_samples.dbf new file mode 100644 index 0000000..187a2a1 Binary files /dev/null and b/data/soil_samples.dbf differ diff --git a/data/soil_samples.prj b/data/soil_samples.prj new file mode 100644 index 0000000..f45cbad --- /dev/null +++ b/data/soil_samples.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/data/soil_samples.shp b/data/soil_samples.shp new file mode 100644 index 0000000..128716f Binary files /dev/null and b/data/soil_samples.shp differ diff --git a/data/soil_samples.shx b/data/soil_samples.shx new file mode 100644 index 0000000..9b42839 Binary files /dev/null and b/data/soil_samples.shx differ diff --git a/main.py b/main.py deleted file mode 100644 index 826ef07..0000000 --- a/main.py +++ /dev/null @@ -1,37 +0,0 @@ -from collections import namedtuple -import altair as alt -import math -import pandas as pd -import streamlit as st - -""" -# Welcome to Streamlit! - -Edit `/streamlit_app.py` to customize this app to your heart's desire :heart: - -If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community -forums](https://discuss.streamlit.io). - -In the meantime, below is an example of what you can do with just a few lines of code: -""" - -with st.echo(code_location='below'): - total_points = st.slider("Number of points in spiral", 1, 5000, 2000) - num_turns = st.slider("Number of turns in spiral", 1, 100, 9) - - Point = namedtuple('Point', 'x y') - data = [] - - points_per_turn = total_points / num_turns - - for curr_point_num in range(total_points): - curr_turn, i = divmod(curr_point_num, points_per_turn) - angle = (curr_turn + 1) * 2 * math.pi * i / points_per_turn - radius = curr_point_num / total_points - x = radius * math.cos(angle) - y = radius * math.sin(angle) - data.append(Point(x, y)) - - st.altair_chart(alt.Chart(pd.DataFrame(data), height=500, width=500) - .mark_circle(color='#0068c9', opacity=0.5) - .encode(x='x:Q', y='y:Q')) diff --git a/readme.md b/readme.md deleted file mode 100644 index 99d5df9..0000000 --- a/readme.md +++ /dev/null @@ -1,21 +0,0 @@ -## Requirements - -Build a simple application using ***existing technologies** (hint: we don’t need you to build a shapefile parser from scratch)* that will allow shapefiles to be visualized on a map. Shapefile data does not need to be persisted into a database, this application should be ephemeral and have the potential to be entirely offline. - -You’re free to use the framework of your choice. We want to understand how you approach this from a product perspective. Remember, we’re chasing ease of use! - -**Please don’t spend more than 1-2 hours on this exercise. Get something working, and we can have a discussion about features that would have followed.** - - - -### Dev steps: -- streamlit getting started (done) -- display a shapefile -- add a legend -- add feature to select shape file - - -### Running the app: -- chmod u+x build_and_run.sh -- ./build_and_run.sh -- Visit http://0.0.0.0:8501 diff --git a/requirements.txt b/requirements.txt index aa99921..76922f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,4 @@ -streamlit==1.41.1 \ No newline at end of file +streamlit==1.41.1 +streamlit-folium==0.24.0 +pandas==2.2.3 +geopandas==1.0.1 \ No newline at end of file