# Uso de Open Street Map para obtención de datos de ciudades en un mapa hecho con matplotlib obteniendo datos de Obsidian
## El proyecto
En este caso lo que quiero hacer es mapear la ciudad (o ciudades) de los papers en los que estoy trabajando en un mapamundi para ver qué elementos faltan o hay que añadir.
El resultado (con N=40 de N número de artículos analizados) es el siguiente

Gracias a [Alexandre B A Villares](https://ciberlandia.pt/@villares) me apuntó al sitio correcto de utilizar la biblioteca OSMnx.
Esta es la referencia:
https://osmnx.readthedocs.io/en/stable/user-reference.html#osmnx.geocoder.geocode
El código de prueba era tan sencillo como este:
```python!
import osmnx
x = osmnx.geocoder.geocode("Aranda de Duero, España")
print(x)
```
Resultado:
(41.6715067, -3.6851172)
Justo lo que necesito. Eso sí, ya nada más ponerlo tarda un segundito en responder. De hecho esto viene porque ya te dice la API esto https://operations.osmfoundation.org/policies/nominatim/

_(que no más de una request por segundo y punto)_
Teniendo en cuenta que iba a tener varias ciudades (ya se ve la marea de pntos) y que no iban a cambiar cada vez, lo que necesitaba era... un caché.
## La parte de Obsidian
Aquí no me voy a detener mucho porque lo he explicado [aquí](/7Hw9TTSbQ666bsFzW6zoSw) y [aquí](/rH5NwvqOSiWdsqbW4EK-zw) en detalle. Pero a las notas de los artículos les he puesto una propiedad y luego con bases he exportado el CSV. Lo único que para artículos que tienen varias ciudades las he separado por punto y coma.

También tengo que hacer una parte del código que omita los artículos que no tienen ninguna ciudad.
Cuando eso está limpio, lo exporto como csv y le pongo la fecha para luego cambiar solo el archivo.
## La parte de python.
En este caso se trabaja con 2 archivos, uno que es un caché y otro que es la tabla exportada de Obsidian
Este es el aspecto de lugares,coordenadas.csv

Y este el de los artículos exportados de Obsidian:

### Detalles
* Poner en el opening de file el utf-8 es MUY obligatorio para que no se descuageringue todo.
* El len(lugar) <2 es para esquivar papers que no tienen ciudad o es un espacio en blanco.
* Al cargar los artículos si hay un punto y coma `;` es cuando tiene que hacer una sub-búsqueda
* Hay que poner muchos .strip porque si no, hay veces que hay espacios en blanco y otras que no.
* Hay que acordarse que el plot te pide que le pongas no latitud y longitud sino longitud y latitud. Aquí tomé la decisión de hacer ese cambio solo cuando me lo pide el matplotlib.
## Código completo
Aquí el cacharro completo por si alguien le sirve = )
```python!
import csv
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import osmnx.geocoder as osm
lugaresFile = "Lugares,coordenadas.csv"
articulosFile = "260212_Analisis_ciudades.csv"
lugaresCache = dict()
lugaresArticulo = dict()
#Cargamos lugares de caché
with open(lugaresFile, encoding='utf-8') as file:
reader = csv.reader(file,dialect="excel")
header = True
#vuelta de rellenar la lista de lugares
for line in reader:
if header:
header = False
continue
# nombre de país, latitud, longitud
try:
lugaresCache[line[0]] = float(line[1]), float(line[2])
except:
pass
#print(lugaresCache)
#Cargamos los artículos
with open(articulosFile, encoding='utf-8') as file:
reader = csv.reader(file,dialect="excel")
header = True
#vuelta de rellenar la lista de lugares de los artículos
for line in reader:
if header:
header = False
continue
# nombre,año,Cuartil,País de la institución,localización estudio
if len(line) < 4:
continue
lugar = line[4].strip()
if len(lugar) <2:
continue
if ";" in lugar:
entradas = lugar.split(";")
for x in entradas:
x= x.strip()
if x not in lugaresArticulo:
lugaresArticulo[x] = [1]
else:
lugaresArticulo[x][0] +=1
elif lugar not in lugaresArticulo:
lugaresArticulo[lugar] = [1]
else:
lugaresArticulo[lugar][0] +=1
print(lugaresArticulo)
print("Lugares en el artículo: ", len(lugaresArticulo))
# Revisamos el caché
elementosEnCache = 0
for lugar in lugaresArticulo:
if lugar in lugaresCache:
lugaresArticulo[lugar].append(lugaresCache[lugar])
elementosEnCache +=1
print("Lugares en el caché: ", elementosEnCache)
# Nos vamos a OSM a buscar latitudes y longitudes y añadimos al caché
with open(lugaresFile, "a", encoding='utf-8') as file:
for lugar in lugaresArticulo:
if len(lugaresArticulo[lugar]) >1:
continue
latitud, longitud = osm.geocode(lugar)
print(lugar, latitud, longitud)
lugaresArticulo[lugar].append(latitud, longitud)
#preparamos la línea para el Caché
line = '"'
line+= lugar
line+= '",'
line+= str(latitud)
line+= ","
line+= str(longitud)
line+= "\n" #fin de línea
file.write(line)
#Aquí comienza la parte de ploteo
ax = plt.axes(projection=ccrs.PlateCarree())
ax.coastlines()
ax.set_global()
for texto, detalles in lugaresArticulo.items():
# lugar: [número de veces, (latitud, longitud)]
latitud = detalles[1][0]
longitud = detalles[1][1]
# El círculo pues le pongo a ojímetro la escala
tamañoCirculo = ((detalles[0]+2.25)*2)**2
ax.text(longitud, latitud -2, texto,
horizontalalignment='center', size = "x-small",
transform=ccrs.PlateCarree())
ax.scatter(longitud, latitud, tamañoCirculo,
color = "blue",
#alpha pensado para que el máximo sea 3
alpha = 0.6+ detalles[0]*0.1)
plt.show()
```