OpenStreetMap - OSM
Contingut
Introducció
L'objectiu és poder editar el mapa, per tal de contribuir a la comunitat.
Em registro a OSM: joanqc@gmail.com / jq********
La primera contribució a OSM que he fet per aprendre'n és l'article sobre els arbres monumentals de Catalunya:
Edició
Un cop logats, podem editar amb l'editor integrat iD. Però quan se'n sap una mica, la gent utilitza un editor com JOSM (basat en Java)
Tutorials de JOSM
Instal·lació JOSM
Instal·lo JOSM a Linux Mint:
$ echo deb https://josm.openstreetmap.de/apt $(lsb_release -sc) universe | sudo tee /etc/apt/sources.list.d/josm.list > /dev/null
Download and register the public key:
$ wget -q https://josm.openstreetmap.de/josm-apt.key -O- | sudo apt-key add -
Now refresh your sources (you may need to install sudo apt-get install apt-transport-https)
$ sudo apt-get update
and install:
# You can skip this first line if these packages were not installed before. sudo apt-get remove josm josm-plugins # For the tested version sudo apt-get install josm # for the development version
S'instal·la bé, però el problema és que de Java tinc el openjdk
$ josm No valid JVM found to run JOSM $ java -version openjdk version "11.0.3" 2019-04-16 OpenJDK Runtime Environment (build 11.0.3+7-Ubuntu-1ubuntu218.04.1) OpenJDK 64-Bit Server VM (build 11.0.3+7-Ubuntu-1ubuntu218.04.1, mixed mode, sharing)
Però amb el jar directament sí que funciona:
$ cd Baixades $ java -jar josm-tested.jar
Programació (querying): Osmapi (API v0.6), OverpassAPI, scripts Python
- https://wiki.openstreetmap.org/wiki/Downloading_data
- https://towardsdatascience.com/loading-data-from-openstreetmap-with-python-and-the-overpass-api-513882a27fd0
La primera manera de descarregar les dades és amb l'editor JOSM. Es selecciona una àrea, i es baixa totes les dades que conté en format XML (extensió .osm).
map request docs:
Per exemple, des del navegador:
descarrega en format osm la informació continguda en aquesta selecció (left, bottom, up, right) Però millor fer-ho amb wget:
$ wget -O muenchen.osm "https://api.openstreetmap.org/api/0.6/map?bbox=11.54,48.14,11.543,48.145"
Per exemple, la informació del Xalet del Moixeró:
$ wget -O xalet_moixero.osm "https://api.openstreetmap.org/api/0.6/map?bbox=1.856,42.254,1.859,42.256" ... <way id="737604624" visible="true" version="1" changeset="76054580" timestamp="2019-10-22T14:30:11Z" user="joanillo" uid="10376780"> <nd ref="6906098710"/> <nd ref="6906098711"/> <nd ref="6906098712"/> <nd ref="6906098713"/> <nd ref="6906098714"/> <nd ref="6906098715"/> <nd ref="6906098710"/> <tag k="building" v="yes"/> <tag k="name" v="Xalet del Moixeró"/> </way> <way id="737614072" visible="true" version="1" changeset="76055749" timestamp="2019-10-22T14:57:02Z" user="joanillo" uid="10376780"> <nd ref="6906169083"/> <nd ref="6906169084"/> <nd ref="6906188385"/> <nd ref="6906188386"/> <nd ref="6906188387"/> <nd ref="6906188388"/> <nd ref="6906188389"/> <nd ref="6854789453"/> <nd ref="6906188390"/> <nd ref="6854789449"/> <nd ref="6854789448"/> <nd ref="1434952694"/> <nd ref="6906188391"/> <nd ref="6906188392"/> <nd ref="6906188393"/> <nd ref="6906188394"/> <nd ref="6906188395"/> <nd ref="6906169083"/> <tag k="landuse" v="farmland"/> <tag k="name" v="Camp Llarg"/> </way> ...
I aquests nodes fan referència a una informació geogràfica que trobem en el mateix fitxer:
... <node id="6906098710" visible="true" version="1" changeset="76054580" timestamp="2019-10-22T14:30:11Z" user="joanillo" uid="10376780" lat="42.2544841" lon="1.8582213"/> ...
OverpassAPI
És una API només per descarregar informació. Per exemple, posant en el següent formulari:nhttp://overpass-api.de/query_form.html
<osm-script> <query type="way"> <bbox-query e="1.859" n="42.256" s="42.254" w="1.856"/> </query> <print/> </osm-script>
<osm-script> <query type="way"> <bbox-query e="1.859" n="42.256" s="42.254" w="1.856"/> </query> <print/> </osm-script>
obtins les vies (unió de nodes). Aquí trobo tant carreteres, camins, però també arees que delimiten un edifici (Xalet del Moixeró) o un camp (Camp Llarg).
Filtrem per building o Xalet:
<osm-script> <query type="way"> <bbox-query e="1.859" n="42.256" s="42.254" w="1.856"/> <has-kv k="building" /> </query> <print/> </osm-script>
<osm-script> <query type="way"> <bbox-query e="1.859" n="42.256" s="42.254" w="1.856"/> <has-kv k="name" v="Xalet del Moixeró" /> </query> <print/> </osm-script>
Resultat:
<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="Overpass API 0.7.55.7 8b86ff77"> <note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note> <meta osm_base="2019-10-22T17:48:02Z"/> <way id="737604624"> <nd ref="6906098710"/> <nd ref="6906098711"/> <nd ref="6906098712"/> <nd ref="6906098713"/> <nd ref="6906098714"/> <nd ref="6906098715"/> <nd ref="6906098710"/> <tag k="building" v="yes"/> <tag k="name" v="Xalet del Moixeró"/> </way> </osm>
Podem aplicar tots els filtres que vulguem:
<osm-script> <query type="way"> <bbox-query e="1.859" n="42.256" s="42.254" w="1.856"/> <has-kv k="name" v="Xalet del Moixeró" /> <has-kv k="building" /> </query> <print/> </osm-script>
Amb overpass-turbo.eu podem visualitzar directament els resultats a la web:
Per exemple, en sortida JSON
/* This shows all mountains (peaks) in the Dolomites. You may want to use the "zoom onto data" button. => */ [out:json]; // search the relation of the Dolomites rel [place=region] ["region:type"="mountain_area"] ["name:en"="Dolomites"]; // show the outline out geom; // turn the relation into an area map_to_area; // get all peaks in the area node [natural=peak] (area); out body qt;
L'opció del mapa també funciona, amb l'inconvenient de què no navega automàticament a l'àrea d'interès.
El formulari admet dos notacions, Overpass QL (la que veiem en l'exemple de les Dolomites) i Overpass XML
Es poden utilitzar expressions regulars:
Com es veu en l'article, al voltant de Overpass API podem programar amb NodeJS o Python:
Python:
overpassify
$ pip install overpass $ pip install overpassify
(no acaba de funcionar, todo)
overpy (Python)
$ pip install setuptools $ pip install overpy
autopistes.py:
import overpy api = overpy.Overpass() # fetch all ways and nodes result = api.query(""" way(50.746,7.154,50.748,7.157) ["highway"]; (._;>;); out body; """) for way in result.ways: print("Name: %s" % way.tags.get("name", "n/a")) print(" Highway: %s" % way.tags.get("highway", "n/a")) print(" Nodes:") for node in way.nodes: print(" Lat: %f, Lon: %f" % (node.lat, node.lon))
$ python autopistes.py Name: Rhedenstraße Highway: residential Nodes: Lat: 50.746874, Lon: 7.156307 Lat: 50.746598, Lon: 7.156237 Lat: 50.746477, Lon: 7.156199 Lat: 50.746360, Lon: 7.156124 ...
query.overpass (NodeJS)
$ sudo npm install -g query-overpass
$ echo 'node(57.7,11.9,57.8,12.0)[amenity=bar];out;' | query-overpass {"type":"FeatureCollection","features":[{"type":"Feature","id":"node/2318390720","properties":{"type":"node","id":"2318390720","tags":{"access":"private","amenity":"bar","name":"Göteborgs vinkällare","website":"http://www.gbgvin.se"},"relations":[],"meta":{}},"geometry":{"type":"Point","coordinates":[11.9643378,57.7064417]}},{"type":"Feature","id":"node/2468969556","properties":{"type":"node","id":"2468969556","tags":{"amenity":"bar","name":"Dancing Dingo"} ...
o bé script query.ql:
node(57.7,11.9,57.8,12.0)[amenity=bar];out;
$ query-overpass query.ql
Seguim article
Seguim article:
We have three basic components in the OSM data model, which are nodes, ways and relations which all come with an id. Many of the elements come with tags which describe specific features represented as key-value pairs. In simple terms, nodes are points on the maps (in latitude and longitude).
Utilitzen overpassturbo per veure els cafès de Bagà:
coordenades de Bagà (box) (es poden trobar fàcilment amb el JOSM, pestanya Límits):
42.2485658 42.2548237 1.8581486 1.8699503
node["amenity"="cafe"]
(42.2485658,1.8581486,42.2548237,1.8699503);
Cerquem els bars de Bagà a overpassturbo:
node["amenity"="bar"] (42.2485658,1.8581486,42.2548237,1.8699503); out;
i visualitzem els resultats en format osm o directament en el mapa:
<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="Overpass API 0.7.55.7 8b86ff77"> <note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note> <meta osm_base="2019-10-23T14:55:02Z"/> <node id="3542340645" lat="42.2526823" lon="1.8604314"> <tag k="amenity" v="bar"/> </node> <node id="3542340794" lat="42.2528414" lon="1.8603926"> <tag k="amenity" v="bar"/> </node> </osm>
Altres possibilitats:
[out:csv(::id,::type,"name","addr:postcode","addr:city","addr:street","addr:housenumber","website"," contact:email=*")][timeout:300]; ( node[amenity=bar](42.2485658,1.8581486,42.2548237,1.8699503); way[amenity=bar](42.2485658,1.8581486,42.2548237,1.8699503); relation[amenity=bar](42.2485658,1.8581486,42.2548237,1.8699503); ); out;
Amb bbox senzillament es fa la consulta sobre el mapa que tenim seleccionat, així no has de saber les coordenades:
[out:csv(::id,::type,"name","addr:postcode","addr:city","addr:street","addr:housenumber","website"," contact:email=*")][timeout:300]; ( node[amenity=drinking_water]({{bbox}}); way[amenity=drinking_water]({{bbox}}); relation[amenity=drinking_water]({{bbox}}); ); out;
Ciutats del Regne Unit:
area["ISO3166-1"="GB"][admin_level=2]; node["place"="city"](area); out;
Per exemple, universitats espanyoles:
area["ISO3166-1"="ES"][admin_level=2]; ( node["amenity"="university"](area); way["amenity"="university"](area); rel["amenity"="university"](area); ); out center;
Amb curl també funciona, però és una mica d'embolic:
curl --globoff -o output.xml "http://overpass-api.de/api/interpreter?data=area[%22ISO3166-1%22=%22ES%22][admin_level=2];node[%22amenity%22=%22university%22](area);out%20center;" <node id="1867257725" lat="40.5442248" lon="-3.6985621"> <tag k="amenity" v="university"/> <tag k="name" v="Facultad de Filosofía y Letras"/> </node>
el mateix en format sortida JSON:
$ curl --globoff -o output.json "http://overpass-api.de/api/interpreter?data=[out:json];area[%22ISO3166-1%22=%22ES%22][admin_level=2];node[%22amenity%22=%22university%22](area);out%20center;" { "type": "node", "id": 6888773464, "lat": 42.3444391, "lon": -7.8567589, "tags": { "amenity": "university", "description": "B3: Vivir en sociedade\nFachada dun edificio da universidade da UVigo. É un edificio da administración pública.", "name": "Pavillón 1 campus UVigo", "note": "I.C.C." } }
I ara utilitzem un script python per mostrar i visualitzar els resultats:
universitats.py:
import requests import json overpass_url = "http://overpass-api.de/api/interpreter" overpass_query = """ [out:json]; area["ISO3166-1"="ES"][admin_level=2]; (node["amenity"="university"](area); way["amenity"="university"](area); rel["amenity"="university"](area); ); out center; """ response = requests.get(overpass_url, params={'data': overpass_query}) data = response.json() #print data import numpy as np import matplotlib.pyplot as plt # Collect coords into list coords = [] for element in data['elements']: if element['type'] == 'node': lon = element['lon'] lat = element['lat'] coords.append((lon, lat)) elif 'center' in element: lon = element['center']['lon'] lat = element['center']['lat'] coords.append((lon, lat)) # Convert coordinates into numpy array X = np.array(coords) plt.plot(X[:, 0], X[:, 1], 'o') plt.title('Universitats a EE') plt.xlabel('Longitude') plt.ylabel('Latitude') plt.axis('equal') plt.show()
... u'amenity': u'university', u'description': u'Universidad t\xe9cnica adscrita a la UAB (Universidad Aut\xf3noma de Barcelona)', u'phone': u'+34 93 2805244', u'addr:street': u'Passeig Sant Joan Bosco', ...
Another way to access the Overpass API with Python is by using the overpy package as a wrapper. Here you can see how we can translate the previous example with the overpy package
script universitats2.py:
import overpy api = overpy.Overpass() r = api.query(""" area["ISO3166-1"="ES"][admin_level=2]; (node["amenity"="university"](area); way["amenity"="university"](area); rel["amenity"="university"](area); ); out center; """) coords = [] coords += [(float(node.lon), float(node.lat)) for node in r.nodes] coords += [(float(way.center_lon), float(way.center_lat)) for way in r.ways] coords += [(float(rel.center_lon), float(rel.center_lat)) for rel in r.relations] print(coords)
... , (-6.2988483, 36.5352253), (-6.2974176, 36.5331516), (2.1264003, 41.3953882), (2.627724, 41.6024539), (-7.8565381, 42.3446821), (-7.8562432, 42.3444397), (-7.8567589, 42.3444391)]
osmapi
Amb osmapi podem també fer cerques. Però el que ens interessa més en la propera secció és que amb osmapi podem crear i editar nodes.
Codi mínim:
from osmapi import OsmApi MyApi = OsmApi() print(MyApi.NodeGet(123)) {u'changeset': 532907, u'uid': 14298, u'timestamp': u'2007-09-29T09:19:17Z', u'lon': 10.790009299999999, u'visible': True, u'version': 1, u'user': u'Mede', u'lat': 59.9503044, u'tag': {}, u'id': 123}
Programació: Osmapi (crear, editar i esborrar nodes amb python)
$ pip install --user osmapi
ja podem crear un node a OSM, seguint l'exemple bàsic de la documentació. script osmpai_v2.py:
# encoding: utf-8 from osmapi import OsmApi MyApi = OsmApi(passwordfile="/home/joan/projectes/arbres_monumentals/.password") MyApi.ChangesetCreate({u"comment": u"Modificació de prova en el Camp Llarg"}) print(MyApi.NodeCreate({u"lat":42.25543, u"lon":1.85804, u"tag": {}})) MyApi.ChangesetClose() #{u'changeset': 76186141, u'version': 1, u'lon': 1.85743, u'tag': {}, u'lat': #42.25532, u'id': 6914599486}
El format del passwordfile és joanqc@gmail.com:jq******** (o bé el nom d'usuari, joanillo)
Ara ja podem crear un arbre: osmapi_v3.py
# encoding: utf-8 from osmapi import OsmApi MyApi = OsmApi(passwordfile="/home/joan/projectes/arbres_monumentals/.password") MyApi.ChangesetCreate({u"comment": u"Modificació de prova en el Camp Llarg"}) data = {u"lat":42.25543, u"lon":1.85804, u"tag": { u"natural": u"tree", u"name": u"Pi del Camp Llarg" }} print(MyApi.NodeCreate(data)) MyApi.ChangesetClose()
I ara ja puc afegir totes les dades de l'arbre, i gravar automàticament les dades (amb flush()): osmapi_v4.py
# encoding: utf-8 from osmapi import OsmApi MyApi = OsmApi(passwordfile="/home/joan/projectes/arbres_monumentals/.password") MyApi.ChangesetCreate({u"comment": u"Modificació de prova en el Camp Llarg"}) data = {u"lat":42.25543, u"lon":1.85804, u"tag": { u"natural": u"tree", u"name": u"Pi del Camp Llarg", u"species": u"Olea europea", u"species:ca": u"Olivera", u"note": u"Arbre Monumental (MA-01.001.01) https://ca.wikipedia.org/wiki/Llista_d%27arbres_monumentals_de_Catalunya" }} print(MyApi.NodeCreate(data)) MyApi.ChangesetClose() MyApi.flush()
Donat un node que ja existeix, el vull actualitzar. Aquesta és la manera de procedir: (osmapi_v5.py):
# encoding: utf-8 from osmapi import OsmApi MyApi = OsmApi(passwordfile="/home/joan/projectes/arbres_monumentals/.password") MyApi.ChangesetCreate({u"comment": u"Modificació de prova4 en el Camp Llarg"}) node = MyApi.NodeGet(6914950285) tags = node["tag"] tags[u"natural"] = u"tree" tags[u"name"] = u"Pi del Camp Llarg" tags[u"species"] = u"Pinus sylvestris" tags[u"species:ca"] = u"pi roig" tags[u"note"] = u"Arbre Monumental (MA-01.001.01) https://ca.wikipedia.org/wiki/Llista_d%27arbres_monumentals_de_Catalunya" print(MyApi.NodeUpdate(node)) MyApi.ChangesetClose() MyApi.flush()
I ara vull esborrar el node programàticament (osmpai_v6.py):
# encoding: utf-8 from osmapi import OsmApi MyApi = OsmApi(passwordfile="/home/joan/projectes/arbres_monumentals/.password") MyApi.ChangesetCreate({u"comment": u"Eliminació del pi en el Camp Llarg"}) node = MyApi.NodeGet(6914920685) MyApi.NodeDelete(node) MyApi.ChangesetClose() MyApi.flush()
Si el que vull és esborrar un tag que ja existeix, he de pensar que els tags és un diccionari, i en un diccionari podem eliminar membres:
#eliminar un tag: https://stackoverflow.com/questions/5844672/delete-an-element-from-a-dictionary del tags[u"recycling"]
(en aquest exemple dels Punts Verds m'havia quedat el tag recyclint="" (cadena buida) degut a un error, i el que s'ha de fer és eliminar el tag.
creat per Joan Quintana Compte, octubre 2019