forked from edward/owl-map
Show images in popups
This commit is contained in:
parent
c4aa27e8f4
commit
e3cefcfcbd
47
matcher/commons.py
Normal file
47
matcher/commons.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
import requests
|
||||
import urllib.parse
|
||||
from . import utils
|
||||
|
||||
commons_start = "http://commons.wikimedia.org/wiki/Special:FilePath/"
|
||||
commons_url = "https://www.wikidata.org/w/api.php"
|
||||
page_size = 50
|
||||
|
||||
|
||||
def commons_uri_to_filename(uri):
|
||||
return urllib.parse.unquote(utils.drop_start(uri, commons_start))
|
||||
|
||||
|
||||
def api_call(params):
|
||||
call_params = {
|
||||
"format": "json",
|
||||
"formatversion": 2,
|
||||
**params,
|
||||
}
|
||||
|
||||
return requests.get(commons_url, params=call_params, timeout=5)
|
||||
|
||||
|
||||
def image_detail(filenames, thumbheight=None, thumbwidth=None):
|
||||
params = {
|
||||
"action": "query",
|
||||
"prop": "imageinfo",
|
||||
"iiprop": "url",
|
||||
}
|
||||
if thumbheight is not None:
|
||||
params["iiurlheight"] = thumbheight
|
||||
if thumbwidth is not None:
|
||||
params["iiurlwidth"] = thumbwidth
|
||||
|
||||
images = {}
|
||||
|
||||
for cur in utils.chunk(filenames, page_size):
|
||||
call_params = params.copy()
|
||||
call_params["titles"] = "|".join(f"File:{f}" for f in cur)
|
||||
|
||||
r = api_call(call_params)
|
||||
|
||||
for image in r.json()["query"]["pages"]:
|
||||
filename = utils.drop_start(image["title"], "File:")
|
||||
images[filename] = image["imageinfo"][0] if "imageinfo" in image else None
|
||||
|
||||
return images
|
170
matcher/utils.py
Normal file
170
matcher/utils.py
Normal file
|
@ -0,0 +1,170 @@
|
|||
from flask import current_app, request
|
||||
from itertools import islice
|
||||
import os.path
|
||||
import json
|
||||
import math
|
||||
import user_agents
|
||||
import re
|
||||
import pattern.en
|
||||
|
||||
metres_per_mile = 1609.344
|
||||
feet_per_metre = 3.28084
|
||||
feet_per_mile = 5280
|
||||
|
||||
|
||||
def chunk(it, size):
|
||||
it = iter(it)
|
||||
return iter(lambda: tuple(islice(it, size)), ())
|
||||
|
||||
|
||||
def flatten(l):
|
||||
return [item for sublist in l for item in sublist]
|
||||
|
||||
|
||||
def drop_start(s, start):
|
||||
assert s.startswith(start)
|
||||
return s[len(start) :]
|
||||
|
||||
|
||||
def remove_start(s, start):
|
||||
return s[len(start) :] if s.startswith(start) else s
|
||||
|
||||
|
||||
def normalize_url(url):
|
||||
for start in "http://", "https://", "www.":
|
||||
url = remove_start(url, start)
|
||||
return url.rstrip("/")
|
||||
|
||||
|
||||
def contains_digit(s):
|
||||
return any(c.isdigit() for c in s)
|
||||
|
||||
|
||||
def cache_dir():
|
||||
return current_app.config["CACHE_DIR"]
|
||||
|
||||
|
||||
def cache_filename(filename):
|
||||
return os.path.join(cache_dir(), filename)
|
||||
|
||||
|
||||
def load_from_cache(filename):
|
||||
return json.load(open(cache_filename(filename)))
|
||||
|
||||
|
||||
def get_radius(default=1000):
|
||||
arg_radius = request.args.get("radius")
|
||||
return int(arg_radius) if arg_radius and arg_radius.isdigit() else default
|
||||
|
||||
|
||||
def get_int_arg(name):
|
||||
if name in request.args and request.args[name].isdigit():
|
||||
return int(request.args[name])
|
||||
|
||||
|
||||
def calc_chunk_size(area_in_sq_km, size=22):
|
||||
side = math.sqrt(area_in_sq_km)
|
||||
return max(1, math.ceil(side / size))
|
||||
|
||||
|
||||
def file_missing_or_empty(filename):
|
||||
return os.path.exists(filename) or os.stat(filename).st_size == 0
|
||||
|
||||
|
||||
def is_bot():
|
||||
""" Is the current request from a web robot? """
|
||||
ua = request.headers.get("User-Agent")
|
||||
return ua and user_agents.parse(ua).is_bot
|
||||
|
||||
|
||||
def log_location():
|
||||
return current_app.config["LOG_DIR"]
|
||||
|
||||
|
||||
def good_location():
|
||||
return os.path.join(log_location(), "complete")
|
||||
|
||||
|
||||
def capfirst(value):
|
||||
""" Uppercase first letter of string, leave rest as is. """
|
||||
return value[0].upper() + value[1:] if value else value
|
||||
|
||||
|
||||
def any_upper(value):
|
||||
return any(c.isupper() for c in value)
|
||||
|
||||
|
||||
def find_log_file(place):
|
||||
start = f"{place.place_id}_"
|
||||
for f in os.scandir(good_location()):
|
||||
if f.name.startswith(start):
|
||||
return f.path
|
||||
|
||||
|
||||
def get_free_space(config):
|
||||
s = os.statvfs(config["FREE_SPACE_PATH"])
|
||||
return s.f_bsize * s.f_bavail
|
||||
|
||||
|
||||
def display_distance(units, dist):
|
||||
if units in ("miles_and_feet", "miles_and_yards"):
|
||||
total_feet = dist * feet_per_metre
|
||||
miles = total_feet / feet_per_mile
|
||||
|
||||
if miles > 0.5:
|
||||
return f"{miles:,.2f} miles"
|
||||
else:
|
||||
return {
|
||||
"miles_and_feet": f"{total_feet:,.0f} feet",
|
||||
"miles_and_yards": f"{total_feet / 3:,.0f} yards",
|
||||
}[units]
|
||||
|
||||
if units == "miles_and_metres":
|
||||
miles = dist / metres_per_mile
|
||||
return f"{miles:,.2f} miles" if miles > 0.5 else f"{dist:,.0f} metres"
|
||||
|
||||
if units == "km_and_metres":
|
||||
units = "km" if dist > 500 else "metres"
|
||||
if units == "metres":
|
||||
return f"{dist:,.0f} m"
|
||||
if units == "km":
|
||||
return f"{dist / 1000:,.2f} km"
|
||||
|
||||
|
||||
re_range = re.compile(r"\b(\d+) ?(?:to|-) ?(\d+)\b", re.I)
|
||||
re_number_list = re.compile(r"\b([\d, ]+) (?:and|&) (\d+)\b", re.I)
|
||||
re_number = re.compile(r"^(?:No\.?|Number)? ?(\d+)\b")
|
||||
|
||||
|
||||
def is_in_range(address_range, address):
|
||||
m_number = re_number.match(address)
|
||||
if not m_number:
|
||||
return False
|
||||
|
||||
m_range = re_range.search(address_range)
|
||||
if m_range:
|
||||
start, end = int(m_range.group(1)), int(m_range.group(2))
|
||||
if re_range.search(address):
|
||||
return False
|
||||
return start <= int(m_number.group(1)) <= end
|
||||
|
||||
m_list = re_number_list.search(address_range)
|
||||
if m_list:
|
||||
numbers = {n.strip() for n in m_list.group(1).split(",")} | {m_list.group(2)}
|
||||
if re_number_list.search(address):
|
||||
return False
|
||||
return m_number.group(1) in numbers
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def pluralize_label(label):
|
||||
text = label["value"]
|
||||
if label["language"] != "en":
|
||||
return text
|
||||
|
||||
# pattern.en.pluralize has the plural of 'mine' as 'ours'
|
||||
if text == "mine":
|
||||
return "mines"
|
||||
|
||||
return pattern.en.pluralize(text)
|
|
@ -297,6 +297,9 @@ function load_wikidata_items() {
|
|||
popup += `<br><a href="${isa_url}">${isa_label}</a> (${isa_qid})`;
|
||||
}
|
||||
}
|
||||
if (item.image_list && item.image_list.length) {
|
||||
popup += `<br><img src="/commons/${item.image_list[0]}">`;
|
||||
}
|
||||
popup += '</p>';
|
||||
marker.bindPopup(popup);
|
||||
marker.addTo(group);
|
||||
|
|
11
web_view.py
11
web_view.py
|
@ -3,7 +3,7 @@
|
|||
from flask import Flask, render_template, request, jsonify, redirect, url_for
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import selectinload
|
||||
from matcher import nominatim, model, database
|
||||
from matcher import nominatim, model, database, commons
|
||||
from collections import Counter
|
||||
from time import time
|
||||
import GeoIP
|
||||
|
@ -162,11 +162,13 @@ def get_markers(all_items):
|
|||
if "en" not in item.labels:
|
||||
continue
|
||||
locations = [list(i.get_lat_lon()) for i in item.locations]
|
||||
image_filenames = item.get_claim("P18")
|
||||
item = {
|
||||
"qid": item.qid,
|
||||
"label": item.label(),
|
||||
"description": item.description(),
|
||||
"markers": locations,
|
||||
"image_list": image_filenames,
|
||||
"isa_list": [v["id"] for v in item.get_claim("P31")],
|
||||
}
|
||||
items.append(item)
|
||||
|
@ -200,6 +202,13 @@ def identifier_index():
|
|||
return render_template("identifier_index.html", property_map=property_map)
|
||||
|
||||
|
||||
@app.route("/commons/<filename>")
|
||||
def get_commons_image(filename):
|
||||
detail = commons.image_detail([filename], thumbheight=250, thumbwidth=250)
|
||||
image = detail[filename]
|
||||
return redirect(image["thumburl"])
|
||||
|
||||
|
||||
@app.route("/identifier/<pid>")
|
||||
def identifier_page(pid):
|
||||
per_page = 10
|
||||
|
|
Loading…
Reference in a new issue