Skip to content

feat: use ECharts to have faster charts (and more interactive)#2234

Merged
Flix6x merged 48 commits into
mainfrom
feat/better-and-fast-asset-charts
Jul 15, 2026
Merged

feat: use ECharts to have faster charts (and more interactive)#2234
Flix6x merged 48 commits into
mainfrom
feat/better-and-fast-asset-charts

Conversation

@Ahmad-Wahid

@Ahmad-Wahid Ahmad-Wahid commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an optional Fast chart mode (Apache ECharts, canvas-based) alongside the existing Vega-Lite charts. A toggle in the chart header switches between them, and the choice is remembered per browser. The fast chart renders and interacts far faster on dense time series while staying visually and functionally equivalent to the Vega-Lite charts.

What's included

  • Canvas rendering with mouse-wheel zoom, drag-to-pan, and a shared ruler + nearest-point tooltip synced across subplots

  • Parity with Vega-Lite: subplot layout, colors, legends (including the Source line-style key), titles/axes/fonts, annotations, replay, and step-vs-linear interpolation based on each sensor's resolution

  • All chart types: line, bar, histogram, heatmap, and charge-point sessions

  • PNG / SVG / CSV export

  • Added changelog item in documentation/changelog.rst

Look & Feel

Vega-Lite:
image

ECharts:
image

EChart will show the legends of those sensors that has data. Although, they are not dropped from the sensors_to_show list.

How to test

Switch between Vega-Lite and ECharts using this toogle button.
image

Further Improvements

Vega-Lite ECharts
Asset page (vconcat line chart)
Sensor page (bar chart)
Sensor page (annotations)
Sensor page (histogram)
Sensor page (heat map)
Speed 🟡 🟢
Interactivity 🟡 🟢
Replay
Toggle legend position
Tooltip
Export (CSV)
Export (PNG)
Export (SVG)
  • 🟡 Good
  • 🟢 Great
  • ✅ Supported
  • ❌ Not supported
  • ⬜ Support unknown

Related Items

  • Drop the report widget after everything is confirmed
image

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
@Ahmad-Wahid Ahmad-Wahid self-assigned this Jun 12, 2026
@Flix6x Flix6x added the UI label Jun 12, 2026
…ing charts

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>

@Flix6x Flix6x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No code review, just observations from testing. Improvements I observe are:

  • Speed
  • Panning
  • Zooming
  • Hover effect to focus on a single trace (line)
  • Heat map interactivity (select a value range to see)

Missing:

  • Asset page shows misaligned x-axis on vertically concatenated subcharts (tip: try the replay on asset 77) (✅ )
  • Bar chart of a year of 15-minute prices has missing data
  • SVG export on large heatmaps has missing data
  • No annotations visible on sensor bar chart (tip: select a time period on sensor 14 covering some Dutch public holidays) (✅ )
  • SVG export should exclude buttons (✅ )
  • Double click to reset
  • Font size too small (✅ )
  • Tooltip hover on nearest data point (this can be discussed)

Ahmad-Wahid and others added 8 commits June 12, 2026 20:09
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mps, fonts)

- show hover annotations on the sensor page, matching the Vega-Lite
  SHADE_LAYER/TEXT_LAYER (gray bands, highlight + label on hover)
- use tableau10 colors and zero-based y-axis like Vega-Lite
- use linear interpolation for instantaneous sensors so ramps are visible
- match Poppins font and Vega-Lite font sizes
- always show the legend below on the sensor page
- mixed date/time x-axis labels and extra inter-subplot spacing
@Ahmad-Wahid

Ahmad-Wahid commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

No code review, just observations from testing. Improvements I observe are:

  • Speed
  • Panning
  • Zooming
  • Hover effect to focus on a single trace (line)
  • Heat map interactivity (select a value range to see)

Missing:

  • Asset page shows misaligned x-axis on vertically concatenated subcharts (tip: try the replay on asset 77)
  • Bar chart of a year of 15-minute prices has missing data (✅ )
  • SVG export on large heatmaps has missing data
  • No annotations visible on sensor bar chart (tip: select a time period on sensor 14 covering some Dutch public holidays) (✅ )
  • SVG export should exclude buttons (✅ )
  • Double click to reset
  • Font size too small (✅ )
  • Tooltip hover on nearest data point (this can be discussed)

Let's discuss the rest of the items in a call.

- dataZoom realtime:false + throttle:80 so a slider drag coalesces into a
  single redraw on release instead of one per tick (removes mid-drag jank)
- large mode on line series for batched canvas drawing
- measured on asset 77 (7,728 rows): initial render 134ms -> 71ms,
  slider drag 25 re-renders -> 1
@Flix6x

Flix6x commented Jun 26, 2026

Copy link
Copy Markdown
Member

Updated list of observations

Sensor page:

  • Reorder buttons: please move the SVG button to between Reset Zoom and PNG. ✅

  • PNG and SVG for 1 year of sensor 14 is still missing data. CSV probably, too, please check. ✅

  • Reset Zoom is very peculiar at the moment. It only does something when the Zoom button function has been used, and whatever window had been selected at that time will be the window that it resets to, until you reload the entire page. I see two options going forward:

    1. Always reset to the full period in the URL, or
    2. Reset to the previously selected period (like a back button). ✅

    The first option is probably simplest.

Asset page:

  • Source legend at the bottom. ✅
  • Instantaneous sensors should not have step-before/after viz ✅
  • Tooltip should always show for the nearest data point (nearest vertical trace, in case of multiple traces, nearest horizontally on the left or right, I don't mind that much; the vega-lite uses the nearest on the left).
  • Subplots should have same x-axis when the play-button is pressed. ✅

Export read the canvas/SVG synchronously right after setOption, but
heatmaps render cells progressively across animation frames, so a
year-long heatmap (>5000 cells) lost every cell past the first chunk.
buildExportOption now forces progressive:0 on every series so the full
chart renders in one pass. CSV is unaffected (it serializes the raw
rows).

Also place the SVG export icon between Reset Zoom and PNG on the sensor
page only, leaving the asset-page toolbox order unchanged.
…asset-charts

# Conflicts:
#	documentation/changelog.rst
- Native ECharts Charge Point sessions chart with companion subplots and
  working per-session tooltips; charge-points type switchable in fast mode
- Do not render the Charge Point sessions panel in the default view
- Align companion subplots on a shared time domain
- Remove the always-visible range-slider zoom bar (keep wheel/drag zoom)
- Move the Source line-style key to the bottom, matching Vega-Lite: a
  vertical column at the bottom-left with side legends, or a second column
  beside the Sensor legend in the legends-below layout
Drop the Arrival/Plug-in/Charging key from the sessions chart along with the
now-unused buildChargePointKey helper and CHARGEPOINT_KEY_ROWS constant.
Vega uses linear interpolation for a subplot row when any of its sensors is
instantaneous (event_resolution 0), and step-after otherwise. The fast chart
had to guess resolution from event spacing because the chart data never carried
the real event_resolution, which mislabeled instantaneous sensors as interval
data (stepped lines where Vega draws smooth ones).

Serialize event_resolution (seconds) through every chart-data path the UI uses:
Sensor.as_dict, Sensor.search_beliefs, GenericAsset.search_beliefs, and the
fixed-value flex-sensor metadata in GenericAsset.chart_data_json. Carry it
through decompressChartData, then decide interpolation per subplot in
fast-chart.js exactly as chart_for_multiple_sensors does, with a spacing-based
fallback for legacy data.
Use axis-trigger tooltips on the line/bar charts so hovering anywhere over a
subplot reveals the nearest data point (the ruler), as in the Vega-Lite charts,
instead of requiring a direct hit on the thin line. The formatter picks the
single point nearest to the ruler and shows its details, scoped to the hovered
subplot.
@Ahmad-Wahid Ahmad-Wahid requested a review from Flix6x July 8, 2026 14:55
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
@Flix6x Flix6x changed the title feat: use ECharts to have faster charts feat: use ECharts to have faster charts (and more interactive) Jul 9, 2026
@Ahmad-Wahid

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: Ahmad-Wahid <59763365+Ahmad-Wahid@users.noreply.github.com>

Copilot AI commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merge conflicts have been resolved and merged with origin/main in commit 009e30ada.

Copilot stopped work on behalf of Ahmad-Wahid due to an error July 14, 2026 14:01
The axis-trigger tooltip picked the nearest point by x, but all series share
the ruler's x, so it could name the wrong sensor. Track the cursor pixel and
pick the series whose point is nearest in 2D (i.e. vertically), so the tooltip
reflects the line under the cursor and switches sensor to sensor between traces.
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>

@Flix6x Flix6x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New functionality just landed on main that needs to be copied over.

Comment thread documentation/changelog.rst Outdated
…asset-charts

# Conflicts:
#	flexmeasures/ui/templates/includes/graphs.html
Render the canvas-based ECharts chart by default (opt-out) instead of opt-in:
treat a missing fm-fast-charts preference as on, so users get the faster chart
automatically and can switch back to Vega-Lite via the toggle if needed. Update
the changelog wording accordingly.
@Ahmad-Wahid Ahmad-Wahid requested a review from Flix6x July 14, 2026 21:51
Flix6x added 12 commits July 15, 2026 13:09
Nine fixes to the fast (ECharts) charts, keeping parity with Vega-Lite:

1. x-axis spans the full requested query window, not just the data extent
   (a day with data only 18:00-22:00 now shows the whole day). Window is
   captured as primitives before getAssetKPIs mutates storeEndDate (+1 day).
2. Port the per-sub-chart y-axis modes from #2244 (zero / data / [min,max]
   floor / {min,max} strict; % floors to [0,105]) to the ECharts value axis.
3. Pre-select the toolbox marquee "zoom" tool so a drag zooms right away.
4. Fix the tooltip/marker mismatch in a two-sensor subchart by disabling the
   axis-hover emphasis pop-out (tooltip already snaps to the nearest line).
5. Move the "Fast chart" toggle into the replay column (top), beside replay.
6. Match the Vega-Lite subplot height (300px) and keep the centered subplot
   title above the y-axis title.
7. Softly round the stepped (interval) line corners via a round line-join.
8. Hide overlapping x-axis tick labels on narrow/small screens (ECharts only).
9. Double-click the chart to reset the zoom to the full range.

Signed-off-by: F.N. Claessen <felix@seita.nl>
Addresses follow-up feedback on the fast (ECharts) charts:

2. y-axis modes now update immediately on a sensors_to_show save (rebuild the
   groupSpec client-side from the saved config) instead of only after a reload.
3. Ctrl+drag pans (inside dataZoom moveOnMouseMove:'ctrl') alongside the
   pre-selected marquee zoom on plain drag.
4. Keep the emphasis pop-out but sync it to the tooltip's series (syncEmphasis),
   so the marker lands on the same sensor the tooltip shows.
5. Make the relocated "Fast chart" toggle wrap/shrink so it no longer overflows
   the narrow replay column onto the chart card.
6. Lower the subplot height to 150px (Vega parity) and raise the subplot title
   further above the y-axis title.
7. Actually-visible rounded step corners (expand to cut step vertices + smooth),
   with the tooltip snapping back to the real data points.
9. Fix double-click-to-reset by listening on the zrender canvas (fires on empty
   plot area) and resetting every dataZoom to full range.

Signed-off-by: F.N. Claessen <felix@seita.nl>
6. Lower subplot height to 150px, raise the title (TITLE_RAISE=60) and drop the
   toolbox to sit level with the first y-axis title.
7. Keep rounded step corners circular at every zoom: cut them a fixed number of
   pixels deep and recompute on each dataZoom (refreshRoundedSteps).
2. Nice-round the "Fixed range" (floor) y-axis bounds ([-3, 3] not [-2.68, 2.79]),
   and make the strict-range clamp toast fire in fast mode too (de-duplicated).
10. Mobile: one-finger swipe scrolls the page (touch-action: pan-y, no chart pan),
    pinch zooms, double-tap resets; a drag no longer resets the zoom.
3. Desktop Ctrl+drag now actually pans (disable the marquee brush while Ctrl held).
11. Drop the fixed x-axis minInterval so zooming reveals finer datetime ticks.

