Skip to content

Commit

Permalink
Implement thumbnails for multidimensional data pages
Browse files Browse the repository at this point in the history
  • Loading branch information
rakyi committed Jan 22, 2025
1 parent 4d6453e commit 8fab58e
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 25 deletions.
27 changes: 24 additions & 3 deletions functions/_common/grapherRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { svg2png, initialize as initializeSvg2Png } from "svg2png-wasm"
import { TimeLogger } from "./timeLogger.js"
import { png } from "itty-router"
import { png, StatusError } from "itty-router"

import svg2png_wasm from "../../node_modules/svg2png-wasm/svg2png_wasm_bg.wasm"

Expand All @@ -11,7 +11,8 @@ import LatoBold from "../_common/fonts/LatoLatin-Bold.ttf.bin"
import PlayfairSemiBold from "../_common/fonts/PlayfairDisplayLatin-SemiBold.ttf.bin"
import { Env } from "./env.js"
import { ImageOptions, extractOptions } from "./imageOptions.js"
import { GrapherIdentifier, initGrapher } from "./grapherTools.js"
import { GrapherIdentifier, initGrapher, MultiDimSlug } from "./grapherTools.js"
import { Grapher } from "@ourworldindata/grapher"

declare global {
// eslint-disable-next-line no-var
Expand Down Expand Up @@ -62,7 +63,27 @@ async function fetchAndRenderGrapherToSvg(
env: Env
) {
const grapherLogger = new TimeLogger("grapher")
const grapher = await initGrapher(identifier, options, searchParams, env)
let grapher: Grapher
try {
grapher = await initGrapher(identifier, options, searchParams, env)
} catch (e) {
if (
identifier.type === "slug" &&
e instanceof StatusError &&
e.status === 404
) {
// Normal graphers and multi-dims have the same URL namespace, but
// we have no way of knowing which of them was requested, so we try
// again with a multi-dim identifier.
const multiDimId: MultiDimSlug = {
type: "multi-dim-slug",
id: identifier.id,
}
grapher = await initGrapher(multiDimId, options, searchParams, env)
} else {
throw e
}
}

grapherLogger.log("initGrapher")
const promises = []
Expand Down
52 changes: 45 additions & 7 deletions functions/_common/grapherTools.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Grapher } from "@ourworldindata/grapher"
import {
GrapherInterface,
MultiDimDataPageConfigEnriched,
R2GrapherConfigDirectory,
} from "@ourworldindata/types"
import { excludeUndefined, Bounds } from "@ourworldindata/utils"
import {
excludeUndefined,
Bounds,
searchParamsToMultiDimView,
} from "@ourworldindata/utils"
import { StatusError } from "itty-router"
import { Env } from "./env.js"
import { fetchFromR2, grapherBaseUrl } from "./grapherRenderer.js"
Expand Down Expand Up @@ -83,11 +88,30 @@ export async function fetchUnparsedGrapherConfig(
return fetchFromR2(requestUrl, etag, fallbackUrl)
}

export async function fetchGrapherConfig(
identifier: GrapherIdentifier,
env: Env,
async function fetchMultiDimGrapherConfig(
multiDimConfig: MultiDimDataPageConfigEnriched,
searchParams: URLSearchParams,
env: Env
) {
const view = searchParamsToMultiDimView(multiDimConfig, searchParams)
const response = await fetchUnparsedGrapherConfig(
{ type: "uuid", id: view.fullConfigId },
env
)
return await response.json()
}

export async function fetchGrapherConfig({
identifier,
env,
etag,
searchParams,
}: {
identifier: GrapherIdentifier
env: Env
etag?: string
): Promise<FetchGrapherConfigResult> {
searchParams?: URLSearchParams
}): Promise<FetchGrapherConfigResult> {
const fetchResponse = await fetchUnparsedGrapherConfig(
identifier,
env,
Expand All @@ -113,7 +137,17 @@ export async function fetchGrapherConfig(
}
}

const grapherConfig: GrapherInterface = await fetchResponse.json()
const config = await fetchResponse.json()
let grapherConfig: GrapherInterface
if (identifier.type === "multi-dim-slug") {
grapherConfig = await fetchMultiDimGrapherConfig(
config as MultiDimDataPageConfigEnriched,
searchParams,
env
)
} else {
grapherConfig = config
}
console.log("grapher title", grapherConfig.title)
return {
grapherConfig,
Expand All @@ -127,7 +161,11 @@ export async function initGrapher(
searchParams: URLSearchParams,
env: Env
): Promise<Grapher> {
const grapherConfigResponse = await fetchGrapherConfig(identifier, env)
const grapherConfigResponse = await fetchGrapherConfig({
identifier,
env,
searchParams,
})

if (grapherConfigResponse.status === 404) {
// we throw 404 errors instad of returning a 404 response so that the router
Expand Down
8 changes: 4 additions & 4 deletions functions/grapher/by-uuid/[uuid].ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ async function handleConfigRequest(
const shouldCache = searchParams.get("nocache") === null
console.log("Preparing json response for uuid ", uuid)

const grapherPageResp = await fetchGrapherConfig(
{ type: "uuid", id: uuid },
const grapherPageResp = await fetchGrapherConfig({
identifier: { type: "uuid", id: uuid },
env,
etag
)
etag,
})

if (grapherPageResp.status === 304) {
return new Response(null, { status: 304 })
Expand Down
24 changes: 24 additions & 0 deletions packages/@ourworldindata/utils/src/MultiDimDataPageConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,27 @@ export const extractMultiDimChoicesFromQueryStr = (

return dimensionChoices
}

export function searchParamsToMultiDimView(
config: MultiDimDataPageConfigEnriched,
searchParams: URLSearchParams
): ViewEnriched {
const mdimConfig = MultiDimDataPageConfig.fromObject(config)
let dimensions: MultiDimDimensionChoices
if (searchParams.size > 0) {
dimensions = extractMultiDimChoicesFromQueryStr(
searchParams.toString(),
mdimConfig
)
} else {
// Get the default dimensions.
dimensions = mdimConfig.filterToAvailableChoices({}).selectedChoices
}
const view = mdimConfig.findViewByDimensions(dimensions)
if (!view) {
throw new Error(
`No view found for dimensions ${JSON.stringify(dimensions)}`
)
}
return view
}
1 change: 1 addition & 0 deletions packages/@ourworldindata/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,5 @@ export {
MultiDimDataPageConfig,
extractMultiDimChoicesFromQueryStr,
multiDimStateToQueryStr,
searchParamsToMultiDimView,
} from "./MultiDimDataPageConfig.js"
16 changes: 5 additions & 11 deletions site/multiembedder/MultiEmbedder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
extractMultiDimChoicesFromQueryStr,

Check warning on line 23 in site/multiembedder/MultiEmbedder.tsx

View workflow job for this annotation

GitHub Actions / eslint

'extractMultiDimChoicesFromQueryStr' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 23 in site/multiembedder/MultiEmbedder.tsx

View workflow job for this annotation

GitHub Actions / eslint

'extractMultiDimChoicesFromQueryStr' is defined but never used. Allowed unused vars must match /^_/u
fetchWithRetry,
ChartViewInfo,
searchParamsToMultiDimView,
} from "@ourworldindata/utils"
import { action } from "mobx"
import ReactDOM from "react-dom"
Expand Down Expand Up @@ -246,20 +247,13 @@ class MultiEmbedder {
const { queryStr, slug } = embedUrl

const mdimConfigUrl = `${MULTI_DIM_DYNAMIC_CONFIG_URL}/${slug}.json`
const mdimJsonConfig = await fetchWithRetry(mdimConfigUrl).then((res) =>
const multiDimConfig = await fetchWithRetry(mdimConfigUrl).then((res) =>
res.json()
)
const mdimConfig = MultiDimDataPageConfig.fromObject(mdimJsonConfig)
const dimensions = extractMultiDimChoicesFromQueryStr(
queryStr,
mdimConfig
const view = searchParamsToMultiDimView(
multiDimConfig,
new URLSearchParams(queryStr)
)
const view = mdimConfig.findViewByDimensions(dimensions)
if (!view) {
throw new Error(
`No view found for dimensions ${JSON.stringify(dimensions)}`
)
}

const configUrl = `${GRAPHER_DYNAMIC_CONFIG_URL}/by-uuid/${view.fullConfigId}.config.json`

Expand Down

0 comments on commit 8fab58e

Please sign in to comment.