diff --git a/app/graphql/resolvers.ts b/app/graphql/resolvers.ts index 31c87cd51..ce76c1ed5 100644 --- a/app/graphql/resolvers.ts +++ b/app/graphql/resolvers.ts @@ -20,7 +20,7 @@ import { queryDatasetCountBySubTheme, queryDatasetCountByTheme, } from "../rdf/query-cube-metadata"; -import { loadGeoShapes } from "../rdf/query-geoshapes"; +import { RawGeoShape } from "../rdf/query-geoshapes"; import truthy from "../utils/truthy"; import { DataCubeResolvers, @@ -312,9 +312,9 @@ export const resolvers: Resolvers = { }, GeoDimension: { ...dimensionResolvers, - geoShapes: async ({ dimension, locale }) => { - const rawShapes = await loadGeoShapes({ dimension, locale }); - const features = rawShapes.map((d) => ({ + geoShapes: async ({ dimension }, _, { loaders }) => { + const resolvedGeoShapes = await loaders.geoShapes.load(dimension.in); + const features = resolvedGeoShapes.map((d: RawGeoShape) => ({ type: "Feature", properties: { iri: d.iri, diff --git a/app/pages/api/graphql.ts b/app/pages/api/graphql.ts index 111eccb7f..ba4248702 100644 --- a/app/pages/api/graphql.ts +++ b/app/pages/api/graphql.ts @@ -10,6 +10,7 @@ import { createOrganizationLoader, createThemeLoader, } from "../../rdf/query-cube-metadata"; +import { createGeoShapesLoader } from "../../rdf/query-geoshapes"; const cors = configureCors(); @@ -22,6 +23,9 @@ const server = new ApolloServer({ }, context: ({ req }) => ({ loaders: { + geoShapes: new DataLoader( + createGeoShapesLoader({ locale: req.headers["accept-language"] }) + ), themes: new DataLoader( createThemeLoader({ locale: req.headers["accept-language"] }) ), diff --git a/app/rdf/query-geoshapes.ts b/app/rdf/query-geoshapes.ts index 47db588eb..dbe58dffc 100644 --- a/app/rdf/query-geoshapes.ts +++ b/app/rdf/query-geoshapes.ts @@ -1,68 +1,71 @@ import { SELECT } from "@tpluscode/sparql-builder"; import { groups } from "d3"; -import { CubeDimension } from "rdf-cube-view-query"; -import ParsingClient from "sparql-http-client/ParsingClient"; import { SPARQL_GEO_ENDPOINT } from "../domain/env"; import * as ns from "./namespace"; import { sparqlClient } from "./sparql-client"; const BATCH_SIZE = 500; +export interface RawGeoShape { + iri: string; + label: string; + wktString: string; +} /** - * Load WKT coords for a list of IDs (e.g. dimension values) + * Creates a WKT geo shapes loader. * - * @param dimensionIri geoDimension IRI - * @param client SparqlClient + * @param dimensionValues IRIs of geo dimension values */ -export async function loadGeoShapes({ - dimension, - locale, - client = sparqlClient, -}: { - dimension: CubeDimension; - locale: string; - client?: ParsingClient; -}): Promise<{ iri: string; label: string; wktString: string }[]> { - if (dimension.in) { - // We query in batches because we might run into "413 – Error: Payload Too Large" - const batched = groups(dimension.in, (_, i) => Math.floor(i / BATCH_SIZE)); - const results = await Promise.all( - batched.map(async ([, values]) => { - const query = SELECT`?geoShapeIri ?label ?WKT`.WHERE` - values ?geoShapeIri { - ${values} - } - - ?geoShapeIri - ${ns.geo.hasGeometry} ?geometry ; - ${ns.schema.name} ?label . +export const createGeoShapesLoader = + ({ locale }: { locale: string }) => + async (dimensionValues?: readonly string[]): Promise => { + if (dimensionValues) { + // We query in batches because we might run into "413 – Error: Payload Too Large" + const batched = groups(dimensionValues.flat(), (_, i) => + Math.floor(i / BATCH_SIZE) + ); - SERVICE <${SPARQL_GEO_ENDPOINT}> { - ?geometry ${ns.geo.asWKT} ?WKT - } + const results = await Promise.all( + batched.map(async ([, values]) => { + const query = SELECT`?geoShapeIri ?label ?WKT`.WHERE` + values ?geoShapeIri { + ${values} + } + + ?geoShapeIri + ${ns.geo.hasGeometry} ?geometry ; + ${ns.schema.name} ?label . + + SERVICE <${SPARQL_GEO_ENDPOINT}> { + ?geometry ${ns.geo.asWKT} ?WKT + } + + FILTER(LANG(?label) = '${locale}') + `; - FILTER(LANG(?label) = '${locale}') - `; + let result: any[] = []; + try { + result = await query.execute(sparqlClient.query, { + operation: "postUrlencoded", + }); + } catch (e) { + console.error(e); + } - let result: any[] = []; - try { - result = await query.execute(client.query, { - operation: "postUrlencoded", - }); - } catch (e) { - console.error(e); - } + return result.map((d) => ({ + iri: d.geoShapeIri.value, + label: d.label?.value, + wktString: d.WKT.value, + })); + }) + ); - return result.map((d) => ({ - iri: d.geoShapeIri.value, - label: d.label?.value, - wktString: d.WKT.value, - })); - }) - ); + return results; + } else { + return []; + } + }; - return results.flat(); - } else { - return []; - } -} +export const loadGeoShapes = ({ locale }: { locale: string }) => { + return createGeoShapesLoader({ locale })(); +};