diff --git a/app/assets/scripts/components/common/data-browser/bisector.layer.js b/app/assets/scripts/components/common/data-browser/bisector.layer.js index ae240635..04abef34 100644 --- a/app/assets/scripts/components/common/data-browser/bisector.layer.js +++ b/app/assets/scripts/components/common/data-browser/bisector.layer.js @@ -12,11 +12,8 @@ import { import { themeVal } from '../../../styles/utils/general'; import { utcDate, bisectByDate } from '../../../utils/utils'; -const getClosestDate = (data, date, timeUnit) => { - // If we're working with a discrete domain, get the closest value. - if (data.length > 2) { - return bisectByDate(data, date, d => utcDate(d)); - } else { +const getClosestDate = (data, date, timeUnit, isPeriodic) => { + if (isPeriodic) { // If we only have start and end, round based on time unit. if (timeUnit === 'day') { const h = getHours(date); @@ -30,6 +27,9 @@ const getClosestDate = (data, date, timeUnit) => { ? startOfMonth(add(date, { months: 1 })) : startOfMonth(date); } + // If we're working with a discrete domain, get the closest value. + } else { + return bisectByDate(data, date, d => utcDate(d)); } }; @@ -72,7 +72,7 @@ export default { .style('pointer-events', 'all') .on('mouseover', function () { const xPos = d3.mouse(this)[0]; - const date = getClosestDate(ctx.props.xDomain, ctx.xScale.invert(xPos), ctx.props.timeUnit); + const date = getClosestDate(ctx.props.xDomain, ctx.xScale.invert(xPos), ctx.props.timeUnit, ctx.props.isPeriodic); const xPosSnap = ctx.xScale(date); bisectorG.select('.bisector-interact').style('display', ''); ctx.onInternalAction('bisector.show', { date, x: xPosSnap }); @@ -83,7 +83,7 @@ export default { }) .on('mousemove', function () { const xPos = d3.mouse(this)[0]; - const date = getClosestDate(ctx.props.xDomain, ctx.xScale.invert(xPos), ctx.props.timeUnit); + const date = getClosestDate(ctx.props.xDomain, ctx.xScale.invert(xPos), ctx.props.timeUnit, ctx.props.isPeriodic); const xPosSnap = ctx.xScale(date); const { height } = ctx.getSize(); bisectorG.select('.bisector-interact') @@ -95,7 +95,7 @@ export default { }) .on('click', function () { const xPos = d3.mouse(this)[0]; - const date = getClosestDate(ctx.props.xDomain, ctx.xScale.invert(xPos), ctx.props.timeUnit); + const date = getClosestDate(ctx.props.xDomain, ctx.xScale.invert(xPos), ctx.props.timeUnit, ctx.props.isPeriodic); ctx.props.onAction('date.set', { date }); }); }, diff --git a/app/assets/scripts/components/common/data-browser/data-extent.layer.js b/app/assets/scripts/components/common/data-browser/data-extent.layer.js index 8751d4a7..1b841996 100644 --- a/app/assets/scripts/components/common/data-browser/data-extent.layer.js +++ b/app/assets/scripts/components/common/data-browser/data-extent.layer.js @@ -29,31 +29,12 @@ export default { const dataSeries = dataCanvas.select('.data-extent'); - const isDiscrete = props.xDomain.length > 2; - - // When we have more than 2 date points, we are showing the individual dates + // When we have non periodic points, we are showing the individual dates // that are available. // These are represented as circles. Continuous data is represented as // a line. - if (isDiscrete) { - dataSeries.selectAll('.line').style('display', 'none'); - const points = dataSeries.selectAll('.point').data(props.xDomain); - - // Remove old. - points.exit().remove(); - // Handle new. - points - .enter() - .append('circle') - .attr('r', 4) - .attr('class', 'point') - .merge(points) - // Update current. - .style('display', '') - .attr('cx', d => xScale(utcDate(d))) - .attr('cy', d => height - 8); - } else { - dataSeries.selectAll('.poin').style('display', 'none'); + if (props.isPeriodic) { + dataSeries.selectAll('.point').style('display', 'none'); const dateDomain = xScale.domain(); const lines = dataSeries.selectAll('.line').data([dateDomain]); @@ -72,6 +53,23 @@ export default { .attr('y1', height - 8) .attr('x2', d => xScale(utcDate(d[1]))) .attr('y2', height - 8); + } else { + dataSeries.selectAll('.line').style('display', 'none'); + const points = dataSeries.selectAll('.point').data(props.xDomain); + + // Remove old. + points.exit().remove(); + // Handle new. + points + .enter() + .append('circle') + .attr('r', 4) + .attr('class', 'point') + .merge(points) + // Update current. + .style('display', '') + .attr('cx', d => xScale(utcDate(d))) + .attr('cy', d => height - 8); } } }; diff --git a/app/assets/scripts/components/common/timeline.js b/app/assets/scripts/components/common/timeline.js index 46115ed1..9f19cc8a 100644 --- a/app/assets/scripts/components/common/timeline.js +++ b/app/assets/scripts/components/common/timeline.js @@ -21,27 +21,27 @@ const checkSameDate = (date, compareDate, interval) => { } }; -const getNextDate = (domain, date, timeUnit) => { +const getNextDate = (domain, date, timeUnit, isPeriodic) => { + // If we only have start and end, round based on time unit. + if (isPeriodic) { + return add(date, getOperationParam(timeUnit)); // If we're working with a discrete domain, get the closest value. - if (domain.length > 2) { + } else { const currIdx = domain.findIndex(d => isSameDay(d, date)); if (currIdx < 0 || currIdx >= domain.length - 1) return null; return domain[currIdx + 1]; - } else { - // If we only have start and end, round based on time unit. - return add(date, getOperationParam(timeUnit)); } }; -const getPrevDate = (domain, date, timeUnit) => { +const getPrevDate = (domain, date, timeUnit, isPeriodic) => { + // If we only have start and end, round based on time unit. + if (isPeriodic) { + return sub(date, getOperationParam(timeUnit)); // If we're working with a discrete domain, get the closest value. - if (domain.length > 2) { + } else { const currIdx = domain.findIndex(d => isSameDay(d, date)); if (currIdx <= 0) return null; return domain[currIdx - 1]; - } else { - // If we only have start and end, round based on time unit. - return sub(date, getOperationParam(timeUnit)); } }; @@ -161,6 +161,7 @@ class Timeline extends React.Component { const swatch = layers[0].swatch.color; const timeUnit = layers[0].timeUnit || 'month'; + const isPeriodic = layers[0].isPeriodic || false; return ( @@ -196,7 +197,7 @@ class Timeline extends React.Component { title='Previous entry' hideText onClick={() => - onAction('date.set', { date: getPrevDate(dateDomain, date, timeUnit) })} + onAction('date.set', { date: getPrevDate(dateDomain, date, timeUnit, isPeriodic) })} > Previous entry @@ -208,7 +209,7 @@ class Timeline extends React.Component { title='Next entry' hideText onClick={() => - onAction('date.set', { date: getNextDate(dateDomain, date, timeUnit) })} + onAction('date.set', { date: getNextDate(dateDomain, date, timeUnit, isPeriodic) })} > Next entry @@ -222,6 +223,7 @@ class Timeline extends React.Component { timeUnit={timeUnit} onAction={onAction} xDomain={dateDomain} + isPeriodic={isPeriodic} swatch={swatch} /> diff --git a/app/assets/scripts/utils/map-explore-utils.js b/app/assets/scripts/utils/map-explore-utils.js index 684d49a1..bda1f46d 100644 --- a/app/assets/scripts/utils/map-explore-utils.js +++ b/app/assets/scripts/utils/map-explore-utils.js @@ -362,9 +362,9 @@ export function toggleLayerCompare (layer) { } } -function isDateInDomain (date, domain) { +function isDateInDomain (date, domain, isPeriodic) { if (!date) return false; - if (domain.length === 2) { + if (isPeriodic) { // Start and end, check if between dates. const [s, e] = domain; return isWithinInterval(date, { start: utcDate(s), end: utcDate(e) }); @@ -381,7 +381,7 @@ export function toggleLayerRasterTimeseries (layer) { this.setState(state => { // Init the timeline date. const timelineDate = state.timelineDate && - isDateInDomain(state.timelineDate, layer.domain) + isDateInDomain(state.timelineDate, layer.domain, layer.isPeriodic) ? state.timelineDate : utcDate(layer.domain[layer.domain.length - 1]);