1:
62:
63: package ;
64:
65: import ;
66: import ;
67: import ;
68: import ;
69: import ;
70: import ;
71: import ;
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77: import ;
78: import ;
79:
80: import ;
81: import ;
82: import ;
83: import ;
84: import ;
85: import ;
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96: import ;
97:
98:
104: public class CompassPlot extends Plot implements Cloneable, Serializable {
105:
106:
107: private static final long serialVersionUID = 6924382802125527395L;
108:
109:
110: public static final Font DEFAULT_LABEL_FONT
111: = new Font("SansSerif", Font.BOLD, 10);
112:
113:
114: public static final int NO_LABELS = 0;
115:
116:
117: public static final int VALUE_LABELS = 1;
118:
119:
120: private int labelType;
121:
122:
123: private Font labelFont;
124:
125:
126: private boolean drawBorder = false;
127:
128:
129: private Paint roseHighlightPaint = Color.black;
130:
131:
132: private Paint rosePaint = Color.yellow;
133:
134:
135: private Paint roseCenterPaint = Color.white;
136:
137:
138: private Font compassFont = new Font("Arial", Font.PLAIN, 10);
139:
140:
141: private transient Ellipse2D circle1;
142:
143:
144: private transient Ellipse2D circle2;
145:
146:
147: private transient Area a1;
148:
149:
150: private transient Area a2;
151:
152:
153: private transient Rectangle2D rect1;
154:
155:
156: private ValueDataset[] datasets = new ValueDataset[1];
157:
158:
159: private MeterNeedle[] seriesNeedle = new MeterNeedle[1];
160:
161:
162: protected static ResourceBundle localizationResources =
163: ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
164:
165:
168: protected double revolutionDistance = 360;
169:
170:
173: public CompassPlot() {
174: this(new DefaultValueDataset());
175: }
176:
177:
182: public CompassPlot(ValueDataset dataset) {
183: super();
184: if (dataset != null) {
185: this.datasets[0] = dataset;
186: dataset.addChangeListener(this);
187: }
188: this.circle1 = new Ellipse2D.Double();
189: this.circle2 = new Ellipse2D.Double();
190: this.rect1 = new Rectangle2D.Double();
191: setSeriesNeedle(0);
192: }
193:
194:
200: public int getLabelType() {
201: return this.labelType;
202: }
203:
204:
209: public void setLabelType(int type) {
210: if ((type != NO_LABELS) && (type != VALUE_LABELS)) {
211: throw new IllegalArgumentException(
212: "MeterPlot.setLabelType(int): unrecognised type."
213: );
214: }
215: if (this.labelType != type) {
216: this.labelType = type;
217: notifyListeners(new PlotChangeEvent(this));
218: }
219: }
220:
221:
226: public Font getLabelFont() {
227: return this.labelFont;
228: }
229:
230:
236: public void setLabelFont(Font font) {
237: if (font == null) {
238: throw new IllegalArgumentException("Null 'font' not allowed.");
239: }
240: this.labelFont = font;
241: notifyListeners(new PlotChangeEvent(this));
242: }
243:
244:
249: public Paint getRosePaint() {
250: return this.rosePaint;
251: }
252:
253:
259: public void setRosePaint(Paint paint) {
260: if (paint == null) {
261: throw new IllegalArgumentException("Null 'paint' argument.");
262: }
263: this.rosePaint = paint;
264: notifyListeners(new PlotChangeEvent(this));
265: }
266:
267:
273: public Paint getRoseCenterPaint() {
274: return this.roseCenterPaint;
275: }
276:
277:
283: public void setRoseCenterPaint(Paint paint) {
284: if (paint == null) {
285: throw new IllegalArgumentException("Null 'paint' argument.");
286: }
287: this.roseCenterPaint = paint;
288: notifyListeners(new PlotChangeEvent(this));
289: }
290:
291:
297: public Paint getRoseHighlightPaint() {
298: return this.roseHighlightPaint;
299: }
300:
301:
307: public void setRoseHighlightPaint(Paint paint) {
308: if (paint == null) {
309: throw new IllegalArgumentException("Null 'paint' argument.");
310: }
311: this.roseHighlightPaint = paint;
312: notifyListeners(new PlotChangeEvent(this));
313: }
314:
315:
320: public boolean getDrawBorder() {
321: return this.drawBorder;
322: }
323:
324:
329: public void setDrawBorder(boolean status) {
330: this.drawBorder = status;
331: }
332:
333:
339: public void setSeriesPaint(int series, Paint paint) {
340:
341: if ((series >= 0) && (series < this.seriesNeedle.length)) {
342: this.seriesNeedle[series].setFillPaint(paint);
343: }
344: }
345:
346:
352: public void setSeriesOutlinePaint(int series, Paint p) {
353:
354: if ((series >= 0) && (series < this.seriesNeedle.length)) {
355: this.seriesNeedle[series].setOutlinePaint(p);
356: }
357:
358: }
359:
360:
366: public void setSeriesOutlineStroke(int series, Stroke stroke) {
367:
368: if ((series >= 0) && (series < this.seriesNeedle.length)) {
369: this.seriesNeedle[series].setOutlineStroke(stroke);
370: }
371:
372: }
373:
374:
379: public void setSeriesNeedle(int type) {
380: setSeriesNeedle(0, type);
381: }
382:
383:
400: public void setSeriesNeedle(int index, int type) {
401: switch (type) {
402: case 0:
403: setSeriesNeedle(index, new ArrowNeedle(true));
404: setSeriesPaint(index, Color.red);
405: this.seriesNeedle[index].setHighlightPaint(Color.white);
406: break;
407: case 1:
408: setSeriesNeedle(index, new LineNeedle());
409: break;
410: case 2:
411: MeterNeedle longNeedle = new LongNeedle();
412: longNeedle.setRotateY(0.5);
413: setSeriesNeedle(index, longNeedle);
414: break;
415: case 3:
416: setSeriesNeedle(index, new PinNeedle());
417: break;
418: case 4:
419: setSeriesNeedle(index, new PlumNeedle());
420: break;
421: case 5:
422: setSeriesNeedle(index, new PointerNeedle());
423: break;
424: case 6:
425: setSeriesPaint(index, null);
426: setSeriesOutlineStroke(index, new BasicStroke(3));
427: setSeriesNeedle(index, new ShipNeedle());
428: break;
429: case 7:
430: setSeriesPaint(index, Color.blue);
431: setSeriesNeedle(index, new WindNeedle());
432: break;
433: case 8:
434: setSeriesNeedle(index, new ArrowNeedle(true));
435: break;
436: case 9:
437: setSeriesNeedle(index, new MiddlePinNeedle());
438: break;
439:
440: default:
441: throw new IllegalArgumentException("Unrecognised type.");
442: }
443:
444: }
445:
446:
452: public void setSeriesNeedle(int index, MeterNeedle needle) {
453:
454: if ((needle != null) && (index < this.seriesNeedle.length)) {
455: this.seriesNeedle[index] = needle;
456: }
457: notifyListeners(new PlotChangeEvent(this));
458:
459: }
460:
461:
468: public ValueDataset[] getDatasets() {
469: return this.datasets;
470: }
471:
472:
477: public void addDataset(ValueDataset dataset) {
478: addDataset(dataset, null);
479: }
480:
481:
487: public void addDataset(ValueDataset dataset, MeterNeedle needle) {
488:
489: if (dataset != null) {
490: int i = this.datasets.length + 1;
491: ValueDataset[] t = new ValueDataset[i];
492: MeterNeedle[] p = new MeterNeedle[i];
493: i = i - 2;
494: for (; i >= 0; --i) {
495: t[i] = this.datasets[i];
496: p[i] = this.seriesNeedle[i];
497: }
498: i = this.datasets.length;
499: t[i] = dataset;
500: p[i] = ((needle != null) ? needle : p[i - 1]);
501:
502: ValueDataset[] a = this.datasets;
503: MeterNeedle[] b = this.seriesNeedle;
504: this.datasets = t;
505: this.seriesNeedle = p;
506:
507: for (--i; i >= 0; --i) {
508: a[i] = null;
509: b[i] = null;
510: }
511: dataset.addChangeListener(this);
512: }
513: }
514:
515:
525: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
526: PlotState parentState,
527: PlotRenderingInfo info) {
528:
529: int outerRadius = 0;
530: int innerRadius = 0;
531: int x1, y1, x2, y2;
532: double a;
533:
534: if (info != null) {
535: info.setPlotArea(area);
536: }
537:
538:
539: RectangleInsets insets = getInsets();
540: insets.trim(area);
541:
542:
543: if (this.drawBorder) {
544: drawBackground(g2, area);
545: }
546:
547: int midX = (int) (area.getWidth() / 2);
548: int midY = (int) (area.getHeight() / 2);
549: int radius = midX;
550: if (midY < midX) {
551: radius = midY;
552: }
553: --radius;
554: int diameter = 2 * radius;
555:
556: midX += (int) area.getMinX();
557: midY += (int) area.getMinY();
558:
559: this.circle1.setFrame(midX - radius, midY - radius, diameter, diameter);
560: this.circle2.setFrame(
561: midX - radius + 15, midY - radius + 15,
562: diameter - 30, diameter - 30
563: );
564: g2.setPaint(this.rosePaint);
565: this.a1 = new Area(this.circle1);
566: this.a2 = new Area(this.circle2);
567: this.a1.subtract(this.a2);
568: g2.fill(this.a1);
569:
570: g2.setPaint(this.roseCenterPaint);
571: x1 = diameter - 30;
572: g2.fillOval(midX - radius + 15, midY - radius + 15, x1, x1);
573: g2.setPaint(this.roseHighlightPaint);
574: g2.drawOval(midX - radius, midY - radius, diameter, diameter);
575: x1 = diameter - 20;
576: g2.drawOval(midX - radius + 10, midY - radius + 10, x1, x1);
577: x1 = diameter - 30;
578: g2.drawOval(midX - radius + 15, midY - radius + 15, x1, x1);
579: x1 = diameter - 80;
580: g2.drawOval(midX - radius + 40, midY - radius + 40, x1, x1);
581:
582: outerRadius = radius - 20;
583: innerRadius = radius - 32;
584: for (int w = 0; w < 360; w += 15) {
585: a = Math.toRadians(w);
586: x1 = midX - ((int) (Math.sin(a) * innerRadius));
587: x2 = midX - ((int) (Math.sin(a) * outerRadius));
588: y1 = midY - ((int) (Math.cos(a) * innerRadius));
589: y2 = midY - ((int) (Math.cos(a) * outerRadius));
590: g2.drawLine(x1, y1, x2, y2);
591: }
592:
593: g2.setPaint(this.roseHighlightPaint);
594: innerRadius = radius - 26;
595: outerRadius = 7;
596: for (int w = 45; w < 360; w += 90) {
597: a = Math.toRadians(w);
598: x1 = midX - ((int) (Math.sin(a) * innerRadius));
599: y1 = midY - ((int) (Math.cos(a) * innerRadius));
600: g2.fillOval(
601: x1 - outerRadius, y1 - outerRadius,
602: 2 * outerRadius, 2 * outerRadius
603: );
604: }
605:
606:
607: for (int w = 0; w < 360; w += 90) {
608: a = Math.toRadians(w);
609: x1 = midX - ((int) (Math.sin(a) * innerRadius));
610: y1 = midY - ((int) (Math.cos(a) * innerRadius));
611:
612: Polygon p = new Polygon();
613: p.addPoint(x1 - outerRadius, y1);
614: p.addPoint(x1, y1 + outerRadius);
615: p.addPoint(x1 + outerRadius, y1);
616: p.addPoint(x1, y1 - outerRadius);
617: g2.fillPolygon(p);
618: }
619:
620:
621: innerRadius = radius - 42;
622: Font f = getCompassFont(radius);
623: g2.setFont(f);
624: g2.drawString("N", midX - 5, midY - innerRadius + f.getSize());
625: g2.drawString("S", midX - 5, midY + innerRadius - 5);
626: g2.drawString("W", midX - innerRadius + 5, midY + 5);
627: g2.drawString("E", midX + innerRadius - f.getSize(), midY + 5);
628:
629:
630: y1 = radius / 2;
631: x1 = radius / 6;
632: Rectangle2D needleArea = new Rectangle2D.Double(
633: (midX - x1), (midY - y1), (2 * x1), (2 * y1)
634: );
635: int x = this.seriesNeedle.length;
636: int current = 0;
637: double value = 0;
638: int i = (this.datasets.length - 1);
639: for (; i >= 0; --i) {
640: ValueDataset data = this.datasets[i];
641:
642: if (data != null && data.getValue() != null) {
643: value = (data.getValue().doubleValue())
644: % this.revolutionDistance;
645: value = value / this.revolutionDistance * 360;
646: current = i % x;
647: this.seriesNeedle[current].draw(g2, needleArea, value);
648: }
649: }
650:
651: if (this.drawBorder) {
652: drawOutline(g2, area);
653: }
654:
655: }
656:
657:
662: public String getPlotType() {
663: return localizationResources.getString("Compass_Plot");
664: }
665:
666:
672: public LegendItemCollection getLegendItems() {
673: return null;
674: }
675:
676:
681: public void zoom(double percent) {
682:
683: }
684:
685:
692: protected Font getCompassFont(int radius) {
693: float fontSize = radius / 10.0f;
694: if (fontSize < 8) {
695: fontSize = 8;
696: }
697: Font newFont = this.compassFont.deriveFont(fontSize);
698: return newFont;
699: }
700:
701:
708: public boolean equals(Object obj) {
709: if (obj == this) {
710: return true;
711: }
712: if (!(obj instanceof CompassPlot)) {
713: return false;
714: }
715: if (!super.equals(obj)) {
716: return false;
717: }
718: CompassPlot that = (CompassPlot) obj;
719: if (this.labelType != that.labelType) {
720: return false;
721: }
722: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
723: return false;
724: }
725: if (this.drawBorder != that.drawBorder) {
726: return false;
727: }
728: if (!PaintUtilities.equal(this.roseHighlightPaint,
729: that.roseHighlightPaint)) {
730: return false;
731: }
732: if (!PaintUtilities.equal(this.rosePaint, that.rosePaint)) {
733: return false;
734: }
735: if (!PaintUtilities.equal(this.roseCenterPaint,
736: that.roseCenterPaint)) {
737: return false;
738: }
739: if (!ObjectUtilities.equal(this.compassFont, that.compassFont)) {
740: return false;
741: }
742: if (!Arrays.equals(this.seriesNeedle, that.seriesNeedle)) {
743: return false;
744: }
745: if (getRevolutionDistance() != that.getRevolutionDistance()) {
746: return false;
747: }
748: return true;
749:
750: }
751:
752:
760: public Object clone() throws CloneNotSupportedException {
761:
762: CompassPlot clone = (CompassPlot) super.clone();
763:
764:
765:
766:
767:
768:
769:
770: if (this.circle1 != null) {
771: clone.circle1 = (Ellipse2D) this.circle1.clone();
772: }
773: if (this.circle2 != null) {
774: clone.circle2 = (Ellipse2D) this.circle2.clone();
775: }
776: if (this.a1 != null) {
777: clone.a1 = (Area) this.a1.clone();
778: }
779: if (this.a2 != null) {
780: clone.a2 = (Area) this.a2.clone();
781: }
782: if (this.rect1 != null) {
783: clone.rect1 = (Rectangle2D) this.rect1.clone();
784: }
785: clone.datasets = (ValueDataset[]) this.datasets.clone();
786: clone.seriesNeedle = (MeterNeedle[]) this.seriesNeedle.clone();
787:
788:
789: for (int i = 0; i < this.datasets.length; ++i) {
790: if (clone.datasets[i] != null) {
791: clone.datasets[i].addChangeListener(clone);
792: }
793: }
794: return clone;
795:
796: }
797:
798:
804: public void setRevolutionDistance(double size) {
805: if (size > 0) {
806: this.revolutionDistance = size;
807: }
808: }
809:
810:
815: public double getRevolutionDistance() {
816: return this.revolutionDistance;
817: }
818: }