From 5a42e100c71332f55d9a6079bd193882de775f40 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sun, 11 Jul 2021 17:18:45 +0200 Subject: [PATCH] Improve OSM search --- frontend/App.vue | 347 ++++++++++++++++++++++++++++++++++++++------- matcher/model.py | 10 +- templates/map.html | 2 + web_view.py | 28 +++- 4 files changed, 329 insertions(+), 58 deletions(-) diff --git a/frontend/App.vue b/frontend/App.vue index f8436be..9ab2c3d 100644 --- a/frontend/App.vue +++ b/frontend/App.vue @@ -36,7 +36,7 @@ @@ -45,23 +45,21 @@
- + + +
@@ -147,7 +145,7 @@ Changes saved. - view your changeset + view your changeset
@@ -218,7 +216,11 @@ -
part of: {{ osm.part_of.join("; ") }} +
part of: + + , + [{{ JSON.stringify(part_of.tags) }}] +

@@ -246,7 +248,7 @@
-
+
@@ -256,19 +258,76 @@
+

Searching for '{{ recent_search }}', found {{ hits.length }} places.

+
+ + + One search result. Click the result to continue. + + + Click a result to continue. + +
+
+
+
Link Wikidata and OpenStreetMap
+
+ + This software is a beta, it works but is incomplete.
See what's broken.
+

This tool will help you link Wikidata items with the matching object on OpenStreetMap (OSM).

+ +

To save changes you need to login via OpenStreetMap.

+ +

Zoom in or search for an area to work on.

