Spaces:
Runtime error
Runtime error
Commit
·
c6b286b
0
Parent(s):
Duplicate from yunusserhat/Crime-Map
Browse filesCo-authored-by: Yunus Serhat Bıçakçı <yunusserhat@users.noreply.huggingface.co>
- .gitattributes +34 -0
- .gitignore +132 -0
- .idea/.gitignore +8 -0
- .idea/Crime-Map.iml +14 -0
- .idea/inspectionProfiles/profiles_settings.xml +6 -0
- .idea/misc.xml +4 -0
- .idea/modules.xml +8 -0
- .idea/vcs.xml +6 -0
- Application.py +29 -0
- LICENSE +21 -0
- README.md +14 -0
- pages/2_↔️_Comparision.py +146 -0
- pages/3_😡_Hate_Tweets.py +74 -0
- pages/4_Test.py +143 -0
- postBuild +6 -0
- requirements.txt +24 -0
- setup.sh +18 -0
.gitattributes
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Byte-compiled / optimized / DLL files
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
# *.html
|
6 |
+
private/
|
7 |
+
.vscode/
|
8 |
+
|
9 |
+
# C extensions
|
10 |
+
*.so
|
11 |
+
|
12 |
+
# Distribution / packaging
|
13 |
+
.Python
|
14 |
+
build/
|
15 |
+
develop-eggs/
|
16 |
+
dist/
|
17 |
+
downloads/
|
18 |
+
eggs/
|
19 |
+
.eggs/
|
20 |
+
lib/
|
21 |
+
lib64/
|
22 |
+
parts/
|
23 |
+
sdist/
|
24 |
+
var/
|
25 |
+
wheels/
|
26 |
+
pip-wheel-metadata/
|
27 |
+
share/python-wheels/
|
28 |
+
*.egg-info/
|
29 |
+
.installed.cfg
|
30 |
+
*.egg
|
31 |
+
MANIFEST
|
32 |
+
|
33 |
+
# PyInstaller
|
34 |
+
# Usually these files are written by a python script from a template
|
35 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
36 |
+
*.manifest
|
37 |
+
*.spec
|
38 |
+
|
39 |
+
# Installer logs
|
40 |
+
pip-log.txt
|
41 |
+
pip-delete-this-directory.txt
|
42 |
+
|
43 |
+
# Unit test / coverage reports
|
44 |
+
htmlcov/
|
45 |
+
.tox/
|
46 |
+
.nox/
|
47 |
+
.coverage
|
48 |
+
.coverage.*
|
49 |
+
.cache
|
50 |
+
nosetests.xml
|
51 |
+
coverage.xml
|
52 |
+
*.cover
|
53 |
+
*.py,cover
|
54 |
+
.hypothesis/
|
55 |
+
.pytest_cache/
|
56 |
+
|
57 |
+
# Translations
|
58 |
+
*.mo
|
59 |
+
*.pot
|
60 |
+
|
61 |
+
# Django stuff:
|
62 |
+
*.log
|
63 |
+
local_settings.py
|
64 |
+
db.sqlite3
|
65 |
+
db.sqlite3-journal
|
66 |
+
|
67 |
+
# Flask stuff:
|
68 |
+
instance/
|
69 |
+
.webassets-cache
|
70 |
+
|
71 |
+
# Scrapy stuff:
|
72 |
+
.scrapy
|
73 |
+
|
74 |
+
# Sphinx documentation
|
75 |
+
docs/_build/
|
76 |
+
|
77 |
+
# PyBuilder
|
78 |
+
target/
|
79 |
+
|
80 |
+
# Jupyter Notebook
|
81 |
+
.ipynb_checkpoints
|
82 |
+
|
83 |
+
# IPython
|
84 |
+
profile_default/
|
85 |
+
ipython_config.py
|
86 |
+
|
87 |
+
# pyenv
|
88 |
+
.python-version
|
89 |
+
|
90 |
+
# pipenv
|
91 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
92 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
93 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
94 |
+
# install all needed dependencies.
|
95 |
+
#Pipfile.lock
|
96 |
+
|
97 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
98 |
+
__pypackages__/
|
99 |
+
|
100 |
+
# Celery stuff
|
101 |
+
celerybeat-schedule
|
102 |
+
celerybeat.pid
|
103 |
+
|
104 |
+
# SageMath parsed files
|
105 |
+
*.sage.py
|
106 |
+
|
107 |
+
# Environments
|
108 |
+
.env
|
109 |
+
.venv
|
110 |
+
env/
|
111 |
+
venv/
|
112 |
+
ENV/
|
113 |
+
env.bak/
|
114 |
+
venv.bak/
|
115 |
+
|
116 |
+
# Spyder project settings
|
117 |
+
.spyderproject
|
118 |
+
.spyproject
|
119 |
+
|
120 |
+
# Rope project settings
|
121 |
+
.ropeproject
|
122 |
+
|
123 |
+
# mkdocs documentation
|
124 |
+
/site
|
125 |
+
|
126 |
+
# mypy
|
127 |
+
.mypy_cache/
|
128 |
+
.dmypy.json
|
129 |
+
dmypy.json
|
130 |
+
|
131 |
+
# Pyre type checker
|
132 |
+
.pyre/
|
.idea/.gitignore
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Default ignored files
|
2 |
+
/shelf/
|
3 |
+
/workspace.xml
|
4 |
+
# Editor-based HTTP Client requests
|
5 |
+
/httpRequests/
|
6 |
+
# Datasource local storage ignored files
|
7 |
+
/dataSources/
|
8 |
+
/dataSources.local.xml
|
.idea/Crime-Map.iml
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<module type="PYTHON_MODULE" version="4">
|
3 |
+
<component name="NewModuleRootManager">
|
4 |
+
<content url="file://$MODULE_DIR$">
|
5 |
+
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
6 |
+
</content>
|
7 |
+
<orderEntry type="jdk" jdkName="Python 3.9 (venv)" jdkType="Python SDK" />
|
8 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
9 |
+
</component>
|
10 |
+
<component name="PyDocumentationSettings">
|
11 |
+
<option name="format" value="PLAIN" />
|
12 |
+
<option name="myDocStringFormat" value="Plain" />
|
13 |
+
</component>
|
14 |
+
</module>
|
.idea/inspectionProfiles/profiles_settings.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<settings>
|
3 |
+
<option name="USE_PROJECT_PROFILE" value="false" />
|
4 |
+
<version value="1.0" />
|
5 |
+
</settings>
|
6 |
+
</component>
|
.idea/misc.xml
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (venv)" project-jdk-type="Python SDK" />
|
4 |
+
</project>
|
.idea/modules.xml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="ProjectModuleManager">
|
4 |
+
<modules>
|
5 |
+
<module fileurl="file://$PROJECT_DIR$/.idea/Crime-Map.iml" filepath="$PROJECT_DIR$/.idea/Crime-Map.iml" />
|
6 |
+
</modules>
|
7 |
+
</component>
|
8 |
+
</project>
|
.idea/vcs.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="VcsDirectoryMappings">
|
4 |
+
<mapping directory="" vcs="Git" />
|
5 |
+
</component>
|
6 |
+
</project>
|
Application.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import leafmap.foliumap as leafmap
|
3 |
+
|
4 |
+
st.set_page_config(layout="wide")
|
5 |
+
|
6 |
+
st.sidebar.info(
|
7 |
+
"""
|
8 |
+
- Web App URL: <https://interactive-crime-map.hf.space/>
|
9 |
+
- HuggingFace repository: <https://huggingface.co/spaces/interactive-crime/map/tree/main>
|
10 |
+
"""
|
11 |
+
)
|
12 |
+
|
13 |
+
st.sidebar.title("Contact")
|
14 |
+
st.sidebar.info(
|
15 |
+
"""
|
16 |
+
Yunus Serhat Bıçakçı at [yunusserhat.com](https://yunusserhat.com) | [GitHub](https://github.com/yunusserhat) | [Twitter](https://twitter.com/yunusserhat) | [LinkedIn](https://www.linkedin.com/in/yunusserhat)
|
17 |
+
"""
|
18 |
+
)
|
19 |
+
|
20 |
+
st.title("Interactive Crime Map Application")
|
21 |
+
|
22 |
+
st.subheader(
|
23 |
+
"""
|
24 |
+
This interactive crime map apps created using [streamlit](https://streamlit.io) and open-source mapping libraries and repositories, such as [leafmap](https://leafmap.org), [geopandas](https://geopandas.org), [streamlit-geospatial](https://huggingface.co/spaces/giswqs/Streamlit) ,and [folium](https://python-visualization.github.io/folium/).
|
25 |
+
"""
|
26 |
+
)
|
27 |
+
|
28 |
+
st.info("Click on the left sidebar menu to navigate to the different apps.")
|
29 |
+
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2023 Yunus Serhat Bicakci
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Streamlit
|
3 |
+
emoji: 🔥
|
4 |
+
colorFrom: indigo
|
5 |
+
colorTo: green
|
6 |
+
sdk: streamlit
|
7 |
+
sdk_version: 1.17.0
|
8 |
+
app_file: Application.py
|
9 |
+
pinned: false
|
10 |
+
license: mit
|
11 |
+
duplicated_from: yunusserhat/Crime-Map
|
12 |
+
---
|
13 |
+
|
14 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
pages/2_↔️_Comparision.py
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import leafmap.foliumap as leafmap
|
3 |
+
import json
|
4 |
+
import requests
|
5 |
+
|
6 |
+
|
7 |
+
# Functions to dynamically fetch columns
|
8 |
+
def get_columns_from_geojson(geojson_data):
|
9 |
+
"""Retrieve column names from a GeoJSON data."""
|
10 |
+
if "features" in geojson_data:
|
11 |
+
properties = geojson_data["features"][0]["properties"]
|
12 |
+
return list(properties.keys())
|
13 |
+
return []
|
14 |
+
|
15 |
+
|
16 |
+
def get_columns_from_url(url):
|
17 |
+
"""Retrieve column names from a GeoJSON URL."""
|
18 |
+
response = requests.get(url)
|
19 |
+
geojson_data = response.json()
|
20 |
+
return get_columns_from_geojson(geojson_data)
|
21 |
+
|
22 |
+
|
23 |
+
st.set_page_config(layout="wide")
|
24 |
+
|
25 |
+
# Sidebar Information
|
26 |
+
st.sidebar.info(
|
27 |
+
'''
|
28 |
+
- Web App URL: <https://interactive-crime-map.hf.space/>
|
29 |
+
- HuggingFace repository: <https://huggingface.co/spaces/interactive-crime/map/tree/main>
|
30 |
+
'''
|
31 |
+
)
|
32 |
+
|
33 |
+
st.sidebar.title("Contact")
|
34 |
+
st.sidebar.info(
|
35 |
+
'''
|
36 |
+
Yunus Serhat Bıçakçı at [yunusserhat.com](https://yunusserhat.com) | [GitHub](https://github.com/yunusserhat) | [Twitter](https://twitter.com/yunusserhat) | [LinkedIn](https://www.linkedin.com/in/yunusserhat)
|
37 |
+
'''
|
38 |
+
)
|
39 |
+
|
40 |
+
# Title and Description
|
41 |
+
st.title("Interactive Analysis of Hate Metrics in London & Custom Dataset Visualization")
|
42 |
+
|
43 |
+
st.markdown(
|
44 |
+
'''
|
45 |
+
Dive into an interactive analysis of hate metrics in London, exploring the disparities and correlations between hate-related tweets and reported crimes in boroughs. This platform offers a comparative visualization based on data from X and the London Metropolitan Police Service as of December 2022.
|
46 |
+
|
47 |
+
Additionally, users can upload and visualize their own GeoJSON datasets, facilitating personalized analysis and insights. Delve deeper into the patterns of hate sentiment, understand how online behavior might mirror real-world incidents, or uncover patterns in your own data.
|
48 |
+
'''
|
49 |
+
)
|
50 |
+
|
51 |
+
# File Uploader
|
52 |
+
uploaded_file = st.file_uploader("Upload a GeoJSON file", type=["geojson"])
|
53 |
+
uploaded_geojson = None
|
54 |
+
|
55 |
+
# Map URLs
|
56 |
+
map_1 = "https://raw.githubusercontent.com/yunusserhat/data/main/data/boroughs_count_df_2022_dec.geojson"
|
57 |
+
map_2 = "https://raw.githubusercontent.com/yunusserhat/data/main/data/mps_hate_2022_dec_count.geojson"
|
58 |
+
map_3 = "https://raw.githubusercontent.com/yunusserhat/data/main/data/mps2022dec_count.geojson"
|
59 |
+
|
60 |
+
if uploaded_file:
|
61 |
+
try:
|
62 |
+
uploaded_geojson = json.load(uploaded_file)
|
63 |
+
if "features" not in uploaded_geojson:
|
64 |
+
st.warning("The uploaded file does not seem to be a valid GeoJSON format.")
|
65 |
+
uploaded_geojson = None
|
66 |
+
else:
|
67 |
+
st.success("GeoJSON file uploaded successfully!")
|
68 |
+
except json.JSONDecodeError:
|
69 |
+
st.error("Failed to decode the uploaded file. Please ensure it's a valid GeoJSON format.")
|
70 |
+
|
71 |
+
# Map Selection
|
72 |
+
map_choices = ["Hate Tweets", "MPS Hate Crime Data", "MPS All Crime Data"]
|
73 |
+
if uploaded_geojson:
|
74 |
+
map_choices.append("Uploaded GeoJSON")
|
75 |
+
|
76 |
+
selected_map_1 = st.selectbox("Select data for Map 1", map_choices)
|
77 |
+
|
78 |
+
# Determine the columns based on the selected dataset for Map 1
|
79 |
+
if selected_map_1 == "Uploaded GeoJSON" and uploaded_geojson:
|
80 |
+
available_columns_1 = get_columns_from_geojson(uploaded_geojson)
|
81 |
+
elif selected_map_1 == "Hate Tweets":
|
82 |
+
available_columns_1 = get_columns_from_url(map_1)
|
83 |
+
elif selected_map_1 == "MPS Hate Crime Data":
|
84 |
+
available_columns_1 = get_columns_from_url(map_2)
|
85 |
+
elif selected_map_1 == "MPS All Crime Data":
|
86 |
+
available_columns_1 = get_columns_from_url(map_3)
|
87 |
+
|
88 |
+
selected_column_1 = st.selectbox("Select column for Map 1 visualization", available_columns_1)
|
89 |
+
|
90 |
+
selected_map_2 = st.selectbox("Select data for Map 2", map_choices)
|
91 |
+
|
92 |
+
# Determine the columns based on the selected dataset for Map 2
|
93 |
+
if selected_map_2 == "Uploaded GeoJSON" and uploaded_geojson:
|
94 |
+
available_columns_2 = get_columns_from_geojson(uploaded_geojson)
|
95 |
+
elif selected_map_2 == "Hate Tweets":
|
96 |
+
available_columns_2 = get_columns_from_url(map_1)
|
97 |
+
elif selected_map_2 == "MPS Hate Crime Data":
|
98 |
+
available_columns_2 = get_columns_from_url(map_2)
|
99 |
+
elif selected_map_2 == "MPS All Crime Data":
|
100 |
+
available_columns_2 = get_columns_from_url(map_3)
|
101 |
+
|
102 |
+
selected_column_2 = st.selectbox("Select column for Map 2 visualization", available_columns_2)
|
103 |
+
|
104 |
+
# Display Maps
|
105 |
+
row1_col1, row1_col2 = st.columns([1, 1])
|
106 |
+
|
107 |
+
with row1_col1:
|
108 |
+
m1 = leafmap.Map(center=[51.50, -0.1], zoom=10)
|
109 |
+
if selected_map_1 == "Uploaded GeoJSON":
|
110 |
+
m1.add_data(uploaded_geojson, column=selected_column_1)
|
111 |
+
elif selected_map_1 == "Hate Tweets":
|
112 |
+
m1.add_data(map_1, column=selected_column_1)
|
113 |
+
elif selected_map_1 == "MPS Hate Crime Data":
|
114 |
+
m1.add_data(map_2, column=selected_column_1)
|
115 |
+
else:
|
116 |
+
m1.add_data(map_3, column=selected_column_1)
|
117 |
+
|
118 |
+
with row1_col2:
|
119 |
+
m2 = leafmap.Map(center=[51.50, -0.1], zoom=10)
|
120 |
+
if selected_map_2 == "Uploaded GeoJSON":
|
121 |
+
m2.add_data(uploaded_geojson, column=selected_column_2)
|
122 |
+
elif selected_map_2 == "Hate Tweets":
|
123 |
+
m2.add_data(map_1, column=selected_column_2)
|
124 |
+
elif selected_map_2 == "MPS Hate Crime Data":
|
125 |
+
m2.add_data(map_2, column=selected_column_2)
|
126 |
+
else:
|
127 |
+
m2.add_data(map_3, column=selected_column_2)
|
128 |
+
|
129 |
+
# Zoom
|
130 |
+
longitude = -0.1
|
131 |
+
latitude = 51.50
|
132 |
+
zoomlevel = st.number_input("Zoom", 0, 20, 10)
|
133 |
+
|
134 |
+
row2_col1, row2_col2 = st.columns([1, 1])
|
135 |
+
|
136 |
+
with row2_col1:
|
137 |
+
m1.set_center(longitude, latitude, zoomlevel)
|
138 |
+
with row2_col2:
|
139 |
+
m2.set_center(longitude, latitude, zoomlevel)
|
140 |
+
|
141 |
+
row3_col1, row3_col2 = st.columns([1, 1])
|
142 |
+
|
143 |
+
with row3_col1:
|
144 |
+
m1.to_streamlit()
|
145 |
+
with row3_col2:
|
146 |
+
m2.to_streamlit()
|
pages/3_😡_Hate_Tweets.py
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import leafmap.foliumap as leafmap
|
3 |
+
import whitebox
|
4 |
+
|
5 |
+
|
6 |
+
st.set_page_config(layout="wide")
|
7 |
+
|
8 |
+
st.sidebar.info(
|
9 |
+
"""
|
10 |
+
- Web App URL: <https://interactive-crime-map.hf.space/>
|
11 |
+
- HuggingFace repository: <https://huggingface.co/spaces/interactive-crime/map/tree/main>
|
12 |
+
"""
|
13 |
+
)
|
14 |
+
|
15 |
+
st.sidebar.title("Contact")
|
16 |
+
st.sidebar.info(
|
17 |
+
"""
|
18 |
+
Yunus Serhat Bıçakçı at [yunusserhat.com](https://yunusserhat.com) | [GitHub](https://github.com/yunusserhat) | [Twitter](https://twitter.com/yunusserhat) | [LinkedIn](https://www.linkedin.com/in/yunusserhat)
|
19 |
+
"""
|
20 |
+
)
|
21 |
+
|
22 |
+
st.title("Hate Speech Interactive Map Application")
|
23 |
+
|
24 |
+
please_note = '<p style="color:Red; font-weight:bold;">Please note that the information displayed may contain sentences containing hateful content. Kindly keep this in mind before reviewing the details.</p>'
|
25 |
+
|
26 |
+
|
27 |
+
st.markdown(
|
28 |
+
"""
|
29 |
+
The interactive map illustrate a hate crime tweets in London boroughs for the month of December 2022. Model 1 is the latest model from [TweetNLP](https://arxiv.org/pdf/2206.14774.pdf) and [CardiffNLP](https://huggingface.co/cardiffnlp/twitter-roberta-base-hate-multiclass-latest), and Model 2 is an older version of Model 1, owned by [Dimosthenis Antypas](https://huggingface.co/antypasd/). Both models have been utilized for hate speech detection.
|
30 |
+
"""
|
31 |
+
)
|
32 |
+
st.markdown(please_note, unsafe_allow_html=True)
|
33 |
+
st.markdown(
|
34 |
+
"""
|
35 |
+
|
36 |
+
"""
|
37 |
+
)
|
38 |
+
|
39 |
+
# add whitebox tools to the map
|
40 |
+
m = leafmap.Map(center=[51.50, -0.1], zoom=10)
|
41 |
+
tweets2 = 'https://raw.githubusercontent.com/yunusserhat/data/main/data/London2022DecHateTweetsLatLong.csv'
|
42 |
+
borough2 = 'https://raw.githubusercontent.com/yunusserhat/data/main/data/londonborough.geojson'
|
43 |
+
|
44 |
+
tweets = 'https://raw.githubusercontent.com/yunusserhat/data/main/data/multiclass_latest_latlng.csv'
|
45 |
+
borough = 'https://raw.githubusercontent.com/yunusserhat/data/main/data/londonborough.geojson'
|
46 |
+
|
47 |
+
map_option = st.selectbox('Choose a Map', ('Model 1', 'Model 2'))
|
48 |
+
palette_model_1 = ["red", "blue", "green", "yellow", "purple", "orange"]
|
49 |
+
palette_model_2 = ["red", "blue", "green", "yellow", "purple"]
|
50 |
+
|
51 |
+
if map_option == 'Model 1':
|
52 |
+
m.add_geojson(borough, layer_name='London Boroughs')
|
53 |
+
m.add_points_from_xy(
|
54 |
+
tweets,
|
55 |
+
x="Longitude",
|
56 |
+
y="Latitude",
|
57 |
+
color_column='Hate Prediction',
|
58 |
+
palette=palette_model_1,
|
59 |
+
spin=True,
|
60 |
+
add_legend=True
|
61 |
+
)
|
62 |
+
else:
|
63 |
+
m.add_geojson(borough2, layer_name='London Boroughs 2')
|
64 |
+
m.add_points_from_xy(
|
65 |
+
tweets2,
|
66 |
+
x="Longitude",
|
67 |
+
y="Latitude",
|
68 |
+
color_column='Hate Prediction',
|
69 |
+
palette=palette_model_2,
|
70 |
+
spin=True,
|
71 |
+
add_legend=True
|
72 |
+
)
|
73 |
+
|
74 |
+
m.to_streamlit()
|
pages/4_Test.py
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import leafmap.foliumap as leafmap
|
3 |
+
import json
|
4 |
+
import requests
|
5 |
+
|
6 |
+
# Functions to dynamically fetch columns
|
7 |
+
def get_columns_from_geojson(geojson_data):
|
8 |
+
"""Retrieve column names from a GeoJSON data."""
|
9 |
+
if "features" in geojson_data:
|
10 |
+
properties = geojson_data["features"][0]["properties"]
|
11 |
+
return list(properties.keys())
|
12 |
+
return []
|
13 |
+
|
14 |
+
def get_columns_from_url(url):
|
15 |
+
"""Retrieve column names from a GeoJSON URL."""
|
16 |
+
response = requests.get(url)
|
17 |
+
geojson_data = response.json()
|
18 |
+
return get_columns_from_geojson(geojson_data)
|
19 |
+
|
20 |
+
st.set_page_config(layout="wide")
|
21 |
+
|
22 |
+
# Sidebar Information
|
23 |
+
st.sidebar.info(
|
24 |
+
'''
|
25 |
+
- Web App URL: <https://interactive-crime-map.hf.space/>
|
26 |
+
- HuggingFace repository: <https://huggingface.co/spaces/interactive-crime/map/tree/main>
|
27 |
+
'''
|
28 |
+
)
|
29 |
+
|
30 |
+
st.sidebar.title("Contact")
|
31 |
+
st.sidebar.info(
|
32 |
+
'''
|
33 |
+
Yunus Serhat Bıçakçı at [yunusserhat.com](https://yunusserhat.com) | [GitHub](https://github.com/yunusserhat) | [Twitter](https://twitter.com/yunusserhat) | [LinkedIn](https://www.linkedin.com/in/yunusserhat)
|
34 |
+
'''
|
35 |
+
)
|
36 |
+
|
37 |
+
# Title and Description
|
38 |
+
st.title("Interactive Analysis of Hate Metrics in London & Custom Dataset Visualization")
|
39 |
+
|
40 |
+
st.markdown(
|
41 |
+
'''
|
42 |
+
Dive into an interactive analysis of hate metrics in London, exploring the disparities and correlations between hate-related tweets and reported crimes in boroughs. This platform offers a comparative visualization based on data from X and the London Metropolitan Police Service as of December 2022.
|
43 |
+
|
44 |
+
Additionally, users can upload and visualize their own GeoJSON datasets, facilitating personalized analysis and insights. Delve deeper into the patterns of hate sentiment, understand how online behavior might mirror real-world incidents, or uncover patterns in your own data.
|
45 |
+
'''
|
46 |
+
)
|
47 |
+
|
48 |
+
# File Uploader
|
49 |
+
uploaded_file = st.file_uploader("Upload a GeoJSON file", type=["geojson"])
|
50 |
+
uploaded_geojson = None
|
51 |
+
|
52 |
+
# Map URLs
|
53 |
+
map_1 = "https://raw.githubusercontent.com/yunusserhat/data/main/data/boroughs_count_df_2022_dec.geojson"
|
54 |
+
map_2 = "https://raw.githubusercontent.com/yunusserhat/data/main/data/mps_hate_2022_dec_count.geojson"
|
55 |
+
map_3 = "https://raw.githubusercontent.com/yunusserhat/data/main/data/mps2022dec_count.geojson"
|
56 |
+
|
57 |
+
if uploaded_file:
|
58 |
+
try:
|
59 |
+
uploaded_geojson = json.load(uploaded_file)
|
60 |
+
if "features" not in uploaded_geojson:
|
61 |
+
st.warning("The uploaded file does not seem to be a valid GeoJSON format.")
|
62 |
+
uploaded_geojson = None
|
63 |
+
else:
|
64 |
+
st.success("GeoJSON file uploaded successfully!")
|
65 |
+
except json.JSONDecodeError:
|
66 |
+
st.error("Failed to decode the uploaded file. Please ensure it's a valid GeoJSON format.")
|
67 |
+
|
68 |
+
# Map Selection
|
69 |
+
map_choices = ["Hate Tweets", "MPS Hate Crime Data", "MPS All Crime Data"]
|
70 |
+
if uploaded_geojson:
|
71 |
+
map_choices.append("Uploaded GeoJSON")
|
72 |
+
|
73 |
+
selected_map_1 = st.selectbox("Select data for Map 1", map_choices)
|
74 |
+
|
75 |
+
# Determine the columns based on the selected dataset for Map 1
|
76 |
+
if selected_map_1 == "Uploaded GeoJSON" and uploaded_geojson:
|
77 |
+
available_columns_1 = get_columns_from_geojson(uploaded_geojson)
|
78 |
+
elif selected_map_1 == "Hate Tweets":
|
79 |
+
available_columns_1 = get_columns_from_url(map_1)
|
80 |
+
elif selected_map_1 == "MPS Hate Crime Data":
|
81 |
+
available_columns_1 = get_columns_from_url(map_2)
|
82 |
+
elif selected_map_1 == "MPS All Crime Data":
|
83 |
+
available_columns_1 = get_columns_from_url(map_3)
|
84 |
+
|
85 |
+
selected_column_1 = st.selectbox("Select column for Map 1 visualization", available_columns_1)
|
86 |
+
|
87 |
+
selected_map_2 = st.selectbox("Select data for Map 2", map_choices)
|
88 |
+
|
89 |
+
# Determine the columns based on the selected dataset for Map 2
|
90 |
+
if selected_map_2 == "Uploaded GeoJSON" and uploaded_geojson:
|
91 |
+
available_columns_2 = get_columns_from_geojson(uploaded_geojson)
|
92 |
+
elif selected_map_2 == "Hate Tweets":
|
93 |
+
available_columns_2 = get_columns_from_url(map_1)
|
94 |
+
elif selected_map_2 == "MPS Hate Crime Data":
|
95 |
+
available_columns_2 = get_columns_from_url(map_2)
|
96 |
+
elif selected_map_2 == "MPS All Crime Data":
|
97 |
+
available_columns_2 = get_columns_from_url(map_3)
|
98 |
+
|
99 |
+
selected_column_2 = st.selectbox("Select column for Map 2 visualization", available_columns_2)
|
100 |
+
|
101 |
+
# Display Maps
|
102 |
+
row1_col1, row1_col2 = st.columns([1, 1])
|
103 |
+
|
104 |
+
with row1_col1:
|
105 |
+
m1 = leafmap.Map(center=[51.50, -0.1], zoom=10)
|
106 |
+
if selected_map_1 == "Uploaded GeoJSON":
|
107 |
+
m1.add_data(uploaded_geojson, column=selected_column_1)
|
108 |
+
elif selected_map_1 == "Hate Tweets":
|
109 |
+
m1.add_data(map_1, column=selected_column_1)
|
110 |
+
elif selected_map_1 == "MPS Hate Crime Data":
|
111 |
+
m1.add_data(map_2, column=selected_column_1)
|
112 |
+
else:
|
113 |
+
m1.add_data(map_3, column=selected_column_1)
|
114 |
+
|
115 |
+
with row1_col2:
|
116 |
+
m2 = leafmap.Map(center=[51.50, -0.1], zoom=10)
|
117 |
+
if selected_map_2 == "Uploaded GeoJSON":
|
118 |
+
m2.add_data(uploaded_geojson, column=selected_column_2)
|
119 |
+
elif selected_map_2 == "Hate Tweets":
|
120 |
+
m2.add_data(map_1, column=selected_column_2)
|
121 |
+
elif selected_map_2 == "MPS Hate Crime Data":
|
122 |
+
m2.add_data(map_2, column=selected_column_2)
|
123 |
+
else:
|
124 |
+
m2.add_data(map_3, column=selected_column_2)
|
125 |
+
|
126 |
+
# Zoom
|
127 |
+
longitude = -0.1
|
128 |
+
latitude = 51.50
|
129 |
+
zoomlevel = st.number_input("Zoom", 0, 20, 10)
|
130 |
+
|
131 |
+
row2_col1, row2_col2 = st.columns([1, 1])
|
132 |
+
|
133 |
+
with row2_col1:
|
134 |
+
m1.set_center(longitude, latitude, zoomlevel)
|
135 |
+
with row2_col2:
|
136 |
+
m2.set_center(longitude, latitude, zoomlevel)
|
137 |
+
|
138 |
+
row3_col1, row3_col2 = st.columns([1, 1])
|
139 |
+
|
140 |
+
with row3_col1:
|
141 |
+
m1.to_streamlit()
|
142 |
+
with row3_col2:
|
143 |
+
m2.to_streamlit()
|
postBuild
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# enable nbserverproxy
|
2 |
+
jupyter serverextension enable --sys-prefix nbserverproxy
|
3 |
+
# streamlit launches at startup
|
4 |
+
mv streamlit_call.py ${NB_PYTHON_PREFIX}/lib/python*/site-packages/
|
5 |
+
# enable streamlit extension
|
6 |
+
jupyter serverextension enable --sys-prefix streamlit_call
|
requirements.txt
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
--find-links=https://girder.github.io/large_image_wheels GDAL
|
2 |
+
# cartopy
|
3 |
+
folium==0.13.0
|
4 |
+
# ipywidgets<8.0.5
|
5 |
+
geemap
|
6 |
+
ffmpeg-python
|
7 |
+
geopandas
|
8 |
+
# jupyter-server-proxy
|
9 |
+
# keplergl
|
10 |
+
leafmap>=0.18.6
|
11 |
+
# localtileserver
|
12 |
+
# nbserverproxy
|
13 |
+
owslib
|
14 |
+
palettable
|
15 |
+
plotly
|
16 |
+
streamlit
|
17 |
+
streamlit-bokeh-events
|
18 |
+
streamlit-folium
|
19 |
+
# streamlit-keplergl
|
20 |
+
# tropycal
|
21 |
+
# git+https://github.com/giswqs/leafmap
|
22 |
+
# git+https://github.com/giswqs/geemap
|
23 |
+
altair<5
|
24 |
+
mapclassify
|
setup.sh
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# sudo add-apt-repository ppa:ubuntugis/ppa && sudo apt-get update
|
2 |
+
# sudo apt-get update
|
3 |
+
# sudo apt-get install python3-dev
|
4 |
+
# sudo apt-get install gdal-bin
|
5 |
+
# sudo apt-get install libgdal-dev
|
6 |
+
# export CPLUS_INCLUDE_PATH=/usr/include/gdal
|
7 |
+
# export C_INCLUDE_PATH=/usr/include/gdal
|
8 |
+
# gdal-config --version
|
9 |
+
# pip install GDAL==$(gdal-config --version | awk -F'[.]' '{print $1"."$2}') localtileserver
|
10 |
+
|
11 |
+
mkdir -p ~/.streamlit/
|
12 |
+
echo "\
|
13 |
+
[server]\n\
|
14 |
+
headless = true\n\
|
15 |
+
port = $PORT\n\
|
16 |
+
enableCORS = false\n\
|
17 |
+
\n\
|
18 |
+
" > ~/.streamlit/config.toml
|