Skip to content

Commit 0ba92be

Browse files
authored
DM-6954: merged, XY plot problems found, pull request #137
DM-6954 XY plot problems found
2 parents 1fe1f3f + 36b279c commit 0ba92be

File tree

6 files changed

+354
-93
lines changed

6 files changed

+354
-93
lines changed
3.32 KB
Loading

src/firefly/js/charts/XYPlotCntlr.js

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
*/
44
import {flux} from '../Firefly.js';
55

6-
import {updateSet, updateMerge} from '../util/WebUtil.js';
7-
import {get, has, omit, omitBy, isUndefined, isString} from 'lodash';
6+
import {updateSet, updateMerge, updateDelete} from '../util/WebUtil.js';
7+
import {get, has, omit, omitBy, isEmpty, isUndefined, isString} from 'lodash';
88

99
import {doFetchTable, getColumn, getTblById, isFullyLoaded, cloneRequest} from '../tables/TableUtil.js';
1010
import * as TablesCntlr from '../tables/TablesCntlr.js';
@@ -149,7 +149,8 @@ export function loadPlotData (rawAction) {
149149

150150
if (!serverCallNeeded) {
151151
const tableModel = getTblById(tblId);
152-
xyPlotParams = getUpdatedParams(xyPlotParams, tableModel);
152+
const dataBoundaries = getDataBoundaries(chartModel.xyPlotData);
153+
xyPlotParams = getUpdatedParams(xyPlotParams, tableModel, dataBoundaries);
153154
}
154155

155156
dispatch({ type : LOAD_PLOT_DATA, payload : {chartId, tblId, xyPlotParams, tblSource, serverCallNeeded}});
@@ -188,6 +189,20 @@ function updatePlotData(data) {
188189

189190
export function reduceXYPlot(state={}, action={}) {
190191
switch (action.type) {
192+
case (TablesCntlr.TABLE_LOADED) :
193+
{
194+
const {tbl_id, invokedBy} = action.payload;
195+
let updatedState = state;
196+
if (invokedBy !== TablesCntlr.TABLE_SORT) {
197+
Object.keys(state).forEach((cid) => {
198+
if (state[cid].tblId === tbl_id && get(state, [cid, 'xyPlotParams', 'boundaries'])) {
199+
// do we need hard boundaries, which do not go away on new table data?
200+
updatedState = updateDelete(updatedState, [cid, 'xyPlotParams'], 'boundaries');
201+
}
202+
});
203+
}
204+
return updatedState;
205+
}
191206
case (TablesCntlr.TABLE_REMOVE) :
192207
{
193208
const tbl_id = action.payload.tbl_id;
@@ -287,8 +302,46 @@ function getServerCallParameters(xyPlotParams) {
287302
return [xyPlotParams.x.columnOrExpr, xyPlotParams.y.columnOrExpr, maxBins, xyRatio, xMin, xMax, yMin, yMax];
288303
}
289304

305+
function getDataBoundaries(xyPlotData) {
306+
if (!isEmpty(xyPlotData)) {
307+
return omitBy({
308+
xMin: xyPlotData.xMin,
309+
xMax: xyPlotData.xMax,
310+
yMin: xyPlotData.yMin,
311+
yMax: xyPlotData.yMax
312+
}, isUndefined);
313+
}
314+
}
290315

291316

317+
/**
318+
* Pad and round data boundaries
319+
* @param {Object} boundaries - object with xMin, xMax, yMin, yMax props
320+
* @param {Number} factor - part of the range to add on both sides
321+
*/
322+
function getPaddedBoundaries(boundaries, factor=100) {
323+
if (!isEmpty(boundaries)) {
324+
let {xMin, xMax, yMin, yMax} = boundaries;
325+
const xRange = xMax - xMin;
326+
327+
if (xRange > 0) {
328+
const xPad = xRange/factor;
329+
xMin = xMin - xPad;
330+
xMax = xMax + xPad;
331+
}
332+
const yRange = yMax - yMin;
333+
if (yRange > 0) {
334+
const yPad = yRange/factor;
335+
yMin = yMin - yPad;
336+
yMax = yMax + yPad;
337+
}
338+
if (xRange > 0 || yRange > 0) {
339+
return {xMin, xMax, yMin, yMax};
340+
}
341+
}
342+
return boundaries;
343+
}
344+
292345
/**
293346
* fetches xy plot data
294347
* set isColStatsReady to true once done.
@@ -352,7 +405,7 @@ function fetchPlotData(dispatch, tblId, xyPlotParams, chartId) {
352405
xyPlotParams,
353406
xyPlotData,
354407
chartId,
355-
newParams: getUpdatedParams(xyPlotParams, tableModel)
408+
newParams: getUpdatedParams(xyPlotParams, tableModel, getDataBoundaries(xyPlotData))
356409
}));
357410
}
358411
).catch(
@@ -368,32 +421,44 @@ function fetchPlotData(dispatch, tblId, xyPlotParams, chartId) {
368421
* derive them from existing parameters or tableModel.
369422
* No selection should be present in updated parameters
370423
*/
371-
function getUpdatedParams(xyPlotParams, tableModel) {
424+
function getUpdatedParams(xyPlotParams, tableModel, dataBoundaries) {
372425
let newParams = xyPlotParams;
373426

374427
if (!get(xyPlotParams, 'x.label')) {
375428
newParams = updateSet(newParams, 'x.label', get(xyPlotParams, 'x.columnOrExpr'));
376429
}
377430
if (!get(xyPlotParams, 'x.unit')) {
378431
const xColumn = getColumn(tableModel, get(xyPlotParams, 'x.columnOrExpr'));
379-
const xUnit = get(xColumn, 'units');
380-
if (xUnit) {
381-
newParams = updateSet(newParams, 'x.unit', xUnit);
382-
}
432+
const xUnit = get(xColumn, 'units', '');
433+
newParams = updateSet(newParams, 'x.unit', xUnit);
383434
}
384435
if (!get(xyPlotParams, 'y.label')) {
385436
newParams = updateSet(newParams, 'y.label', get(xyPlotParams, 'y.columnOrExpr'));
386437
}
387438
if (!get(xyPlotParams, 'y.unit')) {
388439
const yColumn = getColumn(tableModel, get(xyPlotParams, 'y.columnOrExpr'));
389-
const yUnit = get(yColumn, 'units');
390-
if (yUnit) {
391-
newParams = updateSet(newParams, 'y.unit', yUnit);
392-
}
440+
const yUnit = get(yColumn, 'units', '');
441+
newParams = updateSet(newParams, 'y.unit', yUnit);
393442
}
394443
if (get(xyPlotParams, 'selection')) {
395444
newParams = updateSet(newParams, 'selection', undefined);
396445
}
446+
447+
// set plot boundaries,
448+
// if user set boundaries are undefined, use data boundaries
449+
const userSetBoundaries = get(xyPlotParams, 'userSetBoundaries', {});
450+
const boundaries = Object.assign({}, userSetBoundaries);
451+
if (Object.keys(boundaries).length < 4 && !isEmpty(dataBoundaries)) {
452+
const paddedDataBoundaries = getPaddedBoundaries(dataBoundaries);
453+
const [xMin, xMax, yMin, yMax] = ['xMin', 'xMax', 'yMin', 'yMax'].map( (v) => {
454+
return (Number.isFinite(boundaries[v]) ? boundaries[v] : paddedDataBoundaries[v]);
455+
});
456+
const newBoundaries = omitBy({xMin, xMax, yMin, yMax}, isUndefined);
457+
if (!isEmpty(newBoundaries)) {
458+
newParams = updateSet(newParams, 'boundaries', newBoundaries);
459+
}
460+
}
461+
397462
return newParams;
398463
}
399464

src/firefly/js/charts/ui/ColSelectView.jsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {dispatchTableRemove} from '../../tables/TablesCntlr';
77

88
import {BasicTable} from '../../tables/ui/BasicTable.jsx';
99
import {getTblById} from '../../tables/TableUtil.js';
10-
import {dispatchShowDialog} from '../../core/ComponentCntlr.js';
10+
import {dispatchShowDialog, dispatchHideDialog, isDialogVisible} from '../../core/ComponentCntlr.js';
1111
import CompleteButton from '../../ui/CompleteButton.jsx';
1212
//import HelpIcon from '../../ui/HelpIcon.jsx';
1313
const popupId = 'XYColSelect';
@@ -35,8 +35,9 @@ const closeButtonStyle = {'textAlign': 'center', display: 'inline-block', height
3535

3636
export function showColSelectPopup(colValStats,onColSelected,popupTitle,buttonText,currentVal) {
3737

38-
if (getTblById(TBL_ID)) {
39-
        dispatchTableRemove(TBL_ID);
38+
if (getTblById(TBL_ID)) {
39+
hideColSelectPopup();
40+
       dispatchTableRemove(TBL_ID);
4041
    }
4142

4243
const colNames = colValStats.map((colVal) => {return colVal.name;});
@@ -72,6 +73,12 @@ export function showColSelectPopup(colValStats,onColSelected,popupTitle,buttonTe
7273
dispatchShowDialog(popupId);
7374
}
7475

76+
export function hideColSelectPopup() {
77+
if (isDialogVisible(popupId)) {
78+
dispatchHideDialog(popupId);
79+
}
80+
}
81+
7582
function popupForm(tableModel, onColSelected,buttonText,popupId) {
7683
const tblId = tableModel.tbl_id;
7784
return (

src/firefly/js/charts/ui/XYPlot.jsx

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export const plotParamsShape = PropTypes.shape({
3030
stretch : PropTypes.oneOf(['fit','fill']),
3131
selection : selectionShape,
3232
zoom : selectionShape,
33+
boundaries : selectionShape,
34+
userSetBoundaries : selectionShape,
3335
nbins : PropTypes.shape({x : PropTypes.number, y : PropTypes.number}),
3436
shading : PropTypes.oneOf(['lin', 'log']),
3537
x : axisParamsShape,
@@ -107,7 +109,7 @@ const getWeightedDataDescr = function(defaultDescr, numericData, minWeight, maxW
107109
};
108110

109111
const getXAxisOptions = function(params) {
110-
const xTitle = params.x.label + (params.x.unit ? ', ' + params.x.unit : '');
112+
const xTitle = params.x.label + (params.x.unit ? ` (${params.x.unit})` : '');
111113
let xGrid = false, xReversed = false, xLog = false;
112114
const {options:xOptions} = params.x;
113115
if (xOptions) {
@@ -119,7 +121,7 @@ const getXAxisOptions = function(params) {
119121
};
120122

121123
const getYAxisOptions = function(params) {
122-
const yTitle = params.y.label + (params.y.unit ? ', ' + params.y.unit : '');
124+
const yTitle = params.y.label + (params.y.unit ? ` (${params.y.unit})` : '');
123125

124126
let yGrid = false, yReversed = false, yLog = false;
125127
const {options:yOptions} = params.y;
@@ -275,12 +277,19 @@ export class XYPlot extends React.Component {
275277
type: newYOptions.yLog ? 'logarithmic' : 'linear'
276278
});
277279
}
278-
if (!shallowequal(params.zoom, newParams.zoom)) {
280+
if (!shallowequal(params.boundaries, newParams.boundaries)) {
279281
const {xMin, xMax, yMin, yMax} = getZoomSelection(newParams);
280282
const {xMin:xDataMin, xMax:xDataMax, yMin:yDataMin, yMax:yDataMax} = nextProps.data;
281283
Object.assign(xoptions, {min: selFinite(xMin,xDataMin), max: selFinite(xMax, xDataMax)});
282284
Object.assign(yoptions, {min: selFinite(yMin, yDataMin), max: selFinite(yMax, yDataMax)});
283285
}
286+
if (!shallowequal(params.zoom, newParams.zoom) ||
287+
!shallowequal(params.boundaries, newParams.boundaries)) {
288+
const {xMin, xMax, yMin, yMax} = getZoomSelection(newParams);
289+
const {xMin:xDataMin, xMax:xDataMax, yMin:yDataMin, yMax:yDataMax} = get(newParams, 'boundaries', {});
290+
Object.assign(xoptions, {min: selFinite(xMin,xDataMin), max: selFinite(xMax, xDataMax)});
291+
Object.assign(yoptions, {min: selFinite(yMin, yDataMin), max: selFinite(yMax, yDataMax)});
292+
}
284293
const xUpdate = Reflect.ownKeys(xoptions).length > 0;
285294
const yUpdate = Reflect.ownKeys(yoptions).length > 0;
286295
if (xUpdate || yUpdate) {
@@ -439,7 +448,7 @@ export class XYPlot extends React.Component {
439448
}, {selected: [], all: []});
440449

441450

442-
marker = {symbol: 'circle'};
451+
marker = {symbol: 'circle', radius: 3};
443452
allSeries = [
444453
{
445454
id: DATAPOINTS,
@@ -510,7 +519,7 @@ export class XYPlot extends React.Component {
510519
id: HIGHLIGHTED,
511520
name: HIGHLIGHTED,
512521
color: highlightedColor,
513-
marker: {symbol: 'circle', lineColor: '#404040', lineWidth: 1, radius: 5},
522+
marker: {symbol: 'circle', lineColor: '#404040', lineWidth: 1, radius: 4},
514523
data: highlightedData,
515524
showInLegend: false
516525
}, true, false);
@@ -527,7 +536,8 @@ export class XYPlot extends React.Component {
527536
const {xTitle, xGrid, xReversed, xLog} = getXAxisOptions(params);
528537
const {yTitle, yGrid, yReversed, yLog} = getYAxisOptions(params);
529538
const {xMin, xMax, yMin, yMax} = getZoomSelection(params);
530-
const {xMin:xDataMin, xMax:xDataMax, yMin:yDataMin, yMax:yDataMax, decimateKey} = data;
539+
const {decimateKey} = data;
540+
const {xMin:xDataMin, xMax:xDataMax, yMin:yDataMin, yMax:yDataMax} = get(params, 'boundaries', {});
531541

532542
const makeSeries = this.makeSeries;
533543

@@ -635,16 +645,22 @@ export class XYPlot extends React.Component {
635645
type: yLog ? 'logarithmic' : 'linear'
636646
},
637647
series: [{
638-
// this series is to make sure the axis are created
639-
// without actual series xAxis creation is deferred
648+
// This series is to make sure the axis are created.
649+
// Without actual series, xAxis creation is deferred
640650
// and there is no way to get value to pixel conversion
641651
// for sizing the symbol
642652
id: 'minmax',
643653
name: 'minmax',
644-
color: '#f0f0f0',
645-
marker: {radius: 2},
654+
color: 'rgba(240, 240, 240, 0.1)',
655+
marker: {radius: 1},
646656
data: [[selFinite(xMin, xDataMin), selFinite(yMin,yDataMin)], [selFinite(xMax, xDataMax), selFinite(yMax,yDataMax)]],
647-
showInLegend: false
657+
showInLegend: false,
658+
enableMouseTracking: false,
659+
states: {
660+
hover: {
661+
enabled: false
662+
}
663+
}
648664
}],
649665
credits: {
650666
enabled: false // removes a reference to Highcharts.com from the chart

0 commit comments

Comments
 (0)