Flot charts- producing custom trend with unique x-axis values

梦想的初衷 提交于 2019-12-24 00:13:37

问题


I have DB relation that holds information that I wish to display. each record holds a numerical value, and a 'name' that looks like '20140325_064532'.

The order of creating the records doesn't matter, and so I wish to present the numerical value on the y-axis and the x axis to have:

  1. to have constant margin between ticks.
  2. to present the data based on 'name' order (lexicographic).
  3. the x-axis label shouldn't present all the values, but "selected ones", such that no overlapping should be displayed.
  4. to prevent two point to be placed in the same x-value, even if they hold the same 'name' (the inner order has no meaning).

how should I define my Flot chart so this can be achieved?

EDIT: My implementation thoughts so far:

  1. I will fetch (distinctly) from the DB the 'name' field along with a row_number value (starting from 1).
  2. Based on that I'll create a Hash, using the names as the key to the row number. Lets say :name => row#
  3. I'll generate the series(s) with the following structure: rawdata = [ [row#1, numericValue1], [row#2, numericValue2], ....];

    Note that row# will be a numerical value starting at 1.

  4. I'll define the ticks to represent the actual names. (This will be a line graph with dots, and not a bar chart).

Now, all of this will work (I think) but looks really messy. And, ontop, the ticks will almost surely overlap, and as to the fact that it will be hard coded, any zooming-in and out will not render the ticks with overlapping thoughts.

Update #2:

I passed the data to the js using two data attributes. the 'results' has an array that representing the series, and holds 4 fields, such that data-results="[[[0,371144,"20140206_000030-std",375],[1,373141,"20140328_230027",343],[2,374196,"20140401_230051",436],[3,374734,"20140401_230051_2",460],[4,374394,"20140401_230051_3",484],[5,376029,"20140401_230051_4",508]],... ,[another_series]]]

The 3rd field is 'name' and the last one is 'id'

the 'series' reresents the name I have for each series (and how it is displayed in the legend), and looks like:

data-series="["PLATFORM_A__CONFIG_B","PLATFORM_B__CONFIG_NONE"]"

I tried editing the content of the tooltip trying to integrate information from the last part of each point, so that I'll have something like this displayed: Run: ID: value: $y (the 2nd array field, representing the y-axis value)

I searched the web but couldn't find anything that works using tooltip (which looks nicer to handle). in the docs it is mentioned:

If you require even more control over how the tooltip is generated you can pass a callback function(label, xval, yval, flotItem) that must return a string with the format described.

I tried implementing this by calling Chart.getTooltip but all I get in the console is

Uncaught TypeError: undefined is not a function jquery.flot.tooltip.min.js?body=1:2h jquery.flot.tooltip.min.js?body=1:2(anonymous function) jquery.flot.tooltip.min.js?body=1:2jQuery.event.dispatch jquery.js?body=1:4642elemData.handle jquery.js?body=1:4310jQuery.event.trigger jquery.js?body=1:4551(anonymous function) jquery.js?body=1:5261jQuery.extend.each jquery.js?body=1:384jQuery.fn.jQuery.each jquery.js?body=1:137jQuery.fn.extend.trigger jquery.js?body=1:5260Y jquery.flot.cust.min.js?body=1:2V jquery.flot.cust.min.js?body=1:2jQuery.event.dispatch jquery.js?body=1:4642elemData.handle jquery.js?body=1:4310

Update #3 Thanks to @Ryley great tips and guidance, I was able to correctly display my chart using the unique values passed into Flot. I've updated my code so that in order to avoid overlapping in the ticks values, I used flot-tickrotor js to rotate the looks. I've came across with a problem, in which my bottom values gets cut-off. I've also noticed that this is a reported issue, but I couldn't find any solution or workaround for this. Is there any solution for this issue?