+ + +
+
+ +
+
+
Map key
+ +
  • + + Wikidata item without OSM link +
  • +
  • + + Wikidata item with OSM link +
  • +
  • + + Linked OSM object +
  • +
    +
    +
    + +
    @@ -342,6 +401,19 @@
    {{wd_item.closed.join('; ')}} +
    + + +
    + +
    + count | + IsA counts | + item | + tags | + candidates +
    +
    @@ -354,7 +426,9 @@
    + + Image from Wikidata
    @@ -367,12 +441,30 @@
    +
    + Searching for nearby OSM matches +
    + +
    + No OSM matches found nearby +
    - Possible OSM matches
    -
    +
    Login with OpenStreetMap to add Wikidata tags
    + + Possible OSM matches (sorted by distance from item)
    + show: +
    - + +
    +
    + + +
    +
    + +
    @@ -380,7 +472,7 @@
    @@ -392,7 +484,11 @@ @click.stop> - {{ osm.name || "no name" }} + {{ osm.type }} + {{ osm.name }} + no name +
    street address: {{ osm.address }} @@ -414,15 +511,19 @@
    -
    part of: {{ osm.part_of.join("; ") }} +
    part of: + + , + {{ part_of.tags.name }} +

    elevation: {{ osm.tags.ele }} m
    - -
    area: {{ (osm.area / (1000 * 1000)).toFixed(1) }} km² + +
    area: {{ format_area(osm.area) }}
    @@ -453,6 +554,7 @@ import L from "leaflet"; import { ExtraMarkers } from "leaflet-extra-markers"; import axios from "redaxios"; +import {unref, toRaw} from 'vue'; var redMarker = ExtraMarkers.icon({ icon: "fa-wikidata", @@ -503,6 +605,8 @@ export default { startZoom: Number, startRadius: Number, username: String, + startMode: String, + q: String, }, data() { return { @@ -543,13 +647,25 @@ export default { upload_state: undefined, upload_progress: 0, show_tags: false, + show_area: true, + show_presets: true, flag_show_hover_isa: false, debug: false, map_area: undefined, item_count: undefined, + mode: undefined, + current_hit: undefined, + recent_search: undefined, }; }, computed: { + show_instructions() { + return (this.mode != "search" + && !this.loading + && !this.isa_list.length + && !this.view_edits + && !this.current_item); + }, area_too_big() { return this.map_area > 1000 * 1000 * 1000; }, @@ -559,6 +675,9 @@ export default { loading() { return this.osm_loading || this.wikidata_loading; }, + current_qid() { + return this.current_item ? this.current_item.wikidata.qid : undefined; + }, wd_item() { return this.current_item ? this.current_item.wikidata : undefined; }, @@ -610,7 +729,6 @@ export default { if (!edit_lookup[qid]) { qid_order.push(qid); - console.log(edit.item); edit_lookup[qid] = { 'qid': qid, 'wikidata': edit.item.wikidata, @@ -687,6 +805,20 @@ export default { } }, methods: { + format_area(area) { + var value, unit, dp; + if(area > 1000 * 1000) { + value = area / (1000 * 1000); + unit = "km²"; + dp = 1; + } else { + value = area; + unit = "m²"; + dp = 0; + } + + return value.toLocaleString("en-US", {maximumFractionDigits: dp}) + " " + unit + }, bounds_area(bounds) { var width = bounds.getSouthWest().distanceTo(bounds.getSouthEast()); var height = bounds.getSouthWest().distanceTo(bounds.getNorthWest()); @@ -752,8 +884,7 @@ export default { break; case "progress": var edit = app.edits[data.num]; - app.upload_progress = ((edit.num + 1) * 100) / app.edits.length; - console.log(app.upload_progress); + app.upload_progress = ((data.num + 1) * 100) / app.edits.length; edit.osm.upload_state = "progress"; break; case "saved": @@ -785,6 +916,7 @@ export default { return index; }, select_osm(item, osm) { + if (!this.username) return; osm.selected = !osm.selected; var index = this.edit_list_index(item, osm); @@ -825,7 +957,7 @@ export default { var lng = c.lng.toFixed(5); var path = `/map/${zoom}/${lat}/${lng}`; if (this.current_item) { - path += `?item=${this.wd_item.qid}`; + path += `?item=${this.current_qid}`; } return path; }, @@ -897,18 +1029,43 @@ export default { this.drop_hover_circles(); }, map_moved() { + if (this.mode == "search") return; this.auto_load(); this.update_map_path(); }, + current_state() { + var c = this.map.getCenter(); + return { + mode: this.mode, + zoom: this.map.getZoom(), + lat: c.lat.toFixed(5), + lon: c.lng.toFixed(5), + search_text: this.search_text, + detail_qid: this.current_qid, + item_count: this.item_count, + map_area: this.map_area, + hits: toRaw(this.hits), + current_hit: toRaw(this.current_hit), + isa_ticked: toRaw(this.isa_ticked), + isa_labels: toRaw(this.isa_labels), + current_osm: toRaw(this.current_osm), + recent_search: this.recent_search, + }; + }, update_map_path() { - history.replaceState(null, null, this.build_map_path()); + var state = this.current_state(); + history.replaceState(state, '', this.build_map_path()); }, open_item(qid) { var item = this.items[qid]; + if (this.current_item == item) return; // already open this.view_edits = false; this.current_osm = undefined; this.current_item = item; - this.update_map_path(); + + var state = this.current_state(); + history.pushState(state, '', this.build_map_path()); + this.hover_isa = undefined; if (item.detail_requested !== undefined) return; @@ -967,11 +1124,38 @@ export default { var lon = parseFloat(hit.lon).toFixed(5); return `/map/16/${lat}/${lon}` }, - visit(hit) { - var lat = parseFloat(hit.lat).toFixed(5); - var lon = parseFloat(hit.lon).toFixed(5); + fit_bounds_to_hit(hit) { + var bounds = [[hit.boundingbox[0], hit.boundingbox[2]], + [hit.boundingbox[1], hit.boundingbox[3]]]; + this.map.fitBounds(bounds); + }, + show_hit_on_map(hit) { + this.fit_bounds_to_hit(hit); + this.current_hit = hit; + this.update_search_state(); + }, + select_area() { + this.current_hit = undefined; + this.mode = "map"; + this.hits = []; + this.search_text = ""; + this.auto_load(); + + var state = this.current_state(); + history.pushState(state, '', this.build_map_path()); + }, + + visit(hit) { + this.current_hit = undefined; + this.hits = []; + this.recent_search = undefined; + this.search_text = ""; + this.fit_bounds_to_hit(hit); + this.mode = "map"; + + var state = this.current_state(); + history.pushState(state, '', this.build_map_path()); - this.map.setView([lat, lon], 16); this.auto_load(); }, @@ -1074,7 +1258,6 @@ export default { }); }, auto_load(bounds) { - console.log('auto_load'); var count_url = this.api_base_url + "/api/1/count"; bounds ||= this.map.getBounds(); this.map_area = this.bounds_area(bounds); @@ -1089,12 +1272,30 @@ export default { if (!this.too_many_items) this.load_wikidata_items(bounds); }); }, + update_search_state() { + history.replaceState(this.current_state(), '', "/search?q=" + this.search_text); + }, + search_path() { + return "/search?q=" + this.search_text; + }, run_search() { if (!this.search_text) return; + this.current_hit = undefined; var params = { q: this.search_text }; - var search_url = this.api_base_url + "/api/1/search"; - axios.get(search_url, { params: params }).then((response) => { + var api_search_url = this.api_base_url + "/api/1/search"; + axios.get(api_search_url, { params: params }).then((response) => { this.hits = response.data.hits; + if (!this.hits.length) return; + + this.recent_search = this.search_text; + this.item_count = undefined; + this.map_area = undefined; + this.clear_items(); + this.mode = "search"; + + this.current_hit = this.hits[0]; + this.fit_bounds_to_hit(this.current_hit); + history.pushState(this.current_state(), '', this.search_path()); }); }, @@ -1107,7 +1308,6 @@ export default { if (!item.wikidata) missing_qids.push(qid); } - console.log('missing:', missing_qids); if (missing_qids.length == 0) { this.update_wikidata(); this.check_for_missing_done = true; @@ -1179,15 +1379,46 @@ export default { marker_data.marker.setIcon(marker); }); } - } + }, + onpopstate(event) { + var state = event.state; + this.mode = state.mode; + this.zoom = state.zoom; + this.search_text = state.search_text; + this.center = [state.lat, state.lon]; + this.detail_qid = state.detail_qid; + this.recent_search = state.recent_search; + if (!this.detail_qid) this.current_item = undefined; + + this.item_count = state.item_count; + this.map_area = state.map_area; + this.hits = state.hits; + this.current_hit = state.current_hit; + + /* + this.isa_ticked = state.isa_ticked; + this.isa_labels = state.isa_labels; + */ + + this.current_osm = state.current_osm; + + this.map.setView(this.center, this.zoom); + + if (this.mode == "search") { + this.clear_items(); + this.fit_bounds_to_hit(this.current_hit); + } + }, }, created() { var lat = this.startLat ?? 52.19679; var lon = this.startLon ?? 0.15224; this.center = [lat, lon]; this.zoom = this.startZoom; + this.mode = this.startMode; }, mounted() { + this.$nextTick(function () { var options = { center: this.center, @@ -1205,7 +1436,6 @@ export default { var bounds; if (this.startRadius) { - console.log('radius:', this.startRadius); var bounds = L.latLng(this.center).toBounds(this.startRadius * 2000); map.fitBounds(bounds); } else { @@ -1215,12 +1445,20 @@ export default { map.on("moveend", this.map_moved); this.map = map; - this.detail_qid = this.qid_from_url(); - if (this.detail_qid) { - this.load_wikidata_items(bounds); + if (this.mode == "search") { + this.search_text = this.q.trim(); + this.run_search(); } else { - this.auto_load(bounds); + this.detail_qid = this.qid_from_url(); + if (this.detail_qid) { + this.load_wikidata_items(bounds); + } else { + this.auto_load(bounds); + } + this.update_map_path(); } + + window.onpopstate = this.onpopstate; }); }, @@ -1229,26 +1467,27 @@ export default {