Query database instead of overpass

This commit is contained in:
Edward Betts 2021-04-17 18:31:58 +02:00
parent 54b280655f
commit 44241751b2
4 changed files with 23 additions and 55 deletions

View file

@ -2,6 +2,10 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import Column from sqlalchemy.schema import Column
from sqlalchemy.types import Integer, Float, Numeric, String from sqlalchemy.types import Integer, Float, Numeric, String
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
from sqlalchemy.orm import column_property
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import func
from geoalchemy2 import Geometry from geoalchemy2 import Geometry
from .database import session from .database import session
@ -12,10 +16,22 @@ class Polygon(Base):
__tablename__ = "planet_osm_polygon" __tablename__ = "planet_osm_polygon"
osm_id = Column(Integer, primary_key=True, autoincrement=False) osm_id = Column(Integer, primary_key=True, autoincrement=False)
admin_level = Column(String)
way_area = Column(Float) way_area = Column(Float)
tags = Column(postgresql.HSTORE) tags = Column(postgresql.HSTORE)
way = Column(Geometry("GEOMETRY", srid=4326, spatial_index=True), nullable=False) way = Column(Geometry("GEOMETRY", srid=4326, spatial_index=True), nullable=False)
area = column_property(func.ST_Area(way))
@hybrid_property
def area_in_sq_km(self):
return self.area / (1000 * 1000)
@classmethod
def coords_within(cls, lat, lon):
point = func.ST_SetSRID(func.ST_MakePoint(lon, lat), 4326)
return cls.query.filter(cls.admin_level.isnot(None),
func.ST_Within(point, cls.way))
class Scotland(Base): class Scotland(Base):
__tablename__ = "scotland" __tablename__ = "scotland"

View file

@ -1,36 +0,0 @@
from flask import current_app
from . import headers
import os
import json
import requests
OVERPASS_URL = "https://lz4.overpass-api.de"
def run_query(oql):
return requests.post(
OVERPASS_URL + "/api/interpreter", data=oql.encode("utf-8"), headers=headers
)
def is_in_lat_lon(lat, lon):
oql = f"""
[out:json][timeout:25];
is_in({lat},{lon})->.a;
(way(pivot.a); rel(pivot.a););
out bb tags qt;"""
return run_query(oql)
def get_osm_elements(lat, lon):
filename = f"cache/{lat}_{lon}.json"
use_cache = current_app.config["USE_CACHE"]
if use_cache and os.path.exists(filename):
return json.load(open(filename))["elements"]
r = is_in_lat_lon(lat, lon)
if use_cache:
open(filename, "wb").write(r.content)
return r.json()["elements"]

View file

@ -2,10 +2,9 @@
from flask import Flask, render_template, request, jsonify, redirect, url_for from flask import Flask, render_template, request, jsonify, redirect, url_for
import geocode import geocode
from geocode import wikidata, overpass, scotland, database, model from geocode import wikidata, scotland, database, model
import urllib.parse import urllib.parse
import random import random
from geopy.distance import distance
# select gid, code, name from scotland where st_contains(geom, ST_Transform(ST_SetSRID(ST_MakePoint(-4.177, 55.7644), 4326), 27700)); # select gid, code, name from scotland where st_contains(geom, ST_Transform(ST_SetSRID(ST_MakePoint(-4.177, 55.7644), 4326), 27700));
@ -32,15 +31,6 @@ def get_random_lat_lon():
return lat, lon return lat, lon
def bounding_box_area(element):
bbox = element["bounds"]
x = distance((bbox["maxlat"], bbox["minlon"]), (bbox["maxlat"], bbox["maxlon"]))
y = distance((bbox["minlat"], bbox["maxlon"]), (bbox["maxlat"], bbox["minlon"]))
return x.km * y.km
def wd_to_qid(wd): def wd_to_qid(wd):
# expecting {"type": "url", "value": "https://www.wikidata.org/wiki/Q30"} # expecting {"type": "url", "value": "https://www.wikidata.org/wiki/Q30"}
if wd["type"] == "uri": if wd["type"] == "uri":
@ -131,7 +121,7 @@ def lat_lon_to_wikidata(lat, lon):
return {"elements": elements, "result": result} return {"elements": elements, "result": result}
elements = overpass.get_osm_elements(lat, lon) elements = model.Polygon.coords_within(lat, lon)
result = do_lookup(elements, lat, lon) result = do_lookup(elements, lat, lon)
# special case because the City of London is admin_level=6 in OSM # special case because the City of London is admin_level=6 in OSM
@ -190,12 +180,10 @@ def get_commons_cat_from_gss(gss):
def osm_lookup(elements, lat, lon): def osm_lookup(elements, lat, lon):
elements.sort(key=lambda e: bounding_box_area(e)) elements = sorted(elements, key=lambda e: e.area)
for e in elements: for e in elements:
if "tags" not in e: tags = e.tags
continue
tags = e["tags"]
admin_level_tag = tags.get("admin_level") admin_level_tag = tags.get("admin_level")
admin_level = ( admin_level = (
int(admin_level_tag) int(admin_level_tag)
@ -265,7 +253,7 @@ def index():
def random_location(): def random_location():
lat, lon = get_random_lat_lon() lat, lon = get_random_lat_lon()
elements = overpass.get_osm_elements(lat, lon) elements = model.Polygon.coords_within(lat, lon)
result = do_lookup(elements, lat, lon) result = do_lookup(elements, lat, lon)
return render_template( return render_template(
@ -286,7 +274,7 @@ def wikidata_tag():
elements = [] elements = []
result = build_dict(hit, lat, lon) result = build_dict(hit, lat, lon)
else: else:
elements = overpass.get_osm_elements(lat, lon) elements = model.Polygon.coords_within(lat, lon)
result = do_lookup(elements, lat, lon) result = do_lookup(elements, lat, lon)
return render_template( return render_template(

View file

@ -32,7 +32,7 @@
{% for element in elements %} {% for element in elements %}
{% set tags = element.tags %} {% set tags = element.tags %}
<pre>{{ element | pprint }}</pre> <pre>{{ element.tags | pprint }}</pre>
{% endfor %} {% endfor %}