mirror of
https://github.com/apache/superset.git
synced 2026-04-19 16:14:52 +00:00
fix(deckgl): polygon elevation fixed value (#35266)
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import buildQuery, { DeckPolygonFormData } from './buildQuery';
|
||||
|
||||
describe('Polygon buildQuery', () => {
|
||||
const baseFormData: DeckPolygonFormData = {
|
||||
datasource: '1__table',
|
||||
viz_type: 'deck_polygon',
|
||||
line_column: 'polygon_geom',
|
||||
};
|
||||
|
||||
test('should require line_column', () => {
|
||||
const formDataWithoutLineColumn = {
|
||||
...baseFormData,
|
||||
line_column: undefined,
|
||||
};
|
||||
|
||||
expect(() => buildQuery(formDataWithoutLineColumn)).toThrow(
|
||||
'Polygon column is required for Polygon charts',
|
||||
);
|
||||
});
|
||||
|
||||
test('should build basic query with minimal data', () => {
|
||||
const queryContext = buildQuery(baseFormData);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.columns).toEqual(['polygon_geom']);
|
||||
expect(query.metrics).toEqual([]);
|
||||
expect(query.is_timeseries).toBe(false);
|
||||
expect(query.filters).toEqual([
|
||||
{
|
||||
col: 'polygon_geom',
|
||||
op: 'IS NOT NULL',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should include metric in query when provided', () => {
|
||||
const formDataWithMetric = {
|
||||
...baseFormData,
|
||||
metric: 'population',
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithMetric);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual(['population']);
|
||||
expect(query.filters).toContainEqual({
|
||||
col: 'population',
|
||||
op: 'IS NOT NULL',
|
||||
});
|
||||
});
|
||||
|
||||
describe('point_radius_fixed legacy structure', () => {
|
||||
test('should not add metrics to query when value is simple string', () => {
|
||||
const formDataWithFixValue = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
value: '1000',
|
||||
},
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithFixValue);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should not add metrics to query when point_radius_fixed.value is undefined', () => {
|
||||
const formDataWithEmptyValue = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
value: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithEmptyValue);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should not add metrics to query when point_radius_fixed is undefined', () => {
|
||||
const formDataWithoutFixedRadius = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: undefined,
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithoutFixedRadius);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('point_radius_fixed "fix" type', () => {
|
||||
test('should not add metrics to query when point_radius_fixed type is "fix"', () => {
|
||||
const formDataWithFixType = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: '1000',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithFixType);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should not add metrics to query when point_radius_fixed type is "fix" with zero value', () => {
|
||||
const formDataWithZeroFix = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: '0',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithZeroFix);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should not add metrics to query when point_radius_fixed type is "fix" with decimal value', () => {
|
||||
const formDataWithDecimalFix = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: '500.5',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithDecimalFix);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('point_radius_fixed "metric" type', () => {
|
||||
test('should add metric object to query when point_radius_fixed type is "metric"', () => {
|
||||
const metricObject = {
|
||||
expressionType: 'SQL',
|
||||
sqlExpression: 'SUM(population)/SUM(area)',
|
||||
column: null,
|
||||
aggregate: null,
|
||||
datasourceWarning: false,
|
||||
hasCustomLabel: false,
|
||||
label: 'SUM(population)/SUM(area)',
|
||||
optionName: 'metric_c5rvwrzoo86_293h6yrv2ic',
|
||||
};
|
||||
|
||||
const formDataWithMetricType = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: metricObject,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithMetricType);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([metricObject]);
|
||||
});
|
||||
|
||||
test('should add simple column metric to query when point_radius_fixed type is "metric"', () => {
|
||||
const simpleMetricObject = {
|
||||
expressionType: 'simple',
|
||||
column: {
|
||||
column_name: 'avg_elevation',
|
||||
type: 'NUMERIC',
|
||||
},
|
||||
aggregate: 'avg',
|
||||
label: 'AVG(avg_elevation)',
|
||||
};
|
||||
|
||||
const formDataWithSimpleMetric = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: simpleMetricObject,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithSimpleMetric);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([simpleMetricObject]);
|
||||
});
|
||||
|
||||
test('should include both regular metric and point_radius_fixed metric in query when both are specified', () => {
|
||||
const metricObject = {
|
||||
expressionType: 'simple',
|
||||
column: { column_name: 'elevation' },
|
||||
aggregate: 'sum',
|
||||
label: 'SUM(elevation)',
|
||||
};
|
||||
|
||||
const formDataWithBothMetrics = {
|
||||
...baseFormData,
|
||||
metric: 'population',
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: metricObject,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithBothMetrics);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual(['population', metricObject]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases and error handling', () => {
|
||||
test('should not add metrics to query when point_radius_fixed is null', () => {
|
||||
const formDataWithNull = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: null,
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithNull);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should handle null metric values gracefully', () => {
|
||||
const formDataWithNullMetric = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: null,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithNullMetric);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should handle undefined metric values gracefully', () => {
|
||||
const formDataWithUndefinedMetric = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: undefined,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithUndefinedMetric);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should not add metrics to query when point_radius_fixed is empty object', () => {
|
||||
const formDataWithEmptyObject = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {},
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithEmptyObject);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should not add metrics to query when point_radius_fixed has unsupported type', () => {
|
||||
const formDataWithUnsupportedType = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'unsupported_type',
|
||||
value: 'some_value',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithUnsupportedType);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should not add metrics to query when point_radius_fixed has missing type field', () => {
|
||||
const formDataWithMissingType = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
value: 'some_value',
|
||||
},
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithMissingType);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Integration with other form data fields', () => {
|
||||
test('should include js_columns in query columns', () => {
|
||||
const formDataWithJsColumns = {
|
||||
...baseFormData,
|
||||
js_columns: ['custom_col1', 'custom_col2'],
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithJsColumns);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.columns).toEqual([
|
||||
'polygon_geom',
|
||||
'custom_col1',
|
||||
'custom_col2',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should include tooltip_contents columns in query', () => {
|
||||
const formDataWithTooltips = {
|
||||
...baseFormData,
|
||||
tooltip_contents: [
|
||||
{ item_type: 'column', column_name: 'tooltip_col' },
|
||||
'another_tooltip_col',
|
||||
],
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithTooltips);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.columns).toContain('tooltip_col');
|
||||
expect(query.columns).toContain('another_tooltip_col');
|
||||
});
|
||||
|
||||
test('should not add null filters when filter_nulls is false', () => {
|
||||
const formDataWithoutNullFilters = {
|
||||
...baseFormData,
|
||||
filter_nulls: false,
|
||||
metric: 'population',
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithoutNullFilters);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.filters).toEqual([]);
|
||||
});
|
||||
|
||||
test('should build comprehensive query when multiple form data fields are specified', () => {
|
||||
const complexFormData = {
|
||||
...baseFormData,
|
||||
metric: 'population',
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: {
|
||||
expressionType: 'simple',
|
||||
column: { column_name: 'elevation' },
|
||||
aggregate: 'avg',
|
||||
label: 'AVG(elevation)',
|
||||
},
|
||||
},
|
||||
js_columns: ['custom_prop'],
|
||||
tooltip_contents: [
|
||||
{ item_type: 'column', column_name: 'tooltip_info' },
|
||||
],
|
||||
filter_nulls: true,
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(complexFormData);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.columns).toContain('polygon_geom');
|
||||
expect(query.columns).toContain('custom_prop');
|
||||
expect(query.columns).toContain('tooltip_info');
|
||||
expect(query.metrics).toContain('population');
|
||||
expect(query.metrics).toContain(complexFormData.point_radius_fixed.value);
|
||||
expect(query.filters).toContainEqual({
|
||||
col: 'polygon_geom',
|
||||
op: 'IS NOT NULL',
|
||||
});
|
||||
expect(query.filters).toContainEqual({
|
||||
col: 'population',
|
||||
op: 'IS NOT NULL',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Current implementation behavior', () => {
|
||||
test('should not add fixed values to metrics for legacy point_radius_fixed structure', () => {
|
||||
const formDataWithFix = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
value: '1000',
|
||||
},
|
||||
};
|
||||
|
||||
const queryContext = buildQuery(formDataWithFix);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
|
||||
test('should add metric objects to query when point_radius_fixed type is "metric"', () => {
|
||||
const metricObject = {
|
||||
expressionType: 'SQL',
|
||||
sqlExpression: 'AVG(elevation)',
|
||||
label: 'AVG(elevation)',
|
||||
};
|
||||
|
||||
const formDataWithMetricObject = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: metricObject,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithMetricObject);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toContain(metricObject);
|
||||
});
|
||||
|
||||
test('should respect type information when processing point_radius_fixed', () => {
|
||||
const formDataWithTypeInfo = {
|
||||
...baseFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: '500',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const queryContext = buildQuery(formDataWithTypeInfo);
|
||||
const [query] = queryContext.queries;
|
||||
|
||||
expect(query.metrics).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
QueryObjectFilterClause,
|
||||
QueryObject,
|
||||
QueryFormColumn,
|
||||
QueryFormMetric,
|
||||
} from '@superset-ui/core';
|
||||
import { addTooltipColumnsToQuery } from '../buildQueryUtils';
|
||||
|
||||
@@ -31,9 +32,18 @@ export interface DeckPolygonFormData extends SqlaFormData {
|
||||
line_column?: string;
|
||||
line_type?: string;
|
||||
metric?: string;
|
||||
point_radius_fixed?: {
|
||||
value?: string;
|
||||
};
|
||||
point_radius_fixed?:
|
||||
| {
|
||||
value?: string;
|
||||
}
|
||||
| {
|
||||
type: 'fix';
|
||||
value: string;
|
||||
}
|
||||
| {
|
||||
type: 'metric';
|
||||
value: QueryFormMetric;
|
||||
};
|
||||
reverse_long_lat?: boolean;
|
||||
filter_nulls?: boolean;
|
||||
js_columns?: string[];
|
||||
@@ -74,8 +84,16 @@ export default function buildQuery(formData: DeckPolygonFormData) {
|
||||
if (metric) {
|
||||
metrics.push(metric);
|
||||
}
|
||||
if (point_radius_fixed?.value) {
|
||||
metrics.push(point_radius_fixed.value);
|
||||
|
||||
if (point_radius_fixed) {
|
||||
if ('type' in point_radius_fixed) {
|
||||
if (
|
||||
point_radius_fixed.type === 'metric' &&
|
||||
point_radius_fixed.value != null
|
||||
) {
|
||||
metrics.push(point_radius_fixed.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const filters = ensureIsArray(baseQueryObject.filters || []);
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { ChartProps } from '@superset-ui/core';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
interface PolygonFeature {
|
||||
polygon?: number[][];
|
||||
elevation?: number;
|
||||
extraProps?: Record<string, unknown>;
|
||||
metrics?: Record<string, number | string>;
|
||||
}
|
||||
|
||||
jest.mock('../spatialUtils', () => ({
|
||||
...jest.requireActual('../spatialUtils'),
|
||||
getMapboxApiKey: jest.fn(() => 'mock-mapbox-key'),
|
||||
}));
|
||||
|
||||
describe('Polygon transformProps', () => {
|
||||
const mockChartProps: Partial<ChartProps> = {
|
||||
rawFormData: {
|
||||
line_column: 'geom',
|
||||
line_type: 'json',
|
||||
viewport: {},
|
||||
},
|
||||
queriesData: [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
geom: JSON.stringify([
|
||||
[-122.4, 37.8],
|
||||
[-122.3, 37.8],
|
||||
[-122.3, 37.9],
|
||||
[-122.4, 37.9],
|
||||
]),
|
||||
'AVG(elevation)': 150.5,
|
||||
population: 50000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
datasource: { type: 'table' as const, id: 1 },
|
||||
height: 400,
|
||||
width: 600,
|
||||
hooks: {},
|
||||
filterState: {},
|
||||
emitCrossFilters: false,
|
||||
};
|
||||
|
||||
test('should use constant elevation value when point_radius_fixed type is "fix"', () => {
|
||||
const fixProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: '1000',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(fixProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBe(1000);
|
||||
});
|
||||
|
||||
test('should use database metric value for elevation when point_radius_fixed type is "metric"', () => {
|
||||
const metricProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: {
|
||||
expressionType: 'SQL',
|
||||
sqlExpression: 'AVG(elevation)',
|
||||
label: 'AVG(elevation)',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(metricProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBe(150.5);
|
||||
});
|
||||
|
||||
test('should use constant elevation value when point_radius_fixed has legacy structure', () => {
|
||||
const legacyProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
value: '750',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(legacyProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBe(750);
|
||||
});
|
||||
|
||||
test('should not set elevation when point_radius_fixed is not specified', () => {
|
||||
const noElevationProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(noElevationProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should use decimal constant elevation value when point_radius_fixed type is "fix"', () => {
|
||||
const decimalFixProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: '500.75',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(decimalFixProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBe(500.75);
|
||||
});
|
||||
|
||||
test('should handle invalid numeric strings gracefully', () => {
|
||||
const invalidNumericProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: 'not-a-number',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(invalidNumericProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should handle empty string elevation values gracefully', () => {
|
||||
const emptyStringProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'fix',
|
||||
value: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(emptyStringProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should handle null metric elevation values gracefully', () => {
|
||||
const nullMetricProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
type: 'metric',
|
||||
value: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(nullMetricProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should handle invalid JSON in polygon data gracefully', () => {
|
||||
const invalidJsonProps = {
|
||||
...mockChartProps,
|
||||
queriesData: [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
geom: 'invalid-json-string',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = transformProps(invalidJsonProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should handle legacy point_radius_fixed with invalid value gracefully', () => {
|
||||
const legacyInvalidProps = {
|
||||
...mockChartProps,
|
||||
rawFormData: {
|
||||
...mockChartProps.rawFormData,
|
||||
point_radius_fixed: {
|
||||
value: 'invalid-number',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = transformProps(legacyInvalidProps as ChartProps);
|
||||
|
||||
const features = result.payload.data.features as PolygonFeature[];
|
||||
expect(features).toHaveLength(1);
|
||||
expect(features[0]?.elevation).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ChartProps } from '@superset-ui/core';
|
||||
import { ChartProps, getMetricLabel } from '@superset-ui/core';
|
||||
import { addJsColumnsToExtraProps, DataRecord } from '../spatialUtils';
|
||||
import {
|
||||
createBaseTransformResult,
|
||||
@@ -27,6 +27,11 @@ import {
|
||||
} from '../transformUtils';
|
||||
import { DeckPolygonFormData } from './buildQuery';
|
||||
|
||||
function parseElevationValue(value: string): number | undefined {
|
||||
const parsed = parseFloat(value);
|
||||
return Number.isNaN(parsed) ? undefined : parsed;
|
||||
}
|
||||
|
||||
interface PolygonFeature {
|
||||
polygon?: number[][];
|
||||
name?: string;
|
||||
@@ -53,7 +58,28 @@ function processPolygonData(
|
||||
}
|
||||
|
||||
const metricLabel = getMetricLabelFromFormData(metric);
|
||||
const elevationLabel = getMetricLabelFromFormData(point_radius_fixed);
|
||||
|
||||
let elevationLabel: string | undefined;
|
||||
let fixedElevationValue: number | undefined;
|
||||
|
||||
if (point_radius_fixed) {
|
||||
if ('type' in point_radius_fixed) {
|
||||
if (
|
||||
point_radius_fixed.type === 'metric' &&
|
||||
point_radius_fixed.value != null
|
||||
) {
|
||||
elevationLabel = getMetricLabel(point_radius_fixed.value);
|
||||
} else if (
|
||||
point_radius_fixed.type === 'fix' &&
|
||||
point_radius_fixed.value
|
||||
) {
|
||||
fixedElevationValue = parseElevationValue(point_radius_fixed.value);
|
||||
}
|
||||
} else if (point_radius_fixed.value) {
|
||||
fixedElevationValue = parseElevationValue(point_radius_fixed.value);
|
||||
}
|
||||
}
|
||||
|
||||
const excludeKeys = new Set([line_column, ...(js_columns || [])]);
|
||||
|
||||
return records
|
||||
@@ -109,7 +135,9 @@ function processPolygonData(
|
||||
|
||||
feature.polygon = polygonCoords;
|
||||
|
||||
if (elevationLabel && record[elevationLabel] != null) {
|
||||
if (fixedElevationValue !== undefined) {
|
||||
feature.elevation = fixedElevationValue;
|
||||
} else if (elevationLabel && record[elevationLabel] != null) {
|
||||
const elevationValue = parseMetricValue(record[elevationLabel]);
|
||||
if (elevationValue !== undefined) {
|
||||
feature.elevation = elevationValue;
|
||||
|
||||
Reference in New Issue
Block a user