Switch to maxminddb for user location.

This commit is contained in:
Edward Betts 2021-07-06 17:24:14 +02:00
parent 792bfa176d
commit f60aa70472
3 changed files with 126 additions and 32 deletions

View File

@ -48,6 +48,15 @@
</span> </span>
</button> </button>
<div class="alert alert-primary alert-map" role="alert" v-if="area_too_big">
Zoom in to see Wikidata items on the map.
</div>
<div class="alert alert-primary alert-map text-center" role="alert" v-if="!area_too_big && this.too_many_items">
Found {{ this.item_count }} Wikidata items, too many to show on the map.<br>
Zoom in to see them.
</div>
<div id="edit-count" class="p-2" v-if="upload_state === undefined && edits.length"> <div id="edit-count" class="p-2" v-if="upload_state === undefined && edits.length">
<span>edits: {{ edits.length }}</span> <span>edits: {{ edits.length }}</span>
<button class="btn btn-primary btn-sm ms-2" @click="close_item(); view_edits=true"> <button class="btn btn-primary btn-sm ms-2" @click="close_item(); view_edits=true">
@ -172,8 +181,8 @@
<table class="table table-sm table-hover"> <table class="table table-sm table-hover">
<tbody> <tbody>
<tr v-for="osm in edit.osm" class="osm-candidate"> <tr v-for="osm in edit.osm" class="osm-candidate">
<td class="text-end text-nowrap"> <td class="text-end">
{{ osm.distance.toFixed(0) }}m <span class="text-nowrap">{{ osm.distance.toFixed(0) }}m</span><br>
<a <a
:href="'https://www.openstreetmap.org/' + osm.identifier" :href="'https://www.openstreetmap.org/' + osm.identifier"
target="_blank" target="_blank"
@ -352,7 +361,13 @@
</div> </div>
<div v-if="current_item.nearby && current_item.nearby.length"> <div v-if="current_item.nearby && current_item.nearby.length">
<strong>Possible OSM matches</strong><br> <strong>Possible OSM matches</strong><br>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="show-tags" v-model="show_tags">
<label class="form-check-label" for="show-tags">show tags</label>
</div>
<table class="table table-sm table-hover" @mouseleave="this.current_osm = undefined"> <table class="table table-sm table-hover" @mouseleave="this.current_osm = undefined">
<tbody> <tbody>
<tr <tr
@ -361,10 +376,8 @@
:class="{ 'table-primary': osm.selected }" :class="{ 'table-primary': osm.selected }"
@mouseenter="this.current_osm=osm" @mouseenter="this.current_osm=osm"
@click="select_osm(current_item, osm)"> @click="select_osm(current_item, osm)">
<td> <td class="text-nowrap">
<input class="form-check-input" type="checkbox" v-model="osm.selected"/> <input class="form-check-input" type="checkbox" v-model="osm.selected" v-if="username" />
</td>
<td class="text-end text-nowrap">
{{ osm.distance.toFixed(0) }}m {{ osm.distance.toFixed(0) }}m
<a <a
:href="'https://www.openstreetmap.org/' + osm.identifier" :href="'https://www.openstreetmap.org/' + osm.identifier"
@ -397,6 +410,27 @@
<br>part of: {{ osm.part_of.join("; ") }} <br>part of: {{ osm.part_of.join("; ") }}
</span> </span>
<span v-if="osm.tags.ele">
<br>elevation: {{ osm.tags.ele }} m
</span>
<span v-if="osm.area && osm.area > 1000 * 1000">
<br>area: {{ (osm.area / (1000 * 1000)).toFixed(1) }} km²
</span>
<span v-if="osm.tags.wikidata">
<br>Wikidata tag:
<a :href="`https://wikidata.org/wiki/${osm.tags.wikidata}`">{{ osm.tags.wikidata }}</a>
</span>
<div class="card" v-if="show_tags">
<div class="card-body tag-card-body">
<span class="badge bg-secondary float-end">tags</span>
<div class="card-text" v-for="(value, key) of osm.tags">
<strong>{{ key }}</strong>:
{{ value.replace(/;/g, '; ') }}
</div>
</div>
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -460,6 +494,7 @@ export default {
startLat: Number, startLat: Number,
startLon: Number, startLon: Number,
startZoom: Number, startZoom: Number,
startRadius: Number,
username: String, username: String,
}, },
data() { data() {
@ -500,9 +535,20 @@ export default {
changeset_id: undefined, changeset_id: undefined,
upload_state: undefined, upload_state: undefined,
upload_progress: 0, upload_progress: 0,
show_tags: false,
flag_show_hover_isa: false,
debug: false,
map_area: undefined,
item_count: undefined,
}; };
}, },
computed: { computed: {
area_too_big() {
return this.map_area > 1000 * 1000 * 1000;
},
too_many_items() {
return this.item_count > 400;
},
loading() { loading() {
return this.osm_loading || this.wikidata_loading; return this.osm_loading || this.wikidata_loading;
}, },
@ -610,29 +656,39 @@ export default {
item.markers.forEach((marker) => { item.markers.forEach((marker) => {
var coords = marker.getLatLng(); var coords = marker.getLatLng();
var circle = L.circle(coords, { radius: 20, color: "orange" }).addTo(this.map); var circle = L.circleMarker(coords, { radius: 20, color: "orange" }).addTo(this.map);
this.selected_circles.push(circle); this.selected_circles.push(circle);
}); });
}, },
hover_isa(highlight_isa) { hover_isa(highlight_isa) {
// if (!this.flag_show_hover_isa) return;
this.drop_hover_circles(); this.drop_hover_circles();
for(const item of Object.values(this.selected_items)) { for(const item of Object.values(this.selected_items)) {
var opacity = 0.9; // var opacity = 0.9;
if (highlight_isa) { if (highlight_isa) {
var match = item.wikidata.isa_list.some(isa => isa == highlight_isa.qid); var match = item.wikidata.isa_list.some(isa => isa == highlight_isa.qid);
opacity = match ? 1 : 0.2; // opacity = match ? 1 : 0.2;
if (match) { if (match) {
this.add_hover_circles(item); this.add_hover_circles(item);
} }
} }
this.set_item_opacity(item, opacity); // this.set_item_opacity(item, opacity);
} }
} }
}, },
methods: { methods: {
bounds_area(bounds) {
var width = bounds.getSouthWest().distanceTo(bounds.getSouthEast());
var height = bounds.getSouthWest().distanceTo(bounds.getNorthWest());
return width * height;
},
bounds_param() {
return 'bounds=' + this.map.getBounds().toBBoxString();
},
close_edit_list() { close_edit_list() {
this.view_edits = false; this.view_edits = false;
if (this.upload_state == 'done') { if (this.upload_state == 'done') {
@ -797,7 +853,7 @@ export default {
add_hover_circles(item) { add_hover_circles(item) {
item.markers.forEach((marker) => { item.markers.forEach((marker) => {
var coords = marker.getLatLng(); var coords = marker.getLatLng();
var circle = L.circle(coords, { radius: 20 }).addTo(this.map); var circle = L.circleMarker(coords, { radius: 20 }).addTo(this.map);
this.hover_circles.push(circle); this.hover_circles.push(circle);
}); });
}, },
@ -946,10 +1002,10 @@ export default {
} }
this.items = {}; this.items = {};
clear_isa(); this.clear_isa();
}, },
load_wikidata_items() { load_wikidata_items(bounds) {
this.load_button_pressed = true; this.load_button_pressed = true;
this.wikidata_loaded = false; this.wikidata_loaded = false;
this.osm_loaded = false; this.osm_loaded = false;
@ -958,8 +1014,7 @@ export default {
this.wikidata_loading = true; this.wikidata_loading = true;
this.osm_loading = true; this.osm_loading = true;
bounds ||= this.map.getBounds();
var bounds = this.map.getBounds();
var items_url = this.api_base_url + "/api/1/items"; var items_url = this.api_base_url + "/api/1/items";
var osm_objects_url = this.api_base_url + "/api/1/osm"; var osm_objects_url = this.api_base_url + "/api/1/osm";
@ -1011,16 +1066,20 @@ export default {
this.hits = []; this.hits = [];
}); });
}, },
auto_load() { auto_load(bounds) {
console.log('auto_load');
var count_url = this.api_base_url + "/api/1/count"; var count_url = this.api_base_url + "/api/1/count";
var bounds = this.map.getBounds(); bounds ||= this.map.getBounds();
this.map_area = this.bounds_area(bounds);
if (this.area_too_big) {
this.item_count = undefined;
if (this.items) this.clear_items();
return;
}
var params = { bounds: bounds.toBBoxString() }; var params = { bounds: bounds.toBBoxString() };
axios.get(count_url, { params: params }).then((response) => { axios.get(count_url, { params: params }).then((response) => {
var count = response.data.count; this.item_count = response.data.count;
if (count < 1000) { if (!this.too_many_items) this.load_wikidata_items(bounds);
this.load_wikidata_items();
}
}); });
}, },
run_search() { run_search() {
@ -1119,15 +1178,16 @@ export default {
var lat = this.startLat ?? 52.19679; var lat = this.startLat ?? 52.19679;
var lon = this.startLon ?? 0.15224; var lon = this.startLon ?? 0.15224;
this.center = [lat, lon]; this.center = [lat, lon];
this.zoom = this.startZoom || 16; this.zoom = this.startZoom;
}, },
mounted() { mounted() {
this.$nextTick(function () { this.$nextTick(function () {
var options = { var options = {
center: this.center, center: this.center,
zoom: this.zoom, zoom: this.zoom || 16,
}; };
var map = L.map("map", options); var map = L.map("map", options);
var osm_url = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"; var osm_url = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
var tile_url = "https://tile-c.openstreetmap.fr/hot/{z}/{x}/{y}.png"; var tile_url = "https://tile-c.openstreetmap.fr/hot/{z}/{x}/{y}.png";
@ -1136,14 +1196,23 @@ export default {
}); });
osm.addTo(map); osm.addTo(map);
var bounds;
if (this.startRadius) {
console.log('radius:', this.startRadius);
var bounds = L.latLng(this.center).toBounds(this.startRadius * 2000);
map.fitBounds(bounds);
} else {
bounds = map.getBounds();
}
map.on("moveend", this.map_moved); map.on("moveend", this.map_moved);
this.map = map; this.map = map;
this.detail_qid = this.qid_from_url(); this.detail_qid = this.qid_from_url();
if (this.detail_qid) { if (this.detail_qid) {
this.load_wikidata_items(); this.load_wikidata_items(bounds);
} else { } else {
this.auto_load(); this.auto_load(bounds);
} }
}); });
@ -1174,6 +1243,13 @@ export default {
transform: translate(-50%, 0); transform: translate(-50%, 0);
} }
.alert-map {
position: absolute;
bottom: 2rem;
left: 67.5%;
transform: translate(-50%, 0);
}
#edit-count { #edit-count {
position: absolute; position: absolute;
top: 77px; top: 77px;
@ -1205,4 +1281,8 @@ export default {
width: 35%; width: 35%;
} }
.tag-card-body {
padding: 0.5rem 0.5rem;
}
</style> </style>

