Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#8755 Multivariable charts #9655

Merged
merged 3 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web/client/actions/widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const DEPENDENCY_SELECTOR_KEY = "dependencySelector";
export const WIDGETS_REGEX = /^widgets\["?([^"\]]*)"?\]\.?(.*)$/;
export const MAPS_REGEX = /^maps\["?([^"\]]*)"?\]\.?(.*)$/;
export const CHARTS_REGEX = /^charts\["?([^"\]]*)"?\]\.?(.*)$/;
export const TRACES_REGEX = /^traces\["?([^"\]]*)"?\]\.?(.*)$/;
export const WIDGETS_MAPS_REGEX = /^widgets\["?([^"\]]*)"?\]\.maps\["?([^"\]]*)"?\]\.?(.*)$/;

export const TOGGLE_COLLAPSE = "WIDGET:TOGGLE_COLLAPSE";
Expand Down
120 changes: 62 additions & 58 deletions web/client/api/GeoJSONClassification.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@
import uniq from 'lodash/uniq';
import isNil from 'lodash/isNil';
import chroma from 'chroma-js';
import { defaultClassificationColors } from './SLDService';
import { getChromaScaleByName } from '../utils/ClassificationUtils';

const getSimpleStatistics = () => import('simple-statistics').then(mod => mod);
export const getSimpleStatistics = () => import('simple-statistics').then(mod => mod);

const getColorClasses = ({ ramp, intervals, reverse }) => {
const scale = defaultClassificationColors[ramp] || ramp;
const scale = getChromaScaleByName(ramp);
const colorClasses = chroma.scale(scale).colors(intervals);
return reverse ? [...colorClasses].reverse() : colorClasses;
};
/**
* Classify an array of features with quantile method
* @param {object} mod simple statistic library
* @param {object} features array of GeoJSON features
* @param {object} params parameters to compute the classification
* @param {string} params.attribute the name of the attribute to use for classification
Expand All @@ -28,26 +29,26 @@ const getColorClasses = ({ ramp, intervals, reverse }) => {
* @param {boolean} params.reverse reverse the ramp color classification
* @returns {promise} return classification object
*/
const quantile = (features, params) => getSimpleStatistics().then(({ quantileSorted }) => {
const quantile = ({ quantileSorted }, features, params) => {
const values = features.map(feature => feature?.properties?.[params.attribute]).filter(value => !isNil(value)).sort((a, b) => a - b);
if (values.length === 0) {
return [];
}
const intervals = params.intervals;
const classes = [...[...new Array(intervals).keys()].map((n) => n / intervals), 1].map((p) => quantileSorted(values, p));
const colors = getColorClasses({ ...params, intervals });
return {
data: {
classification: classes.reduce((acc, min, idx) => {
const max = classes[idx + 1];
if (max !== undefined) {
const color = colors[idx];
return [ ...acc, { color, min, max }];
}
return acc;
}, [])
return classes.reduce((acc, min, idx) => {
const max = classes[idx + 1];
if (max !== undefined) {
const color = colors[idx];
return [ ...acc, { color, min, max }];
}
};
});
return acc;
}, []);
};
/**
* Classify an array of features with jenks method
* @param {object} mod simple statistic library
* @param {object} features array of GeoJSON features
* @param {object} params parameters to compute the classification
* @param {string} params.attribute the name of the attribute to use for classification
Expand All @@ -56,27 +57,24 @@ const quantile = (features, params) => getSimpleStatistics().then(({ quantileSor
* @param {boolean} params.reverse reverse the ramp color classification
* @returns {promise} return classification object
*/
const jenks = (features, params) => getSimpleStatistics().then(({ jenks: jenksMethod }) => {
const jenks = ({ jenks: jenksMethod }, features, params) => {
const values = features.map(feature => feature?.properties?.[params.attribute]).filter(value => !isNil(value)).sort((a, b) => a - b);
const paramIntervals = params.intervals;
const intervals = paramIntervals > values.length ? values.length : paramIntervals;
const classes = jenksMethod(values, intervals);
const colors = getColorClasses({ ...params, intervals });
return {
data: {
classification: classes.reduce((acc, min, idx) => {
const max = classes[idx + 1];
if (max !== undefined) {
const color = colors[idx];
return [ ...acc, { color, min, max }];
}
return acc;
}, [])
return classes.reduce((acc, min, idx) => {
const max = classes[idx + 1];
if (max !== undefined) {
const color = colors[idx];
return [ ...acc, { color, min, max }];
}
};
});
return acc;
}, []);
};
/**
* Classify an array of features with equal interval method
* @param {object} mod simple statistic library
* @param {object} features array of GeoJSON features
* @param {object} params parameters to compute the classification
* @param {string} params.attribute the name of the attribute to use for classification
Expand All @@ -85,44 +83,39 @@ const jenks = (features, params) => getSimpleStatistics().then(({ jenks: jenksMe
* @param {boolean} params.reverse reverse the ramp color classification
* @returns {promise} return classification object
*/
const equalInterval = (features, params) => getSimpleStatistics().then(({ equalIntervalBreaks }) => {
const equalInterval = ({ equalIntervalBreaks }, features, params) => {
const values = features.map(feature => feature?.properties?.[params.attribute]).filter(value => !isNil(value)).sort((a, b) => a - b);
const classes = equalIntervalBreaks(values, params.intervals);
const colors = getColorClasses(params);
return {
data: {
classification: classes.reduce((acc, min, idx) => {
const max = classes[idx + 1];
if (max !== undefined) {
const color = colors[idx];
return [ ...acc, { color, min, max }];
}
return acc;
}, [])
return classes.reduce((acc, min, idx) => {
const max = classes[idx + 1];
if (max !== undefined) {
const color = colors[idx];
return [ ...acc, { color, min, max }];
}
};
});
return acc;
}, []);
};
/**
* Classify an array of features with unique interval method
* @param {object} mod simple statistic library
* @param {object} features array of GeoJSON features
* @param {object} params parameters to compute the classification
* @param {string} params.attribute the name of the attribute to use for classification
* @param {string} params.ramp the identifier of the color ramp
* @param {boolean} params.reverse reverse the ramp color classification
* @param {boolean} params.sort if true sort the value
* @returns {promise} return classification object
*/
const uniqueInterval = (features, params) => {
const classes = uniq(features.map(feature => feature?.properties?.[params.attribute])).sort((a, b) => a > b ? 1 : -1);
const colors = getColorClasses({ ...params, intervals: classes.length });
return Promise.resolve({
data: {
classification: classes.map((value, idx) => {
return {
color: colors[idx],
unique: value
};
})
}
const uniqueInterval = (mod, features, params) => {
const classes = uniq(features.map(feature => feature?.properties?.[params.attribute]));
const sortedClasses = params.sort === false ? classes : classes.sort((a, b) => a > b ? 1 : -1);
const colors = getColorClasses({ ...params, intervals: sortedClasses.length });
return sortedClasses.map((value, idx) => {
return {
color: colors[idx],
unique: value
};
});
};

Expand All @@ -132,6 +125,16 @@ const methods = {
equalInterval,
uniqueInterval
};

export const createClassifyGeoJSONSync = (mod) => {
return (geojson, params) => {
const features = geojson.type === 'FeatureCollection'
? geojson.features
: [];
return methods[params.method](mod, features, params);
};
};

/**
* Classify a GeoJSON feature collection
* @param {object} geojson a GeoJSON feature collection
Expand All @@ -144,10 +147,11 @@ const methods = {
* @returns {promise} return classification object
*/
export const classifyGeoJSON = (geojson, params) => {
const features = geojson.type === 'FeatureCollection'
? geojson.features
: [];
return methods[params.method](features, params);
return getSimpleStatistics()
.then((mod) => createClassifyGeoJSONSync(mod)(geojson, params))
.then((classification) => ({
data: { classification }
}));
};

export const availableMethods = Object.keys(methods);
25 changes: 2 additions & 23 deletions web/client/api/SLDService.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,11 @@
import { urlParts } from '../utils/URLUtils';

import url from 'url';
import { sortBy, head, castArray, isNumber, isString, uniq } from 'lodash';
import { sortBy, head, castArray, isNumber, isString } from 'lodash';
import assign from 'object-assign';
import chroma from 'chroma-js';
import { getLayerUrl } from '../utils/LayersUtils';

const supportedColorBrewer = uniq(Object.keys(chroma.brewer).map((key) => key.toLocaleLowerCase()))
.map((key) => ({
name: key,
colors: key
}));
import { standardClassificationScales as standardColors } from '../utils/ClassificationUtils';

const isAttributeAllowed = (type) => ['Integer', 'Long', 'Double', 'Float', 'BigDecimal'].indexOf(type) !== -1;
const getSimpleType = () => {
Expand Down Expand Up @@ -50,22 +45,6 @@ const getCustomClassification = (classification) => {
return {};
};

export const defaultClassificationColors = {
red: ['#000', '#f00'],
green: ['#000', '#008000', '#0f0'],
blue: ['#000', '#00f'],
gray: ['#333', '#eee'],
jet: ['#00f', '#ff0', '#f00']
};

const standardColors = [
...Object.keys(defaultClassificationColors).map(name => ({
name,
colors: defaultClassificationColors[name]
})),
...supportedColorBrewer
];

const getColor = (layer, name, intervals, customRamp) => {
const chosenColors = layer
? head((layer.thematic.colors || layer.thematic.additionalColors || [])
Expand Down
4 changes: 2 additions & 2 deletions web/client/api/__tests__/SLDService-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ describe('Test correctness of the SLDService APIs', () => {
});
it('check getColors only standard', () => {
const result = API.getColors(undefined, layer, 10);
expect(result.length).toBe(5 + 36 /* 36 color brewer ramps */);
expect(result.length).toBe(6 + 36 /* 36 color brewer ramps */);
expect(result[0].colors).toExist();
expect(result[0].colors.length).toBe(10);
});
Expand All @@ -452,7 +452,7 @@ describe('Test correctness of the SLDService APIs', () => {
});
it('check getColors layer with additional colors', () => {
const result = API.getColors(undefined, layerWithAdditionalColors, 10);
expect(result.length).toBe(6 + 36 /* 36 color brewer ramps */);
expect(result.length).toBe(7 + 36 /* 36 color brewer ramps */);
expect(result[0].colors).toExist();
expect(result[0].colors.length).toBe(10);
});
Expand Down
Loading