我目前正在使用 itext-pdf生成 PDF。除此之外,我还在使用 JFreeChart在其上创建图表。我创建了一个具有爆炸效果的圆环图,它看起来像这样。
但是我想创建一个看起来更像这样的圆环图。
我希望某些部分能够脱颖而出,但又不会完全脱离圆环图。我非常感谢有关如何实现这一目标的意见。
这是我当前的代码:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Locale;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlotState;
import org.jfree.chart.plot.RingPlot;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.ui.RectangleInsets;
import com.itextpdf.awt.DefaultFontMapper;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;
public class RingChartTest {
public static void main(String[] args) throws Exception {
new RingChartTest().createPDF();
}
private void createPDF() throws Exception {
String destination = "ringchart.pdf";
Document document = new Document(PageSize.A4.rotate());
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(destination));
document.open();
// Create the pages
PdfContentByte cb = writer.getDirectContent();
addChart(cb);
} catch (Exception e) {
System.out.println("Failure to generate the PDF");
e.printStackTrace();
} finally {
if (document != null) {
document.close();
}
}
}
private void addChart(PdfContentByte cb) throws Exception, IOException {
long pctPM = Math.round(20);
long pctOA = Math.round(15);
long pctWPI = Math.round(5);
long pctTDF = Math.round(25);
long pctNE = 100 - (pctPM + pctOA + pctWPI + pctTDF);
long pctEngaged = pctPM + pctOA + pctWPI;
long numEngaged = 3400;
String strNumEngaged = formatNumber(numEngaged, "#,###,###,##0");
JFreeChart chart = createChart(pctPM, pctOA, pctWPI, pctTDF, pctNE);
int width = 300;
int height = 200;
PdfTemplate template = cb.createTemplate(width, height);
Graphics2D graphics2d = template.createGraphics(width, height, new DefaultFontMapper());
Rectangle2D rectangle2d = new Rectangle2D.Double(0, 0, width, height);
chart.draw(graphics2d, rectangle2d);
graphics2d.dispose();
cb.addTemplate(template, 30, 185);
// Add text inside chart
Font engagementFont = createFont("OpenSans-Bold.ttf", 8, 116, 112, 100);
Font percentFont1 = createFont("OpenSans-Light.ttf", 22, 116, 112, 100);
Font percentFont2 = createFont("OpenSans-Light.ttf", 10, 116, 112, 100);
Font numberFont = createFont("OpenSans-Regular.ttf", 8, 116, 112, 100);
addPhrase(cb, "ENGAGE", engagementFont, 135, 290, 230, 310, 10, Element.ALIGN_CENTER);
addPhrase(cb, String.valueOf(pctEngaged), percentFont1, 115, 270, 190, 289, 10, Element.ALIGN_RIGHT);
addPhrase(cb, "%", percentFont2, 191, 275, 201, 299, 10, Element.ALIGN_LEFT);
addPhrase(cb, "(" + strNumEngaged + ")", numberFont, 130, 258, 230, 278, 10, Element.ALIGN_CENTER);
// Create legend
// 290,420,370,520,10,Element.ALIGN_CENTER);
BaseFont engagedPctFont = createBaseFont("OpenSans-Bold.ttf");
BaseFont engagedDescFont = createBaseFont("OpenSans-SemiBold.ttf");
BaseFont nonEngagedDescFont = createBaseFont("OpenSans-Regular.ttf");
BaseColor pmBaseColor = new BaseColor(31, 160, 200);
BaseColor oaBaseColor = new BaseColor(84, 193, 209);
BaseColor wpiBaseColor = new BaseColor(248, 156, 36);
BaseColor tdfBaseColor = new BaseColor(116, 112, 94);
BaseColor nonEngagedBaseColor = new BaseColor(148, 144, 132);
float x = 330;
float y = 350;
float radius = 3;
// Create border around legend
/*
cb.setColorFill(new BaseColor(255, 255, 255));
cb.rectangle(320, 300, 150, 70);
cb.re
cb.fill();
*/
BaseColor borderColor = new BaseColor(192, 189, 178);
cb.setColorStroke(borderColor);
cb.moveTo(320, 300);
cb.lineTo(320, 365);
cb.lineTo(500, 365);
cb.lineTo(500, 300);
cb.lineTo(320, 300);
cb.closePathStroke();
// Prof Mgmt
cb.setColorFill(pmBaseColor);
cb.circle(x, y, radius);
cb.fill();
addTextToCanvas(cb, pctPM+"%", engagedPctFont, 8, new BaseColor(116, 112, 100), x+20, y-2);
addTextToCanvas(cb, "Pg", engagedDescFont, 8, new BaseColor(116, 112, 100), x+50, y-2);
// Online Advice
cb.setColorFill(oaBaseColor);
cb.circle(x, y-20, radius);
cb.fill();
addTextToCanvas(cb, pctOA+"%", engagedPctFont, 8, new BaseColor(116, 112, 100), x+20, y-22);
addTextToCanvas(cb, "Oaa", engagedDescFont, 8, new BaseColor(116, 112, 100), x+50, y-22);
// Clicked WPI/Online Guidance
cb.setColorFill(wpiBaseColor);
cb.circle(x, y-40, radius);
cb.fill();
addTextToCanvas(cb, pctWPI+"%", engagedPctFont, 8, new BaseColor(116, 112, 100), x+20, y-42);
addTextToCanvas(cb, "Ogg", engagedDescFont, 8, new BaseColor(116, 112, 100), x+50, y-42);
if (pctTDF > 0) {
// TDF Users
cb.setColorFill(tdfBaseColor);
cb.circle(x, y-60, radius);
cb.fill();
addTextToCanvas(cb, pctTDF+"%", engagedPctFont, 8, new BaseColor(116, 112, 100), x+20, y-62);
addTextToCanvas(cb, "Pti*", nonEngagedDescFont, 8, new BaseColor(116, 112, 100), x+50, y-62);
// Non-engaged
cb.setColorFill(nonEngagedBaseColor);
cb.circle(x, y-80, radius);
cb.fill();
addTextToCanvas(cb, pctNE+"%", engagedPctFont, 8, new BaseColor(116, 112, 100), x+20, y-82);
addTextToCanvas(cb, "Nng", nonEngagedDescFont, 8, new BaseColor(116, 112, 100), x+50, y-82);
} else {
// Non-engaged
cb.setColorFill(nonEngagedBaseColor);
cb.circle(x, y-60, radius);
cb.fill();
addTextToCanvas(cb, pctNE+"%", engagedPctFont, 8, new BaseColor(116, 112, 100), x+20, y-62);
addTextToCanvas(cb, "ngd", nonEngagedDescFont, 8, new BaseColor(116, 112, 100), x+50, y-62);
}
}
private String formatNumber(double value, String strFormat) {
DecimalFormat df = new DecimalFormat( strFormat );
return df.format(value);
}
private void addPhrase(PdfContentByte cb, String strText, Font font, float llx, float lly, float urx, float ury, float leading, int alignment) throws DocumentException {
Phrase phrase = new Phrase(strText, font);
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(phrase, llx, lly, urx, ury, leading, alignment);
ct.go();
}
private void addTextToCanvas(PdfContentByte cb, String strText, BaseFont font, float fontSize, BaseColor color, float x, float y) {
cb.beginText();
cb.setFontAndSize(font, fontSize);
cb.setColorFill(color);
cb.showTextAligned(Element.ALIGN_LEFT, strText, x, y, 0);
cb.endText();
}
private BaseFont createBaseFont(String fileName) throws DocumentException, IOException {
return BaseFont.createFont(PdfGenerationController.LOCATION_FONTS + fileName ,BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}
private Font createFont(String fileName, float size, int red, int green, int blue) throws DocumentException, IOException {
BaseFont baseFont = BaseFont.createFont(PdfGenerationController.LOCATION_FONTS + fileName ,BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
Font font = new Font(baseFont, size);
font.setColor(red, green, blue);
return font;
}
public JFreeChart createChart(long pctPM, long pctOA, long pctWPI, long pctTDF, long pctNE) {
// Set up the data set for the donut/ring chart
DefaultPieDataset rDataSet = new DefaultPieDataset();
rDataSet.setValue("PM", pctPM );
rDataSet.setValue("OA", pctOA);
rDataSet.setValue("WPI", pctWPI);
rDataSet.setValue("TDF", pctTDF);
rDataSet.setValue("NE", pctNE);
// Initialize values
boolean bShowLegend = false;
String strTitle = null;
// Create ring plot
CustomDonutPlot rPlot = new CustomDonutPlot(rDataSet);
//RingPlot rPlot = new RingPlot(rDataSet);
rPlot.setLabelGenerator(new StandardPieSectionLabelGenerator(Locale.ENGLISH));
rPlot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0));
rPlot.setSectionDepth(0.30);
JFreeChart chart = new JFreeChart(strTitle, JFreeChart.DEFAULT_TITLE_FONT, rPlot, bShowLegend);
ChartFactory.getChartTheme().apply(chart);
// Create the chart
//JFreeChart rChart = ChartFactory.createRingChart(null, rDataSet , false, false, Locale.ENGLISH);
//RingPlot rPlot = (RingPlot) rChart.getPlot();
rPlot.setBackgroundPaint(Color.WHITE);
rPlot.setCenterText(null);
rPlot.setLabelGenerator(null);
rPlot.setOutlineVisible(false);
rPlot.setShadowGenerator(null);
rPlot.setSeparatorsVisible(false);
rPlot.setShadowPaint(null);
rPlot.setSectionOutlinesVisible(false);
rPlot.setOuterSeparatorExtension(0);
rPlot.setInnerSeparatorExtension(0);
// Set colors of the chart
rPlot.setSectionPaint("PM", new Color(31, 160, 200));
rPlot.setSectionPaint("OA", new Color(84, 193, 209));
rPlot.setSectionPaint("WPI", new Color(248, 156, 36));
rPlot.setSectionPaint("TDF", new Color(116, 112, 94));
rPlot.setSectionPaint("NE", new Color(148, 144, 132));
rPlot.setExplodePercent("PM", 0.05);
rPlot.setExplodePercent("OA", 0.05);
rPlot.setExplodePercent("WPI", 0.05);
return chart;
}
public static class CustomDonutPlot extends RingPlot {
private static final long serialVersionUID = 1L;
public CustomDonutPlot(DefaultPieDataset dataSet) {
super(dataSet);
}
@Override
protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, PiePlotState state, int currentPass) {
if (currentPass == 1 && section >=1 && section <= 3) {
}
Rectangle2D area = state.getPieArea();
System.out.println("*** At section=" + section + ", pass="+currentPass);
logDataArea(dataArea, "Data area");
logDataArea(area, "Pie area");
System.out.println(state.getInfo());
super.drawItem(g2, section, dataArea, state, currentPass);
}
private void logDataArea(Rectangle2D dataArea, String msg) {
System.out.println(msg + " h="+dataArea.getHeight() + ", w=" + dataArea.getWidth() + ", x=" + dataArea.getX() + ",y="+dataArea.getY());
}
}
}
此替代版本将图表从 PDF 中分离出来。
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.Locale;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlotState;
import org.jfree.chart.plot.RingPlot;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.ui.RectangleInsets;
/**
* @see http://stackoverflow.com/q/37213030/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
long pctPM = Math.round(20);
long pctOA = Math.round(15);
long pctWPI = Math.round(5);
long pctTDF = Math.round(25);
long pctNE = 100 - (pctPM + pctOA + pctWPI + pctTDF);
f.add(new ChartPanel(createChart(pctPM, pctOA, pctWPI, pctTDF, pctNE)));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public JFreeChart createChart(long pctPM, long pctOA, long pctWPI, long pctTDF, long pctNE) {
// Set up the data set for the donut/ring chart
DefaultPieDataset rDataSet = new DefaultPieDataset();
rDataSet.setValue("PM", pctPM);
rDataSet.setValue("OA", pctOA);
rDataSet.setValue("WPI", pctWPI);
rDataSet.setValue("TDF", pctTDF);
rDataSet.setValue("NE", pctNE);
// Initialize values
boolean bShowLegend = false;
String strTitle = null;
// Create ring plot
CustomDonutPlot rPlot = new CustomDonutPlot(rDataSet);
//RingPlot rPlot = new RingPlot(rDataSet);
rPlot.setLabelGenerator(new StandardPieSectionLabelGenerator(Locale.ENGLISH));
rPlot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0));
rPlot.setSectionDepth(0.30);
JFreeChart chart = new JFreeChart(strTitle, JFreeChart.DEFAULT_TITLE_FONT, rPlot, bShowLegend);
ChartFactory.getChartTheme().apply(chart);
// Create the chart
//JFreeChart rChart = ChartFactory.createRingChart(null, rDataSet , false, false, Locale.ENGLISH);
//RingPlot rPlot = (RingPlot) rChart.getPlot();
rPlot.setBackgroundPaint(Color.WHITE);
rPlot.setCenterText(null);
rPlot.setLabelGenerator(null);
rPlot.setOutlineVisible(false);
rPlot.setShadowGenerator(null);
rPlot.setSeparatorsVisible(false);
rPlot.setShadowPaint(null);
rPlot.setSectionOutlinesVisible(false);
rPlot.setOuterSeparatorExtension(0);
rPlot.setInnerSeparatorExtension(0);
// Set colors of the chart
rPlot.setSectionPaint("PM", new Color(31, 160, 200));
rPlot.setSectionPaint("OA", new Color(84, 193, 209));
rPlot.setSectionPaint("WPI", new Color(248, 156, 36));
rPlot.setSectionPaint("TDF", new Color(116, 112, 94));
rPlot.setSectionPaint("NE", new Color(148, 144, 132));
rPlot.setExplodePercent("PM", 0.05);
rPlot.setExplodePercent("OA", 0.05);
rPlot.setExplodePercent("WPI", 0.05);
return chart;
}
public static class CustomDonutPlot extends RingPlot {
private static final long serialVersionUID = 1L;
public CustomDonutPlot(DefaultPieDataset dataSet) {
super(dataSet);
}
@Override
protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, PiePlotState state, int currentPass) {
super.drawItem(g2, section, dataArea, state, currentPass);
Rectangle2D area = state.getPieArea();
System.out.println("*** At section=" + section + ", pass=" + currentPass);
logDataArea(dataArea, "Data area");
logDataArea(area, "Pie area");
}
private void logDataArea(Rectangle2D dataArea, String msg) {
System.out.println(msg + " h=" + dataArea.getHeight() + ", w=" + dataArea.getWidth() + ", x=" + dataArea.getX() + ",y=" + dataArea.getY());
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Test()::display);
}
}
最佳答案
看来您需要在未分解弧的位置绘制分解弧。为此,您可以覆盖 RingPlot::getArcBounds并处理圆弧的边界。更新您的代码(内部类)以获得下图:
public static class CustomDonutPlot extends RingPlot {
private static final long serialVersionUID = 1L;
public CustomDonutPlot(DefaultPieDataset dataSet) {
super(dataSet);
}
@Override
protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, PiePlotState state, int currentPass) {
if (currentPass == 1 && section >=1 && section <= 3) {
}
Rectangle2D area = state.getPieArea();
System.out.println("*** At section=" + section + ", pass="+currentPass);
logDataArea(dataArea, "Data area");
logDataArea(area, "Pie area");
System.out.println(state.getInfo());
super.drawItem(g2, section, dataArea, state, currentPass);
}
@Override
protected Rectangle2D getArcBounds(Rectangle2D unexploded, Rectangle2D exploded, double angle, double extent, double explodePercent) {
if(explodePercent > 0.0){
this.setSectionDepth(0.33);//to match inner arc
java.awt.geom.Arc2D.Double arc1 = new java.awt.geom.Arc2D.Double(unexploded, angle, extent / 2.0D, 0);
Point2D point1 = arc1.getEndPoint();
//java.awt.geom.Arc2D.Double arc2 = new java.awt.geom.Arc2D.Double(exploded, angle, extent / 2.0D, 0); //original code
Rectangle2D mix = new Rectangle2D.Double(exploded.getX(), exploded.getY(), unexploded.getWidth(), unexploded.getHeight());
java.awt.geom.Arc2D.Double arc2 = new java.awt.geom.Arc2D.Double(mix, angle, extent / 2.0D, 0);
Point2D point2 = arc2.getEndPoint();
double deltaX = (point1.getX() - point2.getX()) * explodePercent;
double deltaY = (point1.getY() - point2.getY()) * explodePercent;
//return new java.awt.geom.Rectangle2D.Double(unexploded.getX() - deltaX, unexploded.getY() - deltaY, unexploded.getWidth(), unexploded.getHeight()); original code
return new java.awt.geom.Rectangle2D.Double(unexploded.getX() - deltaX, unexploded.getY() - deltaY, exploded.getWidth(), exploded.getHeight());
} else {
this.setSectionDepth(0.3);//default depth
return super.getArcBounds(unexploded, exploded, angle, extent, explodePercent);
}
}
private void logDataArea(Rectangle2D dataArea, String msg) {
System.out.println(msg + " h="+dataArea.getHeight() + ", w=" + dataArea.getWidth() + ", x=" + dataArea.getX() + ",y="+dataArea.getY());
}
}
关于java - 在 JFreeChart 中制作自定义环形图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37213030/
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是