Query database instead of overpass
This commit is contained in:
		
							parent
							
								
									54b280655f
								
							
						
					
					
						commit
						44241751b2
					
				| 
						 | 
					@ -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"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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"]
 | 
					 | 
				
			||||||
							
								
								
									
										24
									
								
								lookup.py
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								lookup.py
									
									
									
									
									
								
							| 
						 | 
					@ -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(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue