Tallinn Airport is said to be the 'cosiest' airport in the world. If you search for that on Google they come up first. I noticed none of the shops or other amenities around the gates are marked in OSM.
I wrote some code to see if this was an issue at airports of a similar size or larger nearby in other EU countries. I got a lot of false-positives but this code might help anyone else looking to enrich the Airport metadata in OSM.
TLL is surrounded by dense urban areas and a major shopping mall so I built a polygon mask around the airport gates and counted the shops within it.
Airports that came back with no shops within their gate area mask where:
- Bremen (BRE)
- Bromma (BMA)
- Dresden (DRS)
- Gdansk Lech Walesa (GDN)
- Kiruna_Airport (KRN)
- Kraków-Balice (KRK)
- Lulea (LLA)
- Nurnberg (NUE)
- Oulu (OUL)
- Rovaniemi (RVN)
- Stuttgart (STR)
- Tromsø Langnes (TOS)
- Ulemiste (TLL)
I checked Bremen, Krakow and Oulu and they look to be fairly well populated though I've only ever been in Bremen's airport and that was 20 years ago.
Bremen: https://www.openstreetmap.org/node/10796900775#map=17/53.053980/8.784814
Krakow: https://www.openstreetmap.org/node/10796900775#map=19/50.072307/19.801341
Natural Earth Download Guide: https://tech.marksblogg.com/natural-earth-free-gis-data.html
$ sudo apt update
$ sudo apt install \
aws-cli \
python3-pip \
python3-virtualenv \
unzip
$ virtualenv ~/.osm
$ source ~/.osm/bin/activate
$ pip install \
duckdb \
requests \
shapely
import duckdb
import requests
from shapely import convex_hull, MultiPoint, Polygon, Point
def osm_get(latitude:float,
longitude:float,
radius_meters:int=3_000,
item:str='nwr[shop]'):
query = f"""
[out:json];
(
{item}(around:{radius_meters},{latitude},{longitude});
);
out center;
"""
resp = requests.get('https://overpass-api.de/api/interpreter',
params={'data': query})
if resp.status_code == 200:
return resp.json()['elements']
raise Exception(resp.text)
def get_point(shop):
lon, lat = (shop['lon'], shop['lat']) \
if 'lon' in shop.keys() \
else (shop['center']['lon'], shop['center']['lat'])
return Point(lon, lat)
sql = '''
SELECT name,
abbrev,
ST_X(geom) as lon,
ST_Y(geom) as lat
FROM ST_READ('/home/mark/natural_earth/10m_cultural/ne_10m_airports/ne_10m_airports.shp')
WHERE ST_DISTANCE(ST_POINT(24.801791, 59.416408), geom) < 20
AND scalerank > 3
AND gps_code LIKE 'E%'
ORDER BY name
'''
con = duckdb.connect(database=':memory:')
_ = con.sql('INSTALL spatial; LOAD spatial;')
shop_count = []
for rec in con.sql(sql).to_df().iloc():
features = osm_get(rec['lat'],
rec['lon'],
3_000,
'nwr[aeroway=gate]')
gates_area = convex_hull(MultiPoint([(x['lon'], x['lat'])
for x in features]))
if not gates_area.is_empty:
features = osm_get(gates_area.centroid.y,
gates_area.centroid.x,
3_000,
'nwr[shop]')
shops = [shop for shop in features if gates_area.contains(get_point(shop))]
shop_count.append([rec['name'], rec['abbrev'], len(shops)])
print('\n'.join(['%s (%s)' % (name, abbrev)
for name, abbrev, count_ in shop_count
if count_ < 1]))
Tallinn Airport's OSM data: