From 6b33cb33e79c92bd45a39625b2828904be976b81 Mon Sep 17 00:00:00 2001 From: Giuseppe Nardiello Date: Fri, 18 Oct 2024 20:03:37 +0200 Subject: [PATCH] Code update on 18.10.2024 --- app/components/featureInfoPopup.tsx | 2 +- app/components/openLayers.tsx | 19 +- app/components/queryTools.tsx | 729 +++++++++++++++++++-------- app/css/styleQueryTools.css | 4 + app/enum/filterTypeEnum.tsx | 10 + app/layout.tsx | 3 +- app/libs/graphDbWrapper.tsx | 27 +- app/slice/layerMenuSlice.tsx | 215 ++++++-- app/utilities/LayerMenuUtilities.tsx | 3 +- app/utilities/StringCreateFilter.tsx | 38 +- package-lock.json | 23 + package.json | 1 + pages/api/graphDb.tsx | 5 +- 13 files changed, 799 insertions(+), 280 deletions(-) diff --git a/app/components/featureInfoPopup.tsx b/app/components/featureInfoPopup.tsx index 8b0a3c2..c494a46 100644 --- a/app/components/featureInfoPopup.tsx +++ b/app/components/featureInfoPopup.tsx @@ -144,7 +144,7 @@ const FeatureInfoPopup: React.FC = ({ map, checkedLayerLi const url = source.getFeatureInfoUrl( coordinate, viewResolution, - 'EPSG:3857', + 'EPSG:2056', { 'INFO_FORMAT': 'text/xml' } ); if (!url) return; diff --git a/app/components/openLayers.tsx b/app/components/openLayers.tsx index 50ad24b..8731a55 100644 --- a/app/components/openLayers.tsx +++ b/app/components/openLayers.tsx @@ -15,8 +15,13 @@ import { createExpansLayersList, getFeaturesLayers, getSourceById } from '../uti import { createQueryString } from '../utilities/StringCreateFilter'; import FeatureInfoPopup from './featureInfoPopup'; import { setAttributesConfiguration } from '../slice/layerMenuSlice'; +import proj4 from 'proj4'; +import { register } from 'ol/proj/proj4.js'; +proj4.defs("EPSG:2056", "+proj=somerc +lat_0=46.9524055555556 +lon_0=7.43958333333333 +k_0=1 +x_0=2600000 +y_0=1200000 +ellps=bessel +towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs +type=crs"); +register(proj4); + /** * The map that contains layer ids and tileLayer */ @@ -81,6 +86,7 @@ const MapComponent: React.FC = () => { */ const featuredFilterableLayers = expandedLayerListFiltered.filter(layer => layer.canGetFeatureInfo); const dispatch = useDispatch(); + const defaultExtend = [2479999.9701, 1061999.6351, 2865002.5601, 1302018.7201]; /** * Creates the expanded list of layers by setting the layerTilesMap. * @@ -105,6 +111,7 @@ const MapComponent: React.FC = () => { tileLayer.setZIndex(layer.zIndex); } tileLayers[layer.id] = tileLayer; + console.log(tileLayer); } } }); @@ -172,10 +179,11 @@ const MapComponent: React.FC = () => { layers: [], view: new View({ //in order: longitude, latitude - center: [915788.3813658266, 5909670.533831278], + center: [2660013.54, 1185171.98], zoom: 8.6, minZoom: 0, maxZoom: 28, + projection: 'EPSG:2056' }), controls: defaults({ attribution: false }).extend([ new Attribution({ @@ -186,15 +194,12 @@ const MapComponent: React.FC = () => { new ScaleLine(), new ZoomToExtent({ label: 'D', - extent: new View({ - center: [915788.3813658266, 5909670.533831278], - zoom: 5, - }).getViewStateAndExtent().extent + extent: defaultExtend, }), new Rotate(), ]), }); - + newMap.getView().fit(defaultExtend); setMap(newMap); const layersToAdd = checkedLayers.filter(layer => { @@ -336,7 +341,7 @@ const MapComponent: React.FC = () => { const url = source.getFeatureInfoUrl( center, viewResolution, - 'EPSG:3857', + 'EPSG:2056', { 'INFO_FORMAT': 'text/xml' } ); if (!url) return; diff --git a/app/components/queryTools.tsx b/app/components/queryTools.tsx index 3b8afcf..0e81a20 100644 --- a/app/components/queryTools.tsx +++ b/app/components/queryTools.tsx @@ -5,12 +5,11 @@ import { RootState } from '../store/store'; import { addFilter, Layer, removeFilter, toggleFilter } from '../slice/layerMenuSlice'; //gluestack -import { AddIcon, Card, ChevronDownIcon, Icon, Input, InputField, SelectBackdrop, SelectContent, SelectDragIndicator, SelectDragIndicatorWrapper, SelectIcon, SelectInput, SelectItem, SelectPortal, SelectTrigger, TrashIcon, CircleIcon, Badge, BadgeText, BadgeIcon, CloseIcon } from '@gluestack-ui/themed'; -import { Switch } from '@gluestack-ui/themed'; +import { AddIcon, Card, ChevronDownIcon, Icon, Text, Input, InputField, SelectBackdrop, SelectContent, SelectDragIndicator, SelectDragIndicatorWrapper, SelectIcon, SelectInput, SelectItem, SelectPortal, SelectTrigger, TrashIcon, CircleIcon, Badge, BadgeText, BadgeIcon, CloseIcon, Box, Tooltip, InfoIcon, TooltipContent, TooltipText } from '@gluestack-ui/themed'; import { Button, ButtonText, ButtonIcon, ButtonSpinner, ButtonGroup } from '@gluestack-ui/themed'; /* import { Select } from '@gluestack-ui/themed'; */ import { findLayerById } from '../utilities/LayerMenuUtilities'; -import { FiltersType } from '../enum/filterTypeEnum'; +import { FiltersType, FilterOptionChronostratigraphy } from '../enum/filterTypeEnum'; import { RadioGroup } from '@gluestack-ui/themed'; import { HStack } from '@gluestack-ui/themed'; import { Radio } from '@gluestack-ui/themed'; @@ -22,12 +21,12 @@ import { CheckboxIndicator } from '@gluestack-ui/themed'; import { CheckboxIcon } from '@gluestack-ui/themed'; import { CheckIcon } from '@gluestack-ui/themed'; import { CheckboxLabel } from '@gluestack-ui/themed'; -import { fetchVocabolaryTermByQuery } from '../libs/graphDbWrapper'; import Select from 'react-select'; interface QueryToolsProps { cache: { [key: string]: { label: string; value: string }[] }; } + /** * QueryTool deals with rendering a panel that allows you to enter parameters to filter the * BY ATTRIBUTE, CHRONOSTRATIGRAPHY AGE, and LITHOSTRATIGRAPHY TERM layers @@ -50,8 +49,8 @@ const QueryTools: React.FC = ({ cache }) => { const [filterOption, setFilterOption] = useState("bet"); const [selectedTectonicUnitTerm, setSelectedTectonicUnitTerm] = useState(''); const [includeNarrowers, setIncludeNarrowers] = useState(true); - const [selectedStartTermChronos, setSelectedStartTermChronos] = useState(''); - const [selectedEndTermChronos, setSelectedEndTermChronos] = useState(''); + const [selectedOlderTermChronos, setSelectedOlderTermChronos] = useState(''); + const [selectedYoungerTermChronos, setSelectedYoungerTermChronos] = useState(''); const [selectedTermChronos, setSelectedTermChronos] = useState(''); const handleChange = (isChecked: boolean) => { @@ -110,7 +109,7 @@ const QueryTools: React.FC = ({ cache }) => { * @param {FiltersType} filterType - The type of filter to add (`FilterByAttribute` or `FilterByTectoUnitsTerm`). */ const handleAddFilter = (filterType: FiltersType) => { - console.log('Adding filter:', { selectedFilter, inputValue, selectedLayerId, selectedTectonicUnitTerm, includeNarrowers }); + console.log('Adding filter:', { selectedFilter, inputValue, selectedLayerId, selectedTectonicUnitTerm, includeNarrowers, selectedOlderTermChronos, selectedTermChronos, selectedYoungerTermChronos }); if (filterType === FiltersType.FilterByAttribute && selectedFilter && inputValue && selectedLayerId) { const filter = { @@ -122,9 +121,9 @@ const QueryTools: React.FC = ({ cache }) => { if (filterType === FiltersType.FilterByTectoUnitsTerm && selectedTectonicUnitTerm && selectedLayerId) { const vocab = currentLayer.filterConfiguration?.filterConfigurationByTectoUnitsTerm; - const formattedTerm = selectedTectonicUnitTerm.split('/').pop() || ''; + const formattedTerm = selectedTectonicUnitTerm.replace('#', '/').split('/').pop() || ''; const query = vocab?.queryNarrower.replace('${term}', formattedTerm); - console.log('click on add button in query tool.'); + console.log(query) if (vocab) { if (includeNarrowers) { fetch(`/api/graphDb?vocabulary=${vocab.idVocabulary}`, @@ -161,14 +160,121 @@ const QueryTools: React.FC = ({ cache }) => { } } } + + if (filterType === FiltersType.FilterByChronostratigraphy && selectedLayerId) { + if (filterOption == FilterOptionChronostratigraphy.Younger) { + const vocab = currentLayer.filterConfiguration?.filterChronostratigraphyAge; + const formattedTerm = selectedTermChronos.replace('#', '/').split('/').pop() || ''; + const query = vocab?.queryYouger_strict.replace('${term}', formattedTerm); + console.log(query) + fetch(`/api/graphDb?vocabulary=Chronostratigraphy`, + { + method: "POST", + body: query + } + ) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + console.log('Fetched data:', data); + let yongerTerms: string[] = data; + const filter = { + filterChronostratigraphyAge: [{ type: FilterOptionChronostratigraphy.Younger, idYounger: selectedTermChronos, youngerTerms: yongerTerms }] + }; + console.log('Payload passed to addFilter:', { layerId: selectedLayerId, filter, filterType: 'filterByChronostratigraphy' }); + dispatch(addFilter({ layerId: selectedLayerId, filter, filterType: FiltersType.FilterByChronostratigraphy })); + }) + .catch(error => { + console.error('Failed to fetch data from GraphDB:', error); + }); + } + if (filterOption == FilterOptionChronostratigraphy.Between) { + const vocab = currentLayer.filterConfiguration?.filterChronostratigraphyAge; + const formattedTermOlder = selectedOlderTermChronos.replace('#', '/').split('/').pop() || ''; + const formattedTermYounger = selectedYoungerTermChronos.split('/').pop() || ''; + const query = vocab?.queryBetween_stricty + .replace('${termOlder}', formattedTermOlder) + .replace('${termYounger}', formattedTermYounger); + console.log(query) + fetch(`/api/graphDb?vocabulary=Chronostratigraphy`, + { + method: "POST", + body: query + } + ) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + console.log('Fetched data:', data); + let betweenTerms: string[] = data; + const filter = { + filterChronostratigraphyAge: [{ type: FilterOptionChronostratigraphy.Between, idYounger: selectedYoungerTermChronos, idOlder: selectedOlderTermChronos, betweenTerms: betweenTerms }] + }; + console.log('Payload passed to addFilter:', { layerId: selectedLayerId, filter, filterType: 'filterByChronostratigraphy' }); + dispatch(addFilter({ layerId: selectedLayerId, filter, filterType: FiltersType.FilterByChronostratigraphy })); + }) + .catch(error => { + console.error('Failed to fetch data from GraphDB:', error); + }); + + } + if (filterOption == FilterOptionChronostratigraphy.Older) { + const vocab = currentLayer.filterConfiguration?.filterChronostratigraphyAge; + const formattedTerm = selectedTermChronos.replace('#', '/').split('/').pop() || ''; + const query = vocab?.queryOlder_strict.replace('${term}', formattedTerm); + console.log(query) + fetch(`/api/graphDb?vocabulary=Chronostratigraphy`, + { + method: "POST", + body: query + } + ) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + console.log('Fetched data:', data); + let olderTerms: string[] = data; + const filter = { + filterChronostratigraphyAge: [{ type: FilterOptionChronostratigraphy.Older, idOlder: selectedTermChronos, olderTerms: olderTerms }] + }; + console.log('Payload passed to addFilter:', { layerId: selectedLayerId, filter, filterType: 'filterByChronostratigraphy' }); + dispatch(addFilter({ layerId: selectedLayerId, filter, filterType: FiltersType.FilterByChronostratigraphy })); + }) + .catch(error => { + console.error('Failed to fetch data from GraphDB:', error); + }); + } + } }; /** * Removes a filter to the layer by updating the state in redux. * slice: layerMenuSlice */ - const handleRemoveFilter = (layerId: string, filterKey: string) => { - dispatch(removeFilter({ layerId, filterKey })); + const handleRemoveFilter = ( + layerId: string, + filterKey?: string, + filterType?: FiltersType, + type?: string, + idYounger?: string, + idOlder?: string, + ) => { + console.log('sto mandando allo slice i seguenti parametri:', { layerId, filterKey, idYounger, idOlder, filterType, type }); + dispatch(removeFilter({ layerId, filterKey, idYounger, idOlder, filterType, type })); }; + + //for take the vocabulary labal for terms const getVocabularyLabel = (processedValue: string) => { for (const [key, vocabularyItems] of Object.entries(cache)) { @@ -210,7 +316,7 @@ const QueryTools: React.FC = ({ cache }) => {

{value}

-
handleRemoveFilter(layer.id, key)}> +
handleRemoveFilter(layer.id, key, FiltersType.FilterByAttribute)}>
@@ -244,7 +350,7 @@ const QueryTools: React.FC = ({ cache }) => { )} -
handleRemoveFilter(layer.id, term)}> +
handleRemoveFilter(layer.id, term, FiltersType.FilterByTectoUnitsTerm)}>
@@ -256,6 +362,53 @@ const QueryTools: React.FC = ({ cache }) => { }); } break; + case FiltersType.FilterByChronostratigraphy: + console.log(filters.filterChronostratigraphyAge); + if (filters.filterChronostratigraphyAge && filters.filterChronostratigraphyAge.length > 0) { + filters.filterChronostratigraphyAge.forEach((filter, index) => { + const { idYounger, idOlder, type } = filter; + const labelYounger = idYounger !== undefined ? getVocabularyLabel(idYounger) : null; + const labelOlder = idOlder !== undefined ? getVocabularyLabel(idOlder) : null; + + if (labelYounger || labelOlder) { + let displayText = ''; + + if (labelOlder && labelYounger) { + displayText = `From ${labelOlder} To ${labelYounger}`; + } else { + if (labelOlder) { + displayText += `Older than: ${labelOlder}`; + } + + if (labelYounger) { + if (displayText) { + displayText += ' - '; + } + displayText += `Younger than: ${labelYounger}`; + } + } + + filterList.push( +
+
+
+

{displayText}

+
+
{ + handleRemoveFilter(layer.id, undefined, FiltersType.FilterByChronostratigraphy, type, idYounger, idOlder); + }}> + +
+
+
+ ); + + } else { + console.log('Filtro non valido, manca idYounger o idOlder:', filter); + } + }); + } + break; default: break; } @@ -276,9 +429,11 @@ const QueryTools: React.FC = ({ cache }) => { return filterList; }; - const excludeColumns = currentLayer?.filterConfiguration?.filterConfigurationByTectoUnitsTerm - ? currentLayer.filterConfiguration.filterConfigurationByTectoUnitsTerm.attributeToFilter || [] - : []; + const excludeColumns = [ + ...(currentLayer?.filterConfiguration?.filterConfigurationByTectoUnitsTerm?.attributeToFilter || []), + currentLayer?.filterConfiguration?.filterChronostratigraphyAge?.columnToFilterOld, + currentLayer?.filterConfiguration?.filterChronostratigraphyAge?.columnToFilterYon + ] const attributeOptions = currentLayer.attributesConfiguration?.attributes?.map(attr => ({ value: attr, label: currentLayer.attributesConfiguration?.attributeOverrides?.[attr]?.column || attr @@ -308,28 +463,51 @@ const QueryTools: React.FC = ({ cache }) => { -

Selected layer: {currentLayer.label}

+

Selected layer: {currentLayer.label}

+ + "The Search Box lets you filter map territories by specific attributes or terms for Chronostratigraphy and Tectonic Units.{'\n'}Features in whitch filters are met for all attributes and vocabulary terms simultaneously will be highlighted." + {/* FILTER BY ATTRIBUTE */} -
+
-

Filter by Attribute

+ + +

Filter by Attribute

+ { + return ( + + + + + ) + }} + > + + Filter by Attribute + + +
+ "Filter by values ​​and by attributes of all features" +
{currentLayer && renderFilterList(currentLayer, FiltersType.FilterByAttribute)}
-
+
+ Attribute: = ({ cache }) => {
+ {/* FILTER BY CHRONOSTRATIGRAPHY AGE */} -
- -

Filter by Chronostratigraphy Age

-
- - - - - - - Younger - - - - - - Between - - - - - - Older - - - -
- {filterOption === 'bet' && ( -
-
-
- option.value === selectedEndTermChronos)} - onChange={(selectedOption) => setSelectedEndTermChronos(selectedOption ? selectedOption.value : '')} - options={chronostratigraphyOptions} - placeholder="Select end option" - isSearchable={true} - classNamePrefix="react-select" - menuPortalTarget={document.body} - styles={{ - control: (provided) => ({ - ...provided, - borderRadius: '20px', - fontSize: '12px', - color: 'black', - }), - singleValue: (provided) => ({ - ...provided, - fontSize: '12px', - color: 'black', - }), - option: (provided) => ({ - ...provided, - fontSize: '12px', - color: 'black', - }), - menu: (provided) => ({ - ...provided, - }), + { + currentLayer.filterConfiguration?.filterChronostratigraphyAge && + <> + + AND + +
+ + + +

Filter by Chronostratigraphy Age

+ { + return ( + + + + + ) }} - /> -
+ > + + Filter by Chronostratigraphy Age + + + + "Filter by a Chronostratigraphy term." + + (*) Broader terms are excluded from query results if they are not strictly included into interval +
+ {currentLayer && renderFilterList(currentLayer, FiltersType.FilterByChronostratigraphy)}
-
- )} - {filterOption !== 'bet' && ( -
-
-
- option.value === selectedOlderTermChronos)} + onChange={(selectedOption) => setSelectedOlderTermChronos(selectedOption ? selectedOption.value : '')} + options={chronostratigraphyOptions} + placeholder="Select start option" + isSearchable={true} + classNamePrefix="react-select" + menuPortalTarget={document.body} + styles={{ + control: (provided) => ({ + ...provided, + borderRadius: '20px', + fontSize: '12px', + color: 'black', + }), + singleValue: (provided) => ({ + ...provided, + fontSize: '12px', + color: 'black', + }), + option: (provided) => ({ + ...provided, + fontSize: '12px', + color: 'black', + }), + menu: (provided) => ({ + ...provided, + }), + }} + /> +
+
+ Younger than (*): + option.value === selectedTermChronos)} + onChange={(selectedOption) => setSelectedTermChronos(selectedOption ? selectedOption.value : '')} + options={chronostratigraphyOptions} + placeholder={filterOption === 'old' ? 'Select end option' : 'Select end option'} + isSearchable={true} + classNamePrefix="react-select" + menuPortalTarget={document.body} + styles={{ + control: (provided) => ({ + ...provided, + borderRadius: '20px', + fontSize: '12px', + color: 'black', + }), + singleValue: (provided) => ({ + ...provided, + fontSize: '12px', + color: 'black', + }), + option: (provided) => ({ + ...provided, + fontSize: '12px', + color: 'black', + }), + menu: (provided) => ({ + ...provided, + }), + }} + /> +
+
+ )} + +
+
-
- )} - -
+
+
+ + } + + {/* FILTER BY TECTO TERM */} - {currentLayer.filterConfiguration?.filterConfigurationByTectoUnitsTerm && -
- -

Filter by Tectonic Units term

-
- {currentLayer && renderFilterList(currentLayer, FiltersType.FilterByTectoUnitsTerm)} -
-
-
-
- option.value === selectedTectonicUnitTerm)} + onChange={(selectedOption) => setSelectedTectonicUnitTerm(selectedOption ? selectedOption.value : '')} + options={optionsTectounits} + placeholder="Select Tecto Term" + isSearchable={true} + classNamePrefix="react-select" + menuPortalTarget={document.body} + maxMenuHeight={240} + menuPlacement='top' + styles={{ + control: (provided) => ({ + ...provided, + borderRadius: '20px', + fontSize: '12px', + color: 'black', + }), + singleValue: (provided) => ({ + ...provided, + fontSize: '12px', + color: 'black', + }), + option: (provided) => ({ + ...provided, + fontSize: '12px', + color: 'black', + }), + menu: (provided) => ({ + ...provided, + }), + }} + /> +
+
+ + + + + Include Narrowers + +
-
- - - - - Include Narrower - +
+
-
- -
-
-
-
+ +
+ + } -
+
); } diff --git a/app/css/styleQueryTools.css b/app/css/styleQueryTools.css index bddc183..c8f058d 100644 --- a/app/css/styleQueryTools.css +++ b/app/css/styleQueryTools.css @@ -2,6 +2,7 @@ height: 100vh; min-width: 300px; padding: 20px; + overflow-y: auto; } .bgGrey { @@ -15,6 +16,9 @@ .textBlack { color: black; } +.mTop2 { + margin-top: 2%; +} .mTop4 { margin-top: 4%; diff --git a/app/enum/filterTypeEnum.tsx b/app/enum/filterTypeEnum.tsx index f8eb818..6299bc7 100644 --- a/app/enum/filterTypeEnum.tsx +++ b/app/enum/filterTypeEnum.tsx @@ -5,4 +5,14 @@ export enum FiltersType { FilterByAttribute = 'filterByAttribute', //Identifies a filter type based on attributes. FilterByTectoUnitsTerm = 'filterByTectoUnitsTerm', //Identifies a filter type based on tectonic units terms. + FilterByChronostratigraphy= 'filterByChronostratigraphy', //Identifies a filter type based on chronostratigraphy terms. +} + +/** + * This enum is used to specify the type of filter Chronostratigraphy. + */ +export enum FilterOptionChronostratigraphy { + Younger = "yon", + Between = "bet", + Older = "old", } \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index a077737..5186d76 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -15,7 +15,8 @@ const encodeSansExpanded = Encode_Sans_Expanded({ export const metadata: Metadata = { title: "WebMap - Swissgeol", - description: "Generated by NARDS IT", + description: "Developed by Nards IT", + authors: [{name:"Nards IT", url: "https://nards.it"}] }; export default function RootLayout({ diff --git a/app/libs/graphDbWrapper.tsx b/app/libs/graphDbWrapper.tsx index 3263b72..3892e21 100644 --- a/app/libs/graphDbWrapper.tsx +++ b/app/libs/graphDbWrapper.tsx @@ -8,12 +8,12 @@ interface Literal { value: string; language?: string; } -interface QueryResult { +interface VocabularyQueryResult { term: NamedNode; prefLabel: Literal; } -interface QueryResultNarrowers { - narrowerConcept: NamedNode; +interface ConceptQueryResult { + concept: NamedNode; } /** * Creates the connection to the db with the parameters passed @@ -33,18 +33,11 @@ export async function fetchVocabulariesData() { const { allConcept } = getQueryConfig(vocab.id); const queryExecutor = new QueryExecutor(client, vocab.repositoryId, url, username, password, repositoryUrl); - let sparqlQuery = ''; - - if (vocab.id === 'Chronostratigraphy' || vocab.id === 'TectonicUnits') { - sparqlQuery = allConcept; - } else { - console.warn(`No SPARQL query defined for vocabulary ${vocab.id}`); - continue; - } + let sparqlQuery = allConcept; try { console.log(`5 Executing SPARQL query for vocabulary ${vocab.id}`); - const queryResults: QueryResult[] = await queryExecutor.executeSparqlQuery(sparqlQuery); + const queryResults: VocabularyQueryResult [] = await queryExecutor.executeSparqlQuery(sparqlQuery); /* console.log('Query results:', queryResults); */ results[vocab.id] = queryResults.map(result => ({ label: result.prefLabel.value, @@ -88,12 +81,12 @@ export async function fetchVocabolaryTermByQuery(query: string, vocabId: string) try { console.log(`7 Executing SPARQL query for vocabulary ${vocab.id}`); - const queryResults: QueryResultNarrowers[] = await queryExecutor.executeSparqlQuery(sparqlQuery); - console.log('Query results:', queryResults); + const queryResults: ConceptQueryResult [] = await queryExecutor.executeSparqlQuery(sparqlQuery); + console.log(`Query results ${vocab.id}:`, queryResults); for (const result of queryResults) { - results.push(result.narrowerConcept.id); + results.push(result.concept.id); } - /* console.log(`Successfully fetched data for vocabulary ${vocab.id}:`, results[vocab.id]); */ + console.log(`Successfully fetched data for ${vocab.id}:`, results); } catch (error) { console.log(`Error fetching data for vocabulary ${vocab.id}:`, error); throw new Error(`Error fetching data for vocabulary ${vocab.id}: ${error.message}`); @@ -102,4 +95,4 @@ export async function fetchVocabolaryTermByQuery(query: string, vocabId: string) } return results; -} +} \ No newline at end of file diff --git a/app/slice/layerMenuSlice.tsx b/app/slice/layerMenuSlice.tsx index 8299f3f..7f2e3b0 100644 --- a/app/slice/layerMenuSlice.tsx +++ b/app/slice/layerMenuSlice.tsx @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { findLayerById } from '../utilities/LayerMenuUtilities'; -import { FiltersType } from '../enum/filterTypeEnum'; +import { FilterOptionChronostratigraphy, FiltersType } from '../enum/filterTypeEnum'; /** * Represents an object that can be filtered based on specific criteria. @@ -40,7 +40,7 @@ export interface Layer extends Filterable { export interface Filter { filterByAttribute?: FilterByAttribute[]; filterByTectoUnitsTerm?: FilterByTectoUnitsTermItem[]; - filterChronostratigraphyAge?: FilterChronostratigraphyAge[] + filterChronostratigraphyAge?: FilterChronostratigraphyAgeItem[] } /* export interface LinkLabelItem { @@ -93,13 +93,21 @@ export interface FilterTectoUnitsTerm { * @property idOlder * @property idYounger */ +export interface FilterChronostratigraphyAgeItem { + type: string; + idOlder?: string; + idYounger?: string; + olderTerms?: string[]; + youngerTerms?: string[]; + betweenTerms?: string[]; +} + export interface FilterChronostratigraphyAge { - idVocabulary: string; - queryBetween: string; - queryYounger: string; - queryOlder: string - idOlder: string; - idYounger: string; + queryYouger_strict: string, + queryOlder_strict: string, + queryBetween_stricty: string, + columnToFilterYon: string, + columnToFilterOld: string, } /** * Configuration for managing attributes in a layer. @@ -195,7 +203,7 @@ const initialState: LayerState = { style: { opacity: 0.3, }, - zIndex: 7, + zIndex: 9, }, { id: 'Quat_Surfaces', label: 'Quat Surfaces', @@ -211,7 +219,7 @@ const initialState: LayerState = { style: { opacity: 0.3, }, - zIndex: 6, + zIndex: 8, }, { id: 'Tecto_Units_augm', label: 'Tectonic Units', @@ -220,9 +228,16 @@ const initialState: LayerState = { filters: undefined, filterConfiguration: { layerName: 'Tecto_Units_augm_filtered', + filterChronostratigraphyAge: { + columnToFilterOld: 'Chrono_from_lexic', + columnToFilterYon: 'Chrono_to_lexic', + queryYouger_strict: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n BIND(ex:${term} AS ?subject)\n {\n ?subject time:intervalMeets* ?concept .\n } UNION {\n ?subject time:intervalFinishedBy+ ?narrower .\n ?subject time:intervalMeets* ?younger .\n ?younger skos:narrower* ?concept .\n } UNION {\n ?subject time:intervalStarts* ?concept .\n } UNION {\n ?subject skos:broader+ ?broader .\n ?broader time:intervalMeets+ ?concept .\n }\n}", + queryOlder_strict: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n BIND(ex:${term} AS ?subject)\n {\n ?subject time:intervalMetBy* ?concept .\n } UNION {\n ?subject time:intervalMetBY* ?older .\n ?older skos:narrower* ?concept .\n } UNION {\n ?subject time:intervalFinishes* ?concept .\n } UNION {\n ?subject skos:broader+ ?broader .\n ?broader time:intervalMetBy+ ?concept\n }\n}", + queryBetween_stricty: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n {\n BIND(ex:${termOlder} AS ?olderConcept) {\n ?olderConcept time:intervalMeets* ?concept .\n } UNION {\n ?olderConcept time:intervalMeets* ?younger .\n ?younger skos:narrower* ?concept .\n } UNION {\n ?olderConcept time:intervalStarts* ?concept .\n } UNION {\n ?olderSub skos:broader+ ?broader .\n ?broader time:intervalMeets+ ?object .\n }\n }\n {\n BIND(ex:${termYounger} AS ?youngerConcept) {\n ?youngerConcept time:intervalMetBy* ?concept .\n } UNION {\n ?youngerConcept time:intervalMetBy* ?older .\n ?older skos:narrower* ?concept .\n } UNION {\n ?youngerConcept time:intervalFinishes* ?concept .\n } UNION {\n ?youngerConcept skos:broader+ ?broader .\n ?broader time:intervalMetBy+ ?concept .\n }\n }\n}", + }, filterConfigurationByTectoUnitsTerm: { idVocabulary: 'TectonicUnits', - queryNarrower: 'PREFIX skos: \nPREFIX ex: \n\nSELECT ?narrowerConcept\n\nWHERE { \nex:${term} skos:narrower+ ?narrowerConcept.\n}', + queryNarrower: 'PREFIX skos: \nPREFIX ex: \n\nSELECT ?concept\n\nWHERE { \nex:${term} skos:narrower+ ?concept.\n}', attributeToFilter: ['Tecto_lexic'] }, filterLayer: { @@ -241,9 +256,9 @@ const initialState: LayerState = { crossOrigin: 'anonymous', }, style: { - opacity: 0.3, + opacity: 1.0, }, - zIndex: 5, + zIndex: 7, }, }, canGetFeatureInfo: true, @@ -256,7 +271,7 @@ const initialState: LayerState = { style: { opacity: 0.3, }, - zIndex: 4, + zIndex: 6, attributesConfiguration: { attributeOverrides: { Tecto_lexic: { @@ -275,7 +290,7 @@ const initialState: LayerState = { labelSourceForLink: 'vocabulary_label', }, }, - } + }, }], }, { @@ -293,6 +308,18 @@ const initialState: LayerState = { filters: undefined, filterConfiguration: { layerName: 'GC_BEDROCK_filtered', + filterChronostratigraphyAge: { + columnToFilterOld: 'chrono_from_lexic', + columnToFilterYon: 'chrono_to_lexic', + queryYouger_strict: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n BIND(ex:${term} AS ?subject)\n {\n ?subject time:intervalMeets* ?concept .\n } UNION {\n ?subject time:intervalFinishedBy+ ?narrower .\n ?subject time:intervalMeets* ?younger .\n ?younger skos:narrower* ?concept .\n } UNION {\n ?subject time:intervalStarts* ?concept .\n } UNION {\n ?subject skos:broader+ ?broader .\n ?broader time:intervalMeets+ ?concept .\n }\n}", + queryOlder_strict: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n BIND(ex:${term} AS ?subject)\n {\n ?subject time:intervalMetBy* ?concept .\n } UNION {\n ?subject time:intervalMetBY* ?older .\n ?older skos:narrower* ?concept .\n } UNION {\n ?subject time:intervalFinishes* ?concept .\n } UNION {\n ?subject skos:broader+ ?broader .\n ?broader time:intervalMetBy+ ?concept\n }\n}", + queryBetween_stricty: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n {\n BIND(ex:${termOlder} AS ?olderConcept) {\n ?olderConcept time:intervalMeets* ?concept .\n } UNION {\n ?olderConcept time:intervalMeets* ?younger .\n ?younger skos:narrower* ?concept .\n } UNION {\n ?olderConcept time:intervalStarts* ?concept .\n } UNION {\n ?olderSub skos:broader+ ?broader .\n ?broader time:intervalMeets+ ?object .\n }\n }\n {\n BIND(ex:${termYounger} AS ?youngerConcept) {\n ?youngerConcept time:intervalMetBy* ?concept .\n } UNION {\n ?youngerConcept time:intervalMetBy* ?older .\n ?older skos:narrower* ?concept .\n } UNION {\n ?youngerConcept time:intervalFinishes* ?concept .\n } UNION {\n ?youngerConcept skos:broader+ ?broader .\n ?broader time:intervalMetBy+ ?concept .\n }\n }\n}", + }, + filterConfigurationByTectoUnitsTerm: { + idVocabulary: 'TectonicUnits', + queryNarrower: 'PREFIX skos: \nPREFIX ex: \n\nSELECT ?concept\n\nWHERE { \nex:${term} skos:narrower+ ?concept.\n}', + attributeToFilter: ['tecto_lexic'] + }, filterLayer: { id: 'GC_BEDROCK_filtered', label: 'GC_BEDROCK_filtered', @@ -309,9 +336,9 @@ const initialState: LayerState = { crossOrigin: 'anonymous', }, style: { - opacity: 0.3, + opacity: 1.0, }, - zIndex: 3, + zIndex: 5, }, }, canGetFeatureInfo: true, @@ -326,7 +353,94 @@ const initialState: LayerState = { style: { opacity: 0.3, }, + zIndex: 4, + attributesConfiguration: { + attributeOverrides: { + tecto_lexic: { + column: 'tecto_lexic', + type: 'link', + labelSourceForLink: 'vocabulary_label', + }, + chrono_from_lexic: { + column: 'chrono_from_lexic', + type: 'link', + labelSourceForLink: 'vocabulary_label', + }, + chrono_to_lexic: { + column: 'chrono_to_lexic', + type: 'link', + labelSourceForLink: 'vocabulary_label', + }, + }, + }, + }, { + id: 'GC_UNCO_DEPOSITS', + label: 'GC_UNCO_DEPOSITS', + isChecked: false, + canFilter: true, + filters: undefined, + filterConfiguration: { + layerName: 'GC_UNCO_DEPOSITS_filtered', + filterChronostratigraphyAge: { + columnToFilterOld: 'chrono_from_lexic', + columnToFilterYon: 'chrono_to_lexic', + queryYouger_strict: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n BIND(ex:${term} AS ?subject)\n {\n ?subject time:intervalMeets* ?concept .\n } UNION {\n ?subject time:intervalFinishedBy+ ?narrower .\n ?subject time:intervalMeets* ?younger .\n ?younger skos:narrower* ?concept .\n } UNION {\n ?subject time:intervalStarts* ?concept .\n } UNION {\n ?subject skos:broader+ ?broader .\n ?broader time:intervalMeets+ ?concept .\n }\n}", + queryOlder_strict: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n BIND(ex:${term} AS ?subject)\n {\n ?subject time:intervalMetBy* ?concept .\n } UNION {\n ?subject time:intervalMetBY* ?older .\n ?older skos:narrower* ?concept .\n } UNION {\n ?subject time:intervalFinishes* ?concept .\n } UNION {\n ?subject skos:broader+ ?broader .\n ?broader time:intervalMetBy+ ?concept\n }\n}", + queryBetween_stricty: "PREFIX skos: \nPREFIX time: \nPREFIX ex: \n\nSELECT DISTINCT ?concept\nWHERE {\n {\n BIND(ex:${termOlder} AS ?olderConcept) {\n ?olderConcept time:intervalMeets* ?concept .\n } UNION {\n ?olderConcept time:intervalMeets* ?younger .\n ?younger skos:narrower* ?concept .\n } UNION {\n ?olderConcept time:intervalStarts* ?concept .\n } UNION {\n ?olderSub skos:broader+ ?broader .\n ?broader time:intervalMeets+ ?object .\n }\n }\n {\n BIND(ex:${termYounger} AS ?youngerConcept) {\n ?youngerConcept time:intervalMetBy* ?concept .\n } UNION {\n ?youngerConcept time:intervalMetBy* ?older .\n ?older skos:narrower* ?concept .\n } UNION {\n ?youngerConcept time:intervalFinishes* ?concept .\n } UNION {\n ?youngerConcept skos:broader+ ?broader .\n ?broader time:intervalMetBy+ ?concept .\n }\n }\n}", + }, + filterLayer: { + id: 'GC_UNCO_DEPOSITS_filtered', + label: 'GC_UNCO_DEPOSITS_filtered', + isChecked: false, + canFilter: false, + canGetFeatureInfo: false, + source: { + url: 'https://qgis.swisstopo.demo.epsilon-italia.it/qgis-server/', + params: { + 'LAYERS': 'GC_UNCO_DEPOSITS_filtered', 'TILED': true, + 'FILTER': 'GC_UNCO_DEPOSITS_filtered:1=0' + }, + serverType: 'qgis', + crossOrigin: 'anonymous', + }, + style: { + opacity: 1.0, + }, + zIndex: 3, + }, + }, + canGetFeatureInfo: true, + source: { + url: 'https://qgis.swisstopo.demo.epsilon-italia.it/qgis-server/', + params: { + 'LAYERS': 'GC_UNCO_DEPOSITS', + }, + serverType: 'qgis', + crossOrigin: 'anonymous', + }, + style: { + opacity: 0.3, + }, zIndex: 2, + attributesConfiguration: { + attributeOverrides: { + Tecto_lexic: { + column: 'Tecto_lexic', + type: 'link', + labelSourceForLink: 'vocabulary_label', + }, + Chrono_from_lexic: { + column: 'Chrono_from_lexic', + type: 'link', + labelSourceForLink: 'vocabulary_label', + }, + Chrono_to_lexic: { + column: 'Chrono_to_lexic', + type: 'link', + labelSourceForLink: 'vocabulary_label', + }, + }, + }, },], }, { @@ -498,6 +612,28 @@ export const layerMenuSlice = createSlice({ } } break; + case 'filterByChronostratigraphy': + console.log('Aggiungendo filterByChronostratigraphy al layer:', filter); + if (!layer.filters.filterChronostratigraphyAge) { + layer.filters.filterChronostratigraphyAge = []; + } + + if (filter.filterChronostratigraphyAge) { + const filterChronostratigraphyAgeArray = filter.filterChronostratigraphyAge as FilterChronostratigraphyAgeItem[]; + const existingFilterIndex = layer.filters.filterChronostratigraphyAge.findIndex(f => + f.idYounger === filterChronostratigraphyAgeArray[0]?.idYounger && + f.idOlder === filterChronostratigraphyAgeArray[0]?.idOlder + ); + + if (existingFilterIndex !== -1) { + layer.filters.filterChronostratigraphyAge[existingFilterIndex] = filterChronostratigraphyAgeArray[0]; + console.log('Filtro aggiornato:', filter.filterChronostratigraphyAge); + } else { + layer.filters.filterChronostratigraphyAge.push(...filterChronostratigraphyAgeArray); + console.log('Filtro aggiunto:', filter.filterChronostratigraphyAge); + } + } + break; default: break; } @@ -514,26 +650,40 @@ export const layerMenuSlice = createSlice({ * @param state state of layer * @param action payloadAction is the data passed by the component */ - removeFilter: (state, action: PayloadAction<{ layerId: string, filterKey: string }>) => { - const { layerId, filterKey } = action.payload; - + removeFilter: ( + state, + action: PayloadAction<{ + layerId: string; + filterKey?: string; + idYounger?: string; + idOlder?: string; + filterType?: FiltersType; + type?: string; + }> + ) => { + const { layerId, filterKey, idYounger, idOlder, filterType, type } = action.payload; const layerToUpdate = findLayerById(state.layers, layerId); if (layerToUpdate && layerToUpdate.filters) { - if (layerToUpdate.filters.filterByAttribute) { - layerToUpdate.filters.filterByAttribute = layerToUpdate.filters.filterByAttribute.filter(attr => attr.key !== filterKey); - } - - if (layerToUpdate.filters.filterByTectoUnitsTerm) { - layerToUpdate.filters.filterByTectoUnitsTerm = layerToUpdate.filters.filterByTectoUnitsTerm.filter(term => term.term !== filterKey); + if (filterType === FiltersType.FilterByChronostratigraphy) { + if (layerToUpdate.filters.filterChronostratigraphyAge) { + layerToUpdate.filters.filterChronostratigraphyAge = layerToUpdate.filters.filterChronostratigraphyAge.filter(attr => + (type === FilterOptionChronostratigraphy.Older && attr.type === FilterOptionChronostratigraphy.Older && attr.idOlder === idOlder) || + (type === FilterOptionChronostratigraphy.Younger && attr.type === FilterOptionChronostratigraphy.Younger && attr.idYounger === idYounger) || + (type === FilterOptionChronostratigraphy.Between && attr.type === FilterOptionChronostratigraphy.Between && attr.idOlder === idOlder && attr.idYounger === idYounger) ? false : true + ); + } + } else if (filterType === FiltersType.FilterByAttribute) { + if (layerToUpdate.filters.filterByAttribute) { + layerToUpdate.filters.filterByAttribute = layerToUpdate.filters.filterByAttribute.filter(attr => attr.key !== filterKey); + } + } else if (filterType === FiltersType.FilterByTectoUnitsTerm) { + if (layerToUpdate.filters.filterByTectoUnitsTerm) { + layerToUpdate.filters.filterByTectoUnitsTerm = layerToUpdate.filters.filterByTectoUnitsTerm.filter(term => term.term !== filterKey); + } } - - /* if (layerToUpdate.filters.filterByAttribute.length === 0 && layerToUpdate.filterConfiguration?.filterLayer) { - layerToUpdate.filterLayer.isChecked = false; - } */ } }, - /** * Updates with values passed by the component the opacity value of a given layer found by id * @@ -596,5 +746,4 @@ export const layerMenuSlice = createSlice({ export const { toggleCheck, addFilter, removeFilter, toggleFilter, updateOpacity, setAttributesConfiguration } = layerMenuSlice.actions; -export default layerMenuSlice.reducer; - +export default layerMenuSlice.reducer; \ No newline at end of file diff --git a/app/utilities/LayerMenuUtilities.tsx b/app/utilities/LayerMenuUtilities.tsx index 0071b3f..69bd3a9 100644 --- a/app/utilities/LayerMenuUtilities.tsx +++ b/app/utilities/LayerMenuUtilities.tsx @@ -70,7 +70,8 @@ export const filtersNotEmpty = (filters: Filter | undefined): boolean => { return filters != undefined && ( (filters.filterByAttribute != undefined && filters.filterByAttribute.length > 0) || - (filters.filterByTectoUnitsTerm != undefined && filters.filterByTectoUnitsTerm.length > 0) + (filters.filterByTectoUnitsTerm != undefined && filters.filterByTectoUnitsTerm.length > 0) || + (filters.filterChronostratigraphyAge != undefined && filters.filterChronostratigraphyAge.length > 0) ); } diff --git a/app/utilities/StringCreateFilter.tsx b/app/utilities/StringCreateFilter.tsx index 759ac02..dbe9d79 100644 --- a/app/utilities/StringCreateFilter.tsx +++ b/app/utilities/StringCreateFilter.tsx @@ -12,9 +12,12 @@ export const createQueryString = (layer: Layer): string => { const layerId = layer.filterConfiguration?.layerName; const byAttributeList = layer.filters?.filterByAttribute; const tectoTermsList = layer.filters?.filterByTectoUnitsTerm; + const chronostratigraphyTermsList = layer.filters?.filterChronostratigraphyAge; const conditions: string[] = []; + + // Filtering by value/key attributes if (byAttributeList && byAttributeList.length > 0) { const attributeConditions = byAttributeList.map(attr => @@ -47,16 +50,49 @@ export const createQueryString = (layer: Layer): string => { }); if (tectoConditions.length > 1) { - conditions.push(`(${tectoConditions.join(' OR ')})`); + conditions.push(`( ${tectoConditions.join(' OR ')} )`); } else { conditions.push(tectoConditions[0]); } } } + // Filtering by chronostratigraphy term + if (chronostratigraphyTermsList && chronostratigraphyTermsList.length > 0) { + const chronoConditions: string[] = []; + const columnOld = layer.filterConfiguration?.filterChronostratigraphyAge?.columnToFilterOld; + const columnYon = layer.filterConfiguration?.filterChronostratigraphyAge?.columnToFilterYon; + + chronostratigraphyTermsList.forEach(termItem => { + const { type, olderTerms, youngerTerms, betweenTerms } = termItem; + + if (type === 'old' && olderTerms && olderTerms.length > 0) { + const formattedOlderTerms = olderTerms.map(term => `\'${term}\'`).join(' , '); + chronoConditions.push(`"${columnOld}" IN ( ${formattedOlderTerms} )`); + } else if (type === 'yon' && youngerTerms && youngerTerms.length > 0) { + const formattedYoungerTerms = youngerTerms.map(term => `\'${term}\'`).join(' , '); + chronoConditions.push(`"${columnYon}" IN ( ${formattedYoungerTerms} )`); + } else if (type === 'bet' && betweenTerms && betweenTerms.length > 0) { + const formattedBetweenTerms = betweenTerms.map(term => `\'${term}\'`).join(' , '); + chronoConditions.push(`"${columnOld}" IN ( ${formattedBetweenTerms} )`); + chronoConditions.push(`"${columnYon}" IN ( ${formattedBetweenTerms} )`); + } + }); + + if (chronoConditions.length > 1) { + conditions.push(`( ${chronoConditions.join(' AND ')} )`); + } else { + conditions.push(chronoConditions[0]); + } + } + console.log('Layer filters:', layer.filters); + console.log('Chrono terms:', chronostratigraphyTermsList); + console.log('Conditions:', conditions); queryString = conditions.length > 0 ? `${layerId}:${conditions.join(' AND ')}` : ''; } + + console.log('CREATESTRINGQUERY: ' + queryString); return queryString; }; diff --git a/package-lock.json b/package-lock.json index 0c18ef0..f5745c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "lucide-react": "^0.363.0", "next": "^14.1.4", "ol": "^9.0.0", + "proj4": "^2.12.1", "react": "^18", "react-dom": "^18", "react-draggable": "^4.4.6", @@ -14857,6 +14858,12 @@ } } }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -16127,6 +16134,16 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "peer": true }, + "node_modules/proj4": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.12.1.tgz", + "integrity": "sha512-vmhP3hmstjXjzFwg8QXJwpoj4n7GVrXk3ZW3DzNK/Ur4cuwXq7ZiMXaWYvLYLQbX8n4MXgbwTr4lthOUZltBpA==", + "license": "MIT", + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.3.3" + } + }, "node_modules/promise": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", @@ -18988,6 +19005,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wkt-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==", + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/package.json b/package.json index 6a56a74..a48a9d7 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "lucide-react": "^0.363.0", "next": "^14.1.4", "ol": "^9.0.0", + "proj4": "^2.12.1", "react": "^18", "react-dom": "^18", "react-draggable": "^4.4.6", diff --git a/pages/api/graphDb.tsx b/pages/api/graphDb.tsx index fe2b792..823631a 100644 --- a/pages/api/graphDb.tsx +++ b/pages/api/graphDb.tsx @@ -22,7 +22,7 @@ import { fetchVocabolaryTermByQuery, fetchVocabulariesData } from '../../app/lib export default async function handler(req: NextApiRequest, res: NextApiResponse) { let { vocabulary } = req.query; let query = req.body; - + if (Array.isArray(vocabulary)) { vocabulary = vocabulary[0]; } @@ -33,7 +33,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) if (query && vocabulary) { if (req.method === 'POST') { try { - const data = await fetchVocabolaryTermByQuery(query, vocabulary); + let data; + data = await fetchVocabolaryTermByQuery(query, vocabulary); res.status(200).json(data); } catch (error) { console.error('Failed to fetch data from GraphDB:', error);