Skip to content

Commit

Permalink
[feat] Create layer correctly from saved layer config (#2179)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorDykhta committed Apr 4, 2023
1 parent 4c6e99e commit c847573
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/components/src/common/data-table/grid.tsx
Expand Up @@ -36,6 +36,7 @@ export default class GridHack extends PureComponent<GridProps> {
}
return;
};

_updateRef = x => {
if (!this.grid && x) {
this.grid = x;
Expand Down
30 changes: 24 additions & 6 deletions src/reducers/src/vis-state-merger.ts
Expand Up @@ -79,10 +79,29 @@ export function mergeFilters<S extends VisState>(
};
}

export function isSavedLayerConfigV1(layerConfig: any): boolean {
// exported layer configuration contains visualChannels property
return layerConfig.visualChannels;
}

export function parseLayerConfig(layerConfig: any): Layer | undefined {
// @ts-expect-error ParsedLayer vs Layer
return visStateSchema[CURRENT_VERSION].load({
layers: [layerConfig],
// @ts-expect-error layerOrder not in SavedVisState
layerOrder: [0]
}).visState?.layers?.[0];
}

export function createLayerFromConfig(state: VisState, layerConfig: any): Layer | null {
// check if the layer config is parsed
const parsedLayerConfig = isSavedLayerConfigV1(layerConfig)
? parseLayerConfig(layerConfig)
: layerConfig;

// first validate config against dataset
const {validated, failed} = validateLayersByDatasets(state.datasets, state.layerClasses, [
layerConfig
parsedLayerConfig
]);

if (failed?.length || !validated.length) {
Expand All @@ -95,17 +114,16 @@ export function createLayerFromConfig(state: VisState, layerConfig: any): Layer
return newLayer;
}

export function serializeLayer(newLayer): ParsedLayer {
export function serializeLayer(newLayer): ParsedLayer | undefined {
const savedVisState = visStateSchema[CURRENT_VERSION].save(
// @ts-expect-error not all expected properties are provided
// @ts-expect-error consider MinSavedVisState instead of SavedVisState
{
layers: [newLayer],
layerOrder: [0]
}
).visState;
const loadedLayer = visStateSchema[CURRENT_VERSION].load(savedVisState).visState?.layers?.[0];
// @ts-expect-error
return loadedLayer;

return visStateSchema[CURRENT_VERSION].load(savedVisState).visState?.layers?.[0];
}

/**
Expand Down
8 changes: 5 additions & 3 deletions src/reducers/src/vis-state-updaters.ts
Expand Up @@ -469,9 +469,11 @@ export function layerTextLabelChangeUpdater(

function validateExistingLayerWithData(dataset, layerClasses, layer) {
const loadedLayer = serializeLayer(layer);
return validateLayerWithData(dataset, loadedLayer, layerClasses, {
allowEmptyColumn: true
});
return loadedLayer
? validateLayerWithData(dataset, loadedLayer, layerClasses, {
allowEmptyColumn: true
})
: null;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions test/helpers/mock-state.js
Expand Up @@ -94,7 +94,7 @@ export const InitialState = keplerGlReducer(undefined, {});
* Mock app state with uploaded geojson and csv file
* @returns {Immutable} appState
*/
function mockStateWithFileUpload() {
export function mockStateWithFileUpload() {
const initialState = cloneDeep(InitialState);

// load csv and geojson
Expand Down Expand Up @@ -348,7 +348,7 @@ function mockStateWithTripData() {
);
}

function mockStateWithLayerDimensions(state) {
export function mockStateWithLayerDimensions(state) {
const initialState = state || mockStateWithFileUpload();

const layer0 = initialState.visState.layers.find(
Expand Down
27 changes: 25 additions & 2 deletions test/node/reducers/vis-state-merger-test.js
Expand Up @@ -30,13 +30,14 @@ import keplerGlReducer, {
mergeSplitMaps,
insertLayerAtRightOrder,
VIS_STATE_MERGERS,
createLayerFromConfig,
applyMergersUpdater,
visStateReducer,
keplerGlReducerCore as coreReducer,
defaultInteractionConfig
} from '@kepler.gl/reducers';

import SchemaManager from '@kepler.gl/schemas';
import SchemaManager, {CURRENT_VERSION, visStateSchema} from '@kepler.gl/schemas';
import {processKeplerglJSON} from '@kepler.gl/processors';
import {updateVisData, receiveMapConfig, addDataToMap, registerEntry} from '@kepler.gl/actions';

Expand Down Expand Up @@ -90,7 +91,8 @@ import {
StateWFilesFiltersLayerColor,
StateWSplitMaps,
testCsvDataId,
testGeoJsonDataId
testGeoJsonDataId,
StateWFiles
} from 'test/helpers/mock-state';

import {
Expand All @@ -113,6 +115,7 @@ import {
mergedRateFilter
} from 'test/fixtures/geojson';
import {mockStateWithPolygonFilter} from '../../fixtures/points-with-polygon-filter-map';
import CloneDeep from 'lodash.clonedeep';

test('VisStateMerger.v0 -> mergeFilters -> toEmptyState', t => {
const savedConfig = cloneDeep(savedStateV0);
Expand Down Expand Up @@ -1928,6 +1931,26 @@ test('VisStateMerger -> load polygon filter map', t => {
t.end();
});

test('VisStateMerger -> createLayerFromConfig with Parsed Layer', t => {
const oldState = CloneDeep(StateWFiles);

t.equal(oldState.visState.layers.length, 2, 'Should have layers');

// mock an exported layer config with visual channels
const savedLayer = visStateSchema[CURRENT_VERSION].save({
layers: [oldState.visState.layers[0]],
layerOrder: [0]
}).visState.layers[0];

t.ok(savedLayer.visualChannels, 'Should have visualChannels');

const addedLayer = createLayerFromConfig(oldState.visState, savedLayer);

t.ok(addedLayer.visConfigSettings, 'Should have visConfig settings loaded correctly');

t.end();
});

const MOCK_MERGE_TASK = Task.fromPromise(
time => new Promise(resolve => window.setTimeout(resolve, time)),
'MOCK_MERGE_TASK'
Expand Down
169 changes: 169 additions & 0 deletions test/node/reducers/vis-state-test.js
Expand Up @@ -1186,6 +1186,175 @@ test('#visStateReducer -> DUPLICATE_LAYER', t => {
t.end();
});

// TODO: not sure if this test is correct, it basically checks if the loaded visState is empty
// if we load configuration through the schema manager all layers will be changing from having visualChannels to visualConfig
// test('#visStateReducer -> SERIALIZE', t => {
// const configuration = {
// version: 'v1',
// config: {
// visState: {
// filters: [
// {
// dataId: ['earthquakes'],
// id: 'vo18yorx',
// name: ['DateTime'],
// type: 'timeRange',
// value: [663046722470, 1301519405470],
// enlarged: true,
// plotType: 'histogram',
// animationWindow: 'free',
// yAxis: null,
// speed: 1
// }
// ],
// layers: [
// {
// id: 'hty62yd',
// type: 'point',
// config: {
// dataId: 'earthquakes',
// label: 'Point',
// color: [23, 184, 190],
// highlightColor: [252, 242, 26, 255],
// columns: {
// lat: 'Latitude',
// lng: 'Longitude',
// altitude: null
// },
// isVisible: true,
// visConfig: {
// radius: 10,
// fixedRadius: false,
// opacity: 0.39,
// outline: false,
// thickness: 2,
// strokeColor: [23, 184, 190],
// colorRange: {
// name: 'ColorBrewer PRGn-6',
// type: 'diverging',
// category: 'ColorBrewer',
// colors: ['#762a83', '#af8dc3', '#e7d4e8', '#d9f0d3', '#7fbf7b', '#1b7837'],
// reversed: false
// },
// strokeColorRange: {
// name: 'ColorBrewer PRGn-6',
// type: 'diverging',
// category: 'ColorBrewer',
// colors: ['#762a83', '#af8dc3', '#e7d4e8', '#d9f0d3', '#7fbf7b', '#1b7837'],
// reversed: false
// },
// radiusRange: [4.2, 96.2],
// filled: true
// },
// hidden: false,
// textLabel: [
// {
// field: null,
// color: [255, 255, 255],
// size: 18,
// offset: [0, 0],
// anchor: 'start',
// alignment: 'center'
// }
// ]
// },
// visualChannels: {
// colorField: {
// name: 'Magnitude',
// type: 'real'
// },
// colorScale: 'quantize',
// strokeColorField: null,
// strokeColorScale: 'quantile',
// sizeField: {
// name: 'Magnitude',
// type: 'real'
// },
// sizeScale: 'sqrt'
// }
// }
// ],
// interactionConfig: {
// tooltip: {
// fieldsToShow: {
// earthquakes: [
// {
// name: 'DateTime',
// format: null
// },
// {
// name: 'Latitude',
// format: null
// },
// {
// name: 'Longitude',
// format: null
// },
// {
// name: 'Depth',
// format: null
// },
// {
// name: 'Magnitude',
// format: null
// }
// ]
// },
// compareMode: false,
// compareType: 'absolute',
// enabled: true
// },
// brush: {
// size: 0.5,
// enabled: false
// },
// geocoder: {
// enabled: false
// },
// coordinate: {
// enabled: false
// }
// },
// layerBlending: 'normal',
// splitMaps: [],
// animationConfig: {
// currentTime: null,
// speed: 1
// }
// },
// mapState: {
// bearing: 0,
// dragRotate: false,
// latitude: 37.05881309947238,
// longitude: -122.80009283836715,
// pitch: 0,
// zoom: 5.740491857794806,
// isSplit: false
// },
// mapStyle: {
// styleType: 'light',
// topLayerGroups: {},
// visibleLayerGroups: {
// border: false,
// building: true,
// label: true,
// land: true,
// road: true,
// water: true
// },
// threeDBuildingColor: [9.665468314072013, 17.18305478057247, 31.1442867897876],
// mapStyles: {}
// }
// }
// };
//
// const loadedVisState = visStateSchema[CURRENT_VERSION].load(configuration.config.visState);
//
// t.deepEqual(loadedVisState, {}, 'Should have the same configuration');
//
// t.end();
// });

test('#visStateReducer -> UPDATE_VIS_DATA.1 -> No data', t => {
const oldState = CloneDeep(InitialState).visState;
const nextState1 = reducer(oldState, VisStateActions.updateVisData([{info: null, data: null}]));
Expand Down

0 comments on commit c847573

Please sign in to comment.