|
import streamlit as st |
|
import folium |
|
from streamlit_folium import folium_static |
|
import pyproj |
|
from folium.plugins import Draw |
|
import json |
|
import leafmap.foliumap as leafmap |
|
|
|
st.set_page_config(layout="wide") |
|
|
|
@st.cache_data |
|
def get_coordinate_systems(): |
|
return { |
|
"EPSG:4326": "WGS 84", |
|
"EPSG:3857": "Web Mercator", |
|
"EPSG:2154": "RGF93 / Lambert-93 (France)", |
|
"EPSG:27572": "NTF (Paris) / Lambert zone II (France)", |
|
"EPSG:25832": "ETRS89 / UTM zone 32N (Germany)", |
|
} |
|
|
|
def convert_coordinates(bbox, from_crs, to_crs): |
|
transformer = pyproj.Transformer.from_crs(from_crs, to_crs, always_xy=True) |
|
min_x, min_y = transformer.transform(bbox[0], bbox[1]) |
|
max_x, max_y = transformer.transform(bbox[2], bbox[3]) |
|
return [min_x, min_y, max_x, max_y] |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.main { padding-top: 0; } |
|
.stApp { margin-top: -80px; } |
|
.css-1kyxreq { justify-content: center; } |
|
.css-5rimss { font-size: 14px; } |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.sidebar.title("About") |
|
st.sidebar.info( |
|
"This app is an improved version of the BBox Finder, " |
|
"inspired by bboxfinder.com and using Streamlit." |
|
) |
|
|
|
st.sidebar.title("Contact") |
|
st.sidebar.info( |
|
"Your Name\n" |
|
"[GitHub](https://github.com/yourusername) | " |
|
"[LinkedIn](https://www.linkedin.com/in/yourprofile)" |
|
) |
|
|
|
|
|
col1, col2 = st.columns([4, 1]) |
|
|
|
with col1: |
|
st.title("Improved BBox Finder") |
|
|
|
|
|
m = leafmap.Map(center=[46.2, 6.15], zoom=8) |
|
draw = Draw( |
|
export=True, |
|
position='topleft', |
|
draw_options={'polyline': False, 'polygon': False, 'circle': False, 'marker': False, 'circlemarker': False}, |
|
edit_options={'edit': False} |
|
) |
|
draw.add_to(m) |
|
|
|
|
|
m.add_child(folium.Element(""" |
|
<script> |
|
var drawnItems = new L.FeatureGroup(); |
|
map.addLayer(drawnItems); |
|
map.on(L.Draw.Event.CREATED, function (event) { |
|
drawnItems.clearLayers(); |
|
var layer = event.layer; |
|
drawnItems.addLayer(layer); |
|
var bounds = layer.getBounds(); |
|
var bbox = [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()]; |
|
var bboxString = bbox.join(','); |
|
|
|
window.parent.postMessage({ |
|
type: 'streamlit:set_widget_value', |
|
key: 'bbox_coords', |
|
value: bboxString |
|
}, '*'); |
|
}); |
|
</script> |
|
""")) |
|
|
|
m.to_streamlit(height=600) |
|
|
|
with col2: |
|
st.markdown("<h3 style='text-align: center;'>Coordinates</h3>", unsafe_allow_html=True) |
|
bbox_coords = st.text_input("", key="bbox_coords", label_visibility="collapsed") |
|
|
|
|
|
coordinate_systems = get_coordinate_systems() |
|
selected_crs = st.selectbox("Select Coordinate Reference System", options=list(coordinate_systems.keys()), format_func=lambda x: f"{x} - {coordinate_systems[x]}") |
|
|
|
if bbox_coords: |
|
try: |
|
bbox = [float(coord.strip()) for coord in bbox_coords.split(',')] |
|
if len(bbox) == 4: |
|
st.markdown(f"<b>Selected CRS: {selected_crs}</b>", unsafe_allow_html=True) |
|
converted_bbox = convert_coordinates(bbox, "EPSG:4326", selected_crs) |
|
result = f"{converted_bbox[0]:.6f},{converted_bbox[1]:.6f},{converted_bbox[2]:.6f},{converted_bbox[3]:.6f}" |
|
st.code(result, language="plaintext") |
|
if st.button(f"Copy {selected_crs}"): |
|
st.write(f"{selected_crs} coordinates copied!") |
|
|
|
|
|
st.markdown("<b>Other common CRSs:</b>", unsafe_allow_html=True) |
|
for epsg, name in coordinate_systems.items(): |
|
if epsg != selected_crs: |
|
converted_bbox = convert_coordinates(bbox, "EPSG:4326", epsg) |
|
result = f"{converted_bbox[0]:.6f},{converted_bbox[1]:.6f},{converted_bbox[2]:.6f},{converted_bbox[3]:.6f}" |
|
st.code(f"{epsg}: {result}", language="plaintext") |
|
|
|
|
|
st.markdown("<b>GeoJSON</b>", unsafe_allow_html=True) |
|
geojson = json.dumps({ |
|
"type": "Feature", |
|
"properties": {}, |
|
"geometry": { |
|
"type": "Polygon", |
|
"coordinates": [[ |
|
[bbox[0], bbox[1]], |
|
[bbox[2], bbox[1]], |
|
[bbox[2], bbox[3]], |
|
[bbox[0], bbox[3]], |
|
[bbox[0], bbox[1]] |
|
]] |
|
} |
|
}, indent=2) |
|
st.code(geojson, language="json") |
|
if st.button("Copy GeoJSON"): |
|
st.write("GeoJSON copied!") |
|
|
|
st.markdown("<b>WKT</b>", unsafe_allow_html=True) |
|
wkt = f"POLYGON(({bbox[0]} {bbox[1]}, {bbox[2]} {bbox[1]}, {bbox[2]} {bbox[3]}, {bbox[0]} {bbox[3]}, {bbox[0]} {bbox[1]}))" |
|
st.code(wkt, language="plaintext") |
|
if st.button("Copy WKT"): |
|
st.write("WKT copied!") |
|
|
|
else: |
|
st.error("Please enter 4 coordinates separated by commas.") |
|
except ValueError: |
|
st.error("Please enter valid coordinates (numbers separated by commas).") |
|
|
|
|
|
st.markdown("<div style='text-align: center; color: gray;'>Created with Streamlit - Inspired by bboxfinder.com</div>", unsafe_allow_html=True) |