Signed-off-by: F.N. Claessen <felix@seita.nl>
2. Fix the strict-range y-axis toast never firing: checkStrictYAxisRanges was
   called in graphs.html but never imported, so every call threw a ReferenceError
   (in both Vega and fast mode). Add it to the import.
3. Fix the regression where the marquee zoom stopped being pre-selected: IS_TOUCH
   used maxTouchPoints, which is truthy on many desktops (touchscreens/precision
   touchpads), so they wrongly took the touch branch. Detect the PRIMARY pointer
   via matchMedia("(pointer: coarse)") instead.
6. Raise the toolbox (TOOLBOX_TOP) so it no longer dips into the subchart grid.
10. On touch, show the tooltip on TAP only (tooltip.triggerOn "click"), so a
    one-finger swipe scrolls the page instead of being captured; and ignore
    swipes in the double-tap reset so scrolling no longer resets the zoom.

Signed-off-by: F.N. Claessen <felix@seita.nl>
10. Mobile: one-finger swipe now scrolls the page (re-add preventDefaultMouseMove:
    false alongside touch-action: pan-y, so ECharts stops eating the swipe), and
    tapping the tooltip or outside the chart closes it (enterable + dismiss wiring).
12. Keep x-axis grid lines equidistant while zooming: apply one uniform, visible-
    span-based time interval to the axes and recompute it on every dataZoom
    (refreshTimeTicks), instead of ECharts' unevenly-spaced automatic time ticks.
13. Stack the "Fast chart" toggle vertically so it never overflows the narrow
    replay column onto the chart card before wrapping.

Signed-off-by: F.N. Claessen <felix@seita.nl>
10a. One-finger HORIZONTAL swipe now pans the chart (moveOnMouseMove:true on touch);
     vertical still scrolls the page (touch-action: pan-y + preventDefaultMouseMove:false).
10b. Force pointer-events:auto on the touch tooltip so a tap lands on it and closes it
     (enterable alone left the tap passing through to the canvas).
12.  Drop the x-axis minor gridlines (they were the uneven-at-the-edges lines) and give
     the sessions chart the same uniform, adaptive major ticks (refreshTimeTicks).
13.  Revert the toggle to a single horizontal line that shrinks the label (beta wraps
     under "Fast chart") before wrapping, instead of always stacking vertically.

Signed-off-by: F.N. Claessen <felix@seita.nl>
10b. Close the tooltip on tapping it: ECharts swallows the synthetic click on touch,
     so dismiss on "touchend" too (any tap not on the chart canvas closes it).
12.  Force the x-axis interval via minInterval==maxInterval (a plain time-axis
     `interval` was ignored, giving 4h not 3h for a day) and re-add finer unlabelled
     gridlines at a NICE time sub-step (minorSplitFor), not a blind half.
13.  Revert the toggle to the round-5 vertical, centered stack (no overflow/mis-align).
14.  Zoom toolbox button is now a pan/zoom mode toggle (custom icon swaps zoom<->pan):
     zoom mode = drag zooms, pan mode = drag pans; Ctrl temporarily inverts. Inside
     dataZoom moveOnMouseMove:true so drag pans whenever the marquee is off.

Signed-off-by: F.N. Claessen <felix@seita.nl>
12.  Switch the x-axis from type "time" to type "value" (epoch ms): ECharts ignores
     a forced `interval` on a time axis, so majors came out at 2h/4h. A value axis
     honors it, so majors are exactly the width-adaptive nice step (a day → 3h → 6h
     → 12h as it narrows) and the minor gridlines subdivide it at uniform absolute
     positions — equidistant at the domain edges too.
13.  Revert the toggle to the round-4 single horizontal line (overflow accepted).
14.  Replace the single mode-toggle with separate buttons: keep the built-in Zoom +
     Zoom-reset, add a "Pan" button (short tooltip). All three are desktop-only —
     hidden on touch, where pinch/swipe/double-tap are used. Drop the Ctrl inversion.
10b. Left as-is per feedback (tap-to-close-on-tooltip not pursued).

