diff --git a/app/charts/column/chart-column.tsx b/app/charts/column/chart-column.tsx
index 782e900ef..c7c401c88 100644
--- a/app/charts/column/chart-column.tsx
+++ b/app/charts/column/chart-column.tsx
@@ -29,7 +29,7 @@ import { Tooltip } from "../shared/interaction/tooltip";
import { InteractiveLegendColor, LegendColor } from "../shared/legend-color";
import { ColumnsGrouped } from "./columns-grouped";
import { GroupedColumnChart } from "./columns-grouped-state";
-import { Columns } from "./columns-simple";
+import { Columns, ErrorWhiskers } from "./columns-simple";
import { ColumnsStacked } from "./columns-stacked";
import { StackedColumnsChart } from "./columns-stacked-state";
import { ColumnChart } from "./columns-state";
@@ -171,6 +171,7 @@ export const ChartColumns = memo(
+
{interactiveFiltersConfig?.time.active && }
diff --git a/app/charts/column/columns-simple.tsx b/app/charts/column/columns-simple.tsx
index 989e692fc..d2f772d0b 100644
--- a/app/charts/column/columns-simple.tsx
+++ b/app/charts/column/columns-simple.tsx
@@ -1,8 +1,83 @@
+import { memo } from "react";
import { useTheme } from "../../themes";
import { useChartState } from "../shared/use-chart-state";
import { ColumnsState } from "./columns-state";
import { Column } from "./rendering-utils";
+export const VerticalWhisker = memo(
+ ({
+ x,
+ y1,
+ y2,
+ width,
+ }: {
+ x: number;
+ y1: number;
+ y2: number;
+ width: number;
+ color?: string;
+ }) => {
+ return (
+ <>
+
+
+
+ >
+ );
+ }
+);
+
+export const ErrorWhiskers = () => {
+ const { preparedData, bounds, getX, xScale, getY, getYError, yScale } =
+ useChartState() as ColumnsState;
+ const { margins } = bounds;
+
+ if (!getYError) {
+ return null;
+ }
+
+ return (
+
+ {preparedData.map((d, i) => {
+ const x0 = xScale(getX(d)) as number;
+ const bandwidth = xScale.bandwidth();
+ const barwidth = Math.min(bandwidth, 15);
+ const [y1, y2] = getYError(d);
+ return (
+
+ );
+ })}
+
+ );
+};
+
export const Columns = () => {
const { preparedData, bounds, getX, xScale, getY, yScale } =
useChartState() as ColumnsState;
diff --git a/app/charts/column/columns-state.tsx b/app/charts/column/columns-state.tsx
index d0a5609c4..c25a0944c 100644
--- a/app/charts/column/columns-state.tsx
+++ b/app/charts/column/columns-state.tsx
@@ -59,6 +59,7 @@ export interface ColumnsState {
xEntireScale: ScaleTime;
xScaleInteraction: ScaleBand;
getY: (d: Observation) => number | null;
+ getYError: null | ((d: Observation) => [number, number]);
yScale: ScaleLinear;
getSegment: (d: Observation) => string;
segments: string[];
@@ -102,7 +103,29 @@ const useColumnsState = ({
const getX = useStringVariable(fields.x.componentIri);
const getXAsDate = useTemporalVariable(fields.x.componentIri);
const getY = useOptionalNumericVariable(fields.y.componentIri);
+ const errorMeasure = useMemo(() => {
+ return [...measures, ...dimensions].find((m) => {
+ return m.related?.some(
+ (r) => r.type === "StandardError" && r.iri === fields.y.componentIri
+ );
+ });
+ }, [dimensions, fields.y.componentIri, measures]);
const getSegment = useSegment(fields.segment?.componentIri);
+ const getYError = errorMeasure
+ ? (d: Observation) => {
+ const y = getY(d) as number;
+ const errorIri = errorMeasure.iri;
+ let error =
+ d[errorIri] !== null ? parseFloat(d[errorIri] as string) : null;
+ if (errorMeasure.unit === "%" && error !== null) {
+ error = (error * y) / 100;
+ }
+ return (error === null ? [y, y] : [y - error, y + error]) as [
+ number,
+ number
+ ];
+ }
+ : null;
const sortingType = fields.x.sorting?.sortingType;
const sortingOrder = fields.x.sorting?.sortingOrder;
@@ -271,6 +294,7 @@ const useColumnsState = ({
timeUnit,
xScaleInteraction,
getY,
+ getYError,
yScale,
getSegment,
yAxisLabel,
diff --git a/app/charts/shared/chart-helpers.tsx b/app/charts/shared/chart-helpers.tsx
index b0b03114d..18a471419 100644
--- a/app/charts/shared/chart-helpers.tsx
+++ b/app/charts/shared/chart-helpers.tsx
@@ -118,47 +118,23 @@ export const usePreparedData = ({
return preparedData;
};
-// retrieving variables
-export const useNumericVariable = (
- key: string
-): ((d: Observation) => number) => {
- const getVariable = useCallback((d: Observation) => Number(d[key]), [key]);
-
- return getVariable;
-};
-
-export const useOptionalNumericVariable = (
- key: string
-): ((d: Observation) => number | null) => {
- const getVariable = useCallback(
- (d: Observation) => (d[key] !== null ? Number(d[key]) : null),
- [key]
- );
-
- return getVariable;
-};
-
-export const useStringVariable = (
- key: string
-): ((d: Observation) => string) => {
- const getVariable = useCallback(
- (d: Observation) => (d[key] !== null ? `${d[key]}` : ""),
- [key]
- );
-
- return getVariable;
-};
+export const makeUseParsedVariable =
+ (parser: (d: Observation[string]) => T) =>
+ (key: string) => {
+ return useCallback((d: Observation) => parser(d[key]), [key]);
+ };
-export const useTemporalVariable = (
- key: string
-): ((d: Observation) => Date) => {
- const getVariable = useCallback(
- (d: Observation) => parseDate(`${d[key]}`),
- [key]
- );
-
- return getVariable;
-};
+// retrieving variables
+export const useNumericVariable = makeUseParsedVariable((x) => Number(x));
+export const useOptionalNumericVariable = makeUseParsedVariable((x) =>
+ x !== null ? Number(x) : null
+);
+export const useStringVariable = makeUseParsedVariable((x) =>
+ x !== null ? `${x}` : ""
+);
+export const useTemporalVariable = makeUseParsedVariable((x) =>
+ parseDate(`${x}`)
+);
const getSegment =
(segmentKey: string | undefined) =>
diff --git a/app/docs/columns.docs.tsx b/app/docs/columns.docs.tsx
new file mode 100644
index 000000000..877e9131c
--- /dev/null
+++ b/app/docs/columns.docs.tsx
@@ -0,0 +1,563 @@
+import { markdown, ReactSpecimen } from "catalog";
+import * as React from "react";
+import { Columns, ErrorWhiskers } from "../charts/column/columns-simple";
+import { ColumnChart } from "../charts/column/columns-state";
+import { AxisHeightLinear } from "../charts/shared/axis-height-linear";
+import {
+ AxisWidthBand,
+ AxisWidthBandDomain,
+} from "../charts/shared/axis-width-band";
+import { ChartContainer, ChartSvg } from "../charts/shared/containers";
+import { Tooltip } from "../charts/shared/interaction/tooltip";
+import { InteractiveFiltersProvider } from "../charts/shared/use-interactive-filters";
+import { DimensionMetaDataFragment } from "../graphql/query-hooks";
+
+export const Docs = () => markdown`
+
+## Columns
+
+${(
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* {barFields.segment && } */}
+
+
+
+)}
+`;
+export default Docs;
+
+const columnFields = {
+ x: {
+ componentIri:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0",
+ },
+ y: {
+ componentIri:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1",
+ },
+ // segment: {
+ // componentIri:
+ // "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1",
+ // palette: "category10",
+ // colorMapping: {
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/0":
+ "#1f77b4",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/1":
+ "#ff7f0e",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/2":
+ "#2ca02c",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/3":
+ "#d62728",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/4":
+ "#9467bd",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/5":
+ "#8c564b",
+ // },
+ // interactiveFilterPresets: {
+ // "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/0": true,
+ // "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/1": true,
+ // "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/2": true,
+ // "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/3": true,
+ // "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/4": true,
+ // "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/5": true,
+ // },
+ // },
+};
+const columnMeasures = [
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0",
+ label: "Anzahl Betriebe",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1",
+ label: "Anzahl Waldeigentümer",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/2",
+ label: "Gesamte Waldflächen in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/3",
+ label: "Produktive Waldflächen in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/4",
+ label: "Zertifizierte Waldflächen in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/5",
+ label: "Bundeswälder in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/6",
+ label: "Staatswälder in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/7",
+ label: "Wälder der politischen Gemeinden in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/8",
+ label: "Bürgerwälder in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/9",
+ label: "Korporationswälder in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/10",
+ label: "Übrige Wälder in ha",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/11",
+ label: "Holzproduktion Total in m3",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/12",
+ label: "Stammholz in m3",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/13",
+ label: "Industrieholz in m3",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/14",
+ label: "Energieholz in m3",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/15",
+ label: "Übrige Sortimente in m3",
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/16",
+ label: "Standard Error",
+ related: [
+ {
+ __typename: "RelatedDimension",
+ type: "StandardError",
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1",
+ },
+ ],
+ __typename: "Measure",
+ },
+] as DimensionMetaDataFragment[];
+
+const columnDimensions = [
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0",
+ label: "Jahr",
+ values: [],
+ __typename: "Measure",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0",
+ label: "Jahr",
+ values: [
+ { value: "2004", label: "2004", __typename: "DimensionValue" },
+ { value: "2005", label: "2005", __typename: "DimensionValue" },
+ { value: "2006", label: "2006", __typename: "DimensionValue" },
+ { value: "2007", label: "2007", __typename: "DimensionValue" },
+ { value: "2008", label: "2008", __typename: "DimensionValue" },
+ { value: "2009", label: "2009", __typename: "DimensionValue" },
+ { value: "2010", label: "2010", __typename: "DimensionValue" },
+ { value: "2011", label: "2011", __typename: "DimensionValue" },
+ { value: "2012", label: "2012", __typename: "DimensionValue" },
+ { value: "2013", label: "2013", __typename: "DimensionValue" },
+ { value: "2014", label: "2014", __typename: "DimensionValue" },
+ ],
+ __typename: "TemporalDimension",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1",
+ label: "Forstzone",
+ values: [
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/0",
+ label: "Schweiz",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/1",
+ label: "Jura",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/2",
+ label: "Mittelland",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/3",
+ label: "Voralpen",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/4",
+ label: "Alpen",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1/5",
+ label: "Alpen-Südseite",
+ __typename: "DimensionValue",
+ },
+ ],
+ __typename: "NominalDimension",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2",
+ label: "Kanton",
+ values: [
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/0",
+ label: "Schweiz",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/1",
+ label: "Zürich",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/2",
+ label: "Bern / Berne",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/3",
+ label: "Luzern",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/4",
+ label: "Uri",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/5",
+ label: "Schwyz",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/6",
+ label: "Obwalden",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/7",
+ label: "Nidwalden",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/8",
+ label: "Glarus",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/9",
+ label: "Zug",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/10",
+ label: "Fribourg / Freiburg",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/11",
+ label: "Solothurn",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/12",
+ label: "Basel-Stadt",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/13",
+ label: "Basel-Landschaft",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/14",
+ label: "Schaffhausen",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/15",
+ label: "Appenzell Ausserrhoden",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/16",
+ label: "Appenzell Innerrhoden",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/17",
+ label: "St. Gallen",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/18",
+ label: "Graubünden / Grigioni / Grischun",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/19",
+ label: "Aargau",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/20",
+ label: "Thurgau",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/21",
+ label: "Ticino",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/22",
+ label: "Vaud",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/23",
+ label: "Valais / Wallis",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/24",
+ label: "Neuchâtel",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/25",
+ label: "Genève",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2/26",
+ label: "Jura",
+ __typename: "DimensionValue",
+ },
+ ],
+ __typename: "NominalDimension",
+ },
+ {
+ iri: "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3",
+ label: "Grössenklasse",
+ values: [
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/0",
+ label: "Grössenklasse - Total",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/1",
+ label: "< 50 ha",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/2",
+ label: "50 - 100 ha",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/3",
+ label: "101 - 200 ha",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/4",
+ label: "201 - 500 ha",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/5",
+ label: "501 - 1000 ha",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/6",
+ label: "1001 - 5000 ha",
+ __typename: "DimensionValue",
+ },
+ {
+ value:
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3/7",
+ label: "> 5000 ha",
+ __typename: "DimensionValue",
+ },
+ ],
+ __typename: "NominalDimension",
+ },
+] as unknown as DimensionMetaDataFragment[];
+const columnObservations = [
+ {
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1":
+ "Alpen",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3":
+ "Grössenklasse - Total",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2":
+ "Schweiz",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1": 1086,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0": 390,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/16": 39,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0": "2004",
+ },
+ {
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1":
+ "Alpen-Südseite",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3":
+ "Grössenklasse - Total",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2":
+ "Schweiz",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1": 379,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0": 366,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/16": 36,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0": "2004",
+ },
+ {
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1": "Jura",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3":
+ "Grössenklasse - Total",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2":
+ "Schweiz",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1": 988,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0": 409,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/16": 80,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0": "2004",
+ },
+ {
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1":
+ "Mittelland",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3":
+ "Grössenklasse - Total",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2":
+ "Schweiz",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1": 1507,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0": 1266,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/16": 126,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0": "2004",
+ },
+ {
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1":
+ "Schweiz",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3":
+ "Grössenklasse - Total",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2":
+ "Schweiz",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1": 4663,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0": 3040,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/16": 304,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0": "2004",
+ },
+ {
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/1":
+ "Voralpen",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/3":
+ "Grössenklasse - Total",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/2":
+ "Schweiz",
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/1": 703,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/0": 609,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/measure/16": 60,
+ "http://environment.ld.admin.ch/foen/px/0703010000_103/dimension/0": "2004",
+ },
+];
diff --git a/app/graphql/queries/data-cubes.graphql b/app/graphql/queries/data-cubes.graphql
index 12ab04ae9..79d38e2f4 100644
--- a/app/graphql/queries/data-cubes.graphql
+++ b/app/graphql/queries/data-cubes.graphql
@@ -38,6 +38,10 @@ fragment dimensionMetaData on Dimension {
isKeyDimension
values(filters: $filters)
unit
+ related {
+ iri
+ type
+ }
... on TemporalDimension {
timeUnit
timeFormat
diff --git a/app/graphql/query-hooks.ts b/app/graphql/query-hooks.ts
index 8e11a2abb..931215cc1 100644
--- a/app/graphql/query-hooks.ts
+++ b/app/graphql/query-hooks.ts
@@ -102,6 +102,7 @@ export type Dimension = {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -129,6 +130,7 @@ export type GeoCoordinatesDimension = Dimension & {
isKeyDimension: Scalars['Boolean'];
values: Array;
geoCoordinates?: Maybe>;
+ related?: Maybe>;
};
@@ -146,6 +148,7 @@ export type GeoShapesDimension = Dimension & {
isKeyDimension: Scalars['Boolean'];
values: Array;
geoShapes?: Maybe;
+ related?: Maybe>;
};
@@ -161,6 +164,7 @@ export type Measure = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -176,6 +180,7 @@ export type NominalDimension = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -211,6 +216,7 @@ export type OrdinalDimension = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -276,6 +282,12 @@ export type QueryDatasetcountArgs = {
};
+export type RelatedDimension = {
+ __typename: 'RelatedDimension';
+ type: Scalars['String'];
+ iri: Scalars['String'];
+};
+
export type TemporalDimension = Dimension & {
__typename: 'TemporalDimension';
iri: Scalars['String'];
@@ -286,6 +298,7 @@ export type TemporalDimension = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -314,17 +327,17 @@ export type DataCubesQueryVariables = Exact<{
export type DataCubesQuery = { __typename: 'Query', dataCubes: Array<{ __typename: 'DataCubeResult', highlightedTitle?: Maybe, highlightedDescription?: Maybe, dataCube: { __typename: 'DataCube', iri: string, title: string, description?: Maybe, publicationStatus: DataCubePublicationStatus, datePublished?: Maybe, creator?: Maybe<{ __typename: 'DataCubeOrganization', iri: string, label?: Maybe }>, themes: Array<{ __typename: 'DataCubeTheme', iri: string, label?: Maybe }> } }> };
-type DimensionMetaData_GeoCoordinatesDimension_Fragment = { __typename: 'GeoCoordinatesDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe };
+type DimensionMetaData_GeoCoordinatesDimension_Fragment = { __typename: 'GeoCoordinatesDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe, related?: Maybe> };
-type DimensionMetaData_GeoShapesDimension_Fragment = { __typename: 'GeoShapesDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe };
+type DimensionMetaData_GeoShapesDimension_Fragment = { __typename: 'GeoShapesDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe, related?: Maybe> };
-type DimensionMetaData_Measure_Fragment = { __typename: 'Measure', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe };
+type DimensionMetaData_Measure_Fragment = { __typename: 'Measure', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe, related?: Maybe> };
-type DimensionMetaData_NominalDimension_Fragment = { __typename: 'NominalDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe };
+type DimensionMetaData_NominalDimension_Fragment = { __typename: 'NominalDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe, related?: Maybe> };
-type DimensionMetaData_OrdinalDimension_Fragment = { __typename: 'OrdinalDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe };
+type DimensionMetaData_OrdinalDimension_Fragment = { __typename: 'OrdinalDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe, related?: Maybe> };
-type DimensionMetaData_TemporalDimension_Fragment = { __typename: 'TemporalDimension', timeUnit: TimeUnit, timeFormat: string, iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe };
+type DimensionMetaData_TemporalDimension_Fragment = { __typename: 'TemporalDimension', timeUnit: TimeUnit, timeFormat: string, iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe, related?: Maybe> };
export type DimensionMetaDataFragment = DimensionMetaData_GeoCoordinatesDimension_Fragment | DimensionMetaData_GeoShapesDimension_Fragment | DimensionMetaData_Measure_Fragment | DimensionMetaData_NominalDimension_Fragment | DimensionMetaData_OrdinalDimension_Fragment | DimensionMetaData_TemporalDimension_Fragment;
@@ -550,6 +563,10 @@ export const DimensionMetaDataFragmentDoc = gql`
isKeyDimension
values(filters: $filters)
unit
+ related {
+ iri
+ type
+ }
... on TemporalDimension {
timeUnit
timeFormat
diff --git a/app/graphql/resolver-types.ts b/app/graphql/resolver-types.ts
index e530f5025..20837c2fc 100644
--- a/app/graphql/resolver-types.ts
+++ b/app/graphql/resolver-types.ts
@@ -107,6 +107,7 @@ export type Dimension = {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -134,6 +135,7 @@ export type GeoCoordinatesDimension = Dimension & {
isKeyDimension: Scalars['Boolean'];
values: Array;
geoCoordinates?: Maybe>;
+ related?: Maybe>;
};
@@ -151,6 +153,7 @@ export type GeoShapesDimension = Dimension & {
isKeyDimension: Scalars['Boolean'];
values: Array;
geoShapes?: Maybe;
+ related?: Maybe>;
};
@@ -166,6 +169,7 @@ export type Measure = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -181,6 +185,7 @@ export type NominalDimension = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -216,6 +221,7 @@ export type OrdinalDimension = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -281,6 +287,12 @@ export type QueryDatasetcountArgs = {
};
+export type RelatedDimension = {
+ __typename?: 'RelatedDimension';
+ type: Scalars['String'];
+ iri: Scalars['String'];
+};
+
export type TemporalDimension = Dimension & {
__typename?: 'TemporalDimension';
iri: Scalars['String'];
@@ -291,6 +303,7 @@ export type TemporalDimension = Dimension & {
scaleType?: Maybe;
isKeyDimension: Scalars['Boolean'];
values: Array;
+ related?: Maybe>;
};
@@ -402,6 +415,7 @@ export type ResolversTypes = ResolversObject<{
OrdinalDimension: ResolverTypeWrapper;
Query: ResolverTypeWrapper<{}>;
RawObservation: ResolverTypeWrapper;
+ RelatedDimension: ResolverTypeWrapper;
TemporalDimension: ResolverTypeWrapper;
TimeUnit: TimeUnit;
}>;
@@ -434,6 +448,7 @@ export type ResolversParentTypes = ResolversObject<{
OrdinalDimension: ResolvedDimension;
Query: {};
RawObservation: Scalars['RawObservation'];
+ RelatedDimension: RelatedDimension;
TemporalDimension: ResolvedDimension;
}>;
@@ -492,6 +507,7 @@ export type DimensionResolvers, ParentType, ContextType>;
isKeyDimension?: Resolver;
values?: Resolver, ParentType, ContextType, RequireFields>;
+ related?: Resolver>, ParentType, ContextType>;
}>;
export interface DimensionValueScalarConfig extends GraphQLScalarTypeConfig {
@@ -522,6 +538,7 @@ export type GeoCoordinatesDimensionResolvers;
values?: Resolver, ParentType, ContextType, RequireFields>;
geoCoordinates?: Resolver>, ParentType, ContextType>;
+ related?: Resolver>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
}>;
@@ -537,6 +554,7 @@ export type GeoShapesDimensionResolvers;
values?: Resolver, ParentType, ContextType, RequireFields>;
geoShapes?: Resolver, ParentType, ContextType>;
+ related?: Resolver>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
}>;
@@ -547,6 +565,7 @@ export type MeasureResolvers, ParentType, ContextType>;
isKeyDimension?: Resolver;
values?: Resolver, ParentType, ContextType, RequireFields>;
+ related?: Resolver>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
}>;
@@ -557,6 +576,7 @@ export type NominalDimensionResolvers, ParentType, ContextType>;
isKeyDimension?: Resolver;
values?: Resolver, ParentType, ContextType, RequireFields>;
+ related?: Resolver>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
}>;
@@ -586,6 +606,7 @@ export type OrdinalDimensionResolvers, ParentType, ContextType>;
isKeyDimension?: Resolver;
values?: Resolver, ParentType, ContextType, RequireFields>;
+ related?: Resolver>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
}>;
@@ -603,6 +624,12 @@ export interface RawObservationScalarConfig extends GraphQLScalarTypeConfig = ResolversObject<{
+ type?: Resolver;
+ iri?: Resolver;
+ __isTypeOf?: IsTypeOfResolverFn;
+}>;
+
export type TemporalDimensionResolvers = ResolversObject<{
iri?: Resolver;
label?: Resolver;
@@ -612,6 +639,7 @@ export type TemporalDimensionResolvers, ParentType, ContextType>;
isKeyDimension?: Resolver;
values?: Resolver, ParentType, ContextType, RequireFields>;
+ related?: Resolver>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
}>;
@@ -637,6 +665,7 @@ export type Resolvers = ResolversObject<{
OrdinalDimension?: OrdinalDimensionResolvers;
Query?: QueryResolvers;
RawObservation?: GraphQLScalarType;
+ RelatedDimension?: RelatedDimensionResolvers;
TemporalDimension?: TemporalDimensionResolvers;
}>;
diff --git a/app/graphql/resolvers.ts b/app/graphql/resolvers.ts
index 86ca20634..003f0943f 100644
--- a/app/graphql/resolvers.ts
+++ b/app/graphql/resolvers.ts
@@ -311,6 +311,7 @@ const getDimensionValuesLoader = (
const mkDimensionResolvers = (debugName: string) => ({
iri: ({ data: { iri } }: ResolvedDimension) => iri,
label: ({ data: { name } }: ResolvedDimension) => name,
+ related: ({ data: { related } }: ResolvedDimension) => related,
isKeyDimension: ({ data: { isKeyDimension } }: ResolvedDimension) =>
isKeyDimension,
unit: ({ data: { unit } }: ResolvedDimension) => unit ?? null,
diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql
index ca9f4b1e7..714de0113 100644
--- a/app/graphql/schema.graphql
+++ b/app/graphql/schema.graphql
@@ -45,6 +45,11 @@ type DataCube {
themes: [DataCubeTheme!]!
}
+type RelatedDimension {
+ type: String!
+ iri: String!
+}
+
interface Dimension {
iri: String!
label: String!
@@ -52,6 +57,7 @@ interface Dimension {
scaleType: String
isKeyDimension: Boolean!
values(filters: Filters): [DimensionValue!]!
+ related: [RelatedDimension!]
}
type GeoCoordinates {
@@ -69,6 +75,7 @@ type GeoCoordinatesDimension implements Dimension {
isKeyDimension: Boolean!
values(filters: Filters): [DimensionValue!]!
geoCoordinates: [GeoCoordinates!]
+ related: [RelatedDimension!]
}
type ObservationFilter {
@@ -85,6 +92,7 @@ type GeoShapesDimension implements Dimension {
isKeyDimension: Boolean!
values(filters: Filters): [DimensionValue!]!
geoShapes: GeoShapes
+ related: [RelatedDimension!]
}
type NominalDimension implements Dimension {
@@ -94,6 +102,7 @@ type NominalDimension implements Dimension {
scaleType: String
isKeyDimension: Boolean!
values(filters: Filters): [DimensionValue!]!
+ related: [RelatedDimension!]
}
type OrdinalDimension implements Dimension {
@@ -103,6 +112,7 @@ type OrdinalDimension implements Dimension {
scaleType: String
isKeyDimension: Boolean!
values(filters: Filters): [DimensionValue!]!
+ related: [RelatedDimension!]
}
enum TimeUnit {
@@ -124,6 +134,7 @@ type TemporalDimension implements Dimension {
scaleType: String
isKeyDimension: Boolean!
values(filters: Filters): [DimensionValue!]!
+ related: [RelatedDimension!]
}
type Measure implements Dimension {
@@ -133,6 +144,7 @@ type Measure implements Dimension {
scaleType: String
isKeyDimension: Boolean!
values(filters: Filters): [DimensionValue!]!
+ related: [RelatedDimension!]
}
type DataCubeResult {
diff --git a/app/graphql/shared-types.ts b/app/graphql/shared-types.ts
index fd64f29ec..5b4f07a81 100644
--- a/app/graphql/shared-types.ts
+++ b/app/graphql/shared-types.ts
@@ -1,6 +1,7 @@
import { Cube, CubeDimension } from "rdf-cube-view-query";
import { Literal, NamedNode } from "rdf-js";
import { Observation } from "../domain/data";
+import { RelatedDimension } from "./query-hooks";
import {
DataCubeOrganization,
DataCubePublicationStatus,
@@ -50,6 +51,7 @@ export type ResolvedDimension = {
dataType?: string;
name: string;
dataKind?: "Time" | "GeoCoordinates" | "GeoShape";
+ related: Omit[];
timeUnit?: TimeUnit;
timeFormat?: string;
scaleType?: "Nominal" | "Ordinal" | "Ratio" | "Interval";
diff --git a/app/pages/docs.tsx b/app/pages/docs.tsx
index 949027250..abb0bdf10 100644
--- a/app/pages/docs.tsx
+++ b/app/pages/docs.tsx
@@ -90,9 +90,9 @@ const pages: ConfigPageOrGroup[] = [
content: require("../docs/annotations.docs"),
},
{
- path: "/charts/data-table",
- title: "Data Table",
- content: require("../docs/data-table.docs"),
+ path: "/charts/columns-chart",
+ title: "Columns",
+ content: require("../docs/columns.docs"),
},
{
path: "/charts/line-chart",
@@ -104,6 +104,11 @@ const pages: ConfigPageOrGroup[] = [
title: "Scatterplot",
content: require("../docs/scatterplot.docs"),
},
+ {
+ path: "/charts/data-table",
+ title: "Table",
+ content: require("../docs/data-table.docs"),
+ },
{
path: "/charts/cube-update",
title: "Cube update",
diff --git a/app/rdf/parse.ts b/app/rdf/parse.ts
index 9a19428be..f5ded8ef1 100644
--- a/app/rdf/parse.ts
+++ b/app/rdf/parse.ts
@@ -144,6 +144,36 @@ export const parseDimensionDatatype = (dim: CubeDimension) => {
return { dataType, hasUndefinedValues };
};
+type RelationType = "StandardError";
+
+const sparqlRelationToVisualizeRelation = {
+ "https://cube.link/relation/StandardError": "StandardError",
+} as Record;
+
+export const parseRelatedDimensions = (dim: CubeDimension) => {
+ const relatedDimensionNodes = dim.out(ns.cube`meta/dimensionRelation`);
+
+ const res = relatedDimensionNodes
+ .map((n) => {
+ const rawType = n.out(ns.rdf("type")).value;
+ const type = rawType
+ ? sparqlRelationToVisualizeRelation[rawType]
+ : undefined;
+ const iri = n.out(ns.cube`meta/relatesTo`)?.value;
+ if (!iri || !type) {
+ return null;
+ }
+
+ return {
+ type: type,
+ iri,
+ };
+ })
+ .filter(truthy);
+
+ return res;
+};
+
export const parseCubeDimension = ({
dim,
cube,
@@ -158,6 +188,8 @@ export const parseCubeDimension = ({
const outOpts = { language: getQueryLocales(locale) };
const dataKindTerm = dim.out(ns.cube`meta/dataKind`).out(ns.rdf.type).term;
+ const related = parseRelatedDimensions(dim);
+
const timeUnitTerm = dim
.out(ns.cube`meta/dataKind`)
.out(ns.time.unitType).term;
@@ -194,6 +226,7 @@ export const parseCubeDimension = ({
data: {
iri: dim.path?.value!,
+ related,
isLiteral,
isNumerical,
isKeyDimension,
diff --git a/app/rdf/queries.ts b/app/rdf/queries.ts
index 43f715787..5f1e68d31 100644
--- a/app/rdf/queries.ts
+++ b/app/rdf/queries.ts
@@ -197,11 +197,9 @@ export const getCube = async ({
export const getCubeDimensions = async ({
cube,
locale,
- filters,
}: {
cube: Cube;
locale: string;
- filters?: Filters | null;
}): Promise => {
try {
const dimensions = cube.dimensions.filter(