diff --git a/app/api/common.ts b/app/api/common.ts index f9fe5ea..1421285 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -44,19 +44,25 @@ export const getPhysicalObjectsForSource = async (source: Source) => { } } -export const getAttributesForClass = async (source: Source, classUri: string) => { - const imbor_attributes = await getImborAttributesForClass(classUri); - const gwsw_attributes = await getGwswAttributesForClass(classUri); +export const getAttributesForClass = async (activeSource: Source, classUris: any[]) => { + let attributes_per_source: any = [], head: any; + for(const classUri of classUris) { + if(classUri.indexOf('gwsw') > -1) { + const gwsw_attributes = await getGwswAttributesForClass(classUri); + head = gwsw_attributes.head; + attributes_per_source = [...attributes_per_source, ...gwsw_attributes.results?.bindings]; + } + else { + const imbor_attributes = await getImborAttributesForClass(classUri); + head = imbor_attributes.head; + attributes_per_source = [...attributes_per_source, ...imbor_attributes.results?.bindings]; + } + } - // Merge both objects - let merged_bindings = mergeAttributes( - gwsw_attributes.results?.bindings, - imbor_attributes.results?.bindings - ); const merged = { - head: imbor_attributes.head, + head: head, results: { - bindings: merged_bindings + bindings: deDuplicateAttributes(attributes_per_source) } } @@ -65,34 +71,42 @@ export const getAttributesForClass = async (source: Source, classUri: string) => } export const getEnumsForAttribute = async (attributeUri: string) => { - const imbor_enums = await getImborEnumsForAttribute(attributeUri); + // Don't look up enums for GWSW for now + if(attributeUri.indexOf('gwsw') > -1) return; - // Merge both objects - const merged = imbor_enums; + // Get enums for attributeUri + const imbor_enums = await getImborEnumsForAttribute(attributeUri); // And return - return merged; + return imbor_enums; } -// mergeAttributes :: Merge attributes based on entry_text -const mergeAttributes = (bindings1: Attribute[], bindings2: Attribute[]) => { - if (!bindings1 && !bindings2) return []; - if (!bindings1) return bindings2; - if (!bindings2) return bindings1; - - let merged: Attribute[] = bindings1; - - // Add bindings2 entries, if not present in bindings1 dataset - bindings2.forEach((x2: Attribute) => { - const related_attribute: Attribute | undefined = bindings1.find((x1: Attribute) => x1.entry_text?.value === x2.entry_text?.value); - if(! related_attribute) merged.push(x2); +// deDuplicateAttributes :: Merge attributes based on entry_text +const deDuplicateAttributes = (attributes: any[]) => { + if (!attributes) return []; + + // Create variable to store list of unique attributes in + let deDuplicated: Attribute[] = attributes; + + // Create a Map to store unique entries based on entry_text.value + const uniqueMap = new Map(); + + // Iterate through attributes and keep only unique entries + attributes.forEach((attribute: Attribute) => { + const entryTextValue = attribute?.entry_text?.value; + if (entryTextValue && !uniqueMap.has(entryTextValue)) { + uniqueMap.set(entryTextValue, attribute); + } }); + // Convert Map values back to array + deDuplicated = Array.from(uniqueMap.values()); + (() => { // Check if 'identificatie' exists as an attribute field - const identificatie_exists = merged.find((attribute: Attribute) => attribute?.entry_text?.value === 'identificatie'); + const identificatie_exists = deDuplicated.find((attribute: Attribute) => attribute?.entry_text?.value === 'identificatie'); // If not: Add 'identificatie' object to beginning of the array - if(! identificatie_exists) merged.unshift({ + if(! identificatie_exists) deDuplicated.unshift({ "entry_iri": { "type": "uri", "value": "https://data.crow.nl/imbor/def/5f430c8d-7503-4a69-9e2f-f0b6e6c7f54e" @@ -114,6 +128,6 @@ const mergeAttributes = (bindings1: Attribute[], bindings2: Attribute[]) => { }); })(); - return merged; + return deDuplicated; } diff --git a/app/components/EditObject/EditObject.tsx b/app/components/EditObject/EditObject.tsx index 6b141cd..130d284 100644 --- a/app/components/EditObject/EditObject.tsx +++ b/app/components/EditObject/EditObject.tsx @@ -22,7 +22,8 @@ import Attribute from './Attribute'; // Import helper functions import { getKern, - getGeoClasses + getGeoClasses, + getPhysicalObjects } from '../../api/imbor' import { getAttributesForClass, @@ -45,7 +46,7 @@ import { import './EditObject.css' import SourceLabel from '../SourceLabel/SourceLabel'; -import { Source } from '@/app/types'; +import { PhysicalObject, Source } from '@/app/types'; interface ImborResponse { head: object, @@ -100,9 +101,30 @@ const EditObject = () => { } // Fetch attributes for a specific FysicalObject class - const fetchAttributesForClass = async (source: Source, classUri: URL) => { + const fetchAttributesForClass = async (activeSource: Source, classUri: URL, objectName: String) => { + if(! config) return; if(! classUri) return; - const response = await getAttributesForClass(source, classUri); + + // Define sources we want physical objects from + const sources = ['imbor_kern', 'gwsw_basis_v161']; + + // Fetch physical objects for these sources + let all_physical_objects: any = []; + for(const source_id of sources) { + const source_objects = await fetchPhysicalObjects(config.sources[source_id]); + all_physical_objects = [...all_physical_objects, ...source_objects]; + } + + // Fetch classUri's for all physical objects having name: objectName + const relevant_objects = all_physical_objects.filter((object: PhysicalObject) => { + return object.label?.value === objectName; + }); + + const object_uris = relevant_objects.map((object: PhysicalObject) => { + return object.classURI?.value; + }); + + const response = await getAttributesForClass(activeSource, object_uris); const triples = makeTriplesObject(response); // Make ID the first attribute const sortedTriples = triples.sort((a: any, b: any) => { @@ -159,7 +181,7 @@ const EditObject = () => { setGeometry(object.geometry); } // Fetch attributes for object type - const attributes = await fetchAttributesForClass(source, object.uri); + const attributes = await fetchAttributesForClass(source, object.uri, object.label); // Fill in all attributes after a few milliseconds (so state can update first) setTimeout(() => { if(! object.attributes) return; @@ -216,12 +238,11 @@ const EditObject = () => { const source = config.sources[selectedSource.id]; if(! source) return; - const response = await getPhysicalObjectsForSource(source); - const uniqueTriples = getUniquePhysicalObjects(response.results?.bindings); - const sortedTriples = uniqueTriples.sort((a, b) => { - return a.label.value > b.label.value ? 1 : -1; - }); - dispatch(setPhysicalObjects(sortedTriples)) + // Fetch physical objects + const physical_objects = await fetchPhysicalObjects(source); + + // Set in state + dispatch(setPhysicalObjects(physical_objects)) })(); }, [selectedSource]); @@ -229,9 +250,18 @@ const EditObject = () => { useEffect(() => { if(! selectedObjectType) return; - fetchAttributesForClass(config.sources[selectedSource.id], selectedObjectType.label); + fetchAttributesForClass(config.sources[selectedSource.id], selectedObjectType.label, selectedObjectType.value); }, [selectedObjectType]) + const fetchPhysicalObjects = async (source: Source) => { + const response = await getPhysicalObjectsForSource(source); + const uniqueTriples = getUniquePhysicalObjects(response.results?.bindings); + const sortedTriples = uniqueTriples.sort((a, b) => { + return a.label.value > b.label.value ? 1 : -1; + }); + return sortedTriples; + } + // Function that prepares data to be used for DatalistInput const prepareSourcesForDataList = (sources: any) => { let ret: any = []; diff --git a/app/config.ts b/app/config.ts index 583c2a6..d591ee5 100644 --- a/app/config.ts +++ b/app/config.ts @@ -12,6 +12,7 @@ const config: Config = { name: "imbor_kern", title: "IMBOR kern", url: "https://hub.laces.tech/crow/imbor/2022/p/kern/sparql", + classRootUrl: "http://data.gwsw.nl/1.6/totaal/", fetchOptions: { method: "get", headers: { @@ -24,6 +25,7 @@ const config: Config = { name: "gwsw_basis_v161", title: "GWSW Basis v1.6.1", url: "https://sparql.gwsw.nl/repositories/GWSW_Dataset_v161", + classRootUrl: "https://data.crow.nl/imbor/def/", fetchOptions: { method: "post", headers: { diff --git a/app/types/index.ts b/app/types/index.ts index 4039a26..c19f23a 100644 --- a/app/types/index.ts +++ b/app/types/index.ts @@ -1,14 +1,20 @@ type Source = { - name?: string - title?: string - url: string - fetchOptions?: any + name?: string; + title?: string; + url: string; + classRootUrl: string; + fetchOptions?: any; }; type Sources = { [key: string]: Source }; +type PhysicalObject = { + classURI: any; + label: any; +}; + type Config = { imbor?: any; sources: Sources; @@ -24,5 +30,6 @@ type Attribute = { export type { Config, Source, - Attribute + Attribute, + PhysicalObject }