Signed-off-by: F.N. Claessen <felix@seita.nl>
12.  Revert the x-axis to type "time" so ticks stay on nice clock times when zoomed
     (the value axis honored the interval but placed ticks at the raw domain edges,
     e.g. 08:21). Bias the granularity toward 3h/6h/12h via a width/zoom-adaptive
     minInterval. Minor gridlines stay off (they mis-space at the edges on a time axis).
14.  Custom Zoom / Pan / Zoom-reset toolbox buttons. The active one is highlighted
     blue (Zoom or Pan); Ctrl inversion is back and the blue jumps to the other button
     while Ctrl is held. Reset is a button too, so it also shows on mobile (Zoom/Pan
     stay desktop-only). The built-in dataZoom feature is kept only as the (hidden,
     tooltip-less) marquee brush the buttons drive.

Signed-off-by: F.N. Claessen <felix@seita.nl>
ECharts' time axis honors maxInterval (a cap) but not minInterval (a floor), so a
2-day selection jumped from the day level (only midnights) straight to 4h with
nothing in between. Cap the interval at ~span/4 so a 2-day view shows 12h gridlines,
while a 1-day view keeps its 4h (already below the cap). Recomputed on zoom.

Signed-off-by: F.N. Claessen <felix@seita.nl>
…2234)

The double-tap-to-reset was detected from zrender's synthetic click, which is
unreliable once touch-action:pan-y hands a vertical swipe to the browser for
scrolling — so a scroll could still trip the reset. Detect the double-tap from
native touchstart/touchend on the container instead, measuring real finger
movement: a swipe moves too far to count as a tap, and a swipe between two taps
clears the pending tap, so scrolling can never complete a double-tap.

Signed-off-by: F.N. Claessen <felix@seita.nl>
…croll) (#2234)

The real cause of "scrolling resets the zoom" on mobile: scrolling shows/hides the
browser address bar, firing a window resize (height changes, width doesn't). The
resize handler re-rendered with notMerge, which resets the zoom to full. Chart layout
only depends on width, so ignore height-only resizes and keep the current zoom.

Signed-off-by: F.N. Claessen <felix@seita.nl>
@Flix6x

Flix6x commented Jul 15, 2026

Copy link
Copy Markdown
Member

Great work on this, @Ahmad-Wahid! 🎉 The ECharts "fast chart" is a real step up — noticeably snappier and more interactive than Vega-Lite while staying visually faithful across all the chart types, replay, annotations and export. Really nice contribution.

After some hands-on testing on desktop and mobile, I pushed a series of follow-up refinements straight onto this branch (all on top of your implementation):

Charts & axes

  • X-axis now spans the full selected window (not just the data extent), matching Vega-Lite
  • Ported the Improve chart viz for event values not around zero #2244 per-sub-chart y-axis modes (zero / data / fixed-range floor / strict), with nice-rounded fixed ranges, plus the strict-range clamp toast
  • Nice-aligned time ticks with an intermediate multi-day level (e.g. 12h on a 2-day view)
  • Subplot height/title/toolbox aligned with the Vega-Lite layout; softened (rounded) step corners for interval sensors
  • Tooltip now highlights the same series it reports (no more marker/label mismatch)

Zoom / pan UX

  • Separate Zoom / Pan / Reset toolbox buttons with a blue active highlight; Ctrl temporarily inverts the mode
  • Double-click (desktop) / double-tap (mobile) to reset

Mobile

  • One-finger swipe scrolls the page, horizontal drag pans, pinch zooms, tap shows the tooltip
  • Fixed the "page scroll resets the zoom" bug (height-only resizes no longer force a re-render)

Misc

  • Immediate y-axis updates when saving sensors_to_show, responsive x-axis tick labels, and the "Fast chart" toggle moved into the replay column

All of the above is tested (desktop + mobile) and already merged into this branch. LGTM from my side — great job, and thanks for pushing the charts forward! 🚀

@Flix6x Flix6x merged commit db31a27 into main Jul 15, 2026
17 checks passed
@Flix6x Flix6x deleted the feat/better-and-fast-asset-charts branch July 15, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants