# 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 ![imagen](https://hackmd.io/_uploads/SkqjeOiP-x.png) 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/ ![imagen](https://hackmd.io/_uploads/Hy5-ruiv-x.png) _(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. ![imagen](https://hackmd.io/_uploads/rJsirOowbx.png) 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 ![imagen](https://hackmd.io/_uploads/SyB2EcswZg.png) Y este el de los artículos exportados de Obsidian: ![imagen](https://hackmd.io/_uploads/BJ4eB9iP-e.png) ### 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() ```