Created
June 29, 2025 12:00
-
-
Save hansthen/8433c74378fc8a52a2e51b7b10d4e8a9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import folium | |
import streamlit as st | |
from folium.plugins import GeoMan, MousePosition | |
from folium import JsCode | |
from streamlit_folium import st_folium | |
from scenario import parser | |
from streamlit_argparse import Form | |
import requests | |
from scenario import parser | |
import json | |
import shlex | |
from form import * | |
st.set_page_config( | |
page_title="geoman", | |
layout="wide" | |
) | |
parser.exit = lambda *args, **kwargs: None | |
args = [ | |
"--area", | |
"""{"type": "FeatureCollection", "features": [{"type": "Feature", | |
"properties": {}, "geometry": {"type": "Polygon", "coordinates": | |
[[[-74.090424, 40.723063], [-74.090424, 40.7561], [-74.027252, 40.7561], | |
[-74.027252, 40.723063], [-74.090424, 40.723063]]]}}], "properties": | |
{"type": "whitelist"}}""", | |
"--ignore", | |
""" | |
{"geometry": {"type": "Point", "coordinates": [-73.981681, 40.730975]}, | |
"properties": {"line": "L", "name": "1st Ave", "notes": "L-all times", | |
"objectid": "146", "url": "http://web.mta.info/nyct/service/", "ignore": true}, | |
"type": "Feature"} | |
""" | |
] | |
initial = parser.parse_args(args) | |
st.session_state.setdefault("ignore", initial.ignore) | |
st.session_state.setdefault("area", initial.area) | |
def handle_return(): | |
geojson = st.session_state.map_widget | |
if geojson["properties"]["type"] == "ignore": | |
st.session_state.ignore = geojson["features"] | |
else: | |
st.session_state.area = [geojson] | |
@st.cache_data | |
def get_ignored_stations(): | |
return [ | |
f["properties"]["objectid"] for f in st.session_state.ignore | |
] | |
@st.cache_data | |
def get_initial_layers(): | |
return st.session_state.area | |
left, right = st.columns([1,1]) | |
with left: | |
items = folium.FeatureGroup() | |
m = folium.Map( | |
location=[40.73, -73.94], | |
zoom_start=12, | |
to_streamlit=JsCode(""" | |
() => { | |
console.log("placeholder"); | |
} | |
""") | |
) | |
ignored = get_ignored_stations() | |
rt = folium.plugins.Realtime( | |
"https://raw.githubusercontent.com/python-visualization/folium-example-data/main/subway_stations.geojson", | |
get_feature_id=JsCode("(f) => { return f.properties.objectid; }"), | |
point_to_layer=JsCode(""" | |
(f, latlng) => { | |
return L.circleMarker(latlng, { | |
radius: 5, | |
color: "light gray", | |
fillOpacity: 0.7, | |
// No editing using geoman | |
pmIgnore: true, | |
}) | |
} | |
"""), | |
on_each_feature=JsCode(""" | |
(f, layer) => { | |
layer.on("click", (event) => { | |
var loc = event.sourceTarget.feature.geometry; | |
var layer = event.sourceTarget; | |
var parents = layer._eventParents; | |
var rt = parents[Object.keys(parents)[0]]; | |
var index = rt.options.ignored.indexOf(f.properties.objectid); | |
if (index < 0) { | |
rt.options.ignored.push(f.properties.objectid); | |
layer.setStyle({"color": "green"}); | |
} else { | |
rt.options.ignored.splice(index, 1); | |
layer.setStyle({"color": "light gray"}); | |
} | |
// Return this to streamlit | |
var geojson = rt.toGeoJSON(); | |
// Filter only ignored features | |
geojson.features = geojson.features.filter( | |
(f) => { | |
var i = rt.options.ignored.indexOf( | |
f.properties.objectid | |
) | |
return i >= 0; | |
} | |
); | |
geojson.properties = {type: "ignore"} | |
layer._map.options.toStreamlit(); | |
Streamlit.setComponentValue(geojson); | |
}); | |
} | |
"""), | |
refresh=10, | |
ignored=ignored, | |
) | |
# Do this after creating the realtime layer, because we need the reference | |
style=JsCode(""" | |
# Do this after creating the realtime layer, because we need the reference | |
style=JsCode(""" | |
(f) => { | |
var realtime = %(realtime)s; | |
if (realtime.options.ignored.indexOf(f.properties.objectid) >= 0) { | |
return {"color": "green"}; | |
} else { | |
return {"color": "light gray"}; | |
} | |
} | |
""" % dict(realtime=rt.get_name())) | |
rt.options["style"] = style | |
rt.add_to(m) | |
items.add_to(m) | |
MousePosition().add_to(m) | |
handler = JsCode(""" | |
(e) => { | |
console.log("event", e); | |
var map = %(map)s; | |
var layers = L.PM.Utils.findLayers(map); | |
var jsons = [] | |
layers.forEach((layer) => { | |
var json = layer.toGeoJSON(); | |
if(layer.options.radius) { | |
json.properties.radius = layer.options.radius; | |
} | |
jsons.push(json); | |
}); | |
var geojson = { | |
type: 'FeatureCollection', | |
features: jsons, | |
properties: {type: "area"} | |
}; | |
Streamlit.setComponentValue(geojson); | |
} | |
""" % dict(map=m.get_name())) | |
click = JsCode(""" | |
(e) => { | |
console.log(e.target); | |
console.log(e.target.toGeoJSON()); | |
} | |
""" | |
) | |
for whitelist in get_initial_layers(): | |
wl = folium.GeoJson(whitelist).add_to(items) | |
gm = GeoMan( | |
feature_group=items, | |
draw_marker=False, | |
draw_polyline=False, | |
draw_circle_marker=False, | |
draw_text=False, | |
on={ | |
"click": click, | |
"pm:change": handler, | |
"pm:update": handler, | |
"pm:dragend": handler, | |
"pm:remove": handler, | |
"pm:rotate": handler, | |
} | |
).add_to(m) | |
event_handlers = { | |
"pm:create": handler, | |
"pm:cut": handler, | |
"pm:globaldrawmodetoggled": handler, | |
"pm:globaleditmodetoggled": handler, | |
"pm:globaldragmodetoggled": handler, | |
"pm:globalremovalmodetoggled": handler, | |
"pm:globalcutmodetoggled": handler, | |
"pm:globalrotatemodetoggled": handler, | |
} | |
m.on(**event_handlers) | |
result = st_folium( | |
m, | |
width=1200, | |
height=500, | |
returned_objects=[], | |
key="map_widget", | |
on_change=handle_return | |
) | |
with right: | |
form = Form( | |
parser, | |
area=area_widget, | |
ignore=ignore_widget, | |
dow=dow_widget, | |
) | |
output = form.render(args) | |
output = output.split("--") | |
output = "\n--".join(output).strip() | |
st.code(output, language="bash") | |
1,1 Top |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from scenario import parser | |
import streamlit as st | |
from streamlit_argparse import Form | |
import json | |
import shlex | |
def show_as_form(args): | |
try: | |
parser.exit = lambda *args, **kwargs: None | |
initial = parser.parse_args(args) | |
form = Form( | |
parser, | |
area=area_widget, | |
spots=spots_widget, | |
dow=dow_widget, | |
) | |
output = form.render(args) | |
output = output.split("--") | |
output = "\n--".join(output).strip() | |
return output | |
except Exception as e: | |
st.write(e) | |
st.write("invalid results") | |
def area_widget(form, action, value): | |
area = st.session_state.get("area", []) | |
st.text_area("area", area, disabled=True) | |
return [json.dumps(area)] | |
def ignore_widget(form, action, value): | |
ignore = st.session_state.get("ignore", []) | |
st.text_area("ignore", ignore, disabled=True) | |
return [json.dumps(f) for f in ignore] | |
def dow_widget(form, action, value): | |
names = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"] | |
checkboxes = st.columns(len(names)) | |
result = [] | |
for col, name in zip(checkboxes, names): | |
with col: | |
if st.checkbox(name): | |
result.append(name) | |
return list(result) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment