FranciscoGS commited on
Commit
cd7a895
·
verified ·
1 Parent(s): 41a6c11

Upload 15 files

Browse files
.github/workflows/sync-hf.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face hub
2
+ on:
3
+ push:
4
+ branches: [main]
5
+
6
+ # to run this workflow manually from the Actions tab
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ sync-to-hub:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+ with:
15
+ fetch-depth: 0
16
+ lfs: true
17
+ - name: Push to hub
18
+ env:
19
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
+ run: git push --force https://giswqs:$HF_TOKEN@huggingface.co/spaces/giswqs/solara-geospatial main
.gitignore ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
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
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
Dockerfile ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM jupyter/base-notebook:latest
2
+
3
+ <<<<<<< HEAD
4
+ RUN mamba install -c conda-forge leafmap geopandas localtileserver ipyleaflet ipywidgets -y && \
5
+ =======
6
+ RUN mamba install -c conda-forge leafmap geopandas localtileserver ipyleaflet -y && \
7
+ >>>>>>> origin/main
8
+ fix-permissions "${CONDA_DIR}" && \
9
+ fix-permissions "/home/${NB_USER}"
10
+
11
+ COPY requirements.txt .
12
+ RUN pip install -r requirements.txt
13
+
14
+ <<<<<<< HEAD
15
+ WORKDIR /home/${NB_USER}
16
+ COPY /pages ./pages
17
+ =======
18
+ # Dir
19
+ WORKDIR /home/${NB_USER}
20
+ COPY pages ./pages
21
+ >>>>>>> origin/main
22
+ COPY data ./data
23
+
24
+ ENV PROJ_LIB='/opt/conda/share/proj'
25
+
26
+ USER root
27
+ RUN chown -R ${NB_UID} ${HOME}
28
+ USER ${NB_USER}
29
+
30
+ EXPOSE 8765
31
+
32
+ CMD ["solara", "run", "./pages", "--host=0.0.0.0"]
33
+
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Open Geospatial Solutions
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 CHANGED
@@ -1,11 +1,17 @@
1
  ---
2
- title: Servicios Apoyo
3
- emoji: 📊
4
- colorFrom: pink
5
- colorTo: yellow
6
  sdk: docker
7
  pinned: false
8
  license: mit
 
9
  ---
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
1
  ---
2
+ title: Solara Geospatial
3
+ emoji: 🌍
4
+ colorFrom: green
5
+ colorTo: red
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
+ app_port: 8765
10
  ---
11
 
12
+ ## Introduction
13
+
14
+ A geospatial dashboard to understand the provision of support services for people in conditions of human mobility and its dynamics of concentration in the urban destination or transit within Ecuador.
15
+
16
+ - Diversa Website: <https://giswqs-solara-geospatial.hf.space>
17
+ - Hugging Face: <https://huggingface.co/spaces/DiversaStudio/Servicios_apoyo>
Servicios_apoyo/.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Servicios_apoyo/README.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Servicios Apoyo
3
+ emoji: 📊
4
+ colorFrom: pink
5
+ colorTo: yellow
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ ---
10
+
11
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
data/color_codes.csv ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Categoria,Tipo,Color
2
+ apoyo,iglesia,#7400B8
3
+ apoyo,abogado,#6930C3
4
+ apoyo,centro de apoyo,#5E60CE
5
+ apoyo,albergue,#5390D9
6
+ apoyo,ong,#4EA8DE
7
+ apoyo,centro social,#48BFE3
8
+ apoyo,acnur,#56CFE1
9
+ apoyo,punto de acceso de emergencia,#64DFDF
10
+ apoyo,warmi,#72EFDD
11
+ centros de oportunidades laborales,"comercios, servicios y oficios",#7400B8
12
+ centros de oportunidades laborales,"restaurantes, cafés y bebidas",#6930C3
13
+ educación,escuela,#7400B8
14
+ educación,colegio,#6930C3
15
+ educación,universidad,#5E60CE
16
+ educación,centro de formación técnica,#5390D9
17
+ educación,jardín de infantes,#4EA8DE
18
+ educación,centro de educación continua,#48BFE3
19
+ educación,centros de formación técnica,#56CFE1
20
+ espacio público,parques y naturaleza,#7400B8
21
+ espacio público,deportes y recreación,#6930C3
22
+ espacio público,turismo y cultura,#5E60CE
23
+ espacio público,centro comunitario,#5390D9
24
+ finanzas,banco,#7400B8
25
+ finanzas,cajero automático,#6930C3
26
+ finanzas,cooperativa,#5E60CE
27
+ finanzas,casa de cambio,#5390D9
28
+ finanzas,banco del barrio,#4EA8DE
29
+ gobierno central,policia nacional y organismos de control,#7400B8
30
+ gobierno central,bomberos,#6930C3
31
+ gobierno central,ministerios,#5E60CE
32
+ gobierno central,embajada,#5390D9
33
+ gobierno central,instituciones de protección de derechos,#4EA8DE
34
+ gobierno central,consulado,#48BFE3
35
+ gobierno central,servicios públicos,#56CFE1
36
+ gobierno central,secretarías,#64DFDF
37
+ gobierno descentralizado,gad municipal,#7400B8
38
+ gobierno descentralizado,servicios públicos,#6930C3
39
+ gobierno descentralizado,instituciones de protección de derechos,#5E60CE
40
+ gobierno descentralizado,junta cantonal de proteccion de derechos,#5390D9
41
+ gobierno descentralizado,centro de información de movilidad humana,#4EA8DE
42
+ gobierno descentralizado,gad provincial,#48BFE3
43
+ gobierno descentralizado,policia nacional y organismos de control,#56CFE1
44
+ gobierno descentralizado,centro de desarollo empresarial,#64DFDF
45
+ gobierno descentralizado,distrito de educación,#72EFDD
46
+ gobierno descentralizado,juntas parroquiales,#80FFDB
47
+ hospedaje,hotel,#7400B8
48
+ hospedaje,hostal,#6930C3
49
+ necesidades básicas,lavandería,#7400B8
50
+ necesidades básicas,oficina postal,#6930C3
51
+ necesidades básicas,baño público y acceso a agua,#5E60CE
52
+ salud,farmacia,#7400B8
53
+ salud,consultorios,#6930C3
54
+ salud,clínica,#5E60CE
55
+ salud,hospital,#5390D9
56
+ salud,centro de salud,#4EA8DE
57
+ salud,cruz roja,#48BFE3
58
+ salud,hogar de enfermería,#56CFE1
59
+ salud,estación de ambulancia,#64DFDF
60
+ transporte,parada de buses,#7400B8
61
+ transporte,gasolinera,#6930C3
62
+ transporte,agencia de viajes,#5E60CE
63
+ transporte,terminal terrestre,#5390D9
64
+ transporte,parada de taxis,#4EA8DE
65
+ transporte,estación de tren,#48BFE3
data/logo_diversa.png ADDED
data/logo_giz.png ADDED
data/pois_final_control.csv ADDED
The diff for this file is too large to render. See raw diff
 
data/poiscount_r8hex_large.csv ADDED
The diff for this file is too large to render. See raw diff
 
pages/Servicios_apoyo.py ADDED
@@ -0,0 +1,350 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Libraries
2
+ import solara
3
+ <<<<<<< HEAD
4
+ import solara.website
5
+ from ipyleaflet import Map, GeoJSON, GeoData, LayersControl, Marker, MarkerCluster, basemaps, Choropleth, ZoomControl, LayerGroup, CircleMarker, Popup, LegendControl
6
+ from ipywidgets import HTML
7
+ from branca.colormap import linear
8
+ import plotly.express as px
9
+ import matplotlib
10
+ import seaborn as sns
11
+ =======
12
+ from ipyleaflet import Map, GeoJSON, GeoData, LayersControl, Marker, MarkerCluster, basemaps, Choropleth
13
+ from branca.colormap import linear
14
+ import plotly.express as px
15
+ import matplotlib
16
+ >>>>>>> origin/main
17
+ import geopandas as gpd
18
+ import pandas as pd
19
+ import numpy as np
20
+ from shapely import wkt
21
+ import json
22
+ import re
23
+ <<<<<<< HEAD
24
+ from pathlib import Path
25
+
26
+ #Read and clean Pois Data Set
27
+ df = pd.read_csv('./data/pois_final_control.csv')
28
+ df = df[(df['EnEstudio'] == True)]
29
+ df = df[df['Geometry'].notnull()]
30
+ df['Tipo'] = df['Tipo'].str.lower()
31
+ df['Tipo'] = df['Tipo'].replace('terminal terrestre de buses', 'terminal terrestre')
32
+ df['Tipo'] = df['Tipo'].replace('comercios,servicios y oficios', 'comercios, servicios y oficios')
33
+ df['Tipo'] = df['Tipo'].replace('alimentos y bebidas', 'restaurantes, cafés y bebidas')
34
+ excluded_cantons = ['HUACA', 'EL ORO', 'TUNGURAHUA', 'MIRA', 'MONTUFAR', 'BOLIVAR', 'SAN GABRIEL']
35
+ df = df[~df['Canton'].isin(excluded_cantons)]
36
+ df['geometry'] = df['Geometry'].apply(wkt.loads)
37
+ #Color codes
38
+ color_codes = pd.read_csv('./data/color_codes.csv')
39
+ df = pd.merge(df, color_codes, on=['Categoria', 'Tipo'], how='left')
40
+ #Make a geo dataframe
41
+ =======
42
+
43
+ #Read Pois Data Set
44
+ df = pd.read_csv('./data/pois_final_control.csv')
45
+ df = df[(df['EnEstudio'] == True)]
46
+ df = df[df['Geometry'].notnull()]
47
+ df['geometry'] = df['Geometry'].apply(wkt.loads)
48
+ >>>>>>> origin/main
49
+ pois = gpd.GeoDataFrame(df, geometry='geometry')
50
+ pois = pois.set_crs(epsg=4326, inplace=True)
51
+ pois = pois.drop('EnEstudio', axis=1)
52
+ pois = pois.drop('Geometry', axis=1)
53
+
54
+ #Read H3 Data Set
55
+ df1 = pd.read_csv('./data/poiscount_r8hex_large.csv', index_col=0)
56
+ df1 = df1[df1['Conteo'] > 0]
57
+ df1 = df1.drop('EnEstudio', axis=1)
58
+ <<<<<<< HEAD
59
+ df1['Canton'] = df1['Canton'].replace('RUMIÑAHUI', 'RUMINAHUI')
60
+ =======
61
+ >>>>>>> origin/main
62
+ df1['geometry'] = df1['geometry'].apply(wkt.loads)
63
+ h3 = gpd.GeoDataFrame(df1, geometry='geometry')
64
+ h3 = h3.set_crs(epsg=4326, inplace=True)
65
+
66
+ #Set controls values
67
+ cantones = [str(k) for k in pois['Canton'].unique().tolist()]
68
+ categorias = [str(k) for k in pois['Categoria'].unique().tolist()]
69
+ <<<<<<< HEAD
70
+ canton = solara.reactive('GUAYAQUIL')
71
+ categoria = solara.reactive('salud')
72
+ tipo = solara.reactive([])
73
+ limit = solara.reactive(1000)
74
+
75
+ #Images path
76
+ logo1 = "./data/logo_diversa.png"
77
+ logo2 = "./data/logo_giz.png"
78
+ =======
79
+ canton = solara.reactive('QUITO')
80
+ categoria = solara.reactive('salud')
81
+ limit = solara.reactive(2500)
82
+ >>>>>>> origin/main
83
+
84
+ #Filter function
85
+ def data_filter(df, city, category):
86
+ df_filtered = df.loc[df['Canton']==city]
87
+ <<<<<<< HEAD
88
+ df_filtered = df_filtered[df_filtered['Categoria']==category]
89
+ return df_filtered
90
+
91
+ #Dynamic filter function
92
+ def dynamic_filter(df, types):
93
+ return df[df['Tipo'].isin(types)]
94
+
95
+ =======
96
+ df_filtered = df_filtered[df_filtered['Categoria']==category]
97
+ return df_filtered
98
+
99
+ >>>>>>> origin/main
100
+ #Map function
101
+ def ipyleaflet_map(geo_df, h3_df, h3_geojson):
102
+ # Define map center and initial zoom level
103
+ center = (geo_df.geometry.y.mean(), geo_df.geometry.x.mean())
104
+ <<<<<<< HEAD
105
+ zoom = 12
106
+ # Create the map
107
+ m = Map(center=center,
108
+ zoom=zoom, zoom_control=False, scroll_wheel_zoom=True,
109
+ basemap=basemaps.CartoDB.Positron)
110
+ m.add(ZoomControl(position='bottomleft'))
111
+
112
+ # Initialize a LayerGroup to hold all CircleMarkers
113
+ markers_layer = LayerGroup(name='Servicios')
114
+
115
+ # Iterate over geo_df rows to create CircleMarker for each point with dynamic fillColor
116
+ for index, row in geo_df.iterrows():
117
+ marker = CircleMarker()
118
+ marker.location = (row.geometry.y, row.geometry.x)
119
+ marker.radius = 3
120
+ marker.color = row['Color']
121
+ marker.fill_color = row['Color']
122
+ marker.fill_opacity = 0.65
123
+ marker.weight = 1
124
+ marker.popup = HTML("<b>Nombre:</b> " + str(row['Nombre']) + "<br>" +
125
+ "<b>Tipo:</b> " + str(row['Tipo']) + "<br>" +
126
+ "<b>Categoría:</b> " + str(row['Categoria']))
127
+ markers_layer.add_layer(marker)
128
+
129
+ # Add the LayerGroup to the map
130
+ m.add_layer(markers_layer)
131
+
132
+ #h3 Density
133
+ value_min = min(h3_df['Conteo'])
134
+ value_max = max(h3_df['Conteo'])
135
+ value_med = int(np.median(h3_df['Conteo']))
136
+ legend_labels = {
137
+ f"Min: {value_min}": "#FFEDA0",
138
+ f"Med: {value_med}": "#FEB24C",
139
+ f"Max: {value_max}": "#F03B20"}
140
+ legend = LegendControl(legend_labels, title="Concentración de servicios", position="topright")
141
+ m.add(legend)
142
+ if value_min == value_max:
143
+ value_max += 1
144
+ colormap = linear.YlOrRd_04.scale(value_min, value_max)
145
+ choro_data = dict(zip(h3_df.index.astype(str), h3_df['Conteo']))
146
+
147
+ =======
148
+ zoom = 11
149
+ # Create the map
150
+ m = Map(center=center, zoom=zoom)
151
+
152
+ # Create GeoData from the GeoDataFrame
153
+ services = GeoData(
154
+ geo_dataframe=geo_df,
155
+ style={
156
+ 'color': 'black',
157
+ 'radius': 7,
158
+ 'fillColor': '#3366cc',
159
+ 'opacity': 0.5,
160
+ 'weight': 1.9,
161
+ 'dashArray': '2',
162
+ 'fillOpacity': 0.6
163
+ },
164
+ hover_style={
165
+ 'fillColor': 'red',
166
+ 'fillOpacity': 0.2
167
+ },
168
+ point_style={
169
+ 'radius': 7,
170
+ 'color': 'red',
171
+ 'fillOpacity': 0.8,
172
+ 'fillColor': 'blue',
173
+ 'weight': 3
174
+ },
175
+ name='Servicios de Apoyo'
176
+ )
177
+
178
+ #h3 Density
179
+ colormap = linear.YlOrRd_04.scale(min(h3_df['Conteo']), max(h3_df['Conteo']))
180
+ choro_data = dict(zip(h3_df.index.astype(str), h3_df['Conteo']))
181
+ >>>>>>> origin/main
182
+ h3_choropleth = Choropleth(
183
+ geo_data=h3_geojson,
184
+ choro_data=choro_data,
185
+ colormap=colormap,
186
+ border_color='black',
187
+ <<<<<<< HEAD
188
+ style={'fillOpacity': 0.85},#, 'dashArray': '5, 5'},
189
+ #hover_style={"fillColor": "red"},
190
+ name='Densidad'
191
+ )
192
+
193
+ # Add h3 layer to the map
194
+ =======
195
+ style={'fillOpacity': 0.8, 'dashArray': '5, 5'},
196
+ name='Densidad'
197
+ )
198
+
199
+ # Add GeoData to the map
200
+ m.add_layer(services)
201
+ >>>>>>> origin/main
202
+ m.add_layer(h3_choropleth)
203
+
204
+ # Add Cluster Markers
205
+ markers = [Marker(location=(point.y, point.x)) for point in geo_df.geometry]
206
+
207
+ # Create and add MarkerCluster to the map
208
+ <<<<<<< HEAD
209
+ marker_cluster = MarkerCluster(markers=markers, name='Cluster')
210
+ m.add_layer(marker_cluster)
211
+
212
+ # Add layer control
213
+ m.add_control(LayersControl(position='topleft', collapsed=False))
214
+ =======
215
+ marker_cluster = MarkerCluster(markers=markers, name='Cluster Markers')
216
+ m.add_layer(marker_cluster)
217
+
218
+ # Add layer control
219
+ m.add_control(LayersControl())
220
+ >>>>>>> origin/main
221
+
222
+ # Display the map
223
+ display(m)
224
+
225
+ #Interactive bar chart function
226
+ def type_chart_plotly(df):
227
+ <<<<<<< HEAD
228
+ tipo_counts = df.groupby('Tipo')['Color'].first()
229
+ counts = df['Tipo'].value_counts().sort_values(ascending=False)
230
+ plot_df = counts.rename_axis('Tipo').reset_index(name='Counts')
231
+ plot_df['Color'] = plot_df['Tipo'].map(tipo_counts)
232
+ fig = px.bar(plot_df, orientation='h',
233
+ x='Counts', y='Tipo',
234
+ labels={'Counts': 'Conteo', 'Tipo': 'Tipo'},
235
+ title='Conteo',
236
+ color='Tipo', # Use 'Tipo' for color mapping
237
+ color_discrete_map=tipo_counts.to_dict())
238
+ fig.update_layout(showlegend=False, xaxis_title=None, height=450)
239
+ solara.FigurePlotly(fig)
240
+
241
+ def type_chart_plotly_donut(df):
242
+ # Count the occurrences of each 'Tipo'
243
+ tipo_counts = df['Tipo'].value_counts().reset_index(name='Counts')
244
+ tipo_counts.rename(columns={'index': 'Tipo'}, inplace=True)
245
+
246
+ # Get the first color for each 'Tipo' for coloring the pie chart
247
+ colors = df.groupby('Tipo')['Color'].first().reindex(tipo_counts['Tipo']).to_dict()
248
+
249
+ # Create the pie (donut) chart
250
+ fig = px.pie(tipo_counts, names='Tipo', values='Counts',
251
+ title='Porcentaje',
252
+ color='Tipo',
253
+ color_discrete_map=colors,
254
+ hole=0.6) # Adjust the hole size to create a donut chart
255
+
256
+ # Optional: Customize chart appearance
257
+ fig.update_traces(textinfo='percent')
258
+ fig.update_layout(showlegend=False, height=450)
259
+ solara.FigurePlotly(fig)
260
+
261
+ #Main panel layout
262
+ @solara.component
263
+ def View():
264
+ global tipo
265
+ dff = data_filter(pois, canton.value, categoria.value)
266
+ if tipo.value: # Check if 'tipo' is not empty
267
+ dff = dynamic_filter(dff, tipo.value)
268
+ =======
269
+ tipo_counts = df['Tipo'].value_counts().sort_values(ascending=True)
270
+
271
+ fig = px.bar(tipo_counts, orientation='h',
272
+ x=tipo_counts.values, y=tipo_counts.index,
273
+ labels={'x': 'Conteo', 'y': 'Tipo'},
274
+ title='Servicios')
275
+ solara.FigurePlotly(fig)
276
+
277
+ #Main panel layout
278
+ @solara.component
279
+ def View():
280
+ dff = data_filter(pois, canton.value, categoria.value)
281
+ >>>>>>> origin/main
282
+ h3f = data_filter(h3, canton.value, categoria.value)
283
+ geo_json_H3 = json.loads(h3f.to_json())
284
+ row_count = len(dff)
285
+ if row_count > limit.value:
286
+ <<<<<<< HEAD
287
+ solara.Warning(f"En el mapa sólo se muestran {limit.value} de {row_count:,} puntos")
288
+ with solara.Card(""):
289
+ ipyleaflet_map(dff.iloc[:limit.value], h3f, geo_json_H3)
290
+ if row_count > 0:
291
+ with solara.Columns([1.75, 1.25]):
292
+ with solara.Card(""):
293
+ type_chart_plotly(dff)
294
+ with solara.Card(""):
295
+ type_chart_plotly_donut(dff)
296
+ else:
297
+ solara.Warning("Has filtrado todos los datos, no se muestran gráficos")
298
+
299
+ @solara.component
300
+ def Controls():
301
+ global tipo # Refe
302
+ solara.Select('Cantón', values=cantones, value=canton)
303
+ solara.Select('Categoría', values=categorias, value=categoria)
304
+ # Dynamically update 'tipos' based on the current selection of 'canton' and 'categoria'
305
+ dff = data_filter(pois, canton.value, categoria.value)
306
+ tipos = [str(k) for k in dff['Tipo'].unique().tolist()]
307
+ solara.SelectMultiple('Tipo', all_values=tipos, values=tipo)
308
+ solara.Text("Número máximo de puntos a mostrar en el mapa")
309
+ =======
310
+ solara.Warning(f"Only showing the first {limit.value} of {row_count:,} services on map")
311
+ ipyleaflet_map(dff.iloc[:limit.value], h3f, geo_json_H3)
312
+ if row_count > 0:
313
+ with solara.Card(''):
314
+ type_chart_plotly(dff)
315
+ else:
316
+ solara.Warning("You filtered out all the data, no charts shown")
317
+
318
+ #Controls
319
+ @solara.component
320
+ def Controls():
321
+ solara.Select('Cantón', values=cantones, value=canton)
322
+ solara.Select('Categoría', values=categorias, value=categoria)
323
+ #solara.SelectMultiple('Fuente', all_values=fuentes, values=fuente)
324
+ solara.Text("Maximum number of pois to show on map")
325
+ >>>>>>> origin/main
326
+ solara.SliderInt('', value=limit, min=1, max=5000)
327
+
328
+ #Dashboard Layout
329
+ @solara.component
330
+ def Page():
331
+ with solara.Column():
332
+ <<<<<<< HEAD
333
+ solara.Title('Servicios de apoyo para Movilidad Humana')
334
+ with solara.Sidebar():
335
+ with solara.Card(elevation=0.1):
336
+ with solara.Row(gap="0px", justify="space-around"):
337
+ solara.Image(logo1, width='55%')
338
+ solara.Image(logo2, width='50%')
339
+ solara.Markdown('Este panel interactivo, fruto de la colaboración entre [Diversa Studio](https://diversa.studio/) y GIZ, busca ayudar a personas en condición de movilidad humana y al planteamiento de acciones de protección.\n'
340
+ '\n'
341
+ 'Aquí se puede explorar servicios de apoyo para la movilidad humana en diversos cantones del Ecuador. Visualice la distribución de los servicios, analice las agrupaciones y obtenga información a través de mapas y gráficos interactivos.')
342
+ =======
343
+ solara.Title('Servicios de Apoyo para Migrantes')
344
+ with solara.Sidebar():
345
+ >>>>>>> origin/main
346
+ with solara.Card('Filtros'):
347
+ with solara.Column():
348
+ Controls()
349
+ View()
350
+ Page()
requirements.txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ solara
2
+ <<<<<<< HEAD
3
+ =======
4
+ geemap>=0.21.0
5
+ >>>>>>> origin/main
6
+ leafmap
7
+ mapwidget
8
+ geopandas
9
+ pandas
10
+ <<<<<<< HEAD
11
+ numpy
12
+ plotly
13
+ matplotlib
14
+ seaborn
15
+ =======
16
+ plotly
17
+ matplotlib
18
+ >>>>>>> origin/main
19
+ shapely
20
+ pydantic<2.0
testing_nb.ipynb ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "import solara\n",
10
+ "from ipyleaflet import Map, GeoJSON, GeoData, LayersControl, Marker, MarkerCluster\n",
11
+ "import plotly.express as px\n",
12
+ "import matplotlib\n",
13
+ "import geopandas as gpd\n",
14
+ "import pandas as pd\n",
15
+ "from shapely import wkt\n",
16
+ "import re"
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "code",
21
+ "execution_count": 15,
22
+ "metadata": {},
23
+ "outputs": [],
24
+ "source": [
25
+ "def normalize_wkt_point(geometry_str):\n",
26
+ " # Maneja casos de puntos vacíos\n",
27
+ " if geometry_str == 'POINT ()':\n",
28
+ " return None # O puedes devolver un valor predeterminado, si lo prefieres\n",
29
+ "\n",
30
+ " # Corrige el formato para casos con espacios o comas mal colocados y espacio extra después de \"POINT (\"\n",
31
+ " corrected_str = re.sub(r\"POINT\\s*\\(\\s*(\\-?\\d+(\\.\\d+)?)\\s*,\\s*(\\-?\\d+(\\.\\d+)?)\\s*\\)\", r\"POINT (\\1 \\3)\", geometry_str)\n",
32
+ "\n",
33
+ " # Caso especial para registros con comas entre coordenadas sin espacio adecuado\n",
34
+ " corrected_str = re.sub(r\"POINT\\(\\s*(\\-?\\d+(\\.\\d+)?),(\\-?\\d+(\\.\\d+)?)\\s*\\)\", r\"POINT (\\1 \\3)\", corrected_str)\n",
35
+ "\n",
36
+ " # Caso especial para registros como 'POINT (-77.74394625316971)0.7859743813128189, '\n",
37
+ " corrected_str = re.sub(r\"POINT\\s*\\((\\-?\\d+(\\.\\d+)?)\\)(\\-?\\d+(\\.\\d+)?),\", r\"POINT (\\1 \\3)\", corrected_str)\n",
38
+ "\n",
39
+ " # Verifica si la cadena corregida tiene el formato correcto\n",
40
+ " if re.match(r\"POINT \\(\\-?\\d+(\\.\\d+)? \\-?\\d+(\\.\\d+)?\\)\", corrected_str):\n",
41
+ " return corrected_str\n",
42
+ " \n",
43
+ " # Devuelve None si la cadena no se puede corregir\n",
44
+ " return None\n"
45
+ ]
46
+ },
47
+ {
48
+ "cell_type": "code",
49
+ "execution_count": 22,
50
+ "metadata": {},
51
+ "outputs": [
52
+ {
53
+ "name": "stdout",
54
+ "output_type": "stream",
55
+ "text": [
56
+ "77098\n",
57
+ "77098\n"
58
+ ]
59
+ },
60
+ {
61
+ "data": {
62
+ "text/html": [
63
+ "<div>\n",
64
+ "<style scoped>\n",
65
+ " .dataframe tbody tr th:only-of-type {\n",
66
+ " vertical-align: middle;\n",
67
+ " }\n",
68
+ "\n",
69
+ " .dataframe tbody tr th {\n",
70
+ " vertical-align: top;\n",
71
+ " }\n",
72
+ "\n",
73
+ " .dataframe thead th {\n",
74
+ " text-align: right;\n",
75
+ " }\n",
76
+ "</style>\n",
77
+ "<table border=\"1\" class=\"dataframe\">\n",
78
+ " <thead>\n",
79
+ " <tr style=\"text-align: right;\">\n",
80
+ " <th></th>\n",
81
+ " <th>Nombre</th>\n",
82
+ " <th>Geometry</th>\n",
83
+ " <th>Tipo</th>\n",
84
+ " <th>Categoria</th>\n",
85
+ " <th>Canton</th>\n",
86
+ " <th>Provincia</th>\n",
87
+ " <th>Source</th>\n",
88
+ " <th>Observación</th>\n",
89
+ " <th>geometry</th>\n",
90
+ " </tr>\n",
91
+ " <tr>\n",
92
+ " <th></th>\n",
93
+ " <th></th>\n",
94
+ " <th></th>\n",
95
+ " <th></th>\n",
96
+ " <th></th>\n",
97
+ " <th></th>\n",
98
+ " <th></th>\n",
99
+ " <th></th>\n",
100
+ " <th></th>\n",
101
+ " <th></th>\n",
102
+ " </tr>\n",
103
+ " </thead>\n",
104
+ " <tbody>\n",
105
+ " <tr>\n",
106
+ " <th>1</th>\n",
107
+ " <td>Produbanco-Servipagos</td>\n",
108
+ " <td>POINT (-79.6516941 0.9651532999999998)</td>\n",
109
+ " <td>cajero automático</td>\n",
110
+ " <td>finanzas</td>\n",
111
+ " <td>ESMERALDAS</td>\n",
112
+ " <td>ESMERALDAS</td>\n",
113
+ " <td>Google</td>\n",
114
+ " <td>NaN</td>\n",
115
+ " <td>POINT (-79.65169 0.96515)</td>\n",
116
+ " </tr>\n",
117
+ " <tr>\n",
118
+ " <th>2</th>\n",
119
+ " <td>ATM Banco Bolivariano</td>\n",
120
+ " <td>POINT (-79.671904 0.9323994000000002)</td>\n",
121
+ " <td>cajero automático</td>\n",
122
+ " <td>finanzas</td>\n",
123
+ " <td>ESMERALDAS</td>\n",
124
+ " <td>ESMERALDAS</td>\n",
125
+ " <td>Google</td>\n",
126
+ " <td>NaN</td>\n",
127
+ " <td>POINT (-79.67190 0.93240)</td>\n",
128
+ " </tr>\n",
129
+ " <tr>\n",
130
+ " <th>3</th>\n",
131
+ " <td>CPN</td>\n",
132
+ " <td>POINT (-79.6617982 0.9326862999999997)</td>\n",
133
+ " <td>cajero automático</td>\n",
134
+ " <td>finanzas</td>\n",
135
+ " <td>ESMERALDAS</td>\n",
136
+ " <td>ESMERALDAS</td>\n",
137
+ " <td>Google</td>\n",
138
+ " <td>NaN</td>\n",
139
+ " <td>POINT (-79.66180 0.93269)</td>\n",
140
+ " </tr>\n",
141
+ " <tr>\n",
142
+ " <th>4</th>\n",
143
+ " <td>Banco Solidario</td>\n",
144
+ " <td>POINT (-79.65241619999999 0.9639383999999998)</td>\n",
145
+ " <td>banco</td>\n",
146
+ " <td>finanzas</td>\n",
147
+ " <td>ESMERALDAS</td>\n",
148
+ " <td>ESMERALDAS</td>\n",
149
+ " <td>Google</td>\n",
150
+ " <td>NaN</td>\n",
151
+ " <td>POINT (-79.65242 0.96394)</td>\n",
152
+ " </tr>\n",
153
+ " <tr>\n",
154
+ " <th>5</th>\n",
155
+ " <td>Agencia BGR Esmeraldas |Banco General Rumiñahui</td>\n",
156
+ " <td>POINT (-79.65119609999999 0.9630196000000001)</td>\n",
157
+ " <td>banco</td>\n",
158
+ " <td>finanzas</td>\n",
159
+ " <td>ESMERALDAS</td>\n",
160
+ " <td>ESMERALDAS</td>\n",
161
+ " <td>Google</td>\n",
162
+ " <td>NaN</td>\n",
163
+ " <td>POINT (-79.65120 0.96302)</td>\n",
164
+ " </tr>\n",
165
+ " </tbody>\n",
166
+ "</table>\n",
167
+ "</div>"
168
+ ],
169
+ "text/plain": [
170
+ " Nombre \\\n",
171
+ " \n",
172
+ "1 Produbanco-Servipagos \n",
173
+ "2 ATM Banco Bolivariano \n",
174
+ "3 CPN \n",
175
+ "4 Banco Solidario \n",
176
+ "5 Agencia BGR Esmeraldas |Banco General Rumiñahui \n",
177
+ "\n",
178
+ " Geometry Tipo Categoria \\\n",
179
+ " \n",
180
+ "1 POINT (-79.6516941 0.9651532999999998) cajero automático finanzas \n",
181
+ "2 POINT (-79.671904 0.9323994000000002) cajero automático finanzas \n",
182
+ "3 POINT (-79.6617982 0.9326862999999997) cajero automático finanzas \n",
183
+ "4 POINT (-79.65241619999999 0.9639383999999998) banco finanzas \n",
184
+ "5 POINT (-79.65119609999999 0.9630196000000001) banco finanzas \n",
185
+ "\n",
186
+ " Canton Provincia Source Observación geometry \n",
187
+ " \n",
188
+ "1 ESMERALDAS ESMERALDAS Google NaN POINT (-79.65169 0.96515) \n",
189
+ "2 ESMERALDAS ESMERALDAS Google NaN POINT (-79.67190 0.93240) \n",
190
+ "3 ESMERALDAS ESMERALDAS Google NaN POINT (-79.66180 0.93269) \n",
191
+ "4 ESMERALDAS ESMERALDAS Google NaN POINT (-79.65242 0.96394) \n",
192
+ "5 ESMERALDAS ESMERALDAS Google NaN POINT (-79.65120 0.96302) "
193
+ ]
194
+ },
195
+ "execution_count": 22,
196
+ "metadata": {},
197
+ "output_type": "execute_result"
198
+ }
199
+ ],
200
+ "source": [
201
+ "df = pd.read_csv('data/pois_final.csv', index_col=0, low_memory=False)\n",
202
+ "df = df[(df['EnEstudio'] == 'TRUE') | (df['EnEstudio'] == 'VERDADERO')]\n",
203
+ "df['Geometry'] = df['Geometry'].apply(normalize_wkt_point)\n",
204
+ "print(len(df))\n",
205
+ "df = df[df['Geometry'].notnull()]\n",
206
+ "print(len(df))\n",
207
+ "df['geometry'] = df['Geometry'].apply(wkt.loads)\n",
208
+ "pois = gpd.GeoDataFrame(df, geometry='geometry')\n",
209
+ "pois = pois.set_crs(epsg=4326, inplace=True)\n",
210
+ "pois = pois.drop('EnEstudio', axis=1)\n",
211
+ "pois = pois.drop('Geometry', axis=1)\n",
212
+ "pois.head()"
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "code",
217
+ "execution_count": 15,
218
+ "metadata": {},
219
+ "outputs": [],
220
+ "source": [
221
+ "cantones = [str(k) for k in pois['Canton'].unique().tolist()]\n",
222
+ "categorias = [str(k) for k in pois['Categoria'].unique().tolist()]\n",
223
+ "fuentes = [str(k) for k in pois['Source'].unique().tolist()]"
224
+ ]
225
+ },
226
+ {
227
+ "cell_type": "code",
228
+ "execution_count": 17,
229
+ "metadata": {},
230
+ "outputs": [],
231
+ "source": [
232
+ "canton = solara.reactive('CUENCA')\n",
233
+ "categoria = solara.reactive(['Salud'])\n",
234
+ "fuente = solara.reactive(['OSM'])\n",
235
+ "limit = solara.reactive(1000)"
236
+ ]
237
+ },
238
+ {
239
+ "cell_type": "code",
240
+ "execution_count": 18,
241
+ "metadata": {},
242
+ "outputs": [],
243
+ "source": [
244
+ "def data_filter(df, city, category, source):\n",
245
+ " df_filtered = df.loc[df['Canton']==city]\n",
246
+ " df_filtered = df_filtered.loc[df_filtered['Categoria'].isin(category)]\n",
247
+ " df_filtered = df_filtered.loc[df_filtered['Source'].isin(source)]\n",
248
+ " return df_filtered"
249
+ ]
250
+ },
251
+ {
252
+ "cell_type": "code",
253
+ "execution_count": 19,
254
+ "metadata": {},
255
+ "outputs": [],
256
+ "source": [
257
+ "def ipyleaflet_map(geo_df):\n",
258
+ " # Define map center and initial zoom level\n",
259
+ " center = (geo_df.geometry.y.mean(), geo_df.geometry.x.mean())\n",
260
+ " zoom = 12\n",
261
+ "\n",
262
+ " # Create the map\n",
263
+ " m = Map(center=center, zoom=zoom)\n",
264
+ "\n",
265
+ " # Create GeoData from the GeoDataFrame\n",
266
+ " geo_data = GeoData(\n",
267
+ " geo_dataframe=geo_df,\n",
268
+ " style={\n",
269
+ " 'color': 'black', \n",
270
+ " 'radius': 7, \n",
271
+ " 'fillColor': '#3366cc', \n",
272
+ " 'opacity': 0.5, \n",
273
+ " 'weight': 1.9, \n",
274
+ " 'dashArray': '2', \n",
275
+ " 'fillOpacity': 0.6\n",
276
+ " },\n",
277
+ " hover_style={\n",
278
+ " 'fillColor': 'red', \n",
279
+ " 'fillOpacity': 0.2\n",
280
+ " },\n",
281
+ " point_style={\n",
282
+ " 'radius': 7, \n",
283
+ " 'color': 'red', \n",
284
+ " 'fillOpacity': 0.8, \n",
285
+ " 'fillColor': 'blue', \n",
286
+ " 'weight': 3\n",
287
+ " },\n",
288
+ " name='Servicios de Apoyo'\n",
289
+ " )\n",
290
+ "\n",
291
+ " # Add GeoData to the map\n",
292
+ " m.add_layer(geo_data)\n",
293
+ " \n",
294
+ " # Add Cluster Markers\n",
295
+ " markers = [Marker(location=(point.y, point.x)) for point in geo_df.geometry]\n",
296
+ "\n",
297
+ " # Create and add MarkerCluster to the map\n",
298
+ " marker_cluster = MarkerCluster(markers=markers, name='Cluster Markers')\n",
299
+ " m.add_layer(marker_cluster)\n",
300
+ "\n",
301
+ " # Add layer control\n",
302
+ " m.add_control(LayersControl())\n",
303
+ "\n",
304
+ " # Display the map\n",
305
+ " display(m)"
306
+ ]
307
+ },
308
+ {
309
+ "cell_type": "code",
310
+ "execution_count": 20,
311
+ "metadata": {},
312
+ "outputs": [],
313
+ "source": [
314
+ "#Interactive bar chart function\n",
315
+ "def type_chart_plotly(df):\n",
316
+ " tipo_counts = df['Tipo'].value_counts().sort_values(ascending=True)\n",
317
+ "\n",
318
+ " fig = px.bar(tipo_counts, orientation='h', \n",
319
+ " x=tipo_counts.values, y=tipo_counts.index,\n",
320
+ " labels={'x': 'Conteo', 'y': 'Tipo'},\n",
321
+ " title='Servicios')\n",
322
+ " solara.FigurePlotly(fig)"
323
+ ]
324
+ },
325
+ {
326
+ "cell_type": "code",
327
+ "execution_count": 23,
328
+ "metadata": {},
329
+ "outputs": [],
330
+ "source": [
331
+ "@solara.component\n",
332
+ "def View():\n",
333
+ " dff = data_filter(pois, canton.value, categoria.value, fuente.value)\n",
334
+ " row_count = len(dff)\n",
335
+ " if row_count > limit.value:\n",
336
+ " solara.Warning(f\"Only showing the first {limit.value} of {row_count:,} services on map\")\n",
337
+ " ipyleaflet_map(dff.iloc[:limit.value])\n",
338
+ " if row_count > 0:\n",
339
+ " with solara.Card(''):\n",
340
+ " type_chart_plotly(dff)\n",
341
+ " else:\n",
342
+ " solara.Warning(\"You filtered out all the data, no charts shown\")\n",
343
+ "#View()"
344
+ ]
345
+ },
346
+ {
347
+ "cell_type": "code",
348
+ "execution_count": 24,
349
+ "metadata": {},
350
+ "outputs": [],
351
+ "source": [
352
+ "@solara.component\n",
353
+ "def Controls():\n",
354
+ " solara.Select('Cantón', values=cantones, value=canton)\n",
355
+ " solara.SelectMultiple('Categoría', all_values=categorias, values=categoria)\n",
356
+ " solara.SelectMultiple('Fuente', all_values=fuentes, values=fuente)\n",
357
+ " solara.Text(\"Maximum number of pois to show on map\")\n",
358
+ " solara.SliderInt('', value=limit, min=1, max=1000)\n",
359
+ "#Controls()"
360
+ ]
361
+ },
362
+ {
363
+ "cell_type": "code",
364
+ "execution_count": 26,
365
+ "metadata": {},
366
+ "outputs": [
367
+ {
368
+ "data": {
369
+ "application/vnd.jupyter.widget-view+json": {
370
+ "model_id": "0820e0d59ee746fea8779a184f7057f1",
371
+ "version_major": 2,
372
+ "version_minor": 0
373
+ },
374
+ "text/html": [
375
+ "Cannot show widget. You probably want to rerun the code cell above (<i>Click in the code cell, and press Shift+Enter <kbd>⇧</kbd>+<kbd>↩</kbd></i>)."
376
+ ],
377
+ "text/plain": [
378
+ "Cannot show ipywidgets in text"
379
+ ]
380
+ },
381
+ "metadata": {},
382
+ "output_type": "display_data"
383
+ }
384
+ ],
385
+ "source": [
386
+ "@solara.component\n",
387
+ "def Page():\n",
388
+ " with solara.Column():\n",
389
+ " solara.Title('Servicios de Apoyo para Migrantes')\n",
390
+ " with solara.Sidebar():\n",
391
+ " with solara.Card('Filtros'):\n",
392
+ " with solara.Column():\n",
393
+ " Controls()\n",
394
+ " View()\n",
395
+ "Page()"
396
+ ]
397
+ },
398
+ {
399
+ "cell_type": "code",
400
+ "execution_count": null,
401
+ "metadata": {},
402
+ "outputs": [],
403
+ "source": []
404
+ }
405
+ ],
406
+ "metadata": {
407
+ "kernelspec": {
408
+ "display_name": "solara",
409
+ "language": "python",
410
+ "name": "python3"
411
+ },
412
+ "language_info": {
413
+ "codemirror_mode": {
414
+ "name": "ipython",
415
+ "version": 3
416
+ },
417
+ "file_extension": ".py",
418
+ "mimetype": "text/x-python",
419
+ "name": "python",
420
+ "nbconvert_exporter": "python",
421
+ "pygments_lexer": "ipython3",
422
+ "version": "3.11.5"
423
+ }
424
+ },
425
+ "nbformat": 4,
426
+ "nbformat_minor": 2
427
+ }