Using Per Item Fills in Flex/Actionscript - apache-flex

I am creating a chart using mxml. The mxml tags only create a chart with a horizontal axis and vertical axis.
My result event handler has actionscript code that loops through the xml result set and creates all the series (line series and stacked bar). This part of the code works fine.
Now I need to use the functionfill function to set individual colors to each series. All the examples I have found call the functionfill from within an MXML tag, like so:
<mx:ColumnSeries id="salesGoalSeries"
xField="Name"
yField="SalesGoal"
fillFunction="myFillFunction"
displayName="Sales Goal">
I am having trouble calling functionfill from actionscript.
A portion of the code that build the data series is below:
if (node.attribute("ConfidenceStatus")=="Backlog"
|| node.attribute("ConfidenceStatus")=="Billings") {
// Create the new column series and set its properties.
var localSeries:ColumnSeries = new ColumnSeries();
localSeries.dataProvider = dataArray;
localSeries.yField = node.attribute("ConfidenceStatus");
localSeries.xField = "TimebyDay";
localSeries.displayName = node.attribute("ConfidenceStatus");
localSeries.setStyle("showDataEffect", ChangeEffect);
localSeries.fillFunction(setSeriesColor(xxx));
// Back up the current series on the chart.
var currentSeries:Array = chart.series;
// Add the new series to the current Array of series.
currentSeries.push(localSeries);
//Add Array of series to columnset
colSet.series.push(localSeries);
//assign columnset to chart
chart.series = [colSet];
My setSeriesColor function is:
private function setSeriesColor(element:ChartItem, index:Number):IFill {
var c:SolidColor = new SolidColor(0x00CC00);
var item:ColumnSeriesItem = ColumnSeriesItem(element);
//will put in logic here
return c;
}
What parameters do I put in the line localSeries.fillFunction(setSeriesColor(xxx)) ?
I tried localSeries as the first argument but I get an implicit coercion error telling me localSeries can't be cast as ChartItem.
How do I call the function correctly?

localSeries.fillFunction = setSeriesColor;
The code you have right now is actually CALLING setSeriesColor the way you have it set up. You only want it to refer to a reference of the function, not calling it, so just send it "setSeriesColor" as a variable.

Related

How to use MDX constants/functions in icCube Reporting?

I have several functions/constants defined in the schema. An example is:
_latestPeriodWithData() which returns a date.
Goal: I want to use this value in the Reporting to set a Guide in a chart, using the demo example 'On Widget Options':
This is what I tried so far:
I have assigned this function as 'MDX / Value' for a Report Constant _lastDate and obtained the value for this constant using the java script function: context.eventValue('_lastDate'); but that just gives me the caption. The function context.eventObject("_lastDate").options.asMdx gives me _latestPeriodWithData(), but not its value.
On Widget Options
/**
* Result will be used while rendering.
*/
function(context, options, $box) {
// for debugging purpose
console.log(context.eventObject("_lastDate").options.asMdx);
options.guides[0].value = context.eventObject("_lastDate").options.asMdx; // <-- gives me _latestPeriodeWith data
// but not its value
options.guides[0].toValue = context.eventObject("_lastDate").options.asMdx;
options.guides[0].lineAlpha = 1;
options.guides[0].lineColor = "#c44";
return options;
}

Using GraphView Library for functions to display multiple graphs

I'm currently developing an android app for reading out multiple sensor values via Bluetooth and display them in a graph. When I stumbled upon jjoe64's GraphViewLibrary, I knew this would fit my purposes perfectly. But now I'm kind of stuck. Basically, I wrote a little function that would generate and display the values of three sensors in 3 different graphs one under the other. This works just fine when the activity is started first, all three graphs a nicely rendered and displayed. But when I want to update the graphs with different values using the resetData()-method to render the new values in each graph, only the last of the three graphs is updated. Obviously, because it's the last graph generated using this rather simple function. My question is: Is there any other elegant way to use a function like mine for generating and updating all three graphs one after the other? I already tried to set the GraphView variable back to null and different combinations of removing and adding the view. Passing the function a individual GraphView-variable like graphView1, graphView2... does also not work.
Here is the function:
private GraphView graphView;
private GraphViewSeries graphViewSerie;
private Boolean graphExisting = false;
...
public void makeGraphs (float[] valueArray, String heading, int graphId) {
String graphNumber = "graph"+graphId;
int resId = getResources().getIdentifier(graphNumber,"id", getPackageName());
LinearLayout layout = (LinearLayout) findViewById(resId);
int numElements = valueArray.length;
GraphViewData[] data = new GraphViewData[numElements];
for (int c = 0; c<numElements; c++) {
data[c] = new GraphViewData(c+1, valueArray[c]);
Log.i(tag, "GraphView Graph"+graphId+": ["+(c+1)+"] ["+valueArray[c]+"].");
}
if (!graphExisting) {
// init temperature series data
graphView = new LineGraphView(
this // context
, heading // heading
);
graphViewSerie = new GraphViewSeries(data);
graphView.addSeries(graphViewSerie);
((LineGraphView) graphView).setDrawBackground(true);
graphView.getGraphViewStyle().setNumHorizontalLabels(numElements);
graphView.getGraphViewStyle().setNumVerticalLabels(5);
graphView.getGraphViewStyle().setTextSize(10);
layout.addView(graphView);
}
else {
//graphViewSerie = new GraphViewSeries(data);
//graphViewSerie.resetData(data);
graphViewSerie.resetData(new GraphViewData[] {
new GraphViewData(1, 1.2f)
, new GraphViewData(2, 1.4f)
, new GraphViewData(2.5, 1.5f) // another frequency
, new GraphViewData(3, 1.7f)
, new GraphViewData(4, 1.3f)
, new GraphViewData(5, 1.0f)
});
}
And this is the function-call depending on an previously generated array (which is being monitored to be filled with the right values):
makeGraphs(graphData[0], "TempHistory", 1);
makeGraphs(graphData[1], "AirHistory", 2);
makeGraphs(graphData[2], "SensHistory", 3);
graphExisting = true;
Any help and / or any feedback in general is greatly appreciated! Lots of thanks in advance!
EDIT / UPDATE:
Thanks to jjoe64's answer I was able to modify the function to work properly. I was clearly having a mistake in my thinking, since I thought I'd also be changing a GraphViewSeries-object I would handle my function as additional parameter (which I tried before). Of course this does not work. However, with this minor Improvements I managed to make this work using a Graphviewseries Array. To give people struggling with a similar problem an idea of what I had to change, here the quick-and-dirty draft of the solution.
I just changed
private GraphViewSeries graphViewSerie;
to
private GraphViewSeries graphViewSerie[] = new GraphViewSeries[3];
and access the right Series using the already given parameter graphId within the function (if-clause) like this:
int graphIndex = graphId - 1;
graphViewSerie[graphIndex] = new GraphViewSeries(data);
In the else-clause I'm updating the series likewise by calling
graphViewSerie[graphIndex].resetData(data);
So, once again many thanks for your support, jjoe64. I'm sorry I wasn't able to update the question earlier, but I did not find time for it.
of course it is not working correct, because you save always the latest graphseries-object in the member graphViewSerie.
First you have to store the 3 different graphviewseries (maybe via array or map) and then you have to access the correct graphviewseries-object in the else clause.

Zoom into group of points in Flex

I have an application in Flex 4 with a map, a database of points and a search tool.
When the user types something and does the search it returns name, details and coordinates of the objects in my database.
I have a function that, when i click one of the results of my search, it zooms the selected point of the map.
The question is, i want a function that zooms all the result points at once. For example if i search "tall trees" and it returns 10 points, i want that the map zooms to a position where i can see the 10 points at once.
Below is the code im using to zoom one point at a time, i thought flex would have some kind of function "zoom to group of points", but i cant find anything like this.
private function ResultDG_Click(event:ListEvent):void
{
if (event.rowIndex < 0) return;
var obj:Object = ResultDG.selectedItem;
if (lastIdentifyResultGraphic != null)
{
graphicsLayer.remove(lastIdentifyResultGraphic);
}
if (obj != null)
{
lastIdentifyResultGraphic = obj.graphic as Graphic;
switch (lastIdentifyResultGraphic.geometry.type)
{
case Geometry.MAPPOINT:
lastIdentifyResultGraphic.symbol = objPointSymbol
_map.extent = new Extent((lastIdentifyResultGraphic.geometry as MapPoint).x-0.05,(lastIdentifyResultGraphic.geometry as MapPoint).y-0.05,(lastIdentifyResultGraphic.geometry as MapPoint).x+0.05,(lastIdentifyResultGraphic.geometry as MapPoint).y+0.05,new SpatialReference(29101)).expand(0.001);
break;
case Geometry.POLYLINE:
lastIdentifyResultGraphic.symbol = objPolyLineSymbol;
_map.extent = lastIdentifyResultGraphic.geometry.extent.expand(0.001);
break;
case Geometry.POLYGON:
lastIdentifyResultGraphic.symbol = objPolygonSymbol;
_map.extent = lastIdentifyResultGraphic.geometry.extent.expand(0.001);
break;
}
graphicsLayer.add(lastIdentifyResultGraphic);
}
}
See the GraphicUtil class from com.esri.ags.Utils package. You can use the method "getGraphicsExtent" to generate an extent from an array of Graphics. You then use the extent to set the zoom factor of your map :
var graphics:ArrayCollection = graphicsLayer.graphicProvider as ArrayCollection;
var graphicsArr:Array = graphics.toArray();
// Create an extent from the currently selected graphics
var uExtent:Extent;
uExtent = GraphicUtil.getGraphicsExtent(graphicsArr);
// Zoom to extent created
if (uExtent)
{
map.extent = uExtent;
}
In this case, it would zoom to the full content of your graphics layer. You can always create an array containing only the features you want to zoom to. If you find that the zoom is too close to your data, you can also use map.zoomOut() after setting the extent.
Note: Be careful if you'Ve got TextSymbols in your graphics, it will break the GraphicUtil. In this case you need to filter out the Graphics with TextSymbols
Derp : Did not see the thread was 5 months old... Hope my answer helps other people

Refresh a BarChart after adding a BarSeries

first question here.
I have a BarChart showing several normal bars and a BarSeries, like this:
<mx:BarChart id="barchart" dataProvider="{model.myList}" type="clustered">
<mx:horizontalAxis>
<mx:LinearAxis autoAdjust="true"/>
</mx:horizontalAxis>
<mx:verticalAxis>
<mx:CategoryAxis categoryField="name"/>
</mx:verticalAxis>
<mx:series>
<mx:BarSeries dataProvider="{model.myList}" xField="myValue"/>
</mx:series>
</mx:BarChart>
When a user clicks on a button, i need to calculate some values, put them on the "myCalculatedValue" and add another BarSeries as a comparison. I'm doing this:
var barSerie:BarSeries = new BarSeries();
barSerie.dataProvider = model.myList;
barSerie.xField = "myCalculatedValue";
barchart.series.push(barSerie);
But the BarChart does not change at all. Is there some way to refresh the chart after adding the new BarSeries?
The short answer is to use this code:
var barSerie:BarSeries = new BarSeries();
barSerie.dataProvider = model.myList;
barSerie.xField = "myCalculatedValue";
var allSeries:Array = barchart.series;
allSeries.push(barSerie);
barchart.series = allSeries;
The LONG answer, to give you an understanding of how this works:
The way flex knows when to refresh a ui component that is data backed, such as a chart, is on the function set dataProvider property, or equivalent (in this case series).
Here is the code from mx.charts.chartClasses.ChartBase:
public function set series(value:Array /* of Series */):void
{
value = value == null ? [] : value;
_userSeries = value;
var n:int = value.length;
for (var i:int = 0; i < n; ++i)
{
if (value[i] is Series)
{
(value[i] as Series).owner = this;
}
}
invalidateSeries();
invalidateData();
legendDataChanged();
}
Notice that the relevant "invalidate" methods are called at the end of the set method.
If you say barChart.series.push(x), the set series method is never called, only the getter. Therefore in order to force the chart to know there was a change to the series, you need to assign a new value to the series.
It is worth pointing out that even assigning the series to itself will cause an invalidate, although it isn't good coding
barChart.series = barChart.series
Flex is communicating with the server remotely, correct? You probably need to make another explicit call to the app controller that is housing flex.

flex chart columnseries not visible

I have a flex chart that I'm trying to build via actionscript dynamically. For test purposes I came up with the following data structure and code:
Bindable]
public var columnDat:Array=
[{signalID:"SCL", point2:100},
{signalID:"SCL", point2:50},
{signalID:"SCL", point2:30},
{signalID:"SCL", point2:60},
{signalID:"SCL", point2:220},
{signalID:"SCL", point2:140},
{signalID:"SCL", point2:280}];
public function makeDummyChart(genericChart:CartesianChart, genericLegend:Legend, chartPanel:ChartPanel):void {
var renderers:ArrayCollection = new ArrayCollection();
genericChart = new ColumnChart();
// Define the two axes.
var dispAxis:CategoryAxis = new CategoryAxis();
var axr:AxisRenderer = new AxisRenderer();
axr.axis = dispAxis;
renderers.addItem(axr);
var seriesList:ArrayCollection=new ArrayCollection();
// Add the series
genericChart.horizontalAxis = dispAxis;
var columnSeries:ColumnSeries = new ColumnSeries();
BindingUtils.bindProperty(columnSeries, "dataProvider", this, "columnDat");
columnSeries.xField="signalID";;
columnSeries.yField="point2";
seriesList.addItem(columnSeries);
genericChart.series = seriesList.toArray();
genericLegend.dataProvider = genericChart;
genericChart.horizontalAxisRenderers = renderers.toArray();
genericLegend.dataProvider = genericChart;
// chart panel is just the panel on the screen where chart is displayed
chartPanel.addChild(genericChart);
trace (" make dummy chart done");
}
I just get a blank chart when I run this code.
Can't test it by now, but it should be sufficient if you just assign the columnDat array to the series:
columnSeries.dataProvider = columnDat;
or to the column chart:
genericChart.dataProvider = columnDat;
First, you should probably use MXML for this stuff. It's easier.
Second, I don't think you followed the example very well. There's 2 ways of doing charts:
1) Add data to the chart data provider and have the series specify the x and y field within that data provider.
2) Don't add data to the chart and just add the data directly into series without specifying the x and y field.
Right now, you're doing a mix of both 1 and 2 and the series can't see the data because it's being filtered out, which is why it's blank. Don't set the data provider on the series, but set it on the chart instead and it should work. For further example, look at the docs.

Resources