From 50da8f95a676e5d1d68ecf0d9812498125fe2e30 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 23 Nov 2021 09:26:08 +0100 Subject: [PATCH 01/68] feat: Add interactiveFiltersConfig to maps --- app/charts/map/chart-map-prototype.tsx | 29 +++++++++++++++++++++++--- app/charts/map/map-state.tsx | 21 +++++++++++++++---- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/app/charts/map/chart-map-prototype.tsx b/app/charts/map/chart-map-prototype.tsx index daa1b6900..fac5e6e19 100644 --- a/app/charts/map/chart-map-prototype.tsx +++ b/app/charts/map/chart-map-prototype.tsx @@ -8,10 +8,16 @@ import { } from "topojson-client"; import { Select } from "../../components/form"; import { HintBlue, LoadingOverlay, NoDataHint } from "../../components/hint"; -import { MapFields, PaletteType } from "../../configurator"; +import { + InteractiveFiltersConfig, + MapConfig, + MapFields, + PaletteType, +} from "../../configurator"; import { ControlSection } from "../../configurator/components/chart-controls/section"; import { Observation } from "../../domain/data"; import { DimensionMetaDataFragment } from "../../graphql/query-hooks"; +import { QueryFilters } from "../shared/chart-helpers"; import { ChartContainer } from "../shared/containers"; import { MapComponent } from "./map"; import { MapLegend } from "./map-legend"; @@ -41,7 +47,15 @@ type DataState = ds: Observation[]; }; -export const ChartMapVisualization = () => { +export const ChartMapVisualization = ({ + dataSetIri, + chartConfig, + queryFilters, +}: { + dataSetIri: string; + chartConfig: MapConfig; + queryFilters: QueryFilters; +}) => { const [geoData, setGeoData] = useState({ state: "fetching" }); const [dataset, loadDataset] = useState({ state: "fetching" }); @@ -125,6 +139,8 @@ export const ChartMapVisualization = () => { features={geoData} dimensions={dimensions} measures={measures} + interactiveFiltersConfig={chartConfig.interactiveFiltersConfig} + // Additional props (prototype only) attributes={attributes} /> ); @@ -143,12 +159,15 @@ export const ChartMapPrototype = ({ features, dimensions, measures, + interactiveFiltersConfig, + // Additional props (prototype only) attributes, }: { dataset: Observation[]; features: GeoData; dimensions: Array; measures: DimensionMetaDataFragment[]; + interactiveFiltersConfig: InteractiveFiltersConfig; attributes: DimensionMetaDataFragment[]; }) => { const [activeLayers, setActiveLayers] = useState({ @@ -311,6 +330,7 @@ export const ChartMapPrototype = ({ }} dimensions={dimensions} measures={measures} + interactiveFiltersConfig={interactiveFiltersConfig} // Additional props (prototype only) measure={measure.split("_")[1]} /> @@ -350,15 +370,17 @@ export const ChartMap = memo( ({ observations, features, + fields, dimensions, measures, - fields, + interactiveFiltersConfig, measure, }: { features: GeoData; observations: Observation[]; dimensions: DimensionMetaDataFragment[]; measures: DimensionMetaDataFragment[]; + interactiveFiltersConfig: InteractiveFiltersConfig; // Additional props (prototype only) fields: MapFields; measure: string; @@ -370,6 +392,7 @@ export const ChartMap = memo( fields={fields} dimensions={dimensions} measures={measures} + interactiveFiltersConfig={interactiveFiltersConfig} > diff --git a/app/charts/map/map-state.tsx b/app/charts/map/map-state.tsx index c0522313b..5a72aec08 100644 --- a/app/charts/map/map-state.tsx +++ b/app/charts/map/map-state.tsx @@ -69,6 +69,7 @@ export interface MapState { symbolColorScale: (x: number) => string; }; } + const getColorScale = ({ paletteType, palette, @@ -115,13 +116,18 @@ const getColorScale = ({ .range([paletteDomain[0], paletteDomain[paletteDomain.length - 1]]); } }; + const useMapState = ({ data, features, fields, dimensions, measures, -}: Pick & { + interactiveFiltersConfig, +}: Pick< + ChartProps, + "data" | "dimensions" | "measures" | "interactiveFiltersConfig" +> & { features: GeoData; fields: MapFields; }): MapState => { @@ -174,6 +180,7 @@ const useMapState = ({ dataDomain, nbClass, }); + const getColor = (v: number | undefined) => { if (v === undefined) { return [0, 0, 0]; @@ -241,10 +248,11 @@ const MapChartProvider = ({ fields, dimensions, measures, + interactiveFiltersConfig, children, }: Pick< ChartProps, - "data" | "dimensions" | "measures" // "interactiveFiltersConfig" + "data" | "dimensions" | "measures" | "interactiveFiltersConfig" > & { features: GeoData; children: ReactNode; fields: MapFields }) => { const state = useMapState({ data, @@ -252,6 +260,7 @@ const MapChartProvider = ({ fields, dimensions, measures, + interactiveFiltersConfig, }); return ( {children} @@ -264,8 +273,12 @@ export const MapChart = ({ fields, dimensions, measures, + interactiveFiltersConfig, children, -}: Pick & { +}: Pick< + ChartProps, + "data" | "dimensions" | "measures" | "interactiveFiltersConfig" +> & { features: GeoData; fields: MapFields; @@ -280,7 +293,7 @@ export const MapChart = ({ fields={fields} dimensions={dimensions} measures={measures} - // interactiveFiltersConfig={interactiveFiltersConfig} + interactiveFiltersConfig={interactiveFiltersConfig} > {children} From 3948b3e371d2a27ee4301ce9db09f9ad2c69a8ac Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 23 Nov 2021 11:13:28 +0100 Subject: [PATCH 02/68] feat: Initial addition of GeoDimension --- app/charts/index.ts | 28 +++++++++++-------- app/components/chart-preview.tsx | 9 ++++++ app/components/chart-published.tsx | 4 ++- .../components/chart-type-selector.tsx | 2 +- app/configurator/config-types.ts | 1 + app/graphql/query-hooks.ts | 28 +++++++++++++++++-- app/graphql/resolver-types.ts | 25 ++++++++++++++++- app/graphql/resolvers.ts | 10 +++---- app/graphql/schema.graphql | 9 ++++++ codegen.yml | 1 + 10 files changed, 96 insertions(+), 21 deletions(-) diff --git a/app/charts/index.ts b/app/charts/index.ts index a704a7482..69da50825 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -18,6 +18,7 @@ export const enabledChartTypes: ChartType[] = [ "scatterplot", "pie", "table", + "map", ]; /** @@ -307,11 +308,12 @@ export const getPossibleChartType = ({ const hasZeroQ = measures.length === 0; const hasMultipleQ = measures.length > 1; + const hasGeo = dimensions.some((dim) => dim.__typename === "GeoDimension"); const hasTime = dimensions.some( (dim) => dim.__typename === "TemporalDimension" ); - // const geoBased: ChartType[] = ["map"]; + const geoBased: ChartType[] = ["map"]; const catBased: ChartType[] = ["bar", "column", "pie", "table"]; const multipleQ: ChartType[] = ["scatterplot"]; const timeBased: ChartType[] = ["line", "area"]; @@ -319,18 +321,22 @@ export const getPossibleChartType = ({ let possibles: ChartType[] = []; if (hasZeroQ) { possibles = ["table"]; - } else if (hasMultipleQ && hasTime) { - possibles = [...multipleQ, ...timeBased, ...catBased]; - } else if (hasMultipleQ && !hasTime) { - possibles = [...multipleQ, ...catBased]; - } else if (!hasMultipleQ && hasTime) { - possibles = [...catBased, ...timeBased]; - } else if (!hasMultipleQ && !hasTime) { - possibles = [...catBased]; } else { - // Tables should always be possible - possibles = ["table"]; + possibles.push(...catBased); + + if (hasMultipleQ) { + possibles.push(...multipleQ); + } + + if (hasTime) { + possibles.push(...timeBased); + } + + if (hasGeo) { + possibles.push(...geoBased); + } } + return enabledChartTypes.filter((type) => possibles.includes(type)); }; diff --git a/app/components/chart-preview.tsx b/app/components/chart-preview.tsx index 4993c72f6..3e8999a20 100644 --- a/app/components/chart-preview.tsx +++ b/app/components/chart-preview.tsx @@ -5,6 +5,7 @@ import { ChartAreasVisualization } from "../charts/area/chart-area"; import { ChartBarsVisualization } from "../charts/bar/chart-bar"; import { ChartColumnsVisualization } from "../charts/column/chart-column"; import { ChartLinesVisualization } from "../charts/line/chart-lines"; +import { ChartMapVisualization } from "../charts/map/chart-map-prototype"; import { ChartPieVisualization } from "../charts/pie/chart-pie"; import { ChartScatterplotVisualization } from "../charts/scatterplot/chart-scatterplot"; import { ChartDataFilters } from "../charts/shared/chart-data-filters"; @@ -18,6 +19,7 @@ import { isBarConfig, isColumnConfig, isLineConfig, + isMapConfig, isPieConfig, isScatterPlotConfig, isTableConfig, @@ -215,6 +217,13 @@ const Chart = ({ chartConfig={chartConfig} /> )} + {isMapConfig(chartConfig) && ( + + )} ); }; diff --git a/app/components/chart-published.tsx b/app/components/chart-published.tsx index ff58c058f..f2b80bf76 100644 --- a/app/components/chart-published.tsx +++ b/app/components/chart-published.tsx @@ -6,6 +6,7 @@ import { ChartAreasVisualization } from "../charts/area/chart-area"; import { ChartBarsVisualization } from "../charts/bar/chart-bar"; import { ChartColumnsVisualization } from "../charts/column/chart-column"; import { ChartLinesVisualization } from "../charts/line/chart-lines"; +import { ChartMapVisualization } from "../charts/map/chart-map-prototype"; import { ChartPieVisualization } from "../charts/pie/chart-pie"; import { ChartScatterplotVisualization } from "../charts/scatterplot/chart-scatterplot"; import { ChartDataFilters } from "../charts/shared/chart-data-filters"; @@ -144,6 +145,7 @@ const ChartWithInteractiveFilters = ({ if (presetFrom && presetTo) { dispatch({ type: "ADD_TIME_FILTER", value: [presetFrom, presetTo] }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [dispatch, presetFromStr, presetToStr]); return ( @@ -193,7 +195,7 @@ const getChart = ({ case "table": return ; case "map": - return null; + return ; default: const _exhaustiveCheck: never = chartConfig; return _exhaustiveCheck; diff --git a/app/configurator/components/chart-type-selector.tsx b/app/configurator/components/chart-type-selector.tsx index e44b89c59..caca0ecad 100644 --- a/app/configurator/components/chart-type-selector.tsx +++ b/app/configurator/components/chart-type-selector.tsx @@ -8,10 +8,10 @@ import { useDataCubeMetadataWithComponentValuesQuery } from "../../graphql/query import { DataCubeMetadata } from "../../graphql/types"; import { Icon } from "../../icons"; import { useLocale } from "../../locales/use-locale"; +import { useTheme } from "../../themes"; import { FieldProps, useChartTypeSelectorField } from "../config-form"; import { SectionTitle } from "./chart-controls/section"; import { getFieldLabel, getIconName } from "./ui-helpers"; -import { useTheme } from "../../themes"; export const ChartTypeSelectionButton = ({ label, diff --git a/app/configurator/config-types.ts b/app/configurator/config-types.ts index a6f9350f5..59e911817 100644 --- a/app/configurator/config-types.ts +++ b/app/configurator/config-types.ts @@ -9,6 +9,7 @@ const ComponentType = t.union([ t.literal("TemporalDimension"), t.literal("NominalDimension"), t.literal("OrdinalDimension"), + t.literal("GeoDimension"), ]); export type ComponentType = t.TypeOf; diff --git a/app/graphql/query-hooks.ts b/app/graphql/query-hooks.ts index cab9eca10..361e4a53a 100644 --- a/app/graphql/query-hooks.ts +++ b/app/graphql/query-hooks.ts @@ -104,6 +104,16 @@ export type Dimension = { +export type GeoDimension = Dimension & { + __typename: 'GeoDimension'; + iri: Scalars['String']; + label: Scalars['String']; + unit?: Maybe; + scaleType?: Maybe; + isKeyDimension: Scalars['Boolean']; + values: Array; +}; + export type Measure = Dimension & { __typename: 'Measure'; iri: Scalars['String']; @@ -230,6 +240,8 @@ 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_GeoDimension_Fragment = { __typename: 'GeoDimension', 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 }; type DimensionMetaData_NominalDimension_Fragment = { __typename: 'NominalDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe }; @@ -238,7 +250,7 @@ type DimensionMetaData_OrdinalDimension_Fragment = { __typename: 'OrdinalDimensi type DimensionMetaData_TemporalDimension_Fragment = { __typename: 'TemporalDimension', timeUnit: TimeUnit, timeFormat: string, iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe }; -export type DimensionMetaDataFragment = DimensionMetaData_Measure_Fragment | DimensionMetaData_NominalDimension_Fragment | DimensionMetaData_OrdinalDimension_Fragment | DimensionMetaData_TemporalDimension_Fragment; +export type DimensionMetaDataFragment = DimensionMetaData_GeoDimension_Fragment | DimensionMetaData_Measure_Fragment | DimensionMetaData_NominalDimension_Fragment | DimensionMetaData_OrdinalDimension_Fragment | DimensionMetaData_TemporalDimension_Fragment; export type DataCubePreviewQueryVariables = Exact<{ iri: Scalars['String']; @@ -248,6 +260,9 @@ export type DataCubePreviewQueryVariables = Exact<{ export type DataCubePreviewQuery = { __typename: 'Query', dataCubeByIri?: Maybe<{ __typename: 'DataCube', iri: string, title: string, description?: Maybe, publicationStatus: DataCubePublicationStatus, dimensions: Array<( + { __typename: 'GeoDimension' } + & DimensionMetaData_GeoDimension_Fragment + ) | ( { __typename: 'Measure' } & DimensionMetaData_Measure_Fragment ) | ( @@ -291,6 +306,9 @@ export type DataCubeMetadataWithComponentValuesQueryVariables = Exact<{ export type DataCubeMetadataWithComponentValuesQuery = { __typename: 'Query', dataCubeByIri?: Maybe<{ __typename: 'DataCube', iri: string, title: string, publisher?: Maybe, dimensions: Array<( + { __typename: 'GeoDimension' } + & DimensionMetaData_GeoDimension_Fragment + ) | ( { __typename: 'Measure' } & DimensionMetaData_Measure_Fragment ) | ( @@ -316,6 +334,9 @@ export type DimensionValuesQueryVariables = Exact<{ export type DimensionValuesQuery = { __typename: 'Query', dataCubeByIri?: Maybe<{ __typename: 'DataCube', dimensionByIri?: Maybe<( + { __typename: 'GeoDimension' } + & DimensionMetaData_GeoDimension_Fragment + ) | ( { __typename: 'Measure' } & DimensionMetaData_Measure_Fragment ) | ( @@ -337,7 +358,7 @@ export type TemporalDimensionValuesQueryVariables = Exact<{ }>; -export type TemporalDimensionValuesQuery = { __typename: 'Query', dataCubeByIri?: Maybe<{ __typename: 'DataCube', dimensionByIri?: Maybe<{ __typename: 'Measure' } | { __typename: 'NominalDimension' } | { __typename: 'OrdinalDimension' } | ( +export type TemporalDimensionValuesQuery = { __typename: 'Query', dataCubeByIri?: Maybe<{ __typename: 'DataCube', dimensionByIri?: Maybe<{ __typename: 'GeoDimension' } | { __typename: 'Measure' } | { __typename: 'NominalDimension' } | { __typename: 'OrdinalDimension' } | ( { __typename: 'TemporalDimension', timeUnit: TimeUnit, timeFormat: string } & DimensionMetaData_TemporalDimension_Fragment )> }> }; @@ -352,6 +373,9 @@ export type DataCubeObservationsQueryVariables = Exact<{ export type DataCubeObservationsQuery = { __typename: 'Query', dataCubeByIri?: Maybe<{ __typename: 'DataCube', iri: string, title: string, description?: Maybe, dimensions: Array<( + { __typename: 'GeoDimension' } + & DimensionMetaData_GeoDimension_Fragment + ) | ( { __typename: 'Measure' } & DimensionMetaData_Measure_Fragment ) | ( diff --git a/app/graphql/resolver-types.ts b/app/graphql/resolver-types.ts index 14e0b9c8f..4f162ce82 100644 --- a/app/graphql/resolver-types.ts +++ b/app/graphql/resolver-types.ts @@ -109,6 +109,16 @@ export type Dimension = { +export type GeoDimension = Dimension & { + __typename?: 'GeoDimension'; + iri: Scalars['String']; + label: Scalars['String']; + unit?: Maybe; + scaleType?: Maybe; + isKeyDimension: Scalars['Boolean']; + values: Array; +}; + export type Measure = Dimension & { __typename?: 'Measure'; iri: Scalars['String']; @@ -305,6 +315,7 @@ export type ResolversTypes = ResolversObject<{ Boolean: ResolverTypeWrapper; DimensionValue: ResolverTypeWrapper; Filters: ResolverTypeWrapper; + GeoDimension: ResolverTypeWrapper; Measure: ResolverTypeWrapper; NominalDimension: ResolverTypeWrapper; Observation: ResolverTypeWrapper; @@ -331,6 +342,7 @@ export type ResolversParentTypes = ResolversObject<{ Boolean: Scalars['Boolean']; DimensionValue: Scalars['DimensionValue']; Filters: Scalars['Filters']; + GeoDimension: ResolvedDimension; Measure: ResolvedMeasure; NominalDimension: ResolvedDimension; Observation: Scalars['Observation']; @@ -389,7 +401,7 @@ export type DatasetCountResolvers; export type DimensionResolvers = ResolversObject<{ - __resolveType: TypeResolveFn<'Measure' | 'NominalDimension' | 'OrdinalDimension' | 'TemporalDimension', ParentType, ContextType>; + __resolveType: TypeResolveFn<'GeoDimension' | 'Measure' | 'NominalDimension' | 'OrdinalDimension' | 'TemporalDimension', ParentType, ContextType>; iri?: Resolver; label?: Resolver; unit?: Resolver, ParentType, ContextType>; @@ -406,6 +418,16 @@ export interface FiltersScalarConfig extends GraphQLScalarTypeConfig = ResolversObject<{ + iri?: Resolver; + label?: Resolver; + unit?: Resolver, ParentType, ContextType>; + scaleType?: Resolver, ParentType, ContextType>; + isKeyDimension?: Resolver; + values?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}>; + export type MeasureResolvers = ResolversObject<{ iri?: Resolver; label?: Resolver; @@ -482,6 +504,7 @@ export type Resolvers = ResolversObject<{ Dimension?: DimensionResolvers; DimensionValue?: GraphQLScalarType; Filters?: GraphQLScalarType; + GeoDimension?: GeoDimensionResolvers; Measure?: MeasureResolvers; NominalDimension?: NominalDimensionResolvers; Observation?: GraphQLScalarType; diff --git a/app/graphql/resolvers.ts b/app/graphql/resolvers.ts index 24dc85598..196f6b1ee 100644 --- a/app/graphql/resolvers.ts +++ b/app/graphql/resolvers.ts @@ -289,13 +289,10 @@ export const resolvers: Resolvers = { __resolveType({ data: { dataKind, scaleType, dataType } }) { if (dataKind === "Time") { return "TemporalDimension"; + } else if (dataKind === "GeoShape") { + return "GeoDimension"; } - // TODO: GeoDimension - // if (dataKind === "https://schema.org/GeoShape") { - // return "GeoDimension" - // } - return "NominalDimension"; }, }, @@ -310,6 +307,9 @@ export const resolvers: Resolvers = { timeUnit: ({ data: { timeUnit } }: ResolvedDimension) => timeUnit!, timeFormat: ({ data: { timeFormat } }: ResolvedDimension) => timeFormat!, }, + GeoDimension: { + ...dimensionResolvers, + }, Measure: { ...dimensionResolvers, }, diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index e5957cc50..be0ea6a99 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -52,6 +52,15 @@ interface Dimension { values: [DimensionValue!]! } +type GeoDimension implements Dimension { + iri: String! + label: String! + unit: String + scaleType: String + isKeyDimension: Boolean! + values: [DimensionValue!]! +} + type NominalDimension implements Dimension { iri: String! label: String! diff --git a/codegen.yml b/codegen.yml index 5c9018656..d979aad78 100644 --- a/codegen.yml +++ b/codegen.yml @@ -30,6 +30,7 @@ generates: ObservationsQuery: "./shared-types#ResolvedObservationsQuery" Measure: "./shared-types#ResolvedMeasure" Dimension: "./shared-types#ResolvedDimension" + GeoDimension: "./shared-types#ResolvedDimension" NominalDimension: "./shared-types#ResolvedDimension" OrdinalDimension: "./shared-types#ResolvedDimension" TemporalDimension: "./shared-types#ResolvedDimension" From f5ba35e2bb2eae24ba7923b640aac71b10b279ec Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 23 Nov 2021 13:53:14 +0100 Subject: [PATCH 03/68] feat: Initial integration of maps with configurator: base layers --- app/charts/chart-config-ui-options.ts | 12 +- app/charts/index.ts | 9 +- app/charts/map/chart-map-prototype.tsx | 216 +++++++----------- app/charts/map/map-state.tsx | 23 +- app/charts/map/map.tsx | 7 +- .../components/chart-options-selector.tsx | 6 + app/configurator/components/ui-helpers.ts | 6 + app/configurator/config-types.ts | 18 +- app/configurator/map/map-chart-options.tsx | 36 +++ app/locales/de/messages.po | 90 +++++--- app/locales/en/messages.po | 90 +++++--- app/locales/fr/messages.po | 90 +++++--- app/locales/it/messages.po | 90 +++++--- 13 files changed, 389 insertions(+), 304 deletions(-) create mode 100644 app/configurator/map/map-chart-options.tsx diff --git a/app/charts/chart-config-ui-options.ts b/app/charts/chart-config-ui-options.ts index 14e7ee8cf..a20bf9a93 100644 --- a/app/charts/chart-config-ui-options.ts +++ b/app/charts/chart-config-ui-options.ts @@ -11,10 +11,11 @@ export type DimensionType = | "TemporalDimension" | "NominalDimension" | "OrdinalDimension" + | "GeoDimension" | "Measure" | "Attribute"; -export type EncodingField = "x" | "y" | "segment"; +export type EncodingField = "x" | "y" | "segment" | "settings"; export type EncodingOption = | "chartSubType" | "sorting" @@ -228,7 +229,14 @@ export const chartConfigOptionsUISpec: ChartSpecs = { }, map: { chartType: "map", - encodings: [], + encodings: [ + { + field: "settings", + optional: true, + values: ["GeoDimension"], // FIXME: currently not used anywhere + filters: false, + }, + ], interactiveFilters: [], }, }; diff --git a/app/charts/index.ts b/app/charts/index.ts index 69da50825..50ff440d1 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -263,11 +263,6 @@ export const getInitialConfig = ({ }, }, fields: { - baseLayer: { - componentIri: dimensions[0].iri, - relief: true, - lakes: true, - }, areaLayer: { componentIri: measures[0].iri, show: false, @@ -289,6 +284,10 @@ export const getInitialConfig = ({ componentIri: dimensions[0].iri, }, }, + settings: { + showRelief: true, + showLakes: true, + }, }; // This code *should* be unreachable! If it's not, it means we haven't checked all cases (and we should get a TS error). diff --git a/app/charts/map/chart-map-prototype.tsx b/app/charts/map/chart-map-prototype.tsx index fac5e6e19..a3a92c685 100644 --- a/app/charts/map/chart-map-prototype.tsx +++ b/app/charts/map/chart-map-prototype.tsx @@ -1,30 +1,30 @@ -import { t, Trans } from "@lingui/macro"; import { geoCentroid } from "d3"; import React, { memo, useEffect, useMemo, useState } from "react"; -import { Box, Flex } from "theme-ui"; +import { Box } from "theme-ui"; import { feature as topojsonFeature, mesh as topojsonMesh, } from "topojson-client"; -import { Select } from "../../components/form"; -import { HintBlue, LoadingOverlay, NoDataHint } from "../../components/hint"; +import { Loading, LoadingOverlay, NoDataHint } from "../../components/hint"; import { InteractiveFiltersConfig, MapConfig, MapFields, + MapSettings, PaletteType, } from "../../configurator"; -import { ControlSection } from "../../configurator/components/chart-controls/section"; import { Observation } from "../../domain/data"; -import { DimensionMetaDataFragment } from "../../graphql/query-hooks"; +import { + DimensionMetaDataFragment, + useDataCubeObservationsQuery, +} from "../../graphql/query-hooks"; +import { useLocale } from "../../locales/use-locale"; import { QueryFilters } from "../shared/chart-helpers"; import { ChartContainer } from "../shared/containers"; import { MapComponent } from "./map"; import { MapLegend } from "./map-legend"; import { GeoData, MapChart } from "./map-state"; import { MapTooltip } from "./map-tooltip"; -import { Tab } from "./prototype-components"; -import { PrototypeRightControls } from "./prototype-right-controls"; type GeoDataState = | { @@ -35,18 +35,6 @@ type GeoDataState = } | (GeoData & { state: "loaded" }); -type DataState = - | { - state: "fetching"; - } - | { - state: "error"; - } - | { - state: "loaded"; - ds: Observation[]; - }; - export const ChartMapVisualization = ({ dataSetIri, chartConfig, @@ -57,7 +45,16 @@ export const ChartMapVisualization = ({ queryFilters: QueryFilters; }) => { const [geoData, setGeoData] = useState({ state: "fetching" }); - const [dataset, loadDataset] = useState({ state: "fetching" }); + + const locale = useLocale(); + const [{ data, fetching, error }] = useDataCubeObservationsQuery({ + variables: { + locale, + iri: dataSetIri, + measures: [chartConfig.fields.y.componentIri], // FIXME: Other fields may also be measures + filters: queryFilters, + }, + }); useEffect(() => { const loadGeoData = async () => { @@ -74,6 +71,7 @@ export const ChartMapVisualization = ({ coordinates: geoCentroid(c), }) ); + setGeoData({ state: "loaded", cantons, @@ -85,65 +83,29 @@ export const ChartMapVisualization = ({ setGeoData({ state: "error" }); } }; - loadGeoData(); - }, []); - useEffect(() => { - const loadData = async () => { - try { - const res = await fetch(`/map-data/tidy/holzernte.json`); - const ds = await res.json(); - - loadDataset({ - state: "loaded", - ds, - }); - } catch (e) { - loadDataset({ state: "error" }); - } - }; - loadData(); + loadGeoData(); }, []); - if (geoData.state === "fetching" || dataset.state === "fetching") { - return ; - } else if (geoData.state === "error" || dataset.state === "error") { - return ; - } else { - const dimensions = Object.keys(dataset.ds[0]) - .filter((d) => d.startsWith("D_")) - .map((d) => ({ - __typename: "NominalDimension", - iri: d, - label: d, - dimensionValues: [...new Set(dataset.ds.map((datum) => datum[d]))], - })) as Array; - const measures = Object.keys(dataset.ds[0]) - .filter((d) => d.startsWith("M_")) - .map((d) => ({ - __typename: "Measure", - iri: d, - label: d, - })) as DimensionMetaDataFragment[]; - const attributes = Object.keys(dataset.ds[0]) - .filter((d) => d.startsWith("A_")) - .map((d) => ({ - __typename: "NominalDimension", - iri: d, - label: d, - })) as DimensionMetaDataFragment[]; + if (data?.dataCubeByIri && geoData.state === "loaded") { + const { title, dimensions, measures, observations } = data?.dataCubeByIri; return ( ); + } else if (geoData.state === "fetching" || fetching) { + return ; + } else if (geoData.state === "error" || error) { + return ; + } else { + return ; } }; @@ -155,20 +117,19 @@ export type ActiveLayer = { symbolLayer: boolean; }; export const ChartMapPrototype = ({ - dataset, + observations, features, dimensions, measures, interactiveFiltersConfig, - // Additional props (prototype only) - attributes, + settings, }: { - dataset: Observation[]; + observations: Observation[]; features: GeoData; - dimensions: Array; + dimensions: DimensionMetaDataFragment[]; measures: DimensionMetaDataFragment[]; interactiveFiltersConfig: InteractiveFiltersConfig; - attributes: DimensionMetaDataFragment[]; + settings: MapSettings; }) => { const [activeLayers, setActiveLayers] = useState({ relief: true, @@ -184,7 +145,7 @@ export const ChartMapPrototype = ({ const [symbolMeasure, setSymbolMeasure] = useState(measures[0].iri); const [filters, setFilters] = useState<{ [x: string]: string }>( dimensions.reduce( - (obj, dim, i) => ({ ...obj, [dim.iri]: dim.dimensionValues[0] }), + (obj, dim, i) => ({ ...obj, [dim.iri]: dim.values[0] }), {} ) ); @@ -204,12 +165,13 @@ export const ChartMapPrototype = ({ const filterfunctions = Object.keys(filters).map( (filterKey) => (x: Observation) => x[filterKey] === filters[filterKey] ); - return filterfunctions.reduce((d, f) => d.filter(f), dataset); - }, [dataset, filters]); + + return filterfunctions.reduce((d, f) => d.filter(f), observations); + }, [observations, filters]); return ( <> - ({ + options={dim.values.map(({ value, label }) => ({ value, - label: value, + label, }))} onChange={(e) => updateFilters(dim.iri, e.currentTarget.value) @@ -286,59 +248,48 @@ export const ChartMapPrototype = ({ - + */} - - - - This is a prototype, dont use in production! - - - - {dimensions && measures && data && ( - - )} - + + {dimensions && measures && data && ( + + )} - - + */} ); }; @@ -374,6 +325,7 @@ export const ChartMap = memo( dimensions, measures, interactiveFiltersConfig, + settings, measure, }: { features: GeoData; @@ -383,6 +335,7 @@ export const ChartMap = memo( interactiveFiltersConfig: InteractiveFiltersConfig; // Additional props (prototype only) fields: MapFields; + settings: MapSettings; measure: string; }) => { return ( @@ -393,6 +346,7 @@ export const ChartMap = memo( dimensions={dimensions} measures={measures} interactiveFiltersConfig={interactiveFiltersConfig} + settings={settings} > diff --git a/app/charts/map/map-state.tsx b/app/charts/map/map-state.tsx index 5a72aec08..e70e890ea 100644 --- a/app/charts/map/map-state.tsx +++ b/app/charts/map/map-state.tsx @@ -21,8 +21,8 @@ import { getSingleHueSequentialPalette, } from "../../configurator/components/ui-helpers"; import { - MapBaseLayer, MapFields, + MapSettings, PaletteType, } from "../../configurator/config-types"; import { Observation } from "../../domain/data"; @@ -44,7 +44,8 @@ export interface MapState { data: Observation[]; features: GeoData; getFeatureLabel: (d: Observation | undefined) => string; - baseLayer: MapBaseLayer; + showRelief: boolean; + showLakes: boolean; areaLayer: { showAreaLayer: boolean; areaMeasureLabel: string; @@ -124,12 +125,14 @@ const useMapState = ({ dimensions, measures, interactiveFiltersConfig, + settings, }: Pick< ChartProps, "data" | "dimensions" | "measures" | "interactiveFiltersConfig" > & { features: GeoData; fields: MapFields; + settings: MapSettings; }): MapState => { const width = useWidth(); @@ -220,7 +223,8 @@ const useMapState = ({ features, bounds, getFeatureLabel, - baseLayer: fields["baseLayer"], + showRelief: settings.showRelief, + showLakes: settings.showLakes, areaLayer: { areaMeasureLabel, showAreaLayer: fields.areaLayer.show, @@ -249,17 +253,24 @@ const MapChartProvider = ({ dimensions, measures, interactiveFiltersConfig, + settings, children, }: Pick< ChartProps, "data" | "dimensions" | "measures" | "interactiveFiltersConfig" -> & { features: GeoData; children: ReactNode; fields: MapFields }) => { +> & { + features: GeoData; + children: ReactNode; + fields: MapFields; + settings: MapSettings; +}) => { const state = useMapState({ data, features, fields, dimensions, measures, + settings, interactiveFiltersConfig, }); return ( @@ -274,14 +285,15 @@ export const MapChart = ({ dimensions, measures, interactiveFiltersConfig, + settings, children, }: Pick< ChartProps, "data" | "dimensions" | "measures" | "interactiveFiltersConfig" > & { features: GeoData; - fields: MapFields; + settings: MapSettings; children: ReactNode; }) => { return ( @@ -293,6 +305,7 @@ export const MapChart = ({ fields={fields} dimensions={dimensions} measures={measures} + settings={settings} interactiveFiltersConfig={interactiveFiltersConfig} > {children} diff --git a/app/charts/map/map.tsx b/app/charts/map/map.tsx index 543414f3f..db184d371 100644 --- a/app/charts/map/map.tsx +++ b/app/charts/map/map.tsx @@ -87,7 +87,8 @@ const constrainZoom = ( export const MapComponent = () => { const { data, - baseLayer, + showRelief, + showLakes, features, areaLayer: { showAreaLayer, getColor, getValue }, symbolLayer: { showSymbolLayer, radiusScale, getRadius }, @@ -150,7 +151,7 @@ export const MapComponent = () => { onResize={onResize} controller={{ type: MapController }} > - {baseLayer.relief && ( + {showRelief && ( `https://wmts.geo.admin.ch/1.0.0/ch.swisstopo.leichte-basiskarte_reliefschattierung/default/current/3857/${z}/${x}/${y}.png` @@ -246,7 +247,7 @@ export const MapComponent = () => { )} - {baseLayer.lakes && ( + {showLakes && ( + ) : isMapConfig(state.chartConfig) && + state.activeField === "settings" ? ( + ) : ( { diff --git a/app/configurator/components/ui-helpers.ts b/app/configurator/components/ui-helpers.ts index b9000e1aa..2aa05bf5b 100644 --- a/app/configurator/components/ui-helpers.ts +++ b/app/configurator/components/ui-helpers.ts @@ -396,6 +396,10 @@ const fieldLabels = { id: "controls.column.grouped", message: "Grouped", }), + "controls.map.settings": defineMessage({ + id: "controls.map.settings", + message: "Base layers", + }), "controls.sorting.sortBy": defineMessage({ id: "controls.sorting.sortBy", message: "Sort by", @@ -526,6 +530,8 @@ export function getFieldLabel(field: string): string { case "pie.segment": case "segment": return i18n._(fieldLabels["controls.color"]); + case "map.settings": + return i18n._(fieldLabels["controls.map.settings"]); case "title": return i18n._(fieldLabels["controls.title"]); case "description": diff --git a/app/configurator/config-types.ts b/app/configurator/config-types.ts index 59e911817..46c10b24e 100644 --- a/app/configurator/config-types.ts +++ b/app/configurator/config-types.ts @@ -444,13 +444,6 @@ const PaletteType = t.union([ ]); export type PaletteType = t.TypeOf; -const MapBaseLayer = t.type({ - componentIri: t.string, // FIXME: we don't need this - relief: t.boolean, - lakes: t.boolean, -}); -export type MapBaseLayer = t.TypeOf; - const MapAreaLayer = t.type({ show: t.boolean, label: GenericField, @@ -467,12 +460,16 @@ const MapSymbolLayer = t.type({ }); export type MapSymbolLayer = t.TypeOf; +const MapSettings = t.type({ + showRelief: t.boolean, + showLakes: t.boolean, +}); +export type MapSettings = t.TypeOf; const MapFields = t.type({ - baseLayer: MapBaseLayer, - areaLayer: MapAreaLayer, - symbolLayer: MapSymbolLayer, x: GenericField, y: GenericField, + areaLayer: MapAreaLayer, + symbolLayer: MapSymbolLayer, segment: GenericField, }); @@ -482,6 +479,7 @@ const MapConfig = t.type( interactiveFiltersConfig: InteractiveFiltersConfig, filters: Filters, fields: MapFields, + settings: MapSettings, }, "MapConfig" ); diff --git a/app/configurator/map/map-chart-options.tsx b/app/configurator/map/map-chart-options.tsx new file mode 100644 index 000000000..080c69def --- /dev/null +++ b/app/configurator/map/map-chart-options.tsx @@ -0,0 +1,36 @@ +import { t, Trans } from "@lingui/macro"; +import React, { memo } from "react"; +import { + ControlSection, + ControlSectionContent, + SectionTitle, +} from "../components/chart-controls/section"; +import { ChartOptionCheckboxField } from "../components/field"; + +export const MapSettings = memo(() => { + return ( + + + Map Settings + + + + + + + ); +}); diff --git a/app/locales/de/messages.po b/app/locales/de/messages.po index 881feabda..541e0dde2 100644 --- a/app/locales/de/messages.po +++ b/app/locales/de/messages.po @@ -25,11 +25,11 @@ msgstr "Kein ergebnis" msgid "Search “{0}”" msgstr "Suche “{0}”" -#: app/components/chart-preview.tsx:95 +#: app/components/chart-preview.tsx:97 msgid "annotation.add.description" msgstr "[ Ohne Beschreibung ]" -#: app/components/chart-preview.tsx:79 +#: app/components/chart-preview.tsx:81 msgid "annotation.add.title" msgstr "[ Ohne Titel ]" @@ -91,16 +91,16 @@ msgid "button.share" msgstr "Teilen" #: app/charts/map/chart-map-prototype.tsx:246 -msgid "chart.map.control.data.filters" -msgstr "Datenfilter" +#~ msgid "chart.map.control.data.filters" +#~ msgstr "Datenfilter" #: app/charts/map/chart-map-prototype.tsx:203 -msgid "chart.map.control.layers" -msgstr "Ebenen" +#~ msgid "chart.map.control.layers" +#~ msgstr "Ebenen" #: app/charts/map/chart-map-prototype.tsx:222 -msgid "chart.map.layers.area" -msgstr "Flächen" +#~ msgid "chart.map.layers.area" +#~ msgstr "Flächen" #: app/charts/map/prototype-right-controls.tsx:76 msgid "chart.map.layers.area.add.data" @@ -143,8 +143,8 @@ msgid "chart.map.layers.area.select.measure" msgstr "Messwert auswählen" #: app/charts/map/chart-map-prototype.tsx:210 -msgid "chart.map.layers.base" -msgstr "Basis-Ebene" +#~ msgid "chart.map.layers.base" +#~ msgstr "Basis-Ebene" #: app/charts/map/prototype-right-controls.tsx:59 msgid "chart.map.layers.base.lakes" @@ -159,8 +159,8 @@ msgid "chart.map.layers.no.selected" msgstr "Wählen Sie einen Layer zum Bearbeiten aus." #: app/charts/map/chart-map-prototype.tsx:234 -msgid "chart.map.layers.symbol" -msgstr "Symbole" +#~ msgid "chart.map.layers.symbol" +#~ msgstr "Symbole" #: app/charts/map/prototype-right-controls.tsx:228 msgid "chart.map.layers.symbol.add.symbols" @@ -171,8 +171,8 @@ msgid "chart.map.layers.symbol.select.measure" msgstr "Messwert auswählen" #: app/charts/map/chart-map-prototype.tsx:274 -msgid "chart.map.warning.prototype" -msgstr "Diese Karte ist ein Prototyp und kann nicht publiziert werden." +#~ msgid "chart.map.warning.prototype" +#~ msgstr "Diese Karte ist ein Prototyp und kann nicht publiziert werden." #: app/charts/table/table.tsx:260 msgid "chart.published.toggle.mobile.view" @@ -318,18 +318,18 @@ msgstr "Bitte ein Beschreibungsfeld auswählen, um dieses zu bearbeiten." msgid "controls.imputation" msgstr "Imputationstyp" -#: app/configurator/components/chart-options-selector.tsx:477 +#: app/configurator/components/chart-options-selector.tsx:483 #: app/configurator/components/ui-helpers.ts:447 msgid "controls.imputation.type.linear" msgstr "Lineare Interpolation" -#: app/configurator/components/chart-options-selector.tsx:470 -#: app/configurator/components/chart-options-selector.tsx:482 +#: app/configurator/components/chart-options-selector.tsx:476 +#: app/configurator/components/chart-options-selector.tsx:488 #: app/configurator/components/ui-helpers.ts:439 msgid "controls.imputation.type.none" msgstr "-" -#: app/configurator/components/chart-options-selector.tsx:472 +#: app/configurator/components/chart-options-selector.tsx:478 #: app/configurator/components/ui-helpers.ts:443 msgid "controls.imputation.type.zeros" msgstr "Nullen" @@ -370,6 +370,14 @@ msgstr "Deutsch" msgid "controls.language.italian" msgstr "Italienisch" +#: app/configurator/map/map-chart-options.tsx:26 +msgid "controls.mapSettings.showLakes" +msgstr "" + +#: app/configurator/map/map-chart-options.tsx:18 +msgid "controls.mapSettings.showRelief" +msgstr "" + #: app/configurator/components/ui-helpers.ts:377 msgid "controls.measure" msgstr "Messwert" @@ -406,8 +414,8 @@ msgstr "Filter" msgid "controls.section.description" msgstr "Titel & Beschreibung<<<<<<< HEAD" -#: app/configurator/components/chart-options-selector.tsx:262 -#: app/configurator/components/chart-options-selector.tsx:266 +#: app/configurator/components/chart-options-selector.tsx:268 +#: app/configurator/components/chart-options-selector.tsx:272 #: app/configurator/table/table-chart-options.tsx:301 #: app/configurator/table/table-chart-options.tsx:305 #: app/configurator/table/table-chart-options.tsx:325 @@ -419,11 +427,11 @@ msgstr "Filter" msgid "controls.section.groups" msgstr "Gruppierungen" -#: app/configurator/components/chart-options-selector.tsx:511 +#: app/configurator/components/chart-options-selector.tsx:517 msgid "controls.section.imputation" msgstr "Fehlende Werte" -#: app/configurator/components/chart-options-selector.tsx:516 +#: app/configurator/components/chart-options-selector.tsx:522 msgid "controls.section.imputation.explanation" msgstr "Für diesen Diagrammtyp sollten fehlenden Werten Ersatzwerte zugewiesen werden. Entscheiden Sie sich für die Imputationslogik oder wechseln Sie zu einem anderen Diagrammtyp." @@ -435,7 +443,11 @@ msgstr "Interaktive Filter hinzufügen" msgid "controls.section.interactiveFilters.dataFilters" msgstr "Datenfilter" -#: app/configurator/components/chart-options-selector.tsx:398 +#: app/configurator/map/map-chart-options.tsx:14 +msgid "controls.section.mapSettings" +msgstr "" + +#: app/configurator/components/chart-options-selector.tsx:404 msgid "controls.section.sorting" msgstr "Sortieren<<<<<<< HEAD" @@ -451,8 +463,8 @@ msgstr "Tabellensortierung" msgid "controls.section.tableoptions" msgstr "Tabellenoptionen" -#: app/configurator/components/chart-type-selector.tsx:132 -#: app/configurator/components/chart-type-selector.tsx:135 +#: app/configurator/components/chart-type-selector.tsx:131 +#: app/configurator/components/chart-type-selector.tsx:134 msgid "controls.select.chart.type" msgstr "Diagrammtyp" @@ -464,7 +476,7 @@ msgstr "Diagrammtyp" #~ msgid "controls.select.chart.type.experimental.description" #~ msgstr "Vorschau von zukünftigen Features mit limitierten Daten auf einer separaten Seite." -#: app/configurator/components/chart-options-selector.tsx:308 +#: app/configurator/components/chart-options-selector.tsx:314 msgid "controls.select.column.layout" msgstr "Spaltenstil<<<<<<< HEAD" @@ -500,12 +512,12 @@ msgstr "Schriftfarbe" msgid "controls.select.columnStyle.textStyle" msgstr "Schriftstil" -#: app/configurator/components/chart-options-selector.tsx:171 -#: app/configurator/components/chart-options-selector.tsx:173 +#: app/configurator/components/chart-options-selector.tsx:176 +#: app/configurator/components/chart-options-selector.tsx:178 msgid "controls.select.dimension" msgstr "Dimension auswählen" -#: app/configurator/components/chart-options-selector.tsx:172 +#: app/configurator/components/chart-options-selector.tsx:177 msgid "controls.select.measure" msgstr "Messwert auswählen" @@ -515,12 +527,16 @@ msgstr "Messwert auswählen" msgid "controls.select.optional" msgstr "optional" +#: app/configurator/components/chart-options-selector.tsx:182 +msgid "controls.settings" +msgstr "" + #: app/configurator/table/table-chart-sorting-options.tsx:234 msgid "controls.sorting.addDimension" msgstr "Dimension hinzufügen" -#: app/configurator/components/chart-options-selector.tsx:349 #: app/configurator/components/chart-options-selector.tsx:355 +#: app/configurator/components/chart-options-selector.tsx:361 msgid "controls.sorting.byDimensionLabel" msgstr "Name" @@ -532,7 +548,7 @@ msgstr "A → Z" msgid "controls.sorting.byDimensionLabel.descending" msgstr "Z → A" -#: app/configurator/components/chart-options-selector.tsx:351 +#: app/configurator/components/chart-options-selector.tsx:357 msgid "controls.sorting.byMeasure" msgstr "Messwert" @@ -544,7 +560,7 @@ msgstr "1 → 9" msgid "controls.sorting.byMeasure.descending" msgstr "9 → 1" -#: app/configurator/components/chart-options-selector.tsx:353 +#: app/configurator/components/chart-options-selector.tsx:359 msgid "controls.sorting.byTotalSize" msgstr "Gesamtgrösse" @@ -605,7 +621,7 @@ msgstr "Titel hinzufügen<<<<<<< HEAD" msgid "dataset-preview.back-to-results" msgstr "Zurück zu den Ergebnissen" -#: app/components/chart-published.tsx:80 +#: app/components/chart-published.tsx:81 msgid "dataset.hasImputedValues" msgstr "Einige Daten in diesem Datensatz fehlen und wurden interpoliert, um die Lücken zu füllen." @@ -645,13 +661,13 @@ msgstr "Relevanz" msgid "dataset.order.title" msgstr "Titel<<<<<<< HEAD" -#: app/components/chart-preview.tsx:56 -#: app/components/chart-published.tsx:58 +#: app/components/chart-preview.tsx:58 +#: app/components/chart-published.tsx:59 #: app/configurator/components/dataset-preview.tsx:36 msgid "dataset.publicationStatus.draft.warning" msgstr "Achtung, dieser Datensatz ist im Entwurfs-Stadium.<0/><1>Diese Grafik nicht für Berichte verwenden." -#: app/components/chart-published.tsx:69 +#: app/components/chart-published.tsx:70 msgid "dataset.publicationStatus.expires.warning" msgstr "Achtung, dieser Datensatz ist abgelaufen.<0/><1>Diese Grafik nicht für Berichte verwenden." @@ -711,7 +727,7 @@ msgstr "Sie können diese Visualisierung teilen oder sie einbetten. Zudem könne msgid "hint.loading.data" msgstr "Lade Daten …" -#: app/configurator/components/chart-type-selector.tsx:140 +#: app/configurator/components/chart-type-selector.tsx:139 msgid "hint.no.visualization.with.dataset" msgstr "Mit dem ausgewählten Datensatz kann keine Visualisierung erstellt werden." diff --git a/app/locales/en/messages.po b/app/locales/en/messages.po index 7992ea85f..8025143a6 100644 --- a/app/locales/en/messages.po +++ b/app/locales/en/messages.po @@ -25,11 +25,11 @@ msgstr "No results" msgid "Search “{0}”" msgstr "Search “{0}”" -#: app/components/chart-preview.tsx:95 +#: app/components/chart-preview.tsx:97 msgid "annotation.add.description" msgstr "[ No Description ]" -#: app/components/chart-preview.tsx:79 +#: app/components/chart-preview.tsx:81 msgid "annotation.add.title" msgstr "[ No Title ]" @@ -91,16 +91,16 @@ msgid "button.share" msgstr "Share" #: app/charts/map/chart-map-prototype.tsx:246 -msgid "chart.map.control.data.filters" -msgstr "Data Filters" +#~ msgid "chart.map.control.data.filters" +#~ msgstr "Data Filters" #: app/charts/map/chart-map-prototype.tsx:203 -msgid "chart.map.control.layers" -msgstr "Layers" +#~ msgid "chart.map.control.layers" +#~ msgstr "Layers" #: app/charts/map/chart-map-prototype.tsx:222 -msgid "chart.map.layers.area" -msgstr "Areas" +#~ msgid "chart.map.layers.area" +#~ msgstr "Areas" #: app/charts/map/prototype-right-controls.tsx:76 msgid "chart.map.layers.area.add.data" @@ -143,8 +143,8 @@ msgid "chart.map.layers.area.select.measure" msgstr "Select a measure" #: app/charts/map/chart-map-prototype.tsx:210 -msgid "chart.map.layers.base" -msgstr "Base Layer" +#~ msgid "chart.map.layers.base" +#~ msgstr "Base Layer" #: app/charts/map/prototype-right-controls.tsx:59 msgid "chart.map.layers.base.lakes" @@ -159,8 +159,8 @@ msgid "chart.map.layers.no.selected" msgstr "Select a layer to edit in the left panel." #: app/charts/map/chart-map-prototype.tsx:234 -msgid "chart.map.layers.symbol" -msgstr "Symbols" +#~ msgid "chart.map.layers.symbol" +#~ msgstr "Symbols" #: app/charts/map/prototype-right-controls.tsx:228 msgid "chart.map.layers.symbol.add.symbols" @@ -171,8 +171,8 @@ msgid "chart.map.layers.symbol.select.measure" msgstr "Select a measure" #: app/charts/map/chart-map-prototype.tsx:274 -msgid "chart.map.warning.prototype" -msgstr "This map is a prototype and can't be published." +#~ msgid "chart.map.warning.prototype" +#~ msgstr "This map is a prototype and can't be published." #: app/charts/table/table.tsx:260 msgid "chart.published.toggle.mobile.view" @@ -318,18 +318,18 @@ msgstr "Select an annotation field to modify its content." msgid "controls.imputation" msgstr "Imputation type" -#: app/configurator/components/chart-options-selector.tsx:477 +#: app/configurator/components/chart-options-selector.tsx:483 #: app/configurator/components/ui-helpers.ts:447 msgid "controls.imputation.type.linear" msgstr "Linear interpolation" -#: app/configurator/components/chart-options-selector.tsx:470 -#: app/configurator/components/chart-options-selector.tsx:482 +#: app/configurator/components/chart-options-selector.tsx:476 +#: app/configurator/components/chart-options-selector.tsx:488 #: app/configurator/components/ui-helpers.ts:439 msgid "controls.imputation.type.none" msgstr "-" -#: app/configurator/components/chart-options-selector.tsx:472 +#: app/configurator/components/chart-options-selector.tsx:478 #: app/configurator/components/ui-helpers.ts:443 msgid "controls.imputation.type.zeros" msgstr "Zeros" @@ -370,6 +370,14 @@ msgstr "German" msgid "controls.language.italian" msgstr "Italian" +#: app/configurator/map/map-chart-options.tsx:26 +msgid "controls.mapSettings.showLakes" +msgstr "Show lakes" + +#: app/configurator/map/map-chart-options.tsx:18 +msgid "controls.mapSettings.showRelief" +msgstr "Show relief" + #: app/configurator/components/ui-helpers.ts:377 msgid "controls.measure" msgstr "Measure" @@ -406,8 +414,8 @@ msgstr "Filters" msgid "controls.section.description" msgstr "Title & Description" -#: app/configurator/components/chart-options-selector.tsx:262 -#: app/configurator/components/chart-options-selector.tsx:266 +#: app/configurator/components/chart-options-selector.tsx:268 +#: app/configurator/components/chart-options-selector.tsx:272 #: app/configurator/table/table-chart-options.tsx:301 #: app/configurator/table/table-chart-options.tsx:305 #: app/configurator/table/table-chart-options.tsx:325 @@ -419,11 +427,11 @@ msgstr "Filter" msgid "controls.section.groups" msgstr "Groups" -#: app/configurator/components/chart-options-selector.tsx:511 +#: app/configurator/components/chart-options-selector.tsx:517 msgid "controls.section.imputation" msgstr "Missing values" -#: app/configurator/components/chart-options-selector.tsx:516 +#: app/configurator/components/chart-options-selector.tsx:522 msgid "controls.section.imputation.explanation" msgstr "For this chart type, replacement values should be assigned to missing values. Decide on the imputation logic or switch to another chart type." @@ -435,7 +443,11 @@ msgstr "Interactive Filters" msgid "controls.section.interactiveFilters.dataFilters" msgstr "Data Filters" -#: app/configurator/components/chart-options-selector.tsx:398 +#: app/configurator/map/map-chart-options.tsx:14 +msgid "controls.section.mapSettings" +msgstr "Map Settings" + +#: app/configurator/components/chart-options-selector.tsx:404 msgid "controls.section.sorting" msgstr "Sort" @@ -451,8 +463,8 @@ msgstr "Table Sorting" msgid "controls.section.tableoptions" msgstr "Table Options" -#: app/configurator/components/chart-type-selector.tsx:132 -#: app/configurator/components/chart-type-selector.tsx:135 +#: app/configurator/components/chart-type-selector.tsx:131 +#: app/configurator/components/chart-type-selector.tsx:134 msgid "controls.select.chart.type" msgstr "Chart Type" @@ -464,7 +476,7 @@ msgstr "Chart Type" #~ msgid "controls.select.chart.type.experimental.description" #~ msgstr "Preview upcoming features with a limited subset of data on a separate page." -#: app/configurator/components/chart-options-selector.tsx:308 +#: app/configurator/components/chart-options-selector.tsx:314 msgid "controls.select.column.layout" msgstr "Column layout" @@ -500,12 +512,12 @@ msgstr "Text Color" msgid "controls.select.columnStyle.textStyle" msgstr "Text Style" -#: app/configurator/components/chart-options-selector.tsx:171 -#: app/configurator/components/chart-options-selector.tsx:173 +#: app/configurator/components/chart-options-selector.tsx:176 +#: app/configurator/components/chart-options-selector.tsx:178 msgid "controls.select.dimension" msgstr "Select a dimension" -#: app/configurator/components/chart-options-selector.tsx:172 +#: app/configurator/components/chart-options-selector.tsx:177 msgid "controls.select.measure" msgstr "Select a measure" @@ -515,12 +527,16 @@ msgstr "Select a measure" msgid "controls.select.optional" msgstr "optional" +#: app/configurator/components/chart-options-selector.tsx:182 +msgid "controls.settings" +msgstr "Settings" + #: app/configurator/table/table-chart-sorting-options.tsx:234 msgid "controls.sorting.addDimension" msgstr "Add dimension" -#: app/configurator/components/chart-options-selector.tsx:349 #: app/configurator/components/chart-options-selector.tsx:355 +#: app/configurator/components/chart-options-selector.tsx:361 msgid "controls.sorting.byDimensionLabel" msgstr "Name" @@ -532,7 +548,7 @@ msgstr "A → Z" msgid "controls.sorting.byDimensionLabel.descending" msgstr "Z → A" -#: app/configurator/components/chart-options-selector.tsx:351 +#: app/configurator/components/chart-options-selector.tsx:357 msgid "controls.sorting.byMeasure" msgstr "Measure" @@ -544,7 +560,7 @@ msgstr "1 → 9" msgid "controls.sorting.byMeasure.descending" msgstr "9 → 1" -#: app/configurator/components/chart-options-selector.tsx:353 +#: app/configurator/components/chart-options-selector.tsx:359 msgid "controls.sorting.byTotalSize" msgstr "Total size" @@ -605,7 +621,7 @@ msgstr "Title" msgid "dataset-preview.back-to-results" msgstr "Back to the list" -#: app/components/chart-published.tsx:80 +#: app/components/chart-published.tsx:81 msgid "dataset.hasImputedValues" msgstr "Some data in this dataset is missing and has been interpolated to fill the gaps." @@ -645,13 +661,13 @@ msgstr "Relevance" msgid "dataset.order.title" msgstr "Title" -#: app/components/chart-preview.tsx:56 -#: app/components/chart-published.tsx:58 +#: app/components/chart-preview.tsx:58 +#: app/components/chart-published.tsx:59 #: app/configurator/components/dataset-preview.tsx:36 msgid "dataset.publicationStatus.draft.warning" msgstr "Careful, this dataset is only a draft.<0/><1>Don't use for reporting!" -#: app/components/chart-published.tsx:69 +#: app/components/chart-published.tsx:70 msgid "dataset.publicationStatus.expires.warning" msgstr "Careful, the data for this chart has expired.<0/><1>Don't use for reporting!" @@ -711,7 +727,7 @@ msgstr "You can share this visualization by copying the URL or by embedding it o msgid "hint.loading.data" msgstr "Loading data..." -#: app/configurator/components/chart-type-selector.tsx:140 +#: app/configurator/components/chart-type-selector.tsx:139 msgid "hint.no.visualization.with.dataset" msgstr "No visualization can be created with the selected dataset." diff --git a/app/locales/fr/messages.po b/app/locales/fr/messages.po index 7652d6210..474532987 100644 --- a/app/locales/fr/messages.po +++ b/app/locales/fr/messages.po @@ -25,11 +25,11 @@ msgstr "Aucun résultat" msgid "Search “{0}”" msgstr "Chercher “{0}”" -#: app/components/chart-preview.tsx:95 +#: app/components/chart-preview.tsx:97 msgid "annotation.add.description" msgstr "[ Pas de description ]" -#: app/components/chart-preview.tsx:79 +#: app/components/chart-preview.tsx:81 msgid "annotation.add.title" msgstr "[ Pas de titre ]" @@ -91,16 +91,16 @@ msgid "button.share" msgstr "Partager" #: app/charts/map/chart-map-prototype.tsx:246 -msgid "chart.map.control.data.filters" -msgstr "Filtres de données" +#~ msgid "chart.map.control.data.filters" +#~ msgstr "Filtres de données" #: app/charts/map/chart-map-prototype.tsx:203 -msgid "chart.map.control.layers" -msgstr "Couches" +#~ msgid "chart.map.control.layers" +#~ msgstr "Couches" #: app/charts/map/chart-map-prototype.tsx:222 -msgid "chart.map.layers.area" -msgstr "Aplats de couleur" +#~ msgid "chart.map.layers.area" +#~ msgstr "Aplats de couleur" #: app/charts/map/prototype-right-controls.tsx:76 msgid "chart.map.layers.area.add.data" @@ -143,8 +143,8 @@ msgid "chart.map.layers.area.select.measure" msgstr "Sélectionner une variable" #: app/charts/map/chart-map-prototype.tsx:210 -msgid "chart.map.layers.base" -msgstr "Couche de base" +#~ msgid "chart.map.layers.base" +#~ msgstr "Couche de base" #: app/charts/map/prototype-right-controls.tsx:59 msgid "chart.map.layers.base.lakes" @@ -159,8 +159,8 @@ msgid "chart.map.layers.no.selected" msgstr "Sélectionner une couche à modifier dans le panneau de gauche." #: app/charts/map/chart-map-prototype.tsx:234 -msgid "chart.map.layers.symbol" -msgstr "Symboles proportionnels" +#~ msgid "chart.map.layers.symbol" +#~ msgstr "Symboles proportionnels" #: app/charts/map/prototype-right-controls.tsx:228 msgid "chart.map.layers.symbol.add.symbols" @@ -171,8 +171,8 @@ msgid "chart.map.layers.symbol.select.measure" msgstr "Sélectionner une variable" #: app/charts/map/chart-map-prototype.tsx:274 -msgid "chart.map.warning.prototype" -msgstr "Il s'agit d'un prototype, il n'est pas encore possible de publier des cartes." +#~ msgid "chart.map.warning.prototype" +#~ msgstr "Il s'agit d'un prototype, il n'est pas encore possible de publier des cartes." #: app/charts/table/table.tsx:260 msgid "chart.published.toggle.mobile.view" @@ -318,18 +318,18 @@ msgstr "Sélectionnez une des annotations proposées pour la modifier." msgid "controls.imputation" msgstr "Type d'imputation" -#: app/configurator/components/chart-options-selector.tsx:477 +#: app/configurator/components/chart-options-selector.tsx:483 #: app/configurator/components/ui-helpers.ts:447 msgid "controls.imputation.type.linear" msgstr "Interpolation linéaire" -#: app/configurator/components/chart-options-selector.tsx:470 -#: app/configurator/components/chart-options-selector.tsx:482 +#: app/configurator/components/chart-options-selector.tsx:476 +#: app/configurator/components/chart-options-selector.tsx:488 #: app/configurator/components/ui-helpers.ts:439 msgid "controls.imputation.type.none" msgstr "-" -#: app/configurator/components/chart-options-selector.tsx:472 +#: app/configurator/components/chart-options-selector.tsx:478 #: app/configurator/components/ui-helpers.ts:443 msgid "controls.imputation.type.zeros" msgstr "Zéros" @@ -370,6 +370,14 @@ msgstr "Allemand" msgid "controls.language.italian" msgstr "Italien" +#: app/configurator/map/map-chart-options.tsx:26 +msgid "controls.mapSettings.showLakes" +msgstr "" + +#: app/configurator/map/map-chart-options.tsx:18 +msgid "controls.mapSettings.showRelief" +msgstr "" + #: app/configurator/components/ui-helpers.ts:377 msgid "controls.measure" msgstr "Variable mesurée" @@ -406,8 +414,8 @@ msgstr "Filtres" msgid "controls.section.description" msgstr "Titre & description" -#: app/configurator/components/chart-options-selector.tsx:262 -#: app/configurator/components/chart-options-selector.tsx:266 +#: app/configurator/components/chart-options-selector.tsx:268 +#: app/configurator/components/chart-options-selector.tsx:272 #: app/configurator/table/table-chart-options.tsx:301 #: app/configurator/table/table-chart-options.tsx:305 #: app/configurator/table/table-chart-options.tsx:325 @@ -419,11 +427,11 @@ msgstr "Filtre" msgid "controls.section.groups" msgstr "Groupes" -#: app/configurator/components/chart-options-selector.tsx:511 +#: app/configurator/components/chart-options-selector.tsx:517 msgid "controls.section.imputation" msgstr "Valeurs manquantes" -#: app/configurator/components/chart-options-selector.tsx:516 +#: app/configurator/components/chart-options-selector.tsx:522 msgid "controls.section.imputation.explanation" msgstr "En raison du type de graphique sélectionné, les valeurs manquantes doivent être remplies. Décidez de la logique d'imputation ou choisissez un autre type de graphique." @@ -435,7 +443,11 @@ msgstr "Filtres interactifs" msgid "controls.section.interactiveFilters.dataFilters" msgstr "Filtres de données" -#: app/configurator/components/chart-options-selector.tsx:398 +#: app/configurator/map/map-chart-options.tsx:14 +msgid "controls.section.mapSettings" +msgstr "" + +#: app/configurator/components/chart-options-selector.tsx:404 msgid "controls.section.sorting" msgstr "Trier" @@ -451,8 +463,8 @@ msgstr "Tri du tableau" msgid "controls.section.tableoptions" msgstr "Options du tableau" -#: app/configurator/components/chart-type-selector.tsx:132 -#: app/configurator/components/chart-type-selector.tsx:135 +#: app/configurator/components/chart-type-selector.tsx:131 +#: app/configurator/components/chart-type-selector.tsx:134 msgid "controls.select.chart.type" msgstr "Type de graphique" @@ -464,7 +476,7 @@ msgstr "Type de graphique" #~ msgid "controls.select.chart.type.experimental.description" #~ msgstr "Voir le prototype de la carte avec un nombre limité de jeu de données (lien vers une nouvelle page)." -#: app/configurator/components/chart-options-selector.tsx:308 +#: app/configurator/components/chart-options-selector.tsx:314 msgid "controls.select.column.layout" msgstr "Mise en forme de la colonne" @@ -500,12 +512,12 @@ msgstr "Couleur du texte" msgid "controls.select.columnStyle.textStyle" msgstr "Style du texte" -#: app/configurator/components/chart-options-selector.tsx:171 -#: app/configurator/components/chart-options-selector.tsx:173 +#: app/configurator/components/chart-options-selector.tsx:176 +#: app/configurator/components/chart-options-selector.tsx:178 msgid "controls.select.dimension" msgstr "Sélectionner une dimension" -#: app/configurator/components/chart-options-selector.tsx:172 +#: app/configurator/components/chart-options-selector.tsx:177 msgid "controls.select.measure" msgstr "Sélectionner une variable" @@ -515,12 +527,16 @@ msgstr "Sélectionner une variable" msgid "controls.select.optional" msgstr "optionnel" +#: app/configurator/components/chart-options-selector.tsx:182 +msgid "controls.settings" +msgstr "" + #: app/configurator/table/table-chart-sorting-options.tsx:234 msgid "controls.sorting.addDimension" msgstr "Ajouter une dimension" -#: app/configurator/components/chart-options-selector.tsx:349 #: app/configurator/components/chart-options-selector.tsx:355 +#: app/configurator/components/chart-options-selector.tsx:361 msgid "controls.sorting.byDimensionLabel" msgstr "Nom" @@ -532,7 +548,7 @@ msgstr "A → Z" msgid "controls.sorting.byDimensionLabel.descending" msgstr "Z → A" -#: app/configurator/components/chart-options-selector.tsx:351 +#: app/configurator/components/chart-options-selector.tsx:357 msgid "controls.sorting.byMeasure" msgstr "Mesure" @@ -544,7 +560,7 @@ msgstr "1 → 9" msgid "controls.sorting.byMeasure.descending" msgstr "9 → 1" -#: app/configurator/components/chart-options-selector.tsx:353 +#: app/configurator/components/chart-options-selector.tsx:359 msgid "controls.sorting.byTotalSize" msgstr "Taille totale" @@ -605,7 +621,7 @@ msgstr "Titre" msgid "dataset-preview.back-to-results" msgstr "Revenir aux résultats" -#: app/components/chart-published.tsx:80 +#: app/components/chart-published.tsx:81 msgid "dataset.hasImputedValues" msgstr "Certaines données de cet ensemble de données sont manquantes et ont été interpolées pour combler les lacunes." @@ -645,13 +661,13 @@ msgstr "" msgid "dataset.order.title" msgstr "" -#: app/components/chart-preview.tsx:56 -#: app/components/chart-published.tsx:58 +#: app/components/chart-preview.tsx:58 +#: app/components/chart-published.tsx:59 #: app/configurator/components/dataset-preview.tsx:36 msgid "dataset.publicationStatus.draft.warning" msgstr "Attention, ce jeu de données est à l'état d'ébauche.<0/><1>Ne l'utilisez pas pour une publication!" -#: app/components/chart-published.tsx:69 +#: app/components/chart-published.tsx:70 msgid "dataset.publicationStatus.expires.warning" msgstr "Attention, ce jeu de données est expiré.<0/><1>Ne l'utilisez pas pour une publication!" @@ -711,7 +727,7 @@ msgstr "Vous pouvez partager cette visualisation en copiant l'URL ou en l'intég msgid "hint.loading.data" msgstr "Chargement des données..." -#: app/configurator/components/chart-type-selector.tsx:140 +#: app/configurator/components/chart-type-selector.tsx:139 msgid "hint.no.visualization.with.dataset" msgstr "Aucune visualisation ne peut être créée avec le jeu de données sélectionné." diff --git a/app/locales/it/messages.po b/app/locales/it/messages.po index 79b538c52..489a3e7ee 100644 --- a/app/locales/it/messages.po +++ b/app/locales/it/messages.po @@ -25,11 +25,11 @@ msgstr "Nessun risultato" msgid "Search “{0}”" msgstr "Cerca “{0}”" -#: app/components/chart-preview.tsx:95 +#: app/components/chart-preview.tsx:97 msgid "annotation.add.description" msgstr "[ Nessuna descrizione ]" -#: app/components/chart-preview.tsx:79 +#: app/components/chart-preview.tsx:81 msgid "annotation.add.title" msgstr "[ Nessun titolo ]" @@ -91,16 +91,16 @@ msgid "button.share" msgstr "Condividi" #: app/charts/map/chart-map-prototype.tsx:246 -msgid "chart.map.control.data.filters" -msgstr "Filtri di dati" +#~ msgid "chart.map.control.data.filters" +#~ msgstr "Filtri di dati" #: app/charts/map/chart-map-prototype.tsx:203 -msgid "chart.map.control.layers" -msgstr "Livelli" +#~ msgid "chart.map.control.layers" +#~ msgstr "Livelli" #: app/charts/map/chart-map-prototype.tsx:222 -msgid "chart.map.layers.area" -msgstr "Aree" +#~ msgid "chart.map.layers.area" +#~ msgstr "Aree" #: app/charts/map/prototype-right-controls.tsx:76 msgid "chart.map.layers.area.add.data" @@ -143,8 +143,8 @@ msgid "chart.map.layers.area.select.measure" msgstr "Seleziona una variabile" #: app/charts/map/chart-map-prototype.tsx:210 -msgid "chart.map.layers.base" -msgstr "Livello di base" +#~ msgid "chart.map.layers.base" +#~ msgstr "Livello di base" #: app/charts/map/prototype-right-controls.tsx:59 msgid "chart.map.layers.base.lakes" @@ -159,8 +159,8 @@ msgid "chart.map.layers.no.selected" msgstr "Seleziona un livello da editare nel pannello di sinistra." #: app/charts/map/chart-map-prototype.tsx:234 -msgid "chart.map.layers.symbol" -msgstr "Simboli" +#~ msgid "chart.map.layers.symbol" +#~ msgstr "Simboli" #: app/charts/map/prototype-right-controls.tsx:228 msgid "chart.map.layers.symbol.add.symbols" @@ -171,8 +171,8 @@ msgid "chart.map.layers.symbol.select.measure" msgstr "Seleziona una variabile" #: app/charts/map/chart-map-prototype.tsx:274 -msgid "chart.map.warning.prototype" -msgstr "Questa mappa è un prototipo e non può essere pubblicata." +#~ msgid "chart.map.warning.prototype" +#~ msgstr "Questa mappa è un prototipo e non può essere pubblicata." #: app/charts/table/table.tsx:260 msgid "chart.published.toggle.mobile.view" @@ -318,18 +318,18 @@ msgstr "Seleziona una delle annotazioni proposte per modificarla." msgid "controls.imputation" msgstr "Tipo di imputazione" -#: app/configurator/components/chart-options-selector.tsx:477 +#: app/configurator/components/chart-options-selector.tsx:483 #: app/configurator/components/ui-helpers.ts:447 msgid "controls.imputation.type.linear" msgstr "Interpolazione lineare" -#: app/configurator/components/chart-options-selector.tsx:470 -#: app/configurator/components/chart-options-selector.tsx:482 +#: app/configurator/components/chart-options-selector.tsx:476 +#: app/configurator/components/chart-options-selector.tsx:488 #: app/configurator/components/ui-helpers.ts:439 msgid "controls.imputation.type.none" msgstr "-" -#: app/configurator/components/chart-options-selector.tsx:472 +#: app/configurator/components/chart-options-selector.tsx:478 #: app/configurator/components/ui-helpers.ts:443 msgid "controls.imputation.type.zeros" msgstr "Zeri" @@ -370,6 +370,14 @@ msgstr "Tedesco" msgid "controls.language.italian" msgstr "Italiano" +#: app/configurator/map/map-chart-options.tsx:26 +msgid "controls.mapSettings.showLakes" +msgstr "" + +#: app/configurator/map/map-chart-options.tsx:18 +msgid "controls.mapSettings.showRelief" +msgstr "" + #: app/configurator/components/ui-helpers.ts:377 msgid "controls.measure" msgstr "Misura" @@ -406,8 +414,8 @@ msgstr "Filtri" msgid "controls.section.description" msgstr "Titolo e descrizione" -#: app/configurator/components/chart-options-selector.tsx:262 -#: app/configurator/components/chart-options-selector.tsx:266 +#: app/configurator/components/chart-options-selector.tsx:268 +#: app/configurator/components/chart-options-selector.tsx:272 #: app/configurator/table/table-chart-options.tsx:301 #: app/configurator/table/table-chart-options.tsx:305 #: app/configurator/table/table-chart-options.tsx:325 @@ -419,11 +427,11 @@ msgstr "Filtro" msgid "controls.section.groups" msgstr "Gruppi" -#: app/configurator/components/chart-options-selector.tsx:511 +#: app/configurator/components/chart-options-selector.tsx:517 msgid "controls.section.imputation" msgstr "Valori mancanti" -#: app/configurator/components/chart-options-selector.tsx:516 +#: app/configurator/components/chart-options-selector.tsx:522 msgid "controls.section.imputation.explanation" msgstr "Per questo tipo di grafico, i valori di sostituzione devono essere assegnati ai valori mancanti. Decidi la logica di imputazione o passa a un altro tipo di grafico." @@ -435,7 +443,11 @@ msgstr "Filtri interattivi" msgid "controls.section.interactiveFilters.dataFilters" msgstr "Filtri di dati" -#: app/configurator/components/chart-options-selector.tsx:398 +#: app/configurator/map/map-chart-options.tsx:14 +msgid "controls.section.mapSettings" +msgstr "" + +#: app/configurator/components/chart-options-selector.tsx:404 msgid "controls.section.sorting" msgstr "Ordina" @@ -451,8 +463,8 @@ msgstr "Ordinamento della tabella" msgid "controls.section.tableoptions" msgstr "Opzioni della tabella" -#: app/configurator/components/chart-type-selector.tsx:132 -#: app/configurator/components/chart-type-selector.tsx:135 +#: app/configurator/components/chart-type-selector.tsx:131 +#: app/configurator/components/chart-type-selector.tsx:134 msgid "controls.select.chart.type" msgstr "Tipo di grafico" @@ -464,7 +476,7 @@ msgstr "Tipo di grafico" #~ msgid "controls.select.chart.type.experimental.description" #~ msgstr "Guarda il prototipo della mappa con un numero limitato di dati (link ad una nuova pagina)." -#: app/configurator/components/chart-options-selector.tsx:308 +#: app/configurator/components/chart-options-selector.tsx:314 msgid "controls.select.column.layout" msgstr "Layout a colonne" @@ -500,12 +512,12 @@ msgstr "Colore del testo" msgid "controls.select.columnStyle.textStyle" msgstr "Stile del testo" -#: app/configurator/components/chart-options-selector.tsx:171 -#: app/configurator/components/chart-options-selector.tsx:173 +#: app/configurator/components/chart-options-selector.tsx:176 +#: app/configurator/components/chart-options-selector.tsx:178 msgid "controls.select.dimension" msgstr "Seleziona una dimensione" -#: app/configurator/components/chart-options-selector.tsx:172 +#: app/configurator/components/chart-options-selector.tsx:177 msgid "controls.select.measure" msgstr "Seleziona una misura" @@ -515,12 +527,16 @@ msgstr "Seleziona una misura" msgid "controls.select.optional" msgstr "facoltativo" +#: app/configurator/components/chart-options-selector.tsx:182 +msgid "controls.settings" +msgstr "" + #: app/configurator/table/table-chart-sorting-options.tsx:234 msgid "controls.sorting.addDimension" msgstr "Aggiungi una dimensione" -#: app/configurator/components/chart-options-selector.tsx:349 #: app/configurator/components/chart-options-selector.tsx:355 +#: app/configurator/components/chart-options-selector.tsx:361 msgid "controls.sorting.byDimensionLabel" msgstr "Nome" @@ -532,7 +548,7 @@ msgstr "A → Z" msgid "controls.sorting.byDimensionLabel.descending" msgstr "Z → A" -#: app/configurator/components/chart-options-selector.tsx:351 +#: app/configurator/components/chart-options-selector.tsx:357 msgid "controls.sorting.byMeasure" msgstr "Misura" @@ -544,7 +560,7 @@ msgstr "1 → 9" msgid "controls.sorting.byMeasure.descending" msgstr "9 → 1" -#: app/configurator/components/chart-options-selector.tsx:353 +#: app/configurator/components/chart-options-selector.tsx:359 msgid "controls.sorting.byTotalSize" msgstr "Grandezza totale" @@ -605,7 +621,7 @@ msgstr "Titolo" msgid "dataset-preview.back-to-results" msgstr "Torna ai risultati" -#: app/components/chart-published.tsx:80 +#: app/components/chart-published.tsx:81 msgid "dataset.hasImputedValues" msgstr "In questo set di dati mancano alcuni dati. Questi sono stati interpolati per colmare le lacune.." @@ -645,13 +661,13 @@ msgstr "" msgid "dataset.order.title" msgstr "" -#: app/components/chart-preview.tsx:56 -#: app/components/chart-published.tsx:58 +#: app/components/chart-preview.tsx:58 +#: app/components/chart-published.tsx:59 #: app/configurator/components/dataset-preview.tsx:36 msgid "dataset.publicationStatus.draft.warning" msgstr "Attenzione, questo set di dati è una bozza.<0/><1>Non utilizzare questo grafico per un rapporto!" -#: app/components/chart-published.tsx:69 +#: app/components/chart-published.tsx:70 msgid "dataset.publicationStatus.expires.warning" msgstr "Attenzione, questo set di dati è scaduto.<0/><1>Non utilizzare questo grafico per un rapporto!" @@ -711,7 +727,7 @@ msgstr "È possibile condividere questa visualizzazione copiando l'URL o incorpo msgid "hint.loading.data" msgstr "Caricamento dei dati..." -#: app/configurator/components/chart-type-selector.tsx:140 +#: app/configurator/components/chart-type-selector.tsx:139 msgid "hint.no.visualization.with.dataset" msgstr "Nessuna visualizzazione può essere creata con il dataset selezionato." From d9eac549f92b81da7407708eae99380ed70fc9dc Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 23 Nov 2021 15:21:22 +0100 Subject: [PATCH 04/68] feat: Show areaLayer and symbolLayer in the configurator panel for maps --- app/charts/chart-config-ui-options.ts | 16 +++++++++++++++- app/charts/index.ts | 10 +--------- app/charts/map/chart-map-prototype.tsx | 8 ++++---- app/charts/map/map-state.tsx | 2 +- .../components/chart-configurator.tsx | 14 ++++++-------- .../components/chart-options-selector.tsx | 14 ++++++++++---- app/configurator/components/ui-helpers.ts | 12 ++++++++++++ app/configurator/config-types.ts | 3 --- app/configurator/configurator-state.tsx | 7 +++++-- 9 files changed, 54 insertions(+), 32 deletions(-) diff --git a/app/charts/chart-config-ui-options.ts b/app/charts/chart-config-ui-options.ts index a20bf9a93..2fd7d58cd 100644 --- a/app/charts/chart-config-ui-options.ts +++ b/app/charts/chart-config-ui-options.ts @@ -15,7 +15,9 @@ export type DimensionType = | "Measure" | "Attribute"; -export type EncodingField = "x" | "y" | "segment" | "settings"; +export type BaseEncodingField = "x" | "y" | "segment" | "settings"; +export type GeoEncodingField = "areaLayer" | "symbolLayer"; +export type EncodingField = BaseEncodingField | GeoEncodingField; export type EncodingOption = | "chartSubType" | "sorting" @@ -236,6 +238,18 @@ export const chartConfigOptionsUISpec: ChartSpecs = { values: ["GeoDimension"], // FIXME: currently not used anywhere filters: false, }, + { + field: "areaLayer", + optional: false, + values: ["Measure"], + filters: false, + }, + { + field: "symbolLayer", + optional: true, + values: ["Measure"], + filters: false, + }, ], interactiveFilters: [], }, diff --git a/app/charts/index.ts b/app/charts/index.ts index 50ff440d1..b014e8a6c 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -273,15 +273,7 @@ export const getInitialConfig = ({ }, symbolLayer: { show: false, - componentIri: measures[0].iri, - }, - // FIXME: unused fields - x: { componentIri: dimensions[0].iri }, - y: { - componentIri: measures[0].iri, - }, - segment: { - componentIri: dimensions[0].iri, + componentIri: "", }, }, settings: { diff --git a/app/charts/map/chart-map-prototype.tsx b/app/charts/map/chart-map-prototype.tsx index a3a92c685..e59ed163c 100644 --- a/app/charts/map/chart-map-prototype.tsx +++ b/app/charts/map/chart-map-prototype.tsx @@ -51,7 +51,10 @@ export const ChartMapVisualization = ({ variables: { locale, iri: dataSetIri, - measures: [chartConfig.fields.y.componentIri], // FIXME: Other fields may also be measures + measures: [ + chartConfig.fields.areaLayer.componentIri, + chartConfig.fields.symbolLayer?.componentIri, + ], // FIXME: Other fields may also be measures filters: queryFilters, }, }); @@ -275,9 +278,6 @@ export const ChartMapPrototype = ({ show: activeLayers["symbolLayer"], componentIri: symbolMeasure, }, - x: { componentIri: "a" }, - y: { componentIri: "b" }, - segment: { componentIri: "c" }, }} dimensions={dimensions} measures={measures} diff --git a/app/charts/map/map-state.tsx b/app/charts/map/map-state.tsx index e70e890ea..fd18506e5 100644 --- a/app/charts/map/map-state.tsx +++ b/app/charts/map/map-state.tsx @@ -63,8 +63,8 @@ export interface MapState { | ScaleThreshold; }; symbolLayer: { - symbolMeasureLabel: string; showSymbolLayer: boolean; + symbolMeasureLabel: string; radiusScale: ScalePower; getRadius: (d: Observation) => number | null; symbolColorScale: (x: number) => string; diff --git a/app/configurator/components/chart-configurator.tsx b/app/configurator/components/chart-configurator.tsx index 9a26c2a3c..811c024b3 100644 --- a/app/configurator/components/chart-configurator.tsx +++ b/app/configurator/components/chart-configurator.tsx @@ -1,8 +1,10 @@ import { Trans } from "@lingui/macro"; import * as React from "react"; +import { Box } from "theme-ui"; import { ChartConfig, ConfiguratorStateConfiguringChart } from ".."; -import { chartConfigOptionsUISpec } from "../../charts/chart-config-ui-options"; import { getFieldComponentIris } from "../../charts"; +import { chartConfigOptionsUISpec } from "../../charts/chart-config-ui-options"; +import { Loading } from "../../components/hint"; import { useDataCubeMetadataWithComponentValuesQuery } from "../../graphql/query-hooks"; import { DataCubeMetadata } from "../../graphql/types"; import { useLocale } from "../../locales/use-locale"; @@ -12,12 +14,10 @@ import { SectionTitle, } from "./chart-controls/section"; import { - DataFilterSelect, ControlTabField, + DataFilterSelect, DataFilterSelectTime, } from "./field"; -import { Loading } from "../../components/hint"; -import { Box } from "theme-ui"; export const ChartConfigurator = ({ state, @@ -127,11 +127,10 @@ const ChartFields = ({ chartConfig: ChartConfig; metaData: DataCubeMetadata; }) => { + const { chartType } = chartConfig; const { dimensions, measures } = metaData; - const components = [...dimensions, ...measures]; - const { chartType } = chartConfig; return ( <> {chartConfigOptionsUISpec[chartType].encodings.map((encoding) => { @@ -143,8 +142,7 @@ const ChartFields = ({ component={components.find( (d) => d.iri === - chartConfig.fields[encodingField as "y" | "segment"] - ?.componentIri + (chartConfig.fields as any)[encodingField]?.componentIri )} value={encoding.field} labelId={`${chartConfig.chartType}.${encoding.field}`} diff --git a/app/configurator/components/chart-options-selector.tsx b/app/configurator/components/chart-options-selector.tsx index f645ca204..73ca1ef42 100644 --- a/app/configurator/components/chart-options-selector.tsx +++ b/app/configurator/components/chart-options-selector.tsx @@ -175,6 +175,14 @@ const EncodingOptionsPanel = ({ const getFieldLabelHint = { x: t({ id: "controls.select.dimension", message: "Select a dimension" }), y: t({ id: "controls.select.measure", message: "Select a measure" }), + areaLayer: t({ + id: "controls.select.measure", + message: "Select a measure", + }), + symbolLayer: t({ + id: "controls.select.measure", + message: "Select a measure", + }), segment: t({ id: "controls.select.dimension", message: "Select a dimension", @@ -189,13 +197,11 @@ const EncodingOptionsPanel = ({ }, [field]); const { fields } = state.chartConfig; - - type AnyField = "y"; const otherFields = Object.keys(fields).filter( - (f) => fields[f as AnyField].hasOwnProperty("componentIri") && field !== f + (f) => (fields as any)[f].hasOwnProperty("componentIri") && field !== f ); const otherFieldsIris = otherFields.map( - (f) => fields[f as AnyField].componentIri + (f) => (fields as any)[f].componentIri ); return ( diff --git a/app/configurator/components/ui-helpers.ts b/app/configurator/components/ui-helpers.ts index 2aa05bf5b..24db782c3 100644 --- a/app/configurator/components/ui-helpers.ts +++ b/app/configurator/components/ui-helpers.ts @@ -400,6 +400,14 @@ const fieldLabels = { id: "controls.map.settings", message: "Base layers", }), + "controls.map.layer.area": defineMessage({ + id: "controls.map.layer.area", + message: "Area layer", + }), + "controls.map.layer.symbol": defineMessage({ + id: "controls.map.layer.symbol", + message: "Symbol layer", + }), "controls.sorting.sortBy": defineMessage({ id: "controls.sorting.sortBy", message: "Sort by", @@ -532,6 +540,10 @@ export function getFieldLabel(field: string): string { return i18n._(fieldLabels["controls.color"]); case "map.settings": return i18n._(fieldLabels["controls.map.settings"]); + case "map.areaLayer": + return i18n._(fieldLabels["controls.map.layer.area"]); + case "map.symbolLayer": + return i18n._(fieldLabels["controls.map.layer.symbol"]); case "title": return i18n._(fieldLabels["controls.title"]); case "description": diff --git a/app/configurator/config-types.ts b/app/configurator/config-types.ts index 46c10b24e..10f9630a7 100644 --- a/app/configurator/config-types.ts +++ b/app/configurator/config-types.ts @@ -466,11 +466,8 @@ const MapSettings = t.type({ }); export type MapSettings = t.TypeOf; const MapFields = t.type({ - x: GenericField, - y: GenericField, areaLayer: MapAreaLayer, symbolLayer: MapSymbolLayer, - segment: GenericField, }); const MapConfig = t.type( diff --git a/app/configurator/configurator-state.tsx b/app/configurator/configurator-state.tsx index 24755a6b1..1de510561 100644 --- a/app/configurator/configurator-state.tsx +++ b/app/configurator/configurator-state.tsx @@ -10,7 +10,7 @@ import { } from "react"; import { Client, useClient } from "urql"; import { Reducer, useImmerReducer } from "use-immer"; -import { ImputationType, isAreaConfig, isColumnConfig } from "."; +import { ImputationType, isAreaConfig, isColumnConfig, isMapConfig } from "."; import { fetchChartConfig, saveChartConfig } from "../api"; import { getFieldComponentIris, @@ -547,7 +547,10 @@ const reducer: Reducer = ( ]; if (!f) { // The field was not defined before - if (action.value.field === "segment") { + if ( + action.value.field === "segment" && + !isMapConfig(draft.chartConfig) // maps don't use segments + ) { const component = action.value.dataSetMetadata.dimensions.find( (dim) => dim.iri === action.value.componentIri ); From 04855f6fb80f8ff5b59a6e25ef0af3fcd0811986 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 23 Nov 2021 17:14:41 +0100 Subject: [PATCH 05/68] refactor: Minor code cleaning --- app/charts/chart-config-ui-options.ts | 2 +- app/charts/index.ts | 2 +- app/charts/map/chart-map-prototype.tsx | 2 +- app/charts/map/map-state.tsx | 24 +++++---------------- app/charts/map/map.tsx | 28 ++++++++++++++----------- app/charts/shared/chart-helpers.tsx | 22 ++++++++++++------- app/configurator/config-types.ts | 4 ++-- app/configurator/configurator-state.tsx | 28 ++++++++++++------------- 8 files changed, 55 insertions(+), 57 deletions(-) diff --git a/app/charts/chart-config-ui-options.ts b/app/charts/chart-config-ui-options.ts index 2fd7d58cd..83986273b 100644 --- a/app/charts/chart-config-ui-options.ts +++ b/app/charts/chart-config-ui-options.ts @@ -235,7 +235,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = { { field: "settings", optional: true, - values: ["GeoDimension"], // FIXME: currently not used anywhere + values: ["Attribute"], // FIXME: currently not used anywhere filters: false, }, { diff --git a/app/charts/index.ts b/app/charts/index.ts index b014e8a6c..b35c1edd9 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -272,8 +272,8 @@ export const getInitialConfig = ({ paletteType: "continuous", }, symbolLayer: { - show: false, componentIri: "", + show: false, }, }, settings: { diff --git a/app/charts/map/chart-map-prototype.tsx b/app/charts/map/chart-map-prototype.tsx index e59ed163c..f73c203b8 100644 --- a/app/charts/map/chart-map-prototype.tsx +++ b/app/charts/map/chart-map-prototype.tsx @@ -54,7 +54,7 @@ export const ChartMapVisualization = ({ measures: [ chartConfig.fields.areaLayer.componentIri, chartConfig.fields.symbolLayer?.componentIri, - ], // FIXME: Other fields may also be measures + ], filters: queryFilters, }, }); diff --git a/app/charts/map/map-state.tsx b/app/charts/map/map-state.tsx index fd18506e5..21f12bf64 100644 --- a/app/charts/map/map-state.tsx +++ b/app/charts/map/map-state.tsx @@ -26,6 +26,7 @@ import { PaletteType, } from "../../configurator/config-types"; import { Observation } from "../../domain/data"; +import { useOptionalNumericVariable } from "../shared/chart-helpers"; import { ChartContext, ChartProps } from "../shared/use-chart-state"; import { InteractionProvider } from "../shared/use-interaction"; import { Bounds, Observer, useWidth } from "../shared/use-width"; @@ -137,30 +138,13 @@ const useMapState = ({ const width = useWidth(); const { palette, nbClass, paletteType } = fields["areaLayer"]; - const getValue = useCallback( - (d: Observation): number | null => { - const v = d[fields.areaLayer.componentIri]; - return v !== null && v !== "..." // FIXME: && v !== "..." -> only used for the prototype and mock data - ? +v - : null; - }, - [fields.areaLayer.componentIri] - ); - + const getValue = useOptionalNumericVariable(fields.areaLayer.componentIri); const getFeatureLabel = useCallback( (d: Observation | undefined): string => d ? `${d[fields.areaLayer.label.componentIri]}` : "", [fields.areaLayer.label.componentIri] ); - const getRadius = useCallback( - (d: Observation): number | null => { - const v = d[fields.symbolLayer.componentIri]; - return v !== null && v !== "..." // FIXME: && v !== "..." -> only used for the prototype and mock data - ? +v - : null; - }, - [fields.symbolLayer.componentIri] - ); + const getRadius = useOptionalNumericVariable(fields.symbolLayer.componentIri); const areaMeasureLabel = measures @@ -188,8 +172,10 @@ const useMapState = ({ if (v === undefined) { return [0, 0, 0]; } + const c = colorScale && colorScale(v); const rgb = c && color(`${c}`)?.rgb(); + return rgb ? [rgb.r, rgb.g, rgb.b] : [0, 0, 0]; }; diff --git a/app/charts/map/map.tsx b/app/charts/map/map.tsx index db184d371..3ef3b272f 100644 --- a/app/charts/map/map.tsx +++ b/app/charts/map/map.tsx @@ -5,7 +5,6 @@ import { BitmapLayer, GeoJsonLayer, ScatterplotLayer } from "@deck.gl/layers"; import DeckGL from "@deck.gl/react"; import React, { useCallback, useState } from "react"; import { Box, Button } from "theme-ui"; -import { Observation } from "../../domain/data"; import { Icon, IconName } from "../../icons"; import { useChartState } from "../shared/use-chart-state"; import { useInteraction } from "../shared/use-interaction"; @@ -19,6 +18,7 @@ type TileData = { bbox: { west: number; north: number; east: number; south: number }; signal: { aborted: boolean }; }; + const INITIAL_VIEW_STATE = { latitude: 46.8182, longitude: 8.2275, @@ -113,6 +113,7 @@ export const MapComponent = () => { }, [setViewState] ); + const zoomIn = () => { const newViewState = { ...viewState, @@ -120,6 +121,7 @@ export const MapComponent = () => { }; setViewState(constrainZoom(newViewState, CH_BBOX)); }; + const zoomOut = () => { const newViewState = { ...viewState, @@ -142,8 +144,8 @@ export const MapComponent = () => { flexDirection: "column", }} > - - + + { }} /> )} + {/* { extruded={false} autoHighlight={true} getFillColor={(d: $FixMe) => { - const obs = data.find((x: Observation) => x.id === d.id); + const obs = data.find((x) => x.id === d.id); + return obs ? getColor(getValue(obs) ?? undefined) : [204, 204, 204, 100]; @@ -206,7 +210,7 @@ export const MapComponent = () => { interaction: { visible: true, mouse: { x, y }, - d: data.find((x: Observation) => x.id === object.id), + d: data.find((x) => x.id === object.id), }, }, }); @@ -225,7 +229,8 @@ export const MapComponent = () => { fillPatternAtlas="/static/sprite/sprite.png" fillPatternMapping="/static/sprite/pattern.json" getFillPattern={(d: $FixMe) => { - const obs = data.find((x: Observation) => x.id === d.id); + const obs = data.find((x) => x.id === d.id); + return obs && getValue(obs) === null ? "hatch" : "fill"; }} getFillPatternScale={150} @@ -277,11 +282,12 @@ export const MapComponent = () => { lineWidthMinPixels={1} getPosition={(d: $FixMe) => d.coordinates} getRadius={(d: $FixMe) => { - const obs = data.find((x: Observation) => x.id === d.id); + const obs = data.find((x) => x.id === d.id); + return obs ? radiusScale(getRadius(obs) ?? 0) : 0; }} - getFillColor={(d: $FixMe) => [0, 102, 153]} - getLineColor={(d: $FixMe) => [255, 255, 255]} + getFillColor={[0, 102, 153]} + getLineColor={[255, 255, 255]} onHover={({ x, y, object }: HoverObject) => { if (object && object.id) { dispatch({ @@ -290,7 +296,7 @@ export const MapComponent = () => { interaction: { visible: true, mouse: { x, y }, - d: data.find((x: Observation) => x.id === object.id), + d: data.find((x) => x.id === object.id), }, }, }); @@ -309,11 +315,9 @@ export const MapComponent = () => { }; const ZoomButton = ({ - label, iconName, handleClick, }: { - label: string; iconName: IconName; handleClick: () => void; }) => ( diff --git a/app/charts/shared/chart-helpers.tsx b/app/charts/shared/chart-helpers.tsx index 0b3bc8136..53377c8c6 100644 --- a/app/charts/shared/chart-helpers.tsx +++ b/app/charts/shared/chart-helpers.tsx @@ -171,15 +171,18 @@ export const useTemporalVariable = ( return getVariable; }; +const getSegment = + (segmentKey: string | undefined) => + (d: Observation): string => + segmentKey ? `${d[segmentKey]}` : "segment"; + export const useSegment = ( segmentKey: string | undefined ): ((d: Observation) => string) => { - const getSegment = useCallback( - (d: Observation): string => (segmentKey ? `${d[segmentKey]}` : "segment"), + return useCallback( + (d: Observation) => getSegment(segmentKey)(d), [segmentKey] ); - - return getSegment; }; // Stacking helpers. @@ -344,17 +347,22 @@ export const useImputationNeeded = ({ chartConfig: ChartConfig; data?: Array; }): boolean => { - const getSegment = useSegment(chartConfig.fields.segment?.componentIri); const imputationNeeded = useMemo(() => { if (isAreaConfig(chartConfig) && data) { return checkForMissingValuesInSegments( group(data, (d) => d[chartConfig.fields.x.componentIri] as string), - [...new Set(data.map((d) => getSegment(d)))] + [ + ...new Set( + data.map((d) => + getSegment(chartConfig.fields.segment?.componentIri)(d) + ) + ), + ] ); } else { return false; } - }, [chartConfig, data, getSegment]); + }, [chartConfig, data]); return imputationNeeded; }; diff --git a/app/configurator/config-types.ts b/app/configurator/config-types.ts index 10f9630a7..fc766677c 100644 --- a/app/configurator/config-types.ts +++ b/app/configurator/config-types.ts @@ -445,9 +445,9 @@ const PaletteType = t.union([ export type PaletteType = t.TypeOf; const MapAreaLayer = t.type({ + componentIri: t.string, show: t.boolean, label: GenericField, - componentIri: t.string, palette: t.string, paletteType: PaletteType, nbClass: t.number, @@ -455,8 +455,8 @@ const MapAreaLayer = t.type({ export type MapAreaLayer = t.TypeOf; const MapSymbolLayer = t.type({ - show: t.boolean, componentIri: t.string, + show: t.boolean, }); export type MapSymbolLayer = t.TypeOf; diff --git a/app/configurator/configurator-state.tsx b/app/configurator/configurator-state.tsx index 1de510561..1d9abc2ef 100644 --- a/app/configurator/configurator-state.tsx +++ b/app/configurator/configurator-state.tsx @@ -547,10 +547,7 @@ const reducer: Reducer = ( ]; if (!f) { // The field was not defined before - if ( - action.value.field === "segment" && - !isMapConfig(draft.chartConfig) // maps don't use segments - ) { + if (action.value.field === "segment") { const component = action.value.dataSetMetadata.dimensions.find( (dim) => dim.iri === action.value.componentIri ); @@ -564,16 +561,19 @@ const reducer: Reducer = ( // (no "stacked" for scatterplots for instance) // Filter for table to make TS happy :/ // if (draft.chartConfig.chartType !== "table") { - draft.chartConfig.fields.segment = { - componentIri: action.value.componentIri, - palette: "category10", - type: "stacked", - sorting: { - sortingType: "byDimensionLabel", - sortingOrder: "asc", - }, - colorMapping: colorMapping, - }; + if (!isMapConfig(draft.chartConfig)) { + // maps don't use segments + draft.chartConfig.fields.segment = { + componentIri: action.value.componentIri, + palette: "category10", + type: "stacked", + sorting: { + sortingType: "byDimensionLabel", + sortingOrder: "asc", + }, + colorMapping: colorMapping, + }; + } // Remove this component from the interactive filter, if it is there if (draft.chartConfig.interactiveFiltersConfig) { From d6cae967d2db53c68c33b30c9368dbd1dcf79e07 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Wed, 24 Nov 2021 16:24:38 +0100 Subject: [PATCH 06/68] feat: Add geoShapes fetching to RDF and GraphQL --- app/domain/data.ts | 4 +- app/domain/env.ts | 6 +- app/graphql/queries/data-cubes.graphql | 3 + app/graphql/query-hooks.ts | 8 +- app/graphql/resolver-types.ts | 12 +++ app/graphql/resolvers.ts | 3 + app/graphql/schema.graphql | 2 + app/graphql/shared-types.ts | 5 +- app/package.json | 1 + app/rdf/namespace.ts | 5 +- app/rdf/parse.ts | 17 ++-- app/rdf/query-geoshapes.ts | 62 +++++++++++++ codegen.yml | 1 + yarn.lock | 116 ++++++++++++++++++------- 14 files changed, 196 insertions(+), 49 deletions(-) create mode 100644 app/rdf/query-geoshapes.ts diff --git a/app/domain/data.ts b/app/domain/data.ts index 3d4d77565..5dc98fcc6 100644 --- a/app/domain/data.ts +++ b/app/domain/data.ts @@ -1,6 +1,6 @@ import { Literal, NamedNode } from "rdf-js"; -import { DimensionMetaDataFragment } from "../graphql/query-hooks"; import { DimensionType } from "../charts/chart-config-ui-options"; +import { DimensionMetaDataFragment } from "../graphql/query-hooks"; export type RawObservationValue = Literal | NamedNode; @@ -12,6 +12,8 @@ export type DimensionValue = { value: string | number; label: string }; export type Observation = Record; +export type GeoShape = { iri: string; wktString: string }; + const xmlSchema = "http://www.w3.org/2001/XMLSchema#"; const parseRDFLiteral = (value: Literal): ObservationValue => { const v = value.value; diff --git a/app/domain/env.ts b/app/domain/env.ts index 2ee42c799..e4f0e7afe 100644 --- a/app/domain/env.ts +++ b/app/domain/env.ts @@ -20,10 +20,8 @@ export const PUBLIC_URL = ( "" ).replace(/\/$/, ""); -export const SPARQL_ENDPOINT = - clientEnv?.SPARQL_ENDPOINT ?? - process.env.SPARQL_ENDPOINT ?? - "https://int.lindas.admin.ch/query"; +export const SPARQL_ENDPOINT = "https://int.lindas.admin.ch/query"; +export const SPARQL_GEO_ENDPOINT = "https://ld.geo.admin.ch/query"; export const SPARQL_EDITOR = clientEnv?.SPARQL_EDITOR ?? process.env.SPARQL_EDITOR; diff --git a/app/graphql/queries/data-cubes.graphql b/app/graphql/queries/data-cubes.graphql index 620b2fe7f..3788efc6e 100644 --- a/app/graphql/queries/data-cubes.graphql +++ b/app/graphql/queries/data-cubes.graphql @@ -42,6 +42,9 @@ fragment dimensionMetaData on Dimension { timeUnit timeFormat } + ... on GeoDimension { + geoShapes + } } query DataCubePreview($iri: String!, $locale: String!, $latest: Boolean) { diff --git a/app/graphql/query-hooks.ts b/app/graphql/query-hooks.ts index 361e4a53a..0ce038bf7 100644 --- a/app/graphql/query-hooks.ts +++ b/app/graphql/query-hooks.ts @@ -14,6 +14,7 @@ export type Scalars = { Float: number; DimensionValue: any; Filters: any; + GeoShape: any; Observation: any; RawObservation: any; }; @@ -112,8 +113,10 @@ export type GeoDimension = Dimension & { scaleType?: Maybe; isKeyDimension: Scalars['Boolean']; values: Array; + geoShapes: Array; }; + export type Measure = Dimension & { __typename: 'Measure'; iri: Scalars['String']; @@ -240,7 +243,7 @@ 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_GeoDimension_Fragment = { __typename: 'GeoDimension', iri: string, label: string, isKeyDimension: boolean, values: Array, unit?: Maybe }; +type DimensionMetaData_GeoDimension_Fragment = { __typename: 'GeoDimension', geoShapes: Array, 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 }; @@ -434,6 +437,9 @@ export const DimensionMetaDataFragmentDoc = gql` timeUnit timeFormat } + ... on GeoDimension { + geoShapes + } } `; export const DataCubesDocument = gql` diff --git a/app/graphql/resolver-types.ts b/app/graphql/resolver-types.ts index 4f162ce82..d8b18e3b1 100644 --- a/app/graphql/resolver-types.ts +++ b/app/graphql/resolver-types.ts @@ -1,5 +1,6 @@ import { DimensionValue } from '../domain/data'; import { Filters } from '../configurator'; +import { GeoShape } from '../domain/data'; import { Observation } from '../domain/data'; import { RawObservation } from '../domain/data'; import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'; @@ -19,6 +20,7 @@ export type Scalars = { Float: number; DimensionValue: DimensionValue; Filters: Filters; + GeoShape: GeoShape; Observation: Observation; RawObservation: RawObservation; }; @@ -117,8 +119,10 @@ export type GeoDimension = Dimension & { scaleType?: Maybe; isKeyDimension: Scalars['Boolean']; values: Array; + geoShapes: Array; }; + export type Measure = Dimension & { __typename?: 'Measure'; iri: Scalars['String']; @@ -316,6 +320,7 @@ export type ResolversTypes = ResolversObject<{ DimensionValue: ResolverTypeWrapper; Filters: ResolverTypeWrapper; GeoDimension: ResolverTypeWrapper; + GeoShape: ResolverTypeWrapper; Measure: ResolverTypeWrapper; NominalDimension: ResolverTypeWrapper; Observation: ResolverTypeWrapper; @@ -343,6 +348,7 @@ export type ResolversParentTypes = ResolversObject<{ DimensionValue: Scalars['DimensionValue']; Filters: Scalars['Filters']; GeoDimension: ResolvedDimension; + GeoShape: Scalars['GeoShape']; Measure: ResolvedMeasure; NominalDimension: ResolvedDimension; Observation: Scalars['Observation']; @@ -425,9 +431,14 @@ export type GeoDimensionResolvers, ParentType, ContextType>; isKeyDimension?: Resolver; values?: Resolver, ParentType, ContextType>; + geoShapes?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }>; +export interface GeoShapeScalarConfig extends GraphQLScalarTypeConfig { + name: 'GeoShape'; +} + export type MeasureResolvers = ResolversObject<{ iri?: Resolver; label?: Resolver; @@ -505,6 +516,7 @@ export type Resolvers = ResolversObject<{ DimensionValue?: GraphQLScalarType; Filters?: GraphQLScalarType; GeoDimension?: GeoDimensionResolvers; + GeoShape?: GraphQLScalarType; Measure?: MeasureResolvers; NominalDimension?: NominalDimensionResolvers; Observation?: GraphQLScalarType; diff --git a/app/graphql/resolvers.ts b/app/graphql/resolvers.ts index 196f6b1ee..5fb68ec7c 100644 --- a/app/graphql/resolvers.ts +++ b/app/graphql/resolvers.ts @@ -18,6 +18,7 @@ import { queryDatasetCountBySubTheme, queryDatasetCountByTheme, } from "../rdf/query-cube-metadata"; +import { loadGeoShapes } from "../rdf/query-geoshapes"; import truthy from "../utils/truthy"; import { DataCubeResolvers, @@ -309,6 +310,8 @@ export const resolvers: Resolvers = { }, GeoDimension: { ...dimensionResolvers, + geoShapes: async (dimension: ResolvedDimension) => + await loadGeoShapes({ dimension: dimension.dimension }), }, Measure: { ...dimensionResolvers, diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index be0ea6a99..e6498ef54 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -2,6 +2,7 @@ scalar Observation scalar DimensionValue scalar RawObservation scalar Filters +scalar GeoShape type ObservationsQuery { "Observations with their values parsed to native JS types" @@ -59,6 +60,7 @@ type GeoDimension implements Dimension { scaleType: String isKeyDimension: Boolean! values: [DimensionValue!]! + geoShapes: [GeoShape!]! } type NominalDimension implements Dimension { diff --git a/app/graphql/shared-types.ts b/app/graphql/shared-types.ts index 39fe71c1a..66e53989d 100644 --- a/app/graphql/shared-types.ts +++ b/app/graphql/shared-types.ts @@ -1,10 +1,10 @@ import { Cube, CubeDimension } from "rdf-cube-view-query"; import { Literal, NamedNode } from "rdf-js"; -import { Observation } from "../domain/data"; +import { GeoShape, Observation } from "../domain/data"; import { - DataCubeTheme, DataCubeOrganization, DataCubePublicationStatus, + DataCubeTheme, TimeUnit, } from "./resolver-types"; @@ -52,6 +52,7 @@ export type ResolvedDimension = { timeUnit?: TimeUnit; timeFormat?: string; scaleType?: "Nominal" | "Ordinal" | "Ratio" | "Interval"; + geoShapes?: GeoShape[]; name: string; }; diff --git a/app/package.json b/app/package.json index 78ba77bfb..376238758 100644 --- a/app/package.json +++ b/app/package.json @@ -74,6 +74,7 @@ "react-window": "^1.8.6", "simple-statistics": "^7.6.0", "sparql-http-client": "^2.2.2", + "terraformer-wkt-parser": "^1.2.1", "theme-ui": "^0.10.0", "topojson-client": "^3.1.0", "urql": "^2.0.4", diff --git a/app/rdf/namespace.ts b/app/rdf/namespace.ts index b2e7b7601..f83f80bf1 100644 --- a/app/rdf/namespace.ts +++ b/app/rdf/namespace.ts @@ -3,14 +3,15 @@ import namespace from "@rdfjs/namespace"; export { dcat, dcterms, + geo, qudt, rdf, rdfs, schema, - vcard, + sh, time, + vcard, xsd, - sh, } from "@tpluscode/rdf-ns-builders"; export const classifications = namespace( diff --git a/app/rdf/parse.ts b/app/rdf/parse.ts index 34ffec677..54454b1e1 100644 --- a/app/rdf/parse.ts +++ b/app/rdf/parse.ts @@ -181,13 +181,16 @@ export const parseCubeDimension = ({ hasUndefinedValues, dataType: dataType?.value, name: dim.out(ns.schema.name, outOpts).value ?? dim.path?.value!, - dataKind: dataKindTerm?.equals(ns.time.GeneralDateTimeDescription) - ? "Time" - : dataKindTerm?.equals(ns.schema.GeoCoordinates) - ? "GeoCoordinates" - : dataKindTerm?.equals(ns.schema.GeoShape) - ? "GeoShape" - : undefined, + dataKind: + dim.path?.value! === "https://environment.ld.admin.ch/foen/nfi/prodreg" + ? "GeoShape" + : dataKindTerm?.equals(ns.time.GeneralDateTimeDescription) + ? "Time" + : dataKindTerm?.equals(ns.schema.GeoCoordinates) + ? "GeoCoordinates" + : dataKindTerm?.equals(ns.schema.GeoShape) + ? "GeoShape" + : undefined, timeUnit: timeUnits.get(timeUnitTerm?.value ?? ""), timeFormat: timeFormats.get(dataType?.value ?? ""), scaleType: scaleTypeTerm?.equals(ns.qudt.NominalScale) diff --git a/app/rdf/query-geoshapes.ts b/app/rdf/query-geoshapes.ts new file mode 100644 index 000000000..4789958cc --- /dev/null +++ b/app/rdf/query-geoshapes.ts @@ -0,0 +1,62 @@ +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 { GeoShape } from "../domain/data"; +import { SPARQL_GEO_ENDPOINT } from "../domain/env"; +import * as ns from "./namespace"; +import { sparqlClient } from "./sparql-client"; + +const BATCH_SIZE = 500; + +/** + * Load WKT coords for a list of IDs (e.g. dimension values) + * + * @param dimensionIri geoDimension IRI + * @param client SparqlClient + */ +export async function loadGeoShapes({ + dimension, + client = sparqlClient, +}: { + dimension: CubeDimension; + client?: ParsingClient; +}): Promise { + 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 ?WKT`.WHERE` + values ?geoShapeIri { + ${values} + } + + ?geoShapeIri ${ns.geo.hasGeometry} ?geometry . + + SERVICE <${SPARQL_GEO_ENDPOINT}> { + ?geometry ${ns.geo.asWKT} ?WKT + } + `; + + 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, + wktString: d.WKT.value, + })); + }) + ); + + return results.flat(); + } else { + return []; + } +} diff --git a/codegen.yml b/codegen.yml index d979aad78..07481bbdb 100644 --- a/codegen.yml +++ b/codegen.yml @@ -25,6 +25,7 @@ generates: DimensionValue: "../domain/data#DimensionValue" RawObservation: "../domain/data#RawObservation" Filters: "../configurator#Filters" + GeoShape: "../domain/data#GeoShape" mappers: DataCube: "./shared-types#ResolvedDataCube" ObservationsQuery: "./shared-types#ResolvedObservationsQuery" diff --git a/yarn.lock b/yarn.lock index 918835a50..75cd09cb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -103,7 +103,29 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.0.tgz#ea269d7f78deb3a7826c39a4048eecda541ebdaa" integrity sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew== -"@babel/core@7.12.9", "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.12.9", "@babel/core@^7.14.6", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.7.7": +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.12.9", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.7.7": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4" integrity sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ== @@ -133,7 +155,7 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.16.0": +"@babel/generator@^7.12.5", "@babel/generator@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" integrity sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew== @@ -255,6 +277,20 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5" + integrity sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA== + dependencies: + "@babel/helper-module-imports" "^7.16.0" + "@babel/helper-replace-supers" "^7.16.0" + "@babel/helper-simple-access" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + "@babel/helper-validator-identifier" "^7.15.7" + "@babel/template" "^7.16.0" + "@babel/traverse" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/helper-module-transforms@^7.14.5": version "7.15.8" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz#d8c0e75a87a52e374a8f25f855174786a09498b2" @@ -269,20 +305,6 @@ "@babel/traverse" "^7.15.4" "@babel/types" "^7.15.6" -"@babel/helper-module-transforms@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5" - integrity sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA== - dependencies: - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-replace-supers" "^7.16.0" - "@babel/helper-simple-access" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.0" - "@babel/types" "^7.16.0" - "@babel/helper-optimise-call-expression@^7.14.5", "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" @@ -372,7 +394,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== -"@babel/helpers@^7.16.0": +"@babel/helpers@^7.12.5", "@babel/helpers@^7.16.0": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" integrity sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w== @@ -399,7 +421,12 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@7.12.16", "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.13", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.16.0", "@babel/parser@^7.16.3", "@babel/parser@^7.7.2": +"@babel/parser@7.12.16": + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.16.tgz#cc31257419d2c3189d394081635703f549fc1ed4" + integrity sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw== + +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.13", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.16.0", "@babel/parser@^7.16.3", "@babel/parser@^7.7.2": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== @@ -721,6 +748,15 @@ resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.14.6.tgz#9070bd3cc2bb997d42e14bdf3b0d24a11b00242b" integrity sha512-oAoSp82jhJFnXKybKTOj5QF04XxiDRyiiqrFToiU1udlBXuZoADlPmmnOcuqBrZxSNNUjzJIVK8vt838Qoqjxg== +"@babel/template@^7.12.7", "@babel/template@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" + integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/parser" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/template@^7.15.4", "@babel/template@^7.3.3": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -730,15 +766,6 @@ "@babel/parser" "^7.15.4" "@babel/types" "^7.15.4" -"@babel/template@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" - integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/parser" "^7.16.0" - "@babel/types" "^7.16.0" - "@babel/traverse@7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" @@ -769,7 +796,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3": +"@babel/traverse@^7.12.9", "@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== @@ -809,7 +836,7 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@babel/types@^7.16.0": +"@babel/types@^7.12.7", "@babel/types@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== @@ -3771,6 +3798,16 @@ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad" integrity sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ== +"@types/geojson@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.6.tgz#3e02972728c69248c2af08d60a48cbb8680fffdf" + integrity sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w== + +"@types/geojson@^7946.0.0 || ^1.0.0": + version "7946.0.8" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca" + integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA== + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -7856,7 +7893,7 @@ fwd-stream@^1.0.4: dependencies: readable-stream "~1.0.26-4" -gensync@^1.0.0-beta.2: +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== @@ -12486,7 +12523,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: +resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -12719,7 +12756,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -13567,6 +13604,21 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" +terraformer-wkt-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/terraformer-wkt-parser/-/terraformer-wkt-parser-1.2.1.tgz#8041e2aeb0c9f2b4cbbec8ec2c5c00c45ddfee02" + integrity sha512-+CJyNLWb3lJ9RsZMTM66BY0MT3yIo4l4l22Jd9CrZuwzk54fsu4Sc7zejuS9fCITTuTQy3p06d4MZMVI7v5wSg== + dependencies: + "@types/geojson" "^1.0.0" + terraformer "~1.0.5" + +terraformer@~1.0.5: + version "1.0.12" + resolved "https://registry.yarnpkg.com/terraformer/-/terraformer-1.0.12.tgz#39e08f9c753606421acce02e122440c72dfa12d3" + integrity sha512-MokUp0+MFal4CmJDVL6VAO1bKegeXcBM2RnPVfqcFIp2IIv8EbPAjG0j/vEy/vuKB8NVMMSF2vfpVS/QLe4DBg== + optionalDependencies: + "@types/geojson" "^7946.0.0 || ^1.0.0" + terser@^5.0.0, terser@^5.2.1: version "5.7.0" resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.0.tgz#a761eeec206bc87b605ab13029876ead938ae693" From f1e89d83f5ee50ec7683e1120189f846548a1af4 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Wed, 24 Nov 2021 17:13:59 +0100 Subject: [PATCH 07/68] feat; Add initial custom shapes drawing --- app/charts/map/chart-map-prototype.tsx | 188 +++++---------------- app/charts/map/map-state.tsx | 1 + app/charts/map/map.tsx | 17 ++ app/configurator/map/map-chart-options.tsx | 8 + 4 files changed, 70 insertions(+), 144 deletions(-) diff --git a/app/charts/map/chart-map-prototype.tsx b/app/charts/map/chart-map-prototype.tsx index f73c203b8..a307db4cd 100644 --- a/app/charts/map/chart-map-prototype.tsx +++ b/app/charts/map/chart-map-prototype.tsx @@ -1,5 +1,6 @@ import { geoCentroid } from "d3"; import React, { memo, useEffect, useMemo, useState } from "react"; +import WKT from "terraformer-wkt-parser"; import { Box } from "theme-ui"; import { feature as topojsonFeature, @@ -16,6 +17,7 @@ import { import { Observation } from "../../domain/data"; import { DimensionMetaDataFragment, + GeoDimension, useDataCubeObservationsQuery, } from "../../graphql/query-hooks"; import { useLocale } from "../../locales/use-locale"; @@ -45,7 +47,6 @@ export const ChartMapVisualization = ({ queryFilters: QueryFilters; }) => { const [geoData, setGeoData] = useState({ state: "fetching" }); - const locale = useLocale(); const [{ data, fetching, error }] = useDataCubeObservationsQuery({ variables: { @@ -59,6 +60,25 @@ export const ChartMapVisualization = ({ }, }); + const geoDimension = data?.dataCubeByIri?.dimensions.find( + (d) => d.__typename === "GeoDimension" + ) as GeoDimension | undefined; + + const areaLayer = useMemo(() => { + if (geoDimension) { + return { + type: "FeatureCollection", + features: geoDimension.geoShapes.map((d) => ({ + type: "Feature", + properties: { + iri: d.iri, + }, + geometry: WKT.parse(d.wktString), + })), + } as GeoJSON.FeatureCollection; + } + }, [geoDimension]); + useEffect(() => { const loadGeoData = async () => { try { @@ -96,7 +116,8 @@ export const ChartMapVisualization = ({ return ( - {/* - - - - Layers - - setActiveControl(v)} - iconName="mapMaptype" - upperLabel={""} - lowerLabel={t({ - id: "chart.map.layers.base", - message: "Base Layer", - })} - checked={activeControl === "baseLayer"} - disabled={false} - /> - setActiveControl(v)} - iconName="mapRegions" - upperLabel={""} - lowerLabel={t({ - id: "chart.map.layers.area", - message: "Area Layer", - })} - checked={activeControl === "areaLayer"} - disabled={false} - /> - setActiveControl(v)} - iconName="mapSymbols" - upperLabel={""} - lowerLabel={t({ - id: "chart.map.layers.symbol", - message: "Symbol Layer", - })} - checked={activeControl === "symbolLayer"} - disabled={false} - /> - - - - - - Data Filters - - - {dimensions.map((dim) => ( - - ({ - value: m.iri, - label: m.label.split("_")[1], - }))} - onChange={(e) => setMeasure(e.currentTarget.value)} - /> - - - - - - - - - - - { - setPaletteType(e.currentTarget.value as PaletteType); - }} - /> - - - setPaletteType(e.currentTarget.value as PaletteType) - } - /> - - setPaletteType(e.currentTarget.value as PaletteType) - } - /> - - setPaletteType(e.currentTarget.value as PaletteType) - } - /> - - - - - ); - } else if (activeControl === "symbolLayer") { - return ( - <> - - - updateActiveLayers("symbolLayer")} - /> - - - - -