Next, I want to enable to viewer to show/hide the series with a click. I found bunch of solutions, both official and others, but none of those worked for me. I'll explain:

  1. Official turning series on/off: this works, but looks very messy as the legend is eventually duplicated twice (disappears from official legend once the series turned off).
  2. Hiddengraphs.js: this is a plugin which can be found at the plugin repository, but it doesn't work and interact well with Chrome (tried more than one machine, it just don't work).
  3. This solution is actually really nice (I don't mind that there are no checkboxes to check), but when I integrated it into my code, all I got was "jumping" to the top of the page, and nothing happens.
  4. Lastly, I found this solution, which also works, altough using another js library, called flot.togglelegend.js. In this implementation I found some major conflicts with flot.cust.js, and couldn't get them both to work together.

Finally, I tried to implement the select-zooming and double-click zoom-reset, using flot.selection.js I was able indeed to get the functionality to work, but the re-rendering process generates into my second and third chart the data of the first one.

It is also worth saying that I'm rendering 3 charts in this page, all rendered in the same way.

Here's my updated code:

Here's my current js (written in coffeescript)

colorArray = []
colorArray.push "rgba(180, 0, 75,    0.6)"
colorArray.push "rgba(0, 150, 100,   0.6)"
colorArray.push "rgba(0, 0, 255,     0.6)"
colorArray.push "rgba(140, 0, 255,   0.6)"
colorArray.push "rgba(90, 180, 20,   0.6)"
colorArray.push "rgba(255, 236, 0,   0.6)"
colorArray.push "rgba(234, 170, 21,  0.6)"
colorArray.push "rgba(95, 180, 190,  0.6)"
colorArray.push "rgba(214, 92, 63,   0.6)"
colorArray.push "rgba(218, 106, 234, 0.6)"
colorArray.push "rgba(213, 128, 155, 0.6)"

# chart colors default 
$chrt_border_color = "#efefef"
$chrt_grid_color = "#DDD"
$chrt_main = "#E24913"

# red       
$chrt_second = "#6595b4"
# blue      
$chrt_third = "#FF9F01"
# orange    
$chrt_fourth = "#7e9d3a"
# green     
$chrt_fifth = "#BD362F"
# dark red  
$chrt_mono = "#000"

Chart = 

generateDataObjects: (all_series, all_series_data) ->
    plotData = []

    for series, i in all_series
        obj =
            label: series.replace /__/g, "|"
            data: all_series_data[i]
            color: colorArray[i]

        plotData.push obj

    return plotData

getTooltip: (label, xval, yval, flotItem) ->
    return 'Build: <span>'+ flotItem.series.data[flotItem.dataIndex][2]+'</span>' +" | Run ID: <strong> #{flotItem.series.data[flotItem.dataIndex][3].toString()}</strong>" + '<br> Result: <span>'+Chart.commify(yval)+'</span>'

commify: (x) ->
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

generateChartOptions: (legend_container, ticks) ->
    return (
        series:
            lines:
                show: true

            points:
                show: true

        crosshair:
            mode: "x"

        legend:
            container: $("##{legend_container}")
            # labelFormatter: (label, series) ->
#                   "<a href=\"#\" onClick=\"togglePlot(" + series.idx + "); return false;\">" + label + "</a>"
            noColumns: 4
            # hideable: true

        grid:
          hoverable: true
          clickable: true
          tickColor: $chrt_border_color
          borderWidth: 0
          borderColor: $chrt_border_color

        tooltip: true
        tooltipOpts: 
          content : Chart.getTooltip
          #content : "Value <b>$x</b> Value <span>$y</span>",
          defaultTheme: false

        xaxis:
            ticks: ticks
            rotateTicks: 30

        selection:
            mode: "xy"
        )

jQuery -> if $("#normalized_bw_chart").length # render only if the chart-id is present

    raw_data = $("#normalized_bw_chart").data('results')
    ticks = $("#normalized_bw_chart").data('ticks')
    all_series = $("#normalized_bw_chart").data('series')

    plot = $.plot($("#normalized_bw_chart"), Chart.generateDataObjects(all_series, raw_data), Chart.generateChartOptions('normalized_bw_legend', ticks))    

if $("#concurrent_flows_chart").length      # render only if the chart-id is present

    raw_data = $("#concurrent_flows_chart").data('results')
    ticks = $("#concurrent_flows_chart").data('ticks')
    all_series = $("#concurrent_flows_chart").data('series')

    plot = $.plot($("#concurrent_flows_chart"), Chart.generateDataObjects(all_series, raw_data), Chart.generateChartOptions('concurrent_flows_legend', ticks))

if $("#bandwidth_chart").length         # render only if the chart-id is present

    raw_data = $("#bandwidth_chart").data('results')
    ticks = $("#bandwidth_chart").data('ticks')
    all_series = $("#bandwidth_chart").data('series')

    plot = $.plot($("#bandwidth_chart"), Chart.generateDataObjects(all_series, raw_data), Chart.generateChartOptions('bandwidth_legend', ticks))    

$("[data-behavior~=chart-selection]").bind "plotselected", (event, ranges) ->
        selected_chart = $(this).attr('id')[0...-6] # slicing the name of the selected item
        console.log  ("zooming in to " + selected_chart)
        plot = $.plot($("##{selected_chart}_chart"), plot.getData(), $.extend(true, {}, Chart.generateChartOptions(selected_chart+'_legend', ticks),
          xaxis:
            min: ranges.xaxis.from
            max: ranges.xaxis.to

          yaxis:
            min: ranges.yaxis.from
            max: ranges.yaxis.to
        ))
     return

$("[data-behavior~=chart-selection]").bind "dblclick", (event, pos, item) ->
        selected_chart = $(this).attr('id')[0...-6] # slicing the name of the selected item
        console.log  ("zooming out to " + selected_chart)
        plot = $.plot($("##{selected_chart}_chart"), plot.getData(), $.extend(true, {}, Chart.generateChartOptions(selected_chart+'_legend', ticks),
          xaxis:
            min: null
            max: null
      yaxis:
        min: null
        max: null
    ))
 return

thanks a lot!


回答1:


Well, you got most of the way there, I just finished it off for you...

Basic issues:

  • Data format is [[x1,y1],[x2,y2],...

     data: [[all_series_data[i][0],all_series_data[i][1]]]
    
  • Specify ticks correctly (this just grabs every odd one)

    tick_labels = []
    for series, i in all_series
        if i % 2
            tick_labels.push [i,series]
        else
            tick_labels.push [i,'']  
    

and then in your plot options:

       xaxis:
            ticks: tick_labels  
  • include the tooltip library and use it in the default way. I used a copy from a CDN.
  • fix a basic coffeescript syntax error (probably just from copying it over here). In your GenerateChartOptions you open a ( and never close it at the end.

Here's a working example: http://jsfiddle.net/ryleyb/0tn1uzs9/2/

Based on comments I added this to the example:

    #in the flot options
        tooltipOpts:
            content: Chart.getToolTip

#to the Chart object
getToolTip: (label, xval, yval, flotItem) ->
    return 'Run: '+raw_data[xval][3]+': value: '+Chart.commify(yval)
commify: (x) ->
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");


来源:https://stackoverflow.com/questions/25870691/flot-charts-producing-custom-trend-with-unique-x-axis-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!