- implemented spatial filtering
All checks were successful
CI Pipeline / japa-tests (push) Successful in 51s

- Component 'draw.component.vue' has been extended with the 'preserve' property to control whether the drawn rectangle disappears again
- npm updates
This commit is contained in:
Kaimbacher 2023-10-23 15:27:39 +02:00
parent 2360a81d1e
commit 7bc9f90cca
6 changed files with 637 additions and 448 deletions

View File

@ -8,7 +8,8 @@ import { readFileSync } from 'fs';
import { transform } from 'saxon-js'; import { transform } from 'saxon-js';
import { Client } from '@opensearch-project/opensearch'; import { Client } from '@opensearch-project/opensearch';
const client = new Client({ node: 'http://localhost:9200' }); // replace with your OpenSearch endpoint const opensearchNode = process.env.OPENSEARCH_HOST || 'localhost';
const client = new Client({ node: `http://${opensearchNode}:9200` }); // replace with your OpenSearch endpoint
export default class IndexDatasets extends BaseCommand { export default class IndexDatasets extends BaseCommand {
public static commandName = 'index:datasets'; public static commandName = 'index:datasets';

786
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,10 @@
<fa-icon [icon]="faSearchLocation"></fa-icon> <fa-icon [icon]="faSearchLocation"></fa-icon>
</button> --> </button> -->
<!-- --> <!-- -->
<button <button ref="inputDraw"
ref="inputDraw"
class="inline-flex cursor-pointer justify-center items-center whitespace-nowrap focus:outline-none transition-colors duration-150 border rounded ring-blue-700 text-black border-teal-50 hover:bg-gray-200 text-sm p-1" class="inline-flex cursor-pointer justify-center items-center whitespace-nowrap focus:outline-none transition-colors duration-150 border rounded ring-blue-700 text-black border-teal-50 hover:bg-gray-200 text-sm p-1"
type="button" type="button" :class="[_enabled ? 'cursor-not-allowed bg-cyan-200' : 'bg-teal-50 is-active']"
:class="[_enabled ? 'cursor-not-allowed bg-gray-200' : 'bg-teal-50 is-active']" @click.prevent="toggleDraw">
@click.prevent="draw"
>
<BaseIcon v-if="mdiDrawPen" :path="mdiDrawPen" /> <BaseIcon v-if="mdiDrawPen" :path="mdiDrawPen" />
</button> </button>
</div> </div>
@ -36,19 +33,9 @@ import { LatLngBounds } from 'leaflet/src/geo/LatLngBounds';
}, },
}) })
export default class DrawControlComponent extends Vue { export default class DrawControlComponent extends Vue {
TYPE = 'rectangle'; public TYPE = 'rectangle';
/**
* class properties.
*/
mdiDrawPen = mdiDrawPen; mdiDrawPen = mdiDrawPen;
featuresLayer; // private featuresLayer;
// options = {
// zIndex: 1000,
// // markerClass: Marker, // CylinderGeometry,
// drawingCSSClass: 'gba-editable-drawing',
// drawingCursor: 'crosshair',
// };
options = { options = {
shapeOptions: { shapeOptions: {
@ -66,17 +53,17 @@ export default class DrawControlComponent extends Vue {
metric: true, // Whether to use the metric measurement system or imperial metric: true, // Whether to use the metric measurement system or imperial
}; };
/**
* Connect map id.
*/
@Prop() public mapId: string; @Prop() public mapId: string;
// @Prop() public map: Map; // @Prop() public map: Map;
@Prop public southWest; @Prop public southWest: LatLngBounds;
@Prop public northEast; @Prop public northEast: LatLngBounds;
@Prop({
default: true
}) public preserve: boolean;
mapService = MapService(); mapService = MapService();
// try:
public _enabled; public _enabled;
private _map: Map; private _map: Map;
private _isDrawing: boolean = false; private _isDrawing: boolean = false;
@ -84,8 +71,6 @@ export default class DrawControlComponent extends Vue {
private _mapDraggable; private _mapDraggable;
private _shape: Rectangle | undefined; private _shape: Rectangle | undefined;
// @method enable(): this
// Enables the handler
enable() { enable() {
if (this._enabled) { if (this._enabled) {
return this; return this;
@ -96,8 +81,6 @@ export default class DrawControlComponent extends Vue {
return this; return this;
} }
// @method disable(): this
// Disables the handler
disable() { disable() {
if (!this._enabled) { if (!this._enabled) {
return this; return this;
@ -108,17 +91,13 @@ export default class DrawControlComponent extends Vue {
return this; return this;
} }
// @method enabled(): Boolean
// Returns `true` if the handler is enabled
enabled() { enabled() {
return !!this._enabled; return !!this._enabled;
} }
// @Ref('inputDraw') private _inputDraw: HTMLElement; // @Ref('inputDraw') private _inputDraw: HTMLElement;
// SimpleShape
// @method addHooks(): void
// Add listener hooks to this handler.
private addHooks() { private addHooks() {
// L.Draw.Feature.prototype.addHooks.call(this); // L.Draw.Feature.prototype.addHooks.call(this);
this._map = this.mapService.getMap(this.mapId); this._map = this.mapService.getMap(this.mapId);
@ -146,10 +125,7 @@ export default class DrawControlComponent extends Vue {
} }
} }
// SimpleShape private removeHooks() {
// @method removeHooks(): void
// Remove listener hooks from this handler.
removeHooks() {
// L.Draw.Feature.prototype.removeHooks.call(this); // L.Draw.Feature.prototype.removeHooks.call(this);
if (this._map) { if (this._map) {
if (this._mapDraggable) { if (this._mapDraggable) {
@ -171,11 +147,11 @@ export default class DrawControlComponent extends Vue {
// document.removeEventListener('touchstart', preventDefault); // document.removeEventListener('touchstart', preventDefault);
// If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
// if (this._shape) { if (this._shape && this.preserve == false) {
// this._map.removeLayer(this._shape); this._map.removeLayer(this._shape);
// // delete this._shape; // delete this._shape;
// this._shape = undefined; this._shape = undefined;
// } }
} }
this._isDrawing = false; this._isDrawing = false;
} }
@ -217,7 +193,7 @@ export default class DrawControlComponent extends Vue {
private _fireCreatedEvent(shape) { private _fireCreatedEvent(shape) {
var rectangle = new Rectangle(shape.getBounds(), this.options.shapeOptions); var rectangle = new Rectangle(shape.getBounds(), this.options.shapeOptions);
// L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle); // L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
this._map.fire('Daw.Event.CREATED', { layer: rectangle, type: this.TYPE }); this._map.fire('Draw.Event.CREATED', { layer: rectangle, type: this.TYPE });
} }
public drawShape(southWest, northEast) { public drawShape(southWest, northEast) {
@ -244,11 +220,7 @@ export default class DrawControlComponent extends Vue {
} }
} }
public draw() { public toggleDraw() {
// let map: Map = this.mapService.getMap(this.mapId);
// const bounds: LatLngBoundsExpression = toLatLngBounds(this.southWest, this.northEast);
// map.fitBounds(bounds);
if (this._enabled == true) { if (this._enabled == true) {
this.disable(); this.disable();
} else { } else {

View File

@ -162,7 +162,7 @@ export default class MapComponent extends Vue {
attributionControl.setPrefix(false); attributionControl.setPrefix(false);
map.on( map.on(
'Daw.Event.CREATED', 'Draw.Event.CREATED',
function (event) { function (event) {
// drawnItems.clearLayers(); // drawnItems.clearLayers();
// var type = event.type; // var type = event.type;

View File

@ -106,3 +106,33 @@ export interface Coverage {
time_max?: number; time_max?: number;
time_absolut?: number; time_absolut?: number;
} }
export interface OpensearchDocument {
abstract_additional: Array<string>;
abstract_output: string;
author: Array<string>;
author_sort: Array<string>;
belongs_to_bibliography: boolean;
creating_corporation: string;
doctype: string;
geo_location: string;
id: number;
identifier: Identifier;
language: string;
licence: string;
publisher_name: string;
server_date_published: Array<number>;
subject: Array<string>;
title_output: string;
year: number;
year_inverted: number;
}
export interface Identifier {
created_at: string;
dataset_id: number;
id: number;
status: string; //'findable'
type: string; //'doi'
updated_at: string; //'2023-03-09T09:48:28.000Z'
value: string; //'10.24341/tethys.209'
}

View File

@ -1,10 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { Head } from '@inertiajs/vue3'; import { Head } from '@inertiajs/vue3';
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted, ref, Ref } from 'vue';
// import { MainService } from '@/Stores/main';; // import { MainService } from '@/Stores/main';;
import { import {
mdiChartTimelineVariant, mdiChartTimelineVariant
mdiArrowLeftBoldOutline,
} from '@mdi/js'; } from '@mdi/js';
import LayoutGuest from '@/Layouts/LayoutGuest.vue'; import LayoutGuest from '@/Layouts/LayoutGuest.vue';
import SectionMain from '@/Components/SectionMain.vue'; import SectionMain from '@/Components/SectionMain.vue';
@ -23,6 +22,10 @@ import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
import { tileLayerWMS } from 'leaflet/src/layer/tile/TileLayer.WMS'; import { tileLayerWMS } from 'leaflet/src/layer/tile/TileLayer.WMS';
import { Attribution } from 'leaflet/src/control/Control.Attribution'; import { Attribution } from 'leaflet/src/control/Control.Attribution';
import { stardust } from '@eidellev/adonis-stardust/client'; import { stardust } from '@eidellev/adonis-stardust/client';
import DrawControlComponent from '@/Components/Map/draw.component.vue';
import { MapService } from '@/Stores/map.service';
import { LayerGroup } from 'leaflet/src/layer/LayerGroup'
import { OpensearchDocument } from '@/Dataset';
Map.include({ Map.include({
// @namespace Map; @method getRenderer(layer: Path): Renderer // @namespace Map; @method getRenderer(layer: Path): Renderer
@ -70,15 +73,45 @@ const DEFAULT_BASE_LAYER_ATTRIBUTION = '&copy; <a target="_blank" href="http://o
const OPEN_SEARCH_HOST = 'http://192.168.21.18'; const OPEN_SEARCH_HOST = 'http://192.168.21.18';
let map: Map; let map: Map;
const fitBounds: LatLngBoundsExpression = [ const fitBounds: LatLngBoundsExpression = [
[46.4318173285, 9.47996951665], [46.4318173285, 9.47996951665],
[49.0390742051, 16.9796667823], [49.0390742051, 16.9796667823],
]; ];
const mapId = 'map';
const draw: Ref<DrawControlComponent | null> = ref(null);
const southWest = ref(null);
const northEast = ref(null);
const mapService = MapService();
const coverage = {
x_min: undefined,
y_min: undefined,
x_max: undefined,
y_max: undefined,
elevation_min: undefined,
elevation_max: undefined,
elevation_absolut: undefined,
depth_min: undefined,
depth_max: undefined,
depth_absolut: undefined,
time_min: undefined,
time_max: undefined,
time_absolut: undefined,
};
const filterLayerGroup = new LayerGroup();
// Replace with your actual data
const datasets: Ref<OpensearchDocument[]> = ref([]);
onMounted(() => { onMounted(() => {
initMap(); initMap();
}); });
onUnmounted(() => {
map.off('zoomend zoomlevelschange');
});
const mapOptions: MapOptions = { const mapOptions: MapOptions = {
center: [48.208174, 16.373819], center: [48.208174, 16.373819],
zoom: 3, zoom: 3,
@ -89,7 +122,13 @@ const mapOptions: MapOptions = {
const initMap = async () => { const initMap = async () => {
// init leaflet map // init leaflet map
map = new Map('map', mapOptions); map = new Map('map', mapOptions);
mapService.setMap(mapId, map);
map.scrollWheelZoom.disable();
map.fitBounds(fitBounds); map.fitBounds(fitBounds);
draw.value?.toggleDraw();
map.addLayer(filterLayerGroup);
const attributionControl = new Attribution().addTo(map); const attributionControl = new Attribution().addTo(map);
attributionControl.setPrefix(false); attributionControl.setPrefix(false);
@ -106,28 +145,100 @@ const initMap = async () => {
}; };
layerOptions.layer.addTo(map); layerOptions.layer.addTo(map);
map.on('Draw.Event.CREATED', handleDrawEventCreated);
// // const query = {
// // query: {
// // term: {
// // id: "103"
// // }
// // }
// // };
// // to do : call extra method:
// const query = { // const query = {
// query: { // // q: 'id:103'
// term: { // // q: 'author:"Iglseder, Christoph" OR title:"Datensatz"',
// id: "103" // // q: 'author:"Iglseder"',
// } // q: '*',
// } // _source: 'author,bbox_xmin,bbox_xmax,bbox_ymin,bbox_ymax,abstract,title',
// }; // size: 1000
const query = { // // qf:"title^3 author^2 subject^1",
// q: 'id:103' // }
// q: 'author:"Iglseder, Christoph" OR title:"Datensatz"', // try {
// q: 'author:"Iglseder"', // let response = await axios({
q: '*', // method: 'GET',
_source: 'author,bbox_xmin,bbox_xmax,bbox_ymin,bbox_ymax,abstract,title', // url: OPEN_SEARCH_HOST + '/tethys-records/_search',
size: 1000 // headers: { 'Content-Type': 'application/json' },
// qf:"title^3 author^2 subject^1", // params: query
} // });
// // Loop through the hits in the response
// response.data.hits.hits.forEach(hit => {
// // Get the geo_location attribute
// // var geo_location = hit._source.geo_location;
// let xMin = hit._source.bbox_xmin;
// let xMax = hit._source.bbox_xmax;
// let yMin = hit._source.bbox_ymin;
// let yMax = hit._source.bbox_ymax;
// var bbox: LatLngBoundsExpression = [[yMin, xMin], [yMax, xMax]];
// // Parse the WKT string to get the bounding box coordinates
// // var bbox = wktToBbox(geo_location);
// // // Add the bounding box to the map as a rectangle
// new Rectangle(bbox, { color: "#ff7800", weight: 1 }).addTo(map);
// // console.log(hit._source);
// });
// } catch (error) {
// console.error(error);
// }
};
const handleDrawEventCreated = async (event) => {
filterLayerGroup.clearLayers();
datasets.value = [];
let layer = event.layer;
let bounds = layer.getBounds();
// coverage.x_min = bounds.getSouthWest().lng;
// coverage.y_min = bounds.getSouthWest().lat;
// coverage.x_max = bounds.getNorthEast().lng;
// coverage.y_max = bounds.getNorthEast().lat;
try { try {
let response = await axios({ let response = await axios({
method: 'GET', method: 'POST',
url: OPEN_SEARCH_HOST + '/tethys-records/_search', url: OPEN_SEARCH_HOST + '/tethys-records/_search',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
params: query data: {
"size": 1000,
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_bounding_box": {
"geo_location": { // replace 'location' with your geo-point field name
"top_left": {
"lat": bounds.getNorthEast().lat,
"lon": bounds.getSouthWest().lng
},
"bottom_right": {
"lat": bounds.getSouthWest().lat,
"lon": bounds.getNorthEast().lng
}
}
}
}
}
// _source: 'author,bbox_xmin,bbox_xmax,bbox_ymin,bbox_ymax,abstract,title',
// "size": 1000
}
}
}); });
// Loop through the hits in the response // Loop through the hits in the response
response.data.hits.hits.forEach(hit => { response.data.hits.hits.forEach(hit => {
@ -142,19 +253,15 @@ const initMap = async () => {
// var bbox = wktToBbox(geo_location); // var bbox = wktToBbox(geo_location);
// // Add the bounding box to the map as a rectangle // // Add the bounding box to the map as a rectangle
new Rectangle(bbox, { color: "#ff7800", weight: 1 }).addTo(map); let rect = new Rectangle(bbox, { color: "#ff7800", weight: 1 });
// console.log(hit._source); filterLayerGroup.addLayer(rect);
datasets.value.push(hit._source);
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
}; };
onUnmounted(() => {
map.off('zoomend zoomlevelschange');
});
</script> </script>
<template> <template>
@ -167,18 +274,39 @@ onUnmounted(() => {
<SectionTitleLineWithButton v-bind:icon="mdiChartTimelineVariant" title="Tethys Map" main> <SectionTitleLineWithButton v-bind:icon="mdiChartTimelineVariant" title="Tethys Map" main>
<!-- <BaseButton href="https://gitea.geologie.ac.at/geolba/tethys" target="_blank" :icon="mdiGithub" <!-- <BaseButton href="https://gitea.geologie.ac.at/geolba/tethys" target="_blank" :icon="mdiGithub"
label="Star on Gitea" color="contrast" rounded-full small /> --> label="Star on Gitea" color="contrast" rounded-full small /> -->
<BaseButton :route-name="stardust.route('app.login.show')" label="Login" <BaseButton :route-name="stardust.route('app.login.show')" label="Login" color="white" rounded-full small />
color="white" rounded-full small />
</SectionTitleLineWithButton> </SectionTitleLineWithButton>
<!-- <SectionBannerStarOnGitHub /> --> <!-- <SectionBannerStarOnGitHub /> -->
<!-- <CardBox> --> <!-- <CardBox> -->
<div id="map" class="map-container mapDesktop mt-6 mb-6 rounded-2xl py-12 px-6 text-center"></div> <div id="map" class="map-container mapDesktop mt-6 mb-6 rounded-2xl py-12 px-6 text-center">
<!-- </CardBox> --> <DrawControlComponent ref="draw" :preserve="false" :mapId="mapId" :southWest="southWest"
:northEast="northEast">
</DrawControlComponent>
</div>
<div d="search-result-list-wrapper" class="flex flex-wrap col-span-24">
<div v-for="dataset in datasets" :key="dataset.id" class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 p-4">
<div class="bg-white rounded shadow p-6">
<h2 class="text-xl font-bold mb-2">{{ dataset.title_output }}</h2>
<p class="text-gray-700 mb-2">{{ dataset.abstract_output }}</p>
<div class="text-sm text-gray-600">
<div v-for="author in dataset.author" :key="author" class="mb-1">{{ author }}</div>
</div>
<div class="mt-4">
<span
class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">{{
dataset.year }}</span>
<span
class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">{{
dataset.language }}</span>
</div>
</div>
</div>
</div>
</SectionMain> </SectionMain>
<!-- </section> --> <!-- </section> -->
</LayoutGuest> </LayoutGuest></template>
</template>