I am trying to create LineChartReport using more than 1000 data points. Problem is that the X Axis should show the time stamp, and since there are too many data points, the data gets overlapped and no comprehensible data is shown. So I need help on following 2 points:
1. Limit the data points on X-Axis (only) to say 25. The number of data points for the graph/chart still remains at 1000
2. Rotate the Timestamp data by 90 degrees so that the Timestamp data is recorded correctly and not truncated.
Have tried to get the domain axis and manipulate it, like this, but the library does not allow that:
CategoryAxis domainAxis = chart.getCategoryPlot().getDomainAxis();
domainAxis.setMinorTickMarksVisible(false);
domainAxis.clearCategoryLabelToolTips();
chart.getCategoryPlot().getDataset().getColumnKeys()
CategoryDataset ds = chart.getCategoryPlot().getDataset();
List ls = ds.getColumnKeys();
List ls2 = new ArrayList();
int i = 0;
for (Iterator it = ls.iterator(); it.hasNext(); ) {
it.next();
if (i % 2 != 0) {
ls2.add(ls.get(i));
}
i++;
}
chart.getCategoryPlot().setDataset(ds);
Sample image with 10 data points appear here: https://drive.google.com/drive/u/0/folders/0B-m6SCJULOTRdHZ6cUwxX041SHM
Any suggestions ??
The below codes are based on DynamicReport 4.0.2. I didn't test them in other versions.
Regarding your first question, you want 1000 points of data, and just want the few data in line chart. In this case, you need to use the different data source for you data table and line chart.
Firstly, create the subreport for the data table and set up.
SubreportBuilder subreport = cmp.subreport(
report().setTemplate(Templates.reportTemplate)
.addColumn(
col.column("Name", "name", type.stringType()),
col.column("Counts", "value", type.integerType())
)
);
JasperReportBuilder reportContent = report();
subreport.setDataSource(allDatasource);
reportContent.summary(subreport, cmp.verticalGap(20));
Secondly, prepare another data source for line chart and set up.
reportContent.setTemplate(Templates.reportTemplate)
/* add title */
.title(title, subtitle,
/* add chart in the head of title */
cmp.verticalList(LINE_CHART)
/* set style */
.setStyle(stl.style().setBottomPadding(30).setTopPadding(30)))
/* set data source for line chart*/
.setDataSource(dataSource);
About your second question, you need to create customizer at first.
public class DynamicLineCustomizer implements DRIChartCustomizer, Serializable {
private static final long serialVersionUID = -8493880774698206000L;
#Override
public void customize(JFreeChart jFreeChart, ReportParameters reportParameters) {
CategoryPlot plot = jFreeChart.getCategoryPlot();
CategoryAxis domainAxis = plot.getDomainAxis();
domainAxis.setCategoryLabelPositions(CategoryLabelPositions
.createUpRotationLabelPositions(Math.PI / 6.0));
}
}
Then use this customizer in line chard builder.
LineChartBuilder lineChart = cht.lineChart()
.customizers(new DynamicLineCustomizer())
.setCategory(columns[0])
.series(createSeries(columns))
.setCategoryAxisFormat(cht.axisFormat().setLabel("TimeStamp"))
.seriesColors(seriesColors);
The line chart and data table will be like below:
This finally worked for me (hope it helps somebody) :
Ref: http://www.dynamicreports.org/forum/viewtopic.php?f=1&t=1046
private void build(String startDate, String endDate) {
TextColumnBuilder<Integer> i = col.column("I", "I", type.integerType());
TextColumnBuilder<Integer> b = col.column("B", "B", type.integerType());
TextColumnBuilder<Integer> t = col.column("T", "T", type.integerType());
TextColumnBuilder<Date> timeColumn = col.column("TimeStamp", "TimeStamp", type.dateType());
createDataSource(startDate, endDate);
try {
TimeSeriesChartBuilder timeSeriesChartBuilder1 = cht.timeSeriesChart();
timeSeriesChartBuilder1.series(cht.serie(b), cht.serie(t), cht.serie(i));
timeSeriesChartBuilder1.setShowShapes(false);
timeSeriesChartBuilder1.setDataSource(dataSource);
timeSeriesChartBuilder1.setTimePeriod(timeColumn);
timeSeriesChartBuilder1.setTimePeriodType(TimePeriod.SECOND);
timeSeriesChartBuilder1.setTitle("ABC Information");
JasperReportBuilder builder = report()
.summary(cht.multiAxisChart(timeSeriesChartBuilder1))
.setTemplate(Templates.reportTemplate)
.title(Templates.createTitleComponent("ABC Complete Info"))
;
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}
Related
I am currently using the OptaPlanner's job schedule algorithm to create a certain planning. I want every execution mode used in the planning to be shown in a different color (instead of all different projects to be shown in different colors). Is it possible to implement this and if so, how? I have been searching through the code for a while now and have no idea how to do this.
This cannot be done easily with the Project Scheduling Swing application that's part of OptaPlanner project. It plots the data using JFreeChart and I couldn't find a simple way to associate metadata (like color) with the data that's being plotted.
You can override YIntervalRenderer behavior to return color of your choice based on data item's row (seriesIndex) and column (item's index in the series) but you have to keep the mapping between execution mode and [row, column] yourself, which is cumbersome.
Here's an example of modified ProjectJobSchedulingPanel that does the above:
public class ProjectJobSchedulingPanel extends SolutionPanel<Schedule> {
private static final Logger logger = LoggerFactory.getLogger(ProjectJobSchedulingPanel.class);
private static final Paint[] PAINT_SEQUENCE = DefaultDrawingSupplier.DEFAULT_PAINT_SEQUENCE;
public static final String LOGO_PATH = "/org/optaplanner/examples/projectjobscheduling/swingui/projectJobSchedulingLogo.png";
public ProjectJobSchedulingPanel() {
setLayout(new BorderLayout());
}
#Override
public void resetPanel(Schedule schedule) {
removeAll();
ChartPanel chartPanel = new ChartPanel(createChart(schedule));
add(chartPanel, BorderLayout.CENTER);
}
private JFreeChart createChart(Schedule schedule) {
YIntervalSeriesCollection seriesCollection = new YIntervalSeriesCollection();
Map<Project, YIntervalSeries> projectSeriesMap = new LinkedHashMap<>(
schedule.getProjectList().size());
ExecutionMode[][] executionModeByRowAndColumn = new ExecutionMode[schedule.getProjectList().size()][schedule.getAllocationList().size()];
YIntervalRenderer renderer = new YIntervalRenderer() {
#Override
public Paint getItemPaint(int row, int column) {
ExecutionMode executionMode = executionModeByRowAndColumn[row][column];
logger.info("getItemPaint: ExecutionMode [{},{}]: {}", row, column, executionMode);
return executionMode == null
? TangoColorFactory.ALUMINIUM_5
: PAINT_SEQUENCE[(int) (executionMode.getId() % PAINT_SEQUENCE.length)];
}
};
Map<Project, Integer> seriesIndexByProject = new HashMap<>();
int maximumEndDate = 0;
int seriesIndex = 0;
for (Project project : schedule.getProjectList()) {
YIntervalSeries projectSeries = new YIntervalSeries(project.getLabel());
seriesCollection.addSeries(projectSeries);
projectSeriesMap.put(project, projectSeries);
renderer.setSeriesShape(seriesIndex, new Rectangle());
renderer.setSeriesStroke(seriesIndex, new BasicStroke(3.0f));
seriesIndexByProject.put(project, seriesIndex);
seriesIndex++;
}
for (Allocation allocation : schedule.getAllocationList()) {
int startDate = allocation.getStartDate();
int endDate = allocation.getEndDate();
YIntervalSeries projectSeries = projectSeriesMap.get(allocation.getProject());
int column = projectSeries.getItemCount();
executionModeByRowAndColumn[seriesIndexByProject.get(allocation.getProject())][column] = allocation.getExecutionMode();
logger.info("ExecutionMode [{},{}] = {}", seriesIndexByProject.get(allocation.getProject()), column, allocation.getExecutionMode());
projectSeries.add(allocation.getId(), (startDate + endDate) / 2.0,
startDate, endDate);
maximumEndDate = Math.max(maximumEndDate, endDate);
}
NumberAxis domainAxis = new NumberAxis("Job");
domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
domainAxis.setRange(-0.5, schedule.getAllocationList().size() - 0.5);
domainAxis.setInverted(true);
NumberAxis rangeAxis = new NumberAxis("Day (start to end date)");
rangeAxis.setRange(-0.5, maximumEndDate + 0.5);
XYPlot plot = new XYPlot(seriesCollection, domainAxis, rangeAxis, renderer);
plot.setOrientation(PlotOrientation.HORIZONTAL);
// Uncomment this to use Tango color sequence instead of JFreeChart default sequence.
// This results in color per project mode.
// DefaultDrawingSupplier drawingSupplier = new DefaultDrawingSupplier(
// TangoColorFactory.SEQUENCE_1,
// DefaultDrawingSupplier.DEFAULT_FILL_PAINT_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE);
// plot.setDrawingSupplier(drawingSupplier);
return new JFreeChart("Project Job Scheduling", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
}
}
Result:
Another approach would be to implement JFreeChart interfaces and make custom Dataset and Renderer so that you could plot Allocations directly. Similar to the Gantt chart implementaion in JFreeChart.
Or write your custom UI from the ground up. Depends op how much effort you're willing to put into it :)
I am looking for some solution where i want to pass 100s of records to the form where i am having more than 50 fields. I did some research for the testNG data providers but it looks like that it returns only strings so i feel that it will not be feasible to go with data providers as if its not good to pass 50 string arguments to specific function. Also i did some research to read excel file and i get two ways that either i can go with the jxl or Apache poi but with that also i am not able to read the data by the column header as if i can not go with the row and column number of approach as i have so many fields that i need to work with. The reason behind that is that in future is one field has added to single form that its going to be rework and again its not feasible.
enter image description here
I have been following this link:
http://www.softwaretestinghelp.com/selenium-framework-design-selenium-tutorial-21/
for reading data column wise but any how i am not getting the records based on the column header. Do we have any other way to achieve this.
Thanks
"testNG data providers but it looks like that it returns only strings" - incorrect. It allows you to return a multidimensional array of type Object. What kind of object you create is your own code. You may choose to read from the excel, encapsulate all the fields in one object (your own pojo) or multiple objects and then the method argument can have just those object types declared and not the 50 strings.
Both jxl and poi are libraries to interact with excel. If you want to have specific interaction with excel, like reading based on header, then you need to write code for that - it doesn't come out of the box.
If you are concerned about addition of one more column , then build your indices first by reading the header column, then put it in a relevant data structure and then go about reading your data.
I finally achieved that with the help of apache poi. I created on centralized function that is returning the hashmap having title as an index.
Here is that function:
Here is my main test function:
#Test(dataProvider="dpCreateNewCust")
public void createNewCustomer(List<Map<String, String>> sheetList){
try{
//Step 2. Login
UtilityMethods.SignIn();
for(Map<String, String> map : sheetList){
//Step 3. New Customer
if(map.get("Testcase").equals("Yes"))
{
//Process with excel data
ProcessNewCustomer(map);
}
}
}
catch(InterruptedException e)
{
System.out.println ("Login Exception Raised: <br> The exception get caught" + e);
}
}
//My data provider
#DataProvider(name = "dpCreateNewCust")
public Object[][] dpCreateNewCust(){
XLSfilename = System.getProperty("user.dir")+"//src//watts//XLSFiles//testcust.xlsx";
List<Map<String, String>> arrayObject = UtilityMethods.getXLSData(XLSfilename,Sheetname));
return new Object[][] { {arrayObject } };
}
//----GetXLSData Method in UtilityMethods Class :
public static List<Map<String, String>> getXLSData(String filename, String sheetname)
{
List<String> titleList = new ArrayList<String>();
List<Map<String, String>> sheetList = new ArrayList<Map<String, String>>();
try {
FileInputStream file = new FileInputStream(filename);
//Get the workbook instance for XLS file
XSSFWorkbook XLSbook = new XSSFWorkbook(file);
//Get first sheet from the workbook
//HSSFSheet sheet = workbook.getSheetAt(0);
WorkSheet = XLSbook.getSheet(sheetname);
//Iterate through each rows from first sheet
int i = 0;
Iterator<Row> rowIterator = WorkSheet.iterator();
while(rowIterator.hasNext()) {
Row row = rowIterator.next();
//For each row, iterate through each columns
Iterator<Cell> cellIterator = row.cellIterator();
int j = 0;
Map<String, String> valueMap = new HashMap<>();
while(cellIterator.hasNext()) {
Cell cell = cellIterator.next();
if(i==0){
titleList.add(cell.getStringCellValue());
}
else
{
String cellval = "";
switch(cell.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN:
cellval = cell.getBooleanCellValue()+"";
break;
case Cell.CELL_TYPE_NUMERIC:
cellval = String.valueOf(cell.getNumericCellValue())+"";
break;
case Cell.CELL_TYPE_STRING:
cellval = cell.getStringCellValue();
break;
default:
break;
}
if(cellval!="")
{
valueMap.put(titleList.get(j), cellval); valueMap.put("ResultRow",String.valueOf(row.getRowNum()));
valueMap.put("ResultCol",String.valueOf(0));
}
}
j++;
}
if(i!=0 && !valueMap.isEmpty()){
//System.out.println(valueMap);
sheetList.add(valueMap);
}
i++;
}
//System.out.println(sheetList); System.exit(0);
file.close();
XLSbook.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sheetList;
}
I am using Jfreechart API 1.0.8 to generate the TimeSeriesChart(line chart).
when I am generating the chart, i am facing the problem of overlapping.
Here I am trying to display the rendered points(graph rendered points), by using XYLineAndShapeRenderer with StandardXYItemLabelGenerator.
When the points are displayed, the data-point is overlapping with the generated line chart (graph).
I am taking X-Axis as time, and y-Axis as revenue of organization, and i am using line chart here.
I'm displaying the points as discussed below.
By using "renderer.setBasePositiveItemLabelPosition" method, globally i am setting the position of graph points(data points) inside the xyplot rendered chart while considering the rendered "ItemLabelAnchor".
I am sending my sample code here:
chart = ChartFactory.createTimeSeriesChart("", "", "", newxyseries, false, true, false);
renderer = new XYLineAndShapeRenderer();
renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer();
renderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator("{2}", monthDate, formatSymbol));
renderer.setBaseItemLabelsVisible(true);
renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.TOP_RIGHT));
chart.getXYPlot().setRenderer(renderer);
But when I am generating the graph chart using Ms-office of Excel tools, there is no problem of overlapping of labels, the points are displayed in an effective manner without any overlapping.
JFreeChart doesn't do any overlap detection for item labels. It would be a great feature to have, but nobody has written the code to do it.
you have to refrain the labels to get overlap by define your own algo or logic because as the graph keep growing sooner the labels will start to get overlap.
the key is to make some or alternative labels transparent so that no overlapping occur
For example in the following P.O.C, only 35 labels will be drawn but the tick of graphs will remai same just the labels will reduce
CategoryAxis domainAxis = plot.getDomainAxis();
CategoryLabelPositions pos = domainAxis.getCategoryLabelPositions();
double size = plot.getCategories().size();
double occurence = Math.ceil(plot.getCategories().size() / 20);
double interval = Math.ceil(plot.getCategories().size() / 20);
for (int i = 1; i <= plot.getCategories().size(); i++) {
if (plot.getCategories().size() > 35) {
if(plot.getCategories().size()>35 && plot.getCategories().size()<250) {
if(i%8==0){
String cat_Name = (String) plot.getCategories().get(i-1);
} else{
String cat_Names = (String) plot.getCategories().get(i-1);
domainAxis.setTickLabelPaint(cat_Names, new Color(0,0,0,0));
}
}else if(plot.getCategories().size()>250){
[![enter image description here][1]][1]
if (i == occurence) {
String cat_Name = (String) plot.getCategories().get(i - 1);
if (occurence + interval >= size) {
// occurence=size;
} else {
occurence = occurence + interval;
}
} else {
String cat_Names = (String) plot.getCategories().get(i - 1);
domainAxis.setTickLabelPaint(cat_Names, new Color(0, 0, 0, 0));
}
}
}
Below line will make label transparent
domainAxis.setTickLabelPaint(cat_Names, new Color(0,0,0,0));
Hi I have used following code but sometime I didnt get the pushpin, I am using Basic key can any one please suggest me.
public MainPage()
{
InitializeComponent();
Geocode("8800 Lyra Avenue, Columbus, OH 43240", 1);
Geocode("2137 Birchwood Dr, Redmond,WA 78214,U.S.", 1);
Geocode("Santa Cruz, Duval Co., TX", 1);
}
private void Geocode(string address, int waypointIndex)
{
PlatformServices.GeocodeServiceClient geocodingService = new PlatformServices.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
geocodingService.GeocodeCompleted += new EventHandler<TestSL.PlatformServices.GeocodeCompletedEventArgs>(geocodingService_GeocodeCompleted);
PlatformServices.GeocodeRequest request = new PlatformServices.GeocodeRequest();
request.Credentials = new TestSL.PlatformServices.Credentials();
request.Credentials.ApplicationId = ((Microsoft.Maps.MapControl.ClientTokenCredentialsProvider)(MyMap.CredentialsProvider)).Token;
request.Query = address;
geocodingService.GeocodeAsync(request, waypointIndex);
}
public void geocodingService_GeocodeCompleted(object sender, TestSL.PlatformServices.GeocodeCompletedEventArgs e)
{
MapLayer myMapLayer = new MapLayer();
MyMap.Children.Add(myMapLayer);
// create a location collection class
LocationCollection myLocationColl = new LocationCollection();
var geoResult = (from r in e.Result.Results
orderby (int)r.Confidence ascending
select r).FirstOrDefault();
if (geoResult != null)
{
Pushpin myPushPin = new Pushpin();
// set it to first found location
myPushPin.Location = new Microsoft.Maps.MapControl.Location(geoResult.Locations[0].Latitude, geoResult.Locations[0].Longitude);
ToolTipService.SetToolTip(myPushPin, geoResult.DisplayName);
// add it to location collection
// which would be used to set the map's bound
myLocationColl.Add(myPushPin.Location);
// Add the drawn point to the route layer.
myMapLayer.Children.Add(myPushPin);
}
}
Sometime i get two pushpin and Sometime I didnt get anything and sometimes i get 1 or 3. can any one please tell me why this is happening.
You should geocode all your data ahead of time and store the coordinates. Trying to geocode a bunch of addresses on the fly like this will drive up the number of transactions generated by your application. When using a basic key it can be rate limited if a bunch of requests are made in a short period of time. When the request is rate limited a flag is added to the header of the response to indicate this. This is documented at the bottom half of this page: http://msdn.microsoft.com/en-us/library/ff701703.aspx
I'm trying to plot a step chart with the following properties:
x-axis: Time (ms) [Actual data contains this as a double value]
y-axis: Another value stored as an integer.
I'm filling up the dataset as follows:
private XYSeries populateStepChartDataSet(HashMap<Double, Integer> dataGrid){
XYSeries xySeries = new XYSeries("Step Plot", true, true);
if(dataGrid != null){
for (Double timeStamp : dataGrid.keySet()) {
xySeries.add(timeStamp, dataGrid.get(timeStamp));
}
}
return xySeries;
}
And the section where I create the plot is as follows:
final XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(populateStepChartDataSet(dspDataGrid));
final JFreeChart chart = ChartFactory.createXYStepChart(
title,
xAxisLabel, yAxisLabel,
dataset,
PlotOrientation.VERTICAL,
true, // legend
true, // tooltips
false // urls
);
What I expect is the plot to show time in ms at the x-axis but this value is getting converted to some weird time. Here's how the plot looks
Can someone please help me get back the timestamp in ms format for the x-axis?
It looks like the x Axis is formatting as a date one way of adressing this is to provide a NumberFormatOverride
Add this code after your chart is created:
XYPlot plot = (XYPlot)chart.getPlot();
plot.setDomainAxis(0, new NumberAxis());
NumberAxis axis = (NumberAxis) plot.getDomainAxis();
axis.setNumberFormatOverride( new NumberFormat(){
#Override
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
return new StringBuffer(String.format("%f", number));
}
#Override
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
return new StringBuffer(String.format("%9.0f", number));
}
#Override
public Number parse(String source, ParsePosition parsePosition) {
return null;
}
} );
axis.setAutoRange(true);
axis.setAutoRangeIncludesZero(false);
You shold then get this chart:
FYI, use entrySet() whenever you iterate through a Map instead of iterating through the keySet() and then getting the value for each key.