1const ENDPOINT = 'https://cached.lindas.admin.ch/query'; 2 3function buildQuery() { 4 return ` 5 PREFIX : <https://agriculture.ld.admin.ch/inspection/> 6 PREFIX schema: <http://schema.org/> 7 PREFIX dct: <http://purl.org/dc/terms/> 8 SELECT DISTINCT ?item ?class ?name ?description ?parent ?identifier 9 FROM <https://lindas.admin.ch/foag/inspections> 10 WHERE 11 { 12 :42EA1020A8742ACABFB1B7A426619C42 schema:hasPart+ / :includesInspectionPoints* ?item . 13 ?item a ?class . 14 VALUES ?class { dct:Collection :InspectionPoint } 15 OPTIONAL { 16 { ?item schema:name ?name } 17 UNION 18 { ?item schema:description ?description } 19 } 20 OPTIONAL { 21 ?item ?link ?parent . 22 VALUES ?link { schema:isPartOf :belongsToGroup } 23 } 24 OPTIONAL { ?item schema:identifier ?identifier } 25 } 26 ORDER BY ?identifier ?item 27 `; 28} 29 30export async function fetchBindings() { 31 const res = await fetch(ENDPOINT, { 32 method: 'POST', 33 headers: { 34 'Content-Type': 'application/sparql-query', 35 'Accept': 'application/sparql-results+json' 36 }, 37 body: buildQuery() 38 }); 39 if (!res.ok) { 40 throw new Error(`SPARQL request failed: ${res.status} - ${res.statusText}`); 41 } 42 return res.json(); 43} 44 45export function buildNodeMap(bindingsJson) { 46 const rows = bindingsJson.results.bindings; 47 const v = (row, key) => row[key]?.value; 48 const l = (row, key) => row[key]?.['xml:lang']; 49 50 const map = new Map(); 51 for (const row of rows) { 52 const uri = v(row, 'item'); 53 if (!map.has(uri)) { 54 map.set(uri, { 55 uri, 56 type: v(row, 'class').includes('Collection') ? 'Collection' : 'InspectionPoint', 57 label: {}, 58 comment: {}, 59 identifier: v(row, 'identifier') || null, 60 subGroups: [], 61 inspectionPoints: [], 62 superGroup: null, 63 parentGroup: null 64 }); 65 } 66 const node = map.get(uri); 67 if (v(row, 'name')) node.label[l(row, 'name')] = v(row, 'name'); 68 if (v(row, 'description')) node.comment[l(row, 'description')] = v(row, 'description'); 69 } 70 71 const processedLinks = new Set(); 72 for (const row of rows) { 73 const uri = v(row, 'item'); 74 const parent = v(row, 'parent'); 75 if (!parent) continue; 76 77 const linkId = `${uri}->${parent}`; 78 if (processedLinks.has(linkId)) continue; 79 processedLinks.add(linkId); 80 81 const node = map.get(uri); 82 const parentNode = map.get(parent); 83 if (!node || !parentNode) continue; 84 85 if (node.type === 'Collection') { 86 node.superGroup = parent; 87 if (!parentNode.subGroups.includes(uri)) parentNode.subGroups.push(uri); 88 } else { 89 node.parentGroup = parent; 90 if (!parentNode.inspectionPoints.includes(uri)) parentNode.inspectionPoints.push(uri); 91 } 92 } 93 return map; 94} 95 96export function getDescendantIPs(collectionURI, nodeMap, visited = new Set()) { 97 if (visited.has(collectionURI)) return []; 98 visited.add(collectionURI); 99 100 const node = nodeMap.get(collectionURI); 101 if (!node || node.type !== 'Collection') return []; 102 103 let ips = [...node.inspectionPoints]; 104 for (const sub of node.subGroups) { 105 ips = ips.concat(getDescendantIPs(sub, nodeMap, visited)); 106 } 107 return ips; 108} 109 110export function getBreadcrumbs(uri, nodeMap) { 111 const trail = []; 112 let cur = uri; 113 while (cur) { 114 const n = nodeMap.get(cur); 115 if (!n) break; 116 trail.unshift(n.label || cur.split('/').pop()); 117 cur = n.superGroup || n.parentGroup; 118 } 119 return trail; 120}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.