View File

@ -21,6 +21,7 @@
startLat: {{ lat }}, startLat: {{ lat }},
startLon: {{ lon }}, startLon: {{ lon }},
startZoom: {{ zoom }}, startZoom: {{ zoom }},
startRadius: {{ radius | tojson }},
username: {{ username | tojson }}, username: {{ username | tojson }},
}; };

View File

@ -12,6 +12,7 @@ import flask_login
import json import json
import GeoIP import GeoIP
import re import re
import maxminddb
srid = 4326 srid = 4326
re_point = re.compile(r'^POINT\((.+) (.+)\)$') re_point = re.compile(r'^POINT\((.+) (.+)\)$')
@ -24,6 +25,7 @@ login_manager = flask_login.LoginManager(app)
login_manager.login_view = 'login_route' login_manager.login_view = 'login_route'
osm_api_base = 'https://api.openstreetmap.org/api/0.6' osm_api_base = 'https://api.openstreetmap.org/api/0.6'
maxminddb_reader = maxminddb.open_database(app.config["GEOLITE2"])
DB_URL = "postgresql:///matcher" DB_URL = "postgresql:///matcher"
database.init_db(DB_URL) database.init_db(DB_URL)
@ -72,11 +74,14 @@ def check_for_tagged_qid(qid):
def geoip_user_record(): def geoip_user_record():
gi = GeoIP.open(app.config["GEOIP_DATA"], GeoIP.GEOIP_STANDARD) gi = GeoIP.open(app.config["GEOIP_DATA"], GeoIP.GEOIP_STANDARD)
remote_ip = request.remote_addr remote_ip = request.get('ip', request.remote_addr)
return gi.record_by_addr(remote_ip) return gi.record_by_addr(remote_ip)
def get_user_location(): def get_user_location():
remote_ip = request.args.get('ip', request.remote_addr)
return maxminddb_reader.get(remote_ip)["location"]
gir = geoip_user_record() gir = geoip_user_record()
return (gir["latitude"], gir["longitude"]) if gir else None return (gir["latitude"], gir["longitude"]) if gir else None
@ -167,14 +172,15 @@ def old_map_location(zoom, lat, lng):
@app.route("/map") @app.route("/map")
def map_start_page(): def map_start_page():
location = get_user_location() loc = get_user_location()
lat, lon = location
return redirect(url_for( return redirect(url_for(
'map_location', 'map_location',
lat=f'{loc["latitude"]:.5f}',
lon=f'{loc["longitude"]:.5f}',
zoom=16, zoom=16,
lat=f'{lat:.5f}', radius=loc["accuracy_radius"],
lon=f'{lon:.5f}', ip=request.args.get('ip'),
)) ))
@app.route("/map/<int:zoom>/<float(signed=True):lat>/<float(signed=True):lon>") @app.route("/map/<int:zoom>/<float(signed=True):lat>/<float(signed=True):lon>")
@ -182,7 +188,14 @@ def map_location(zoom, lat, lon):
user = flask_login.current_user user = flask_login.current_user
username = user.username if user.is_authenticated else None username = user.username if user.is_authenticated else None
return render_template("map.html", zoom=zoom, lat=lat, lon=lon, username=username) return render_template(
"map.html",
zoom=zoom,
lat=lat,
lon=lon,
radius=request.args.get('radius'),
username=username
)
@app.route("/old_map") @app.route("/old_map")