terriajs
Version:
Geospatial data visualization platform.
926 lines • 108 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import i18next from "i18next";
import { action, computed, makeObservable, observable, ObservableMap, reaction, runInAction } from "mobx";
import filterOutUndefined from "../../Core/filterOutUndefined";
import isDefined from "../../Core/isDefined";
import TerriaError from "../../Core/TerriaError";
import ConstantColorMap from "../../Map/ColorMap/ConstantColorMap";
import ContinuousColorMap from "../../Map/ColorMap/ContinuousColorMap";
import DiscreteColorMap from "../../Map/ColorMap/DiscreteColorMap";
import EnumColorMap from "../../Map/ColorMap/EnumColorMap";
import { allIcons, getMakiIcon } from "../../Map/Icons/Maki/MakiIcons";
import ProtomapsImageryProvider from "../../Map/ImageryProvider/ProtomapsImageryProvider";
import { getName } from "../../ModelMixins/CatalogMemberMixin";
import { ImageryParts, isDataSource } from "../../ModelMixins/MappableMixin";
import TableMixin from "../../ModelMixins/TableMixin";
import { QualitativeColorSchemeOptionRenderer, QuantitativeColorSchemeOptionRenderer } from "../../ReactViews/SelectableDimensions/ColorSchemeOptionRenderer";
import { MarkerOptionRenderer } from "../../ReactViews/SelectableDimensions/MarkerOptionRenderer";
import Icon from "../../Styled/Icon";
import { DEFAULT_DIVERGING, DEFAULT_QUALITATIVE, DEFAULT_SEQUENTIAL, DIVERGING_SCALES, QUALITATIVE_SCALES, SEQUENTIAL_CONTINUOUS_SCALES, SEQUENTIAL_SCALES } from "../../Table/TableColorMap";
import TableColumnType from "../../Table/TableColumnType";
import CommonStrata from "../Definition/CommonStrata";
import updateModelFromJson from "../Definition/updateModelFromJson";
import ViewingControls from "../ViewingControls";
/** Columns/Styles with the following TableColumnTypes will be hidden unless we are showing "advanced" options */
export const ADVANCED_TABLE_COLUMN_TYPES = [
TableColumnType.latitude,
TableColumnType.longitude,
TableColumnType.region,
TableColumnType.time
];
/** SelectableDimensionWorkflow to set styling options for TableMixin models */
export default class TableStylingWorkflow {
item;
static type = "table-styling";
/** This is used to simplify SelectableDimensions available to the user.
* For example - if equal to `diverging-continuous` - then only Diverging continuous color scales will be presented as options
* See setColorSchemeTypeFromPalette and setColorSchemeType for how this is set. */
colorSchemeType;
styleType = "fill";
/** Which bin is currently open in `binMaximumsSelectableDims` or `enumColorsSelectableDim`.
* This is used in `SelectableDimensionGroup.onToggle` and `SelectableDimensionGroup.isOpen` to make the groups act like an accordion - so only one bin can be edited at any given time.
*/
openBinIndex = new ObservableMap();
activeStyleDisposer;
constructor(item) {
this.item = item;
makeObservable(this);
// We need to reset colorSchemeType every time Table.activeStyle changes
this.activeStyleDisposer = reaction(() => this.item.activeStyle, () => {
// If the active style is of "advanced" TableColumnType, then set colorSchemeType to "no-style"
if (isDefined(this.item.activeTableStyle.colorColumn) &&
ADVANCED_TABLE_COLUMN_TYPES.includes(this.item.activeTableStyle.colorColumn.type)) {
runInAction(() => (this.colorSchemeType = "no-style"));
}
else {
this.setColorSchemeTypeFromPalette();
}
});
this.setColorSchemeTypeFromPalette();
}
onClose() {
this.activeStyleDisposer();
}
get menu() {
return {
options: filterOutUndefined([
{
text: this.showAdvancedOptions
? i18next.t("models.tableStyling.hideAdvancedOptions")
: i18next.t("models.tableStyling.showAdvancedOptions"),
onSelect: action(() => {
this.showAdvancedOptions = !this.showAdvancedOptions;
})
},
this.showAdvancedOptions
? {
text: i18next.t("models.tableStyling.copyUserStratum"),
onSelect: () => {
const stratum = JSON.stringify(this.item.strata.get(CommonStrata.user));
try {
navigator.clipboard.writeText(JSON.stringify(this.item.strata.get(CommonStrata.user)));
}
catch (e) {
TerriaError.from(e).raiseError(this.item.terria, "Failed to copy to clipboard. User stratum has been printed to console");
console.log(stratum);
}
},
disable: !this.showAdvancedOptions
}
: undefined
])
};
}
get name() {
return i18next.t("models.tableStyling.name");
}
get icon() {
return Icon.GLYPHS.layers;
}
get footer() {
return {
buttonText: i18next.t("models.tableStyling.reset"),
/** Delete all user strata values for TableColumnTraits and TableStyleTraits for the current activeStyle */
onClick: action(() => {
this.getTableColumnTraits(CommonStrata.user)?.strata.delete(CommonStrata.user);
this.getTableStyleTraits(CommonStrata.user)?.strata.delete(CommonStrata.user);
this.setColorSchemeTypeFromPalette();
})
};
}
/** This will look at the current `colorMap` and `colorPalette` to guess which `colorSchemeType` is active.
* This is because `TableMixin` doesn't have an explicit `"colorSchemeType"` flag - it will choose the appropriate type based on `TableStyleTraits`
* `colorTraits.colorPalette` is also set here if we are only using `tableColorMap.defaultColorPaletteName`
*/
setColorSchemeTypeFromPalette() {
const colorMap = this.tableStyle.colorMap;
const colorPalette = this.tableStyle.colorTraits.colorPalette;
const defaultColorPalette = this.tableStyle.tableColorMap.defaultColorPaletteName;
const colorPaletteWithDefault = colorPalette ?? defaultColorPalette;
this.colorSchemeType = undefined;
if (colorMap instanceof ConstantColorMap) {
this.colorSchemeType = "no-style";
}
else if (colorMap instanceof ContinuousColorMap) {
if (SEQUENTIAL_SCALES.includes(colorPaletteWithDefault) ||
SEQUENTIAL_CONTINUOUS_SCALES.includes(colorPaletteWithDefault)) {
this.colorSchemeType = "sequential-continuous";
if (!colorPalette) {
this.getTableStyleTraits(CommonStrata.user)?.color.setTrait(CommonStrata.user, "colorPalette", DEFAULT_SEQUENTIAL);
}
}
else if (DIVERGING_SCALES.includes(colorPaletteWithDefault)) {
this.colorSchemeType = "diverging-continuous";
if (!colorPalette) {
this.getTableStyleTraits(CommonStrata.user)?.color.setTrait(CommonStrata.user, "colorPalette", DEFAULT_DIVERGING);
}
}
}
else if (colorMap instanceof DiscreteColorMap) {
if (this.tableStyle.colorTraits.binColors &&
this.tableStyle.colorTraits.binColors.length > 0) {
this.colorSchemeType = "custom-discrete";
}
else if (SEQUENTIAL_SCALES.includes(colorPaletteWithDefault)) {
this.colorSchemeType = "sequential-discrete";
if (!colorPalette) {
this.getTableStyleTraits(CommonStrata.user)?.color.setTrait(CommonStrata.user, "colorPalette", DEFAULT_SEQUENTIAL);
}
}
else if (DIVERGING_SCALES.includes(colorPaletteWithDefault)) {
this.colorSchemeType = "diverging-discrete";
if (!colorPalette) {
this.getTableStyleTraits(CommonStrata.user)?.color.setTrait(CommonStrata.user, "colorPalette", DEFAULT_DIVERGING);
}
}
}
else if (colorMap instanceof EnumColorMap &&
QUALITATIVE_SCALES.includes(colorPaletteWithDefault)) {
if (this.tableStyle.colorTraits.enumColors &&
this.tableStyle.colorTraits.enumColors.length > 0) {
this.colorSchemeType = "custom-qualitative";
}
else {
this.colorSchemeType = "qualitative";
if (!colorPalette) {
this.getTableStyleTraits(CommonStrata.user)?.color.setTrait(CommonStrata.user, "colorPalette", DEFAULT_QUALITATIVE);
}
}
}
}
/** Handle change on colorType - this is called by the "Type" selectable dimension in `this.colorSchemeSelectableDim` */
setColorSchemeType(stratumId, id) {
if (!id)
return;
// Set `activeStyle` trait so the value doesn't change
this.item.setTrait(stratumId, "activeStyle", this.tableStyle.id);
// Hide any open bin
this.openBinIndex.clear();
// Here we use item.activeTableStyle.colorTraits.colorPalette instead of this.colorPalette because we only want this to be defined, if the trait is defined - we don't care about defaultColorPaletteName
const colorPalette = this.tableStyle.colorTraits.colorPalette;
// **Discrete color maps**
// Reset bins
if (id === "sequential-discrete" || id === "diverging-discrete") {
this.colorSchemeType = id;
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "mapType", "bin");
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "binColors", []);
// Set numberOfBins according to limits of sequential and diverging color scales:
// - Sequential is [3,9]
// - Diverging is [3,11]
// If numberOfBins is 0 - set to sensible default (7)
if (this.tableStyle.colorTraits.numberOfBins === 0) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "numberOfBins", 7);
}
else if (id === "sequential-discrete" &&
this.tableStyle.tableColorMap.binColors.length > 9) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "numberOfBins", 9);
}
else if (id === "diverging-discrete" &&
this.tableStyle.tableColorMap.binColors.length > 11) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "numberOfBins", 11);
}
else if (this.tableStyle.tableColorMap.binColors.length < 3) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "numberOfBins", 3);
}
// If no user stratum - reset bin maximums
if (!this.tableStyle.colorTraits.strata.get(stratumId)?.binMaximums) {
this.resetBinMaximums(stratumId);
}
}
// **Continuous color maps**
else if (id === "sequential-continuous" || id === "diverging-continuous") {
this.colorSchemeType = id;
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "mapType", "continuous");
}
// **Qualitative (enum) color maps**
else if (id === "qualitative") {
this.colorSchemeType = id;
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "mapType", "enum");
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "enumColors", []);
}
// **No style (constant) color maps**
else if (id === "no-style") {
this.colorSchemeType = id;
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "mapType", "constant");
}
// If the current colorPalette is incompatible with the selected type - change colorPalette to default for the selected type
if (id === "sequential-continuous" &&
(!colorPalette ||
![...SEQUENTIAL_SCALES, ...SEQUENTIAL_CONTINUOUS_SCALES].includes(colorPalette))) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "colorPalette", DEFAULT_SEQUENTIAL);
}
if (id === "sequential-discrete" &&
(!colorPalette || !SEQUENTIAL_SCALES.includes(colorPalette))) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "colorPalette", DEFAULT_SEQUENTIAL);
}
if ((id === "diverging-continuous" || id === "diverging-discrete") &&
(!colorPalette || !DIVERGING_SCALES.includes(colorPalette))) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "colorPalette", DEFAULT_DIVERGING);
}
if (id === "qualitative" &&
(!colorPalette || !QUALITATIVE_SCALES.includes(colorPalette))) {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "colorPalette", DEFAULT_QUALITATIVE);
}
}
/** Show advances options
* - Show all column types in "Data" select
* - Show "Data type (advanced)" select. This allow user to change column type */
showAdvancedOptions = false;
get hasCesiumDataSource() {
return !!this.item.mapItems.find((d) => isDataSource(d) && d.entities.values.length > 0);
}
get hasProtomapsImageryProvider() {
return !!this.item.mapItems.find((d) => ImageryParts.is(d) &&
d.imageryProvider instanceof ProtomapsImageryProvider);
}
/** Table Style dimensions:
* - Dataset (Table models in workbench)
* - Variable (Table style in model)
* - TableColumn type (advanced only)
*/
get tableStyleSelectableDim() {
// Show point style options if current catalog item has any points showing (or uses ProtomapsImageryProvider)
const showPointStyles = this.hasCesiumDataSource || this.hasProtomapsImageryProvider;
// Show point size options if current catalog item has points and any scalar columns
const showPointSize = this.hasCesiumDataSource &&
(this.tableStyle.pointSizeColumn ||
this.item.tableColumns.find((t) => t.type === TableColumnType.scalar));
// Show label style options if current catalog item has points
const showLabelStyles = this.hasCesiumDataSource;
// Show trail style options if current catalog item has time-series points
const showTrailStyles = this.hasCesiumDataSource && this.tableStyle.isTimeVaryingPointsWithId();
return {
type: "group",
id: "data",
name: i18next.t("models.tableStyling.data.name"),
selectableDimensions: filterOutUndefined([
{
type: "select",
id: "dataset",
name: i18next.t("models.tableStyling.data.selectableDimensions.dataset.name"),
selectedId: this.item.uniqueId,
// Find all workbench items which have TableStylingWorkflow
options: this.item.terria.workbench.items
.filter((item) => ViewingControls.is(item) &&
item.viewingControls.find((control) => control.id === TableStylingWorkflow.type))
.map((item) => ({
id: item.uniqueId,
name: getName(item)
})),
setDimensionValue: (_stratumId, value) => {
const item = this.item.terria.workbench.items.find((i) => i.uniqueId === value);
if (item && TableMixin.isMixedInto(item)) {
// Trigger new TableStylingWorkflow
if (item.viewingControls.find((control) => control.id === TableStylingWorkflow.type)) {
item.terria.selectableDimensionWorkflow =
new TableStylingWorkflow(item);
}
}
}
},
{
type: "select",
id: "table-style",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyle.name"),
selectedId: this.tableStyle.id,
options: this.item.tableStyles.map((style) => ({
id: style.id,
name: style.title
})),
allowCustomInput: true,
setDimensionValue: (stratumId, value) => {
if (!this.item.tableStyles.find((style) => style.id === value)) {
this.getTableStyleTraits(stratumId, value);
}
this.item.setTrait(stratumId, "activeStyle", value);
// Note - the activeStyle reaction in TableStylingWorkflow.constructor handles all side effects
// The reaction will call this.setColorSchemeTypeFromPalette()
}
},
{
type: "select",
id: "table-style-type",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyleType.name"),
selectedId: this.styleType,
options: filterOutUndefined([
{
id: "fill",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyleType.options.fill.name")
},
showPointSize
? {
id: "point-size",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyleType.options.pointSize.name")
}
: undefined,
showPointStyles
? {
id: "point",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyleType.options.point.name")
}
: undefined,
{
id: "outline",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyleType.options.outline.name")
},
showLabelStyles
? {
id: "label",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyleType.options.label.name")
}
: undefined,
showTrailStyles
? {
id: "trail",
name: i18next.t("models.tableStyling.data.selectableDimensions.tableStyleType.options.trail.name")
}
: undefined
]),
setDimensionValue: (_stratumId, value) => {
if (value === "fill" ||
value === "point-size" ||
value === "point" ||
value === "outline" ||
value === "trail" ||
value === "label")
this.styleType = value;
}
}
])
};
}
/** List of color schemes available for given `colorSchemeType` */
get colorSchemesForType() {
const type = this.colorSchemeType;
if (!isDefined(type))
return [];
if (type === "sequential-continuous")
return [...SEQUENTIAL_SCALES, ...SEQUENTIAL_CONTINUOUS_SCALES];
if (type === "sequential-discrete")
return SEQUENTIAL_SCALES;
if (type === "diverging-discrete" || type === "diverging-continuous")
return DIVERGING_SCALES;
if (type === "qualitative")
return QUALITATIVE_SCALES;
return [];
}
/** Color scheme dimensions:
* - Type (see `this.colorSchemeType`)
* - Color scheme (see `this.colorSchemesForType`)
* - Number of bins (for discrete)
*/
get colorSchemeSelectableDim() {
return {
type: "group",
id: "fill",
name: i18next.t("models.tableStyling.fill.name"),
selectableDimensions: filterOutUndefined([
// Show "Variable" selector to pick colorColumn if tableStyle ID is different from colorColumn ID
// OR if we are in advanced mode
this.tableStyle.id !== this.tableStyle.colorColumn?.name ||
this.showAdvancedOptions
? {
type: "select",
id: "table-color-column",
name: i18next.t("models.tableStyling.fill.selectableDimensions.tableColorColumn.name"),
selectedId: this.tableStyle.colorColumn?.name,
options: this.item.tableColumns.map((col) => ({
id: col.name,
name: col.title
})),
setDimensionValue: (stratumId, value) => {
// Make sure `activeStyle` is set, otherwise it may change when we change `colorColumn`
this.item.setTrait(stratumId, "activeStyle", this.tableStyle.id);
const prevColumnType = this.tableStyle.colorColumn?.type;
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "colorColumn", value);
const newColumnType = this.tableStyle.colorColumn?.type;
// Reset color palette and color scheme type if color column type has changed
// For example, if the color column type changes from `scalar` to `enum` - we don't want to still have a `scalar` color palette
if (prevColumnType !== newColumnType) {
this.tableStyle.colorTraits.setTrait(stratumId, "colorPalette", undefined);
this.setColorSchemeTypeFromPalette();
}
}
}
: undefined,
this.showAdvancedOptions
? {
type: "select",
id: "data-type",
name: i18next.t("models.tableStyling.fill.selectableDimensions.dataType.name"),
options: Object.keys(TableColumnType)
.filter((type) => type.length > 1)
.map((colType) => ({ id: colType })),
selectedId: isDefined(this.tableStyle.colorColumn?.type)
? TableColumnType[this.tableStyle.colorColumn.type]
: undefined,
setDimensionValue: (stratumId, id) => {
this.getTableColumnTraits(stratumId)?.setTrait(stratumId, "type", id);
this.setColorSchemeTypeFromPalette();
}
}
: undefined,
this.tableStyle.colorColumn
? {
type: "select",
id: "type",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.name"),
undefinedLabel: i18next.t("models.tableStyling.fill.selectableDimensions.type.undefinedLabel"),
options: filterOutUndefined([
{
id: "no-style",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.noStyle.name")
},
...(this.tableStyle.colorColumn.type === TableColumnType.scalar
? [
{
id: "sequential-continuous",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.sequentialContinuous.name")
},
{
id: "sequential-discrete",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.sequentialDiscrete.name")
},
{
id: "diverging-continuous",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.divergingContinuous.name")
},
{
id: "diverging-discrete",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.divergingDiscrete.name")
}
]
: []),
{
id: "qualitative",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.qualitative.name")
},
// Add options for "custom" color palettes if we are in "custom-qualitative" or "custom-discrete" mode
this.colorSchemeType === "custom-qualitative"
? {
id: "custom-qualitative",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.customQualitative.name")
}
: undefined,
this.colorSchemeType === "custom-discrete"
? {
id: "custom-discrete",
name: i18next.t("models.tableStyling.fill.selectableDimensions.type.options.customDiscrete.name")
}
: undefined
]),
selectedId: this.colorSchemeType,
setDimensionValue: (stratumId, id) => {
this.setColorSchemeType(stratumId, id);
}
}
: undefined,
{
type: "select",
id: "scheme",
name: i18next.t("models.tableStyling.fill.selectableDimensions.scheme.name"),
selectedId: this.tableStyle.colorTraits.colorPalette ??
this.tableStyle.tableColorMap.defaultColorPaletteName,
options: this.colorSchemesForType.map((style) => ({
id: style
})),
optionRenderer: this.colorSchemeType === "qualitative"
? QualitativeColorSchemeOptionRenderer
: QuantitativeColorSchemeOptionRenderer(this.colorSchemeType === "sequential-discrete" ||
this.colorSchemeType === "diverging-discrete"
? this.tableStyle.tableColorMap.binColors.length
: undefined),
setDimensionValue: (stratumId, id) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "colorPalette", id);
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "binColors", []);
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "enumColors", []);
}
},
// Show "Number of Bins" if in discrete mode
this.colorSchemeType === "sequential-discrete" ||
this.colorSchemeType === "diverging-discrete"
? {
type: "numeric",
id: "number-of-bins",
name: i18next.t("models.tableStyling.fill.selectableDimensions.numberOfBins.name"),
allowUndefined: true,
min:
// Sequential and diverging color scales must have at least 3 bins
this.colorSchemeType === "sequential-discrete" ||
this.colorSchemeType === "diverging-discrete"
? 3
: // Custom color scales only need at least 1
1,
max:
// Sequential discrete color scales support up to 9 bins
this.colorSchemeType === "sequential-discrete"
? 9
: // Diverging discrete color scales support up to 11 bins
this.colorSchemeType === "diverging-discrete"
? 11
: // Custom discrete color scales can be any number of bins
undefined,
value: this.tableStyle.colorTraits.numberOfBins,
setDimensionValue: (stratumId, value) => {
if (!isDefined(value))
return;
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "numberOfBins", value);
this.resetBinMaximums(stratumId);
}
}
: undefined
])
};
}
get minimumValueSelectableDim() {
return {
type: "numeric",
id: "min",
name: i18next.t("models.tableStyling.min.name"),
max: this.tableStyle.tableColorMap.maximumValue,
value: this.tableStyle.tableColorMap.minimumValue,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "minimumValue", value);
if (this.colorSchemeType === "sequential-discrete" ||
this.colorSchemeType === "diverging-discrete" ||
this.colorSchemeType === "custom-discrete") {
this.setBinMaximums(stratumId);
}
}
};
}
/** Display range dimensions:
* - Minimum value
* - Maximum value
*/
get displayRangeDim() {
return {
type: "group",
id: "display-range",
name: i18next.t("models.tableStyling.displayRange.name"),
isOpen: false,
selectableDimensions: filterOutUndefined([
this.minimumValueSelectableDim,
{
type: "numeric",
id: "max",
name: i18next.t("models.tableStyling.displayRange.selectableDimensions.max.name"),
min: this.tableStyle.tableColorMap.minimumValue,
value: this.tableStyle.tableColorMap.maximumValue,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "maximumValue", value);
}
},
this.item.outlierFilterDimension
])
};
}
/** Group to show bins with color, start/stop numbers.
*/
get binColorDims() {
return {
type: "group",
id: "bins",
name: i18next.t("models.tableStyling.bins.name"),
isOpen: false,
selectableDimensions: [
...this.tableStyle.tableColorMap.binMaximums
.map((bin, idx) => ({
type: "group",
id: `bin-${idx}-start`,
name: getColorPreview(this.tableStyle.tableColorMap.binColors[idx] ?? "#aaa", i18next.t("models.tableStyling.bins.selectableDimensions.start.name", {
value1: idx === 0
? this.minimumValueSelectableDim.value
: this.tableStyle.tableColorMap.binMaximums[idx - 1],
value2: bin
})),
isOpen: this.openBinIndex.get("fill") === idx,
onToggle: (open) => {
if (open && this.openBinIndex.get("fill") !== idx) {
runInAction(() => this.openBinIndex.set("fill", idx));
return true;
}
},
selectableDimensions: [
{
type: "color",
id: `bin-${idx}-color`,
name: i18next.t("models.tableStyling.bins.selectableDimensions.start.selectableDimensions.color.name"),
value: this.tableStyle.tableColorMap.binColors[idx],
setDimensionValue: (stratumId, value) => {
const binColors = [
...this.tableStyle.tableColorMap.binColors
];
if (isDefined(value))
binColors[idx] = value;
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "binColors", binColors);
this.colorSchemeType = "custom-discrete";
}
},
idx === 0
? this.minimumValueSelectableDim
: {
type: "numeric",
id: `bin-${idx}-start`,
name: i18next.t("models.tableStyling.bins.selectableDimensions.start.selectableDimensions.start.name"),
value: this.tableStyle.tableColorMap.binMaximums[idx - 1],
setDimensionValue: (stratumId, value) => {
const binMaximums = [
...this.tableStyle.tableColorMap.binMaximums
];
if (isDefined(value))
binMaximums[idx - 1] = value;
this.setBinMaximums(stratumId, binMaximums);
}
},
{
type: "numeric",
id: `bin-${idx}-stop`,
name: i18next.t("models.tableStyling.bins.selectableDimensions.start.selectableDimensions.stop.name"),
value: bin,
setDimensionValue: (stratumId, value) => {
const binMaximums = [
...this.tableStyle.tableColorMap.binMaximums
];
if (isDefined(value))
binMaximums[idx] = value;
this.setBinMaximums(stratumId, binMaximums);
}
}
]
}))
.reverse() // Reverse array of bins to match Legend (descending order)
]
};
}
/** Groups to show enum "bins" with colors and value */
get enumColorDims() {
return {
type: "group",
id: "colors",
name: i18next.t("models.tableStyling.colors.name"),
isOpen: false,
selectableDimensions: filterOutUndefined([
...this.tableStyle.tableColorMap.enumColors.map((enumCol, idx) => {
if (!enumCol.value)
return;
const dims = {
type: "group",
id: `enum-${idx}-start`,
name: getColorPreview(enumCol.color ?? "#aaa", enumCol.value),
isOpen: this.openBinIndex.get("fill") === idx,
onToggle: (open) => {
if (open && this.openBinIndex.get("fill") !== idx) {
runInAction(() => this.openBinIndex.set("fill", idx));
return true;
}
},
selectableDimensions: [
{
type: "color",
id: `enum-${idx}-color`,
name: i18next.t("models.tableStyling.colors.selectableDimensions.color.name"),
value: enumCol.color,
setDimensionValue: (stratumId, value) => {
this.colorSchemeType = "custom-qualitative";
this.setEnumColorTrait(stratumId, idx, enumCol.value, value);
}
},
{
type: "select",
id: `enum-${idx}-value`,
name: i18next.t("models.tableStyling.colors.selectableDimensions.value.name"),
selectedId: enumCol.value,
// Find unique column values which don't already have an enumCol
// We prepend the current enumCol.value
options: [
{ id: enumCol.value },
...(this.tableStyle.colorColumn?.uniqueValues.values
?.filter((value) => !this.tableStyle.tableColorMap.enumColors.find((enumCol) => enumCol.value === value))
.map((id) => ({ id })) ?? [])
],
setDimensionValue: (stratumId, value) => {
this.colorSchemeType = "custom-qualitative";
this.setEnumColorTrait(stratumId, idx, value, enumCol.color);
}
},
{
type: "button",
id: `enum-${idx}-remove`,
value: i18next.t("models.tableStyling.colors.selectableDimensions.remove.value"),
setDimensionValue: (stratumId) => {
this.colorSchemeType = "custom-qualitative";
// Remove element by clearing `value`
this.setEnumColorTrait(stratumId, idx, undefined, undefined);
}
}
]
};
return dims;
}),
// Are there more colors to add (are there more unique values in the column than enumCols)
// Create "Add" to user can add more
this.tableStyle.colorColumn &&
this.tableStyle.tableColorMap.enumColors.filter((col) => col.value)
.length < this.tableStyle.colorColumn?.uniqueValues.values.length
? {
type: "button",
id: `enum-add`,
value: i18next.t("models.tableStyling.colors.selectableDimensions.add.value"),
setDimensionValue: (stratumId) => {
this.colorSchemeType = "custom-qualitative";
const firstValue = this.tableStyle.colorColumn?.uniqueValues.values.find((value) => !this.tableStyle.tableColorMap.enumColors.find((col) => col.value === value));
if (!isDefined(firstValue))
return;
// Can we find any unused colors in the colorPalette
const unusedColor = this.tableStyle.tableColorMap
.colorScaleCategorical(this.tableStyle.tableColorMap.enumColors.length + 1)
.find((col) => !this.tableStyle.tableColorMap.enumColors.find((enumColor) => enumColor.color === col));
this.setEnumColorTrait(stratumId, this.tableStyle.tableColorMap.enumColors.length, firstValue, unusedColor ?? "#000000");
this.openBinIndex.set("fill", this.tableStyle.tableColorMap.enumColors.length - 1);
}
}
: undefined
])
};
}
get nullColorDimension() {
return {
type: "color",
id: `null-color`,
name: i18next.t("models.tableStyling.nullColor.name"),
value: this.tableStyle.colorTraits.nullColor,
allowUndefined: true,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "nullColor", value);
}
};
}
get outlierColorDimension() {
return {
type: "color",
id: `outlier-color`,
name: i18next.t("models.tableStyling.outlierColor.name"),
allowUndefined: true,
value: this.tableStyle.colorTraits.outlierColor ??
this.tableStyle.tableColorMap.outlierColor?.toCssHexString(),
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "outlierColor", value);
}
};
}
/** Misc table style color dimensions:
* - Region color
* - Null color
* - Outlier color
*/
get additionalColorDimensions() {
return {
type: "group",
id: "additional-colors",
name: i18next.t("models.tableStyling.additionalColors.name"),
// Open group by default is no activeStyle is selected
isOpen: !this.item.activeStyle || this.colorSchemeType === "no-style",
selectableDimensions: filterOutUndefined([
this.tableStyle.colorColumn?.type === TableColumnType.region
? {
type: "color",
id: `region-color`,
name: i18next.t("models.tableStyling.additionalColors.selectableDimensions.regionColor.name"),
value: this.tableStyle.colorTraits.regionColor,
allowUndefined: true,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "regionColor", value);
}
}
: {
type: "color",
id: `null-color`,
name: i18next.t("models.tableStyling.additionalColors.selectableDimensions.nullColor.name"),
value: this.tableStyle.colorTraits.nullColor,
allowUndefined: true,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "nullColor", value);
}
},
this.tableStyle.colorColumn?.type === TableColumnType.scalar
? {
type: "color",
id: `outlier-color`,
name: i18next.t("models.tableStyling.additionalColors.selectableDimensions.outlierColor.name"),
allowUndefined: true,
value: this.tableStyle.colorTraits.outlierColor ??
this.tableStyle.tableColorMap.outlierColor?.toCssHexString(),
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.color.setTrait(stratumId, "outlierColor", value);
}
}
: undefined
])
};
}
get pointSizeDimensions() {
return {
type: "group",
id: "point-size",
name: i18next.t("models.tableStyling.pointSize.name"),
isOpen: true,
selectableDimensions: [
{
type: "select",
id: `point-size-column`,
name: i18next.t("models.tableStyling.pointSize.selectableDimensions.pointSizeColumn.name"),
selectedId: this.tableStyle.pointSizeTraits.pointSizeColumn,
options: this.item.tableColumns
.filter((col) => col.type === TableColumnType.scalar)
.map((col) => ({
id: col.name,
name: col.title
})),
allowUndefined: true,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.pointSize.setTrait(stratumId, "pointSizeColumn", value);
}
},
...(this.tableStyle.pointSizeColumn
? [
{
type: "numeric",
id: "point-size-null",
name: i18next.t("models.tableStyling.pointSize.selectableDimensions.pointSizeNull.name"),
min: 0,
value: this.tableStyle.pointSizeTraits.nullSize,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.pointSize.setTrait(stratumId, "nullSize", value);
}
},
{
type: "numeric",
id: "point-sizes-factor",
name: i18next.t("models.tableStyling.pointSize.selectableDimensions.pointSizesFactor.name"),
min: 0,
value: this.tableStyle.pointSizeTraits.sizeFactor,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.pointSize.setTrait(stratumId, "sizeFactor", value);
}
},
{
type: "numeric",
id: "point-size-offset",
name: i18next.t("models.tableStyling.pointSize.selectableDimensions.pointSizeOffset.name"),
min: 0,
value: this.tableStyle.pointSizeTraits.sizeOffset,
setDimensionValue: (stratumId, value) => {
this.getTableStyleTraits(stratumId)?.pointSize.setTrait(stratumId, "sizeOffset", value);
}
}
]
: [])
]
};
}
/** Advanced region mapping - pulled from TableMixin.regionColumnDimensions and TableMixin.regionProviderDimensions
*/
get advancedRegionMappingDimensions() {
return {
type: "group",
id: "region-mapping",
name: i18next.t("models.tableStyling.regionMapping.name"),
isOpen: false,
selectableDimensions: filterOutUndefined([
this.item.regionColumnDimensions,
this.item.regionProviderDimensions
])
};
}
/** Advanced table dimensions:
* - Legend (title, ticks, item titles...)
* - Style options (title, lat/lon columns)
* - Time options (column, id columns, spread start/finish time, ...)
* - Workbench options (show style selector, show disable style/time in workbench, ...)
* - Variable/