Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

  • Isomorphic
    replied
    As of the latest 4.1d builds, you can now use Date values for the X-axis of a Scatter chart, so the approach mentioned in this thread (conversion to numeric type, then formatters to show values as dates or times) is no longer necessary.

    Sample code is below.

    Code:
    package com.smartgwt.sample.client;
    
    import com.google.gwt.core.client.EntryPoint;
    
    import com.smartgwt.client.data.Record;
    import com.smartgwt.client.types.ChartType;
    import com.smartgwt.client.widgets.chart.FacetChart;
    import com.smartgwt.client.widgets.cube.Facet;
    import com.smartgwt.client.widgets.cube.FacetValue;
    import com.smartgwt.client.widgets.drawing.DrawItem;
    import com.smartgwt.client.widgets.form.DynamicForm;
    import com.smartgwt.client.widgets.form.fields.SelectItem;
    import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
    import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
    import com.smartgwt.client.widgets.layout.VLayout;
    
    import java.util.Date;
    import java.util.LinkedHashMap;
    
    
    public class DateScatterChartSample implements EntryPoint {
    
        private static final long SECONDS = 1000;
        private static final long MINUTES = 60 * SECONDS;
        private static final long HOURS = 60 * MINUTES;
        private static final long DAYS = 24 * HOURS;
        private static final long YEARS = 365 * DAYS;
    
        private static final int NUM_TIME_SCALES = 10;
    
        private static final String[] TIME_SCALE_PROPERTIES = {
            "year5", "year", "day90", "day30", "day7",
            "day", "hour10", "hour5", "hour", "min15"
        };
    
        private static final long[] TIME_SCALES = {
            5 * YEARS, 1 * YEARS, 90 * DAYS, 30 * DAYS, 7 * DAYS,
            1 * DAYS, 10 * HOURS, 5 * HOURS, 1 * HOURS, 15 * MINUTES
        };
    
        private VLayout layout;
        private Record[] chartData;
    
        public void onModuleLoad() {
            String defaultTimeScale = "day90";
    
            @SuppressWarnings("deprecation")
            Date start = new Date(110, 0, 1);
    
            chartData = generateRandomData(start, -10.0, 10.0, 100);
    
            VLayout layout = this.layout = new VLayout();
            layout.setID("dateScatterChartLayout");
            layout.setTop(10);
            layout.setLeft(10);
            layout.setWidth(420);
            layout.setHeight(450);
            layout.setMembersMargin(20);
    
            DynamicForm timeScaleSelector = createTimeScaleSelector(defaultTimeScale);
            FacetChart dateScatterChart = createDateScatterChart(defaultTimeScale);
            layout.setMembers(timeScaleSelector, dateScatterChart);
    
            layout.draw();
        }
    
        private FacetChart createDateScatterChart(String timeScale) {
            FacetChart dateScatter = new FacetChart();
            dateScatter.setID("dateScatterChart");
            dateScatter.setChartType(ChartType.SCATTER);
            dateScatter.setWidth100();
            dateScatter.setHeight100();
            dateScatter.setShowTitle(false);
    
            Facet metricFacet = new Facet("metric");
            metricFacet.setInlinedValues(true);
            metricFacet.setValues(new FacetValue("y"), new FacetValue(timeScale));
            dateScatter.setFacets(metricFacet);
    
            dateScatter.setData(chartData);
    
            dateScatter.setShowChartRect(true);
            dateScatter.setChartRectMargin(10);
            dateScatter.setBandedBackground(false);
    
            DrawItem dataPoint = new DrawItem();
            dataPoint.setFillColor("#7f62b4");
            dataPoint.setLineWidth(0);
            dateScatter.setDataPointProperties(dataPoint);
    
            return dateScatter;
        }
    
        private DynamicForm createTimeScaleSelector(String defaultTimeScale) {
    
            SelectItem selectItem = new SelectItem("timeScale", "Time Scale");
            selectItem.setDefaultValue(defaultTimeScale);
    
            LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>(10);
            valueMap.put("year5", "5 Years");
            valueMap.put("year", "1 Year");
            valueMap.put("day90", "90 Days");
            valueMap.put("day30", "30 Days");
            valueMap.put("day7", "1 Week");
            valueMap.put("day", "1 Day");
            valueMap.put("hour10", "10 Hours");
            valueMap.put("hour5", "5 Hours");
            valueMap.put("hour", "1 Hour");
            valueMap.put("min15", "15 Minutes");
            selectItem.setValueMap(valueMap);
    
            selectItem.addChangedHandler(new ChangedHandler() {
                    @Override
                    public void onChanged(ChangedEvent event) {
                        String newTimeScale = (String)event.getValue();
    
                        VLayout layout = DateScatterChartSample.this.layout;
                        layout.removeMember(layout.getMember(1));
                        layout.addMember(createDateScatterChart(newTimeScale));
                    }
                });
    
            DynamicForm form = new DynamicForm();
            form.setItems(selectItem);
            return form;
        }
    
        private static Record[] generateRandomData(Date start, double minValue, double maxValue, int n) {
    
            long startTime = start.getTime();
            Record[] data = new Record[n];
    
            for (int i = 0; i < n; i++) {
                Record record = data[i] = new Record();
                record.setAttribute("y", minValue + (maxValue - minValue) * Math.random());
    
                for (int j = 0; j < NUM_TIME_SCALES; j++) {
                    Date value = new Date(startTime + (long)Math.floor(Math.random() * TIME_SCALES[j]));
                    record.setAttribute(TIME_SCALE_PROPERTIES[j], value);
                }
            }
            return data;
        }
    }

    Leave a comment:


  • nwilton
    replied
    I've worked it out. :-)

    Hi Everyone,

    I think I've worked it out; and have created some nice example code for everyone. There isn't much documentation (at the moment), so this should be helpful.

    Some notes:
    * The format method is passed an argument of type 'Object'. It appears that this Object may in fact be any primitive wrapper class (i.e. Integer, Long, Double, etc)... so the best way to deal with this is to call the toString() method and parse the result. Maybe Isomorphic can tidy this up in a future release to consistently pass the same object type.
    * The value passed represents each graduation from the 0-based left-side side, up to your maximum X-Axis data value. You can use this to format the X-Axis value.
    * There is no way to pre-determine what the graduations will be. But behind the scenes it tries to pick nice rounded values that are passed into the format() method. This might not be ideal, depending on your final formatting.

    My test code:
    * The attached test code will create a FacetChart of type ScatterType.
    * The Y-Axis will be formatted as percentages (i.e. 0%, 5%, 10%, etc).
    * The X-Axis will be formatted as time values, formatted as 23:59 ("HH:mm").
    * The raw data is CPU utilization measured every 5 minutes, represented as an epoch long timestamp for the time, and a float for the value.

    I have attached a screenshot which demonstrates the final output.

    Regards,
    Nick
    Attached Files

    Leave a comment:


  • nwilton
    replied
    Using XAxisValueFormatter

    Hi Isomorphic,

    I'm looking at using the new setXAxisValueFormatter feature in my application for the Scatter chart type. Specifically, I'm looking at using a Date metric formatted in hours:minutes on the XAxis.

    I have some questions regarding proper use of setXAxisValueFormatter:

    * I have observed that that ValueFormatter.format method passes an integer value of the ordinal of the XAxis label. i.e. The first label has a value of 0, the second = 1, the third = 2, etc.

    * To format the value correctly, I need to calculate where along the XAxis the label with the given ordinal will appear. (i.e. The nth label intersects at which point along the x-axis).

    * To calculate this value, it is unclear how to determine the total number of labels will be drawn on the XAxis. Obviously I need to divide the ordinal of the label by the total number of labels.

    * The example code you posted above uses hard coded values, so this is of limited benefit.

    * I believe that either, a getNumValueLabels() or similar function is required, or alternatively the ValueFormatter interface be altered to return the XAxis intersect position rather than an ordinal.

    Any help or suggestions would be appreciated.

    Thanks,
    Nick

    Leave a comment:


  • mkrellenstein
    replied
    interesting

    Hello,

    So are sparse points now support in a graph?

    If so that might be a reason to upgrade to 4.0 for us. I think I could make a case this way.

    Evan

    Leave a comment:


  • levi
    replied
    Cool thanks!
    I'll take this up again when we're using a 4.0 build.

    Leave a comment:


  • Isomorphic
    replied
    Here's some sample code showing how you could use Dates as axis values using the new axis formatter APIs.

    Note, that the purpose of dividing the millisecond times of the Date values by 1 day (1000 * 60 * 60 * 24) is so that FacetChart will pick a multiple of 1 day for the gradation interval and therefore the formatted x-axis values will be more accurate.

    Code:
        import com.smartgwt.client.data.Record;
        import com.smartgwt.client.types.ChartType;
        import com.smartgwt.client.widgets.chart.FacetChart;
        import com.smartgwt.client.widgets.chart.ValueFormatter;
        import com.smartgwt.client.widgets.cube.Facet;
        import com.smartgwt.client.widgets.cube.FacetValue;
        
        import com.google.gwt.i18n.client.DateTimeFormat;
        
        import java.util.Date;
        
        
        final DateTimeFormat dtf = DateTimeFormat.getFormat("yyyy-MM-dd");
        
        FacetChart chart = new FacetChart();
        chart.setID("dateScatter");
        chart.setChartType(ChartType.SCATTER);
        
        Facet metricFacet = new Facet("metric");
        metricFacet.setInlinedValues(true);
        metricFacet.setValues(new FacetValue("value"), new FacetValue("date"));
        
        chart.setFacets(metricFacet, new Facet("label"));
        chart.setShowTitle(false);
        chart.setTop(10);
        chart.setLeft(10);
        chart.setWidth(500);
        chart.setHeight(500);
        
        final String[] labels = new String[] { "A", "B", "C" };
        int numLabels = labels.length;
        int dataPointsPerLabel = 10;
        float minValue = -10.0f, maxValue = 10.0f;
        
        Date now = new Date();
        Date thirtyDaysAgo = new Date(now.getYear(), now.getMonth(), now.getDate() - 30);
        final long maxTime = now.getTime(), minTime = thirtyDaysAgo.getTime();
        
        final float day = 1000f * 60 * 60 * 24;
        
        Record[] data = new Record[numLabels * dataPointsPerLabel];
        for (int l = 0; l < numLabels; ++l) {
            String label = labels[l];
            for (int j = 0; j < dataPointsPerLabel; ++j) {
                int i = dataPointsPerLabel * l + j;
                long time = minTime + (long) Math.floor((maxTime - minTime + 1) * Math.random());
        
                Record record = data[i] = new Record();
                record.setAttribute("date", (time - minTime) / day);
                record.setAttribute("value", minValue + (maxValue - minValue) * Math.random());
                record.setAttribute("label", label);
            }
        }
        
        chart.setXAxisValueFormatter(new ValueFormatter() {
                public String format(Object value) {
                    int dateValue = ((Integer) value).intValue();
                    long time = minTime + (long) (day * dateValue);
                    return dtf.format(new Date(time));
                }
            });

    Leave a comment:


  • Isomorphic
    replied
    APIs for defining axis-specific formatters will appear in tomorrow's 4.0d builds.

    Leave a comment:


  • Isomorphic
    replied
    We do plan to add axis-specific formatters.

    You should automatically see axes update, but make sure you have the latest nightly build of 4.0d as there was a bug affecting this, fixed several weeks ago.

    Also note again that currently, axes will always include 0. So if you're expecting auto-scaling to eliminate zero since all your points have numeric values in the 10s of millions, that won't currently happen.

    Leave a comment:


  • levi
    replied
    Ok thanks, we'll keep it mind.
    I'm just creating a quick POC to include a chart with a bunch of points & lines for a lot of data. The requirements might change, but I feel the X is going to be datetime based.

    I do get good results already:
    I reformat a date "yyyy-mm-dd hh:mm:ss" to parse as float as "yyyymmdd.hhmmss" and that looks great in the chart and thought indeed of using a formatter to make the labels pretty again. I don't immediately understand why there's 1 and the same formatter used for all axes (I assume it's for X and all Ys)? Is that for switching facets?
    Is this also something possible to enhance?

    I do see something weird:
    * you can have a tooltip formatter which has (value, record) as arguments. But my record is inside an attribute named "record" in the incoming record there.
    * the graph doesn't reset its 'X & Y scale' when I do setData a few times on the same graph.

    Leave a comment:


  • Isomorphic
    replied
    Neither 3.1 nor 4.0 would currently automatically recognize Date instances as axis values on a Scatter plot.

    Mapping dates to [0,1] would only sort of work or might require hacks to work. You would need an AxisValueFormatter to format the [0,1] values into legible dates, but the AxisValueFormatter is used for both axes without any parameter to tell it which axis it's being used for.

    If the other axis displays values in the [0,1] range you'll have ambiguity; regardless you've have ambiguity for 0. You might be able to put together a hack based on knowing which axis draws first. For a supported solution, consider sponsoring official support for axes with date values.

    Leave a comment:


  • levi
    replied
    Hi,

    Originally posted by Isomorphic View Post
    If the spacing should reflect the date, you're basically looking for a scatter plot, which isn't a built-in chart type. You could:

    1. pad out the data with blank values to get the spacing right

    2. sponsor an officially supported scatter plot mode

    3. build your own scatter plot based on DrawPane
    In the meantime, SmartGWT 3.1 does have a Scatter plot type, with the blogpost about v4.0 mentioning even more cooler things for the Scatter plot.

    Does it now also recognize dates (/with time) as values so irregular data points will be spaced according to its true 'place' time? Or would we need to map all dates to a [0,1] float interval first and use these to get the spacing right? (Question for current v3.1p or coming up in v4?)

    Leave a comment:


  • evansmith1
    replied
    Hello,

    Ok I dropped in the Annotate graph from google

    This draws a line graph that I wanted. But the resizing does not seem to work. I have tried several things. It does work the window is first drawn, but when resizes occur it does not figure it out.

    How does one get the Resize event/method for a VLayout>
    Evan

    public GoogleLineGraph(ListGrid lg, VLayout tempChartLayout) {
    listGrid = lg;
    chartLayout = tempChartLayout;
    Runnable onLoadCallback = new Runnable() {
    public void run() {
    DataTable data = createHistoryTable(listGrid);
    AnnotatedTimeLine.Options options = AnnotatedTimeLine.Options.create();
    options.setDisplayAnnotations(true);
    options.setDisplayZoomButtons(true);
    options.setScaleType(AnnotatedTimeLine.ScaleType.ALLFIXED);
    options.setLegendPosition(AnnotatedTimeLine.AnnotatedLegendPosition.SAME_ROW);
    // options.setOption("Title", CHART_TITLE);
    // getting height / width is tricky ...
    myGraph = new AnnotatedTimeLine(data, options, listGrid.getWidthAsString(), chartLayout.getHeightAsString());
    chartLayout.addMember(myGraph);
    }

    };
    // Load the visualization api, passing the onLoadCallback to be called
    // when loading is done.
    VisualizationUtils.loadVisualizationApi(onLoadCallback, AnnotatedTimeLine.PACKAGE);
    }

    Leave a comment:


  • Isomorphic
    replied
    If the spacing should reflect the date, you're basically looking for a scatter plot, which isn't a built-in chart type. You could:

    1. pad out the data with blank values to get the spacing right

    2. sponsor an officially supported scatter plot mode

    3. build your own scatter plot based on DrawPane

    Leave a comment:


  • evansmith1
    replied
    private RecordList GetRecordList () {
    RecordList myRecList = new RecordList();
    Record[] recArray = listGridHRAVI.getRecordList().toArray();
    DateTimeFormat dtf = DateTimeFormat.getFormat("yyyy-MM-dd");
    for(int i = 0; i < recArray.length; i++) {
    Record record = recArray[i];
    Record entryRecord = new Record();
    Date valueDate = record.getAttributeAsDate("rdate");
    String strDate = dtf.format(valueDate);
    String strValue = record.getAttribute("ravi");
    entryRecord.setAttribute("rdate", strDate);
    entryRecord.setAttribute("type", "ravi1");
    entryRecord.setAttribute("ravi", strValue);
    System.out.println("Date is " + strDate + ", value is " + record.getAttribute("ravi"));
    myRecList.add(entryRecord);
    }
    return myRecList;
    }

    Which gives me
    Date is 1986-05-31, value is 7
    Date is 1986-07-23, value is 23
    Date is 1986-10-07, value is 23
    Date is 1993-02-01, value is 13
    Date is 1996-03-05, value is 13
    Date is 2011-08-31, value is 13


    However when I graph this the xaxis, the dates, are all evenly spaced.

    Howe do I get the xaxis to see it as a graph and to space stuff out over time?

    Thanks,
    Evan

    Leave a comment:


  • Isomorphic
    replied
    If you want any particular String format for that date, just use DateTimeFormat to produce it and put it into "entryRecord" as a String.

    Leave a comment:

Working...
X