OpenStreetMap - OSM

De Wikijoan
Dreceres ràpides: navegació, cerca

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)

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

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."
  }
}
Universitats.png

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

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
Institut Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines