Skip to content

Commit

Permalink
data-search svelte add map view
Browse files Browse the repository at this point in the history
  • Loading branch information
moogoo78 committed May 2, 2024
1 parent 8f62253 commit 9172643
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 173 deletions.
61 changes: 37 additions & 24 deletions app/blueprints/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,14 @@ def get_search():
## range
start = int(payload['range'][0])
end = int(payload['range'][1])
#print(view, flush=True)

if view == 'map':
end = 500 # TODO
end = 2000 # TODO

if start == 0 and end == -1:
pass # no limit
else:
limit = min((end-start), 1000) # TODO: max query range
limit = min((end-start), 2000) # TODO: max query range
stmt = stmt.limit(limit)
if start > 0:
stmt = stmt.offset(start)
Expand Down Expand Up @@ -288,7 +288,6 @@ def get_search():
elapsed_mapping = None

rows = result.all()
#print(rows, flush=True)
for r in rows:
unit = r[0]
if record := r[1]:
Expand All @@ -308,26 +307,40 @@ def get_search():
taxon_text = record.proxy_taxon_scientific_name
if record.proxy_taxon_common_name:
taxon_text = f'{record.proxy_taxon_scientific_name} ({record.proxy_taxon_common_name})'
data.append({
'unit_id': unit.id if unit else '',
'collection_id': record.id,
'record_key': f'u{unit.id}' if unit else f'c{record.id}',
# 'accession_number': unit.accession_number if unit else '',
'accession_number': unit.accession_number if unit else '',
'image_url': image_url,
'field_number': record.field_number,
'collector': record.collector.to_dict() if record.collector else '',
'collect_date': record.collect_date.strftime('%Y-%m-%d') if record.collect_date else '',
'taxon_text': taxon_text,
'taxon': t.to_dict() if t else {},
'named_areas': [x.to_dict() for x in record.get_named_area_list('default')],
'locality_text': record.locality_text,
'altitude': record.altitude,
'altitude2': record.altitude2,
'longitude_decimal': record.longitude_decimal,
'latitude_decimal': record.latitude_decimal,
'type_status': unit.type_status if unit and (unit.type_status and unit.pub_status=='P' and unit.type_is_published is True) else '',
})
if not view or view == 'table':
data.append({
'unit_id': unit.id if unit else '',
'collection_id': record.id,
'record_key': f'u{unit.id}' if unit else f'c{record.id}',
# 'accession_number': unit.accession_number if unit else '',
'accession_number': unit.accession_number if unit else '',
'image_url': image_url,
'field_number': record.field_number,
'collector': record.collector.to_dict() if record.collector else '',
'collect_date': record.collect_date.strftime('%Y-%m-%d') if record.collect_date else '',
'taxon_text': taxon_text,
'taxon': t.to_dict() if t else {},
'named_areas': [x.to_dict() for x in record.get_named_area_list('default')],
'locality_text': record.locality_text,
'altitude': record.altitude,
'altitude2': record.altitude2,
'longitude_decimal': record.longitude_decimal,
'latitude_decimal': record.latitude_decimal,
'type_status': unit.type_status if unit and (unit.type_status and unit.pub_status=='P' and unit.type_is_published is True) else '',
})
elif view == 'map':
if record.longitude_decimal and record.latitude_decimal:
data.append({
'accession_number': unit.accession_number if unit else '',
'image_url': image_url,
'field_number': record.field_number,
'collector': record.collector.to_dict() if record.collector else '',
'collect_date': record.collect_date.strftime('%Y-%m-%d') if record.collect_date else '',
'taxon_text': taxon_text,
'longitude_decimal': record.longitude_decimal,
'latitude_decimal': record.latitude_decimal,
})

elapsed_mapping = time.time() - begin_time

