-
Ciro Brodmann authored4f9049d5
App.vue 8.39 KiB
<template>
<v-app>
<v-app-bar class="px-6"
><strong class="site-title">OSM Place Search Logger</strong>
<v-spacer></v-spacer>
<a href="https://wiki.openstreetmap.org/wiki/OSM_Place_Search_Logger">About</a>
</v-app-bar>
<v-main fill-height>
<v-container fluid class="fill-height primary">
<v-row class="fill-height">
<v-col cols="6">
<v-card class="px-1 overflow-y-auto" flat>
<v-toolbar dense floating flat>
<v-text-field
ref="searchField"
hide-details
single-line
variant="outlined"
v-model="search"
label="Search"
v-on:keyup.enter="onEnter"
clearable
></v-text-field>
<v-btn icon @click="onSearchClick">
<v-icon>mdi-magnify</v-icon>
</v-btn>
</v-toolbar>
This web application stores your search queries in anonymised form and is used for
educational and research purposes in order to improve the awesome OpenStreetMap project. By
using this web application you agree with these conditions.
</v-card>
<div style="max-height: 70vh; overflow-y: auto">
<result-table
:result-list="listItems"
:on-submit-solution="submitSolution"
:on-select-option="onSelection"
></result-table>
<div class="no-entries-label" v-if="searchedWithoutResults">
No entries found
</div>
</div>
<div v-if="searchedWithoutResults || listItems.length !== 0" width="100%"
class="d-flex flex-row my-6 align-center flex-wrap" style="gap: 10px">
<v-text-field
hide-details
density="compact"
placeholder="Any other comments?"
v-model="comment"
></v-text-field>
<v-btn :disabled="!selected" @click="submitSolution" class="selected-button">
Submit Solution
</v-btn>
<v-btn @click="noSolutionFound" class="bg-red-lighten-4"
>Entry not present
</v-btn>
</div>
</v-col>
<v-col cols="6">
<leaflet-map :poi="selectedGeoJson" @on-b-box-changed="onBBoxChanged"></leaflet-map>
</v-col>
</v-row>
</v-container>
</v-main>
</v-app>
</template>
<script setup lang="ts">
import {ref, computed, onMounted} from 'vue'
import type GeoJsonSearchResult from './types/geojson-search-result'
import LeafletMap from './LeafletMap.vue'
import ResultTable from './ResultTable.vue'
import Feature from './types/feature'
import BoundingBox from "./types/BoundingBox";
const sessionId = ref<string>()
const userId = ref<string>()
const rawSearchResult = ref<GeoJsonSearchResult>()
const search = ref<string>('')
const comment = ref<string>('')
const searchField = ref<HTMLElement | null>(null)
const boundingBox = ref<BoundingBox>()
const bBoxWhenSearched = ref<BoundingBox>()
const baseUrl = 'https://nominatim.openstreetmap.org/search.php'
function onEnter() {
searchField.value?.blur()
onSearchClick()
}
function onSelection(el: Feature) {
console.log(el)
selected.value = el
}
function onSearchClick() {
getResults()
}
function onBBoxChanged(bbox) {
boundingBox.value = bbox // this step might be unnecessary -> check if this is only a reference to the object from
// LeafletMap.vue. if so, we don't need to constantly reassign this value
}
const viewbox = computed(() => {
return `${boundingBox.value.lonNE},${boundingBox.value.latNE},${boundingBox.value.lonSW},${boundingBox.value.latSW}`
})
const backendBaseUrl = 'https://osm-place-search-logger.infs.ch/api'
// const backendBaseUrl = 'http://localhost:8080/api'
function submitSolution(result: Feature) {
let body = {
userId: userId.value,
sessionId: sessionId.value,
search: search.value,
rawSearchResult: rawSearchResult.value,
selection: result,
success: true,
bbox: bBoxWhenSearched.value
}
if (comment.value !== '') {
body = {...body, comment: comment.value}
}
fetch(`${backendBaseUrl}/submit`, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(body)
}).then(reset)
}
function noSolutionFound() {
let body = {
userId: userId.value,
sessionId: sessionId.value,
search: search.value,
rawSearchResult: rawSearchResult.value,
success: false,
bbox: bBoxWhenSearched.value
}
if (comment.value !== '') {
body = {...body, comment: comment.value}
}
fetch(`${backendBaseUrl}/submit`, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(body)
}).then(reset)
}
function reset() {
rawSearchResult.value = undefined
search.value = ''
comment.value = ''
sessionId.value = crypto.randomUUID()
}
function getResults() {
bBoxWhenSearched.value = {...boundingBox.value}
selected.value = null;
const query = queryString.value;
const requestString = `${baseUrl}?q=${query}&format=geojson&limit=20&viewbox=${viewbox.value}`;
fetch(requestString, {method: 'GET'})
.then((res) => res.json())
.then((json) => (rawSearchResult.value = json))
.then(() =>
fetch(`${backendBaseUrl}/search`, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
userId: userId.value,
sessionId: sessionId.value,
search: search.value,
rawSearchResult: rawSearchResult.value,
bbox: boundingBox.value
})
})
)
//.catch((err) => (error.value = err))
}
onMounted(() => {
sessionId.value = crypto.randomUUID()
userId.value = crypto.randomUUID()
})
const queryString = computed(() => {
const query = search.value ? search.value : ''
const encodedValue = encodeURIComponent(query)
return encodedValue.replaceAll('%20', '+')
})
const listItems = computed(() => {
return rawSearchResult.value?.features || ([] as Feature[])
})
const selected = ref<Feature | null>(null)
const selectedGeoJson = computed(() => {
if (selected.value === null) {
return {...rawSearchResult.value, features: []}
}
return {...rawSearchResult.value, features: [selected.value]}
})
const searchedWithoutResults = computed(() => {
return rawSearchResult.value && listItems.value.length === 0
})
// https://nominatim.openstreetmap.org/search.php?q=oberer+gubel+48+jona&format=jsonv2
</script>
<style scoped lang="scss">
.site-title {
color: #8c195f;
font-size: 22px;
}
.v-app-bar a {
text-decoration: none;
}
.no-entries-label {
font-weight: 500;
font-size: 12pt;
color: #717171;
text-align: center;
margin: 20px 0;
}
.selected-button {
background: #7d92f5 !important;
color: white;
&.v-btn--disabled {
background-color: lighten(#7d92f5, 20) !important;
}
}
</style>