resp = jsonify({
Expand Down
5 changes: 1 addition & 4 deletions client/data-search/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@
$: { console.log('[formant]', $formant);}
const popState = (event) => {
console.log(location.search);
let res = formant.fromSearchParams();
formant.
formant.goSearch();
formant.searchFromSearchParams();
}
</script>

Expand Down
97 changes: 52 additions & 45 deletions client/data-search/src/Filter.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@
export let isSidebarOpen;
export let isLanding;
let select2State = {
option: {},
loading: {},
group: {},
};
const init = async () => {
let urls = [];
let tmpGroup = [];
Expand Down Expand Up @@ -48,10 +42,13 @@
$formant.selectState.option[key] = results.data;
$formant.selectState.loading[key] = false;
}));
formant.searchFromSearchParams();
}; // end of init
const onSelect2 = async (key, value, label) => {
formant.addFilter({
formant.addFilterWithFunnel({
[key]: {
value: value,
display: label,
Expand All @@ -61,16 +58,18 @@
}
const onSelect2Clear = (key) => {
formant.removeFilter(key);
formant.removeFilterWithFunnel(key);
};
const handleSelectContinent = async (e) => {
let arr = $register.continent.fetch.split('?');
let url = arr[0];
let searchParams = appendQuery(arr[1], 'filter', {'continent': e.target.value})
url = `${$HOST}/${url}?${searchParams.toString()}`;
let url = `api/v1/named-areas`;
let filter = {
area_class_id: 7,
continent: e.target.value,
};
url = `${$HOST}/${url}?filter=${JSON.stringify(filter)}`;
let results = await fetchData(url);
select2State.option.country = results.data;
$formant.selectState.option.country = results.data;
}
Expand All @@ -89,23 +88,31 @@
}
const onScientificNameSelect = (selected) => {
formant.clearSelected('scientific_name')
let rank = selected.rank;
let label = selected.display_name;
formant.addFilter({
[rank]: {
value: selected.id,
display: label,
name: rank,
}
});
// formant.removeFilter('scientific_name');
// let rank = selected.rank;
// let label = selected.display_name;
// formant.addFilter({
// [rank]: {
// value: selected.id,
// display: label,
// name: rank,
// }
// });
//goSearch();
onSelect2(rank, selected.id, label);
//console.log(rank, selected.id, label);
formant.addFilter({
scientific_name: {
value: selected.id,
display: selected.display_name,
name: 'scientific_name',
}
});
};
const onScientificNameInput = async (input) => {
if (input && input.length >= 2) {
select2State.loading.scientific_name = true;
$formant.selectState.loading.scientific_name = true;
let url = '';
if (input.length >= 4) {
url = `${$HOST}/api/v1/taxa?filter={"q":"${input}"}`;
Expand All @@ -114,17 +121,16 @@
}
let results = await fetchData(url);
if (results.data) {
select2State.option.scientific_name = results.data.map( (x) => ({...x, value: x.id}));
$formant.selectState.option.scientific_name = results.data;
}
}
}
onMount(async () => {
//const res = await fetch(`/tutorial/api/album`);
//photos = await res.json();
init();
});
$:{ console.log('select2State: ', select2State); }
//$:{ console.log('select2State: ', select2State); }
</script>

<div class="uk-card uk-card-default uk-card-small uk-card-body">
Expand Down Expand Up @@ -152,14 +158,15 @@
onCallbackClear={ () => onSelect2Clear('scientific_name')}
value={$formant.formValues.scientific_name}
onInput={onScientificNameInput}
displayValue={$formant.formValues.scientific_name?.display}
/>
</svelte:fragment>
</FormWidget>
<FormWidget>
<svelte:fragment slot="label">科名 (Family)</svelte:fragment>
<svelte:fragment slot="control">
{#if $formant.selectState.loading.family === false}
<Select2 options={$formant.selectState.option.family} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('family', x, y)} onCallbackClear={ () => onSelect2Clear('family')} value={$formant.formValues.family} displayValue={$formant.formValues.family?.display || '' }/>
<Select2 options={$formant.selectState.option.family} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('family', x, y)} onCallbackClear={ () => onSelect2Clear('family')} value={$formant.formValues.family} displayValue={$formant.formValues.family?.display}/>
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -169,7 +176,7 @@
<svelte:fragment slot="label">屬名 (Genus)</svelte:fragment>
<svelte:fragment slot="control">
{#if $formant.selectState.loading.genus === false}
<Select2 options={$formant.selectState.option.genus} optionText="display_name" optionValue="id" disabled={($formant.selectState.option.genus && $formant.selectState.option.genus.length > 0) ? false : true} onCallback={(x, y) => onSelect2('genus', x , y)} onCallbackClear={ () => onSelect2Clear('genus')} value={$formant.formValues.genus} />
<Select2 options={$formant.selectState.option.genus} optionText="display_name" optionValue="id" disabled={($formant.selectState.option.genus && $formant.selectState.option.genus.length > 0) ? false : true} onCallback={(x, y) => onSelect2('genus', x , y)} onCallbackClear={ () => onSelect2Clear('genus')} value={$formant.formValues.genus} displayValue={$formant.formValues.genus?.display}/>
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -179,7 +186,7 @@
<svelte:fragment slot="label">種名 (Species)</svelte:fragment>
<svelte:fragment slot="control">
{#if $formant.selectState.loading.species === false}
<Select2 options={$formant.selectState.option.species} optionText="display_name" optionValue="id" disabled={($formant.selectState.option.species && $formant.selectState.option.species.length > 0) ? false : true} onCallback={(x, y) => onSelect2('species', x, y)} onCallbackClear={ () => onSelect2Clear('species')} value={$formant.formValues.species} data/>
<Select2 options={$formant.selectState.option.species} optionText="display_name" optionValue="id" disabled={($formant.selectState.option.species && $formant.selectState.option.species.length > 0) ? false : true} onCallback={(x, y) => onSelect2('species', x, y)} onCallbackClear={ () => onSelect2Clear('species')} value={$formant.formValues.species} displayValue={$formant.formValues.species?.display}/>
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -191,7 +198,7 @@
<FormWidget>
<svelte:fragment slot="label">採集者</svelte:fragment>
<svelte:fragment slot="control">
<Select2 options={$formant.selectState.option.collector} optionText="display_name" optionValue="id" onCallback={ (x, y) => onSelect2('collector', x, y)} onCallbackClear={ x => onSelect2Clear('collector')} value={$formant.formValues.collector}/>
<Select2 options={$formant.selectState.option.collector} optionText="display_name" optionValue="id" onCallback={ (x, y) => onSelect2('collector', x, y)} onCallbackClear={ x => onSelect2Clear('collector')} value={$formant.formValues.collector} displayValue={$formant.formValues.collector?.display} />
</svelte:fragment>
</FormWidget>
<FormWidget>
Expand Down Expand Up @@ -253,8 +260,8 @@
<FormWidget>
<svelte:fragment slot="label">國家/地區</svelte:fragment>
<svelte:fragment slot="control">
{#if select2State.loading.country === false}
<Select2 options={select2State.option.country} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('country', x, y, {target: 'adm1', query: {parent_id: x}, resets: ['adm2']})} onCallbackClear={ () => onSelect2Clear('country', {resets: ['adm1', 'adm2', 'adm3']})} value={$formant.formValues.country} />
{#if $formant.selectState.loading.country === false}
<Select2 options={$formant.selectState.option.country} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('country', x, y)} onCallbackClear={ () => onSelect2Clear('country')} value={$formant.formValues.country} />
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -263,8 +270,8 @@
<FormWidget>
<svelte:fragment slot="label">行政區1</svelte:fragment>
<svelte:fragment slot="control">
{#if select2State.loading.adm1 === false}
<Select2 options={select2State.option.adm1} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('adm1', x, y, {target: 'adm2', query: {parent_id: x}, resets: ['adm3']})} onCallbackClear={ () => onSelect2Clear('country', {resets: ['adm2', 'adm3']})} value={$formant.formValues.adm1} />
{#if $formant.selectState.loading.adm1 === false}
<Select2 options={$formant.selectState.option.adm1} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('adm1', x, y)} onCallbackClear={ () => onSelect2Clear('country')} value={$formant.formValues.adm1} />
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -273,8 +280,8 @@
<FormWidget>
<svelte:fragment slot="label">行政區2</svelte:fragment>
<svelte:fragment slot="control">
{#if select2State.loading.adm2 === false}
<Select2 options={select2State.option.adm2} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('adm2', x, y, {target: 'adm3', query: {parent_id: x}})} onCallbackClear={ () => onSelect2Clear('country', {resets: ['adm3']})} value={$formant.formValues.adm2} />
{#if $formant.selectState.loading.adm2 === false}
<Select2 options={$formant.selectState.option.adm2} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('adm2', x, y)} onCallbackClear={ () => onSelect2Clear('country')} value={$formant.formValues.adm2} />
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -283,8 +290,8 @@
<FormWidget>
<svelte:fragment slot="label">行政區3</svelte:fragment>
<svelte:fragment slot="control">
{#if select2State.loading.adm3 === false}
<Select2 options={select2State.option.adm3} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('adm3', x, y)} onCallbackClear={ () => onSelect2Clear('country')} value={$formant.formValues.adm3} />
{#if $formant.selectState.loading.adm3 === false}
<Select2 options={$formant.selectState.option.adm3} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('adm3', x, y)} onCallbackClear={ () => onSelect2Clear('country')} value={$formant.formValues.adm3} />
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -293,8 +300,8 @@
<FormWidget>
<svelte:fragment slot="label">國家公園/保護留區</svelte:fragment>
<svelte:fragment slot="control">
{#if select2State.loading.named_area__park === false}
<Select2 options={select2State.option.named_area__park} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('named_area__park', x, y)} onCallbackClear={ () => onSelect2Clear('named_area__park')} value={$formant.formValues.named_area__park} />
{#if $formant.selectState.loading.named_area__park === false}
<Select2 options={$formant.selectState.option.named_area__park} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('named_area__park', x, y)} onCallbackClear={ () => onSelect2Clear('named_area__park')} value={$formant.formValues.named_area__park} />
{:else}
<div uk-spinner></div>
{/if}
Expand All @@ -303,8 +310,8 @@
<FormWidget>
<svelte:fragment slot="label">地點名稱</svelte:fragment>
<svelte:fragment slot="control">
{#if select2State.loading.named_area__locality === false}
<Select2 options={select2State.option.named_area__locality} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('named_area__locality', x, y)} onCallbackClear={ () => onSelect2Clear('named_area__locality')} value={$formant.formValues.named_area__locality} />
{#if $formant.selectState.loading.named_area__locality === false}
<Select2 options={$formant.selectState.option.named_area__locality} optionText="display_name" optionValue="id" onCallback={(x, y) => onSelect2('named_area__locality', x, y)} onCallbackClear={ () => onSelect2Clear('named_area__locality')} value={$formant.formValues.named_area__locality} />
{:else}
<div uk-spinner></div>
{/if}
Expand Down
15 changes: 10 additions & 5 deletions client/data-search/src/FilterTags.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import { formant } from './formant.js';
const handleRemove = (param, tag) => {
console.log(tag);
let funnel = formant.findFunnel(tag.key);
if (funnel) {
formant.removeFilter(funnel);
let funnelKey = tag.key;
if (param === 'named_area__admin') {
// HACK
formant.removeFilter(['adm1', 'adm2', 'adm3']);
} else {
formant.removeFilter(tag.key);
let funnel = formant.findFunnel(tag.key);
if (funnel) {
formant.removeFilter(funnel);
} else {
formant.removeFilterWithFunnel(tag.key);
}
}
formant.goSearch();
}
Expand Down
35 changes: 35 additions & 0 deletions client/data-search/src/MapView.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script>
import { onMount } from 'svelte';
export let rows = [];
onMount(() => {
let resultMap = L.map('data-search-result-map').setView([23.181, 121.932], 7);
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(resultMap);
/*for(let i=0; i<state..length; i++) {
state.map.removeLayer(state.mapMarkers[i]);
}*/
rows.forEach( (x) => {
const html = `<div>館號: ${x.accession_number}</div><div>採集者:${x.collector.display_name}</div><div>採集號: ${x.field_number}</div><div>採集日期: ${x.collect_date}</div><div>物種: ${x.taxon_text}</div><div><a href="/specimens/HAST:${x.accession_number}" target="_blank">查看</a></div>`;
const marker = L
.marker([parseFloat(x.latitude_decimal), parseFloat(x.longitude_decimal)])
//.addTo(state.map)
.bindPopup(html)
.openPopup();
resultMap.addLayer(marker);
//resultMap.mapMarkers.push(marker);
});
});
</script>
<div class="uk-text-muted uk-margin">有經緯度的資料筆數: {rows.length}</div>
<div id="data-search-result-map" data-view="map" class="data-search-result-view"></div>

<style>
#data-search-result-map { height: 100vh }
</style>
Loading

0 comments on commit 9172643

Please sign in to comment.