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: import ;
80: import ;
81: import ;
82:
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: import ;
98: import ;
99: import ;
100: import ;
101:
102:
106: public class CompassPlot extends Plot implements Cloneable, Serializable {
107:
108:
109: private static final long serialVersionUID = 6924382802125527395L;
110:
111:
112: public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
113: Font.BOLD, 10);
114:
115:
116: public static final int NO_LABELS = 0;
117:
118:
119: public static final int VALUE_LABELS = 1;
120:
121:
122: private int labelType;
123:
124:
125: private Font labelFont;
126:
127:
128: private boolean drawBorder = false;
129:
130:
131: private transient Paint roseHighlightPaint = Color.black;
132:
133:
134: private transient Paint rosePaint = Color.yellow;
135:
136:
137: private transient Paint roseCenterPaint = Color.white;
138:
139:
140: private Font compassFont = new Font("Arial", Font.PLAIN, 10);
141:
142:
143: private transient Ellipse2D circle1;
144:
145:
146: private transient Ellipse2D circle2;
147:
148:
149: private transient Area a1;
150:
151:
152: private transient Area a2;
153:
154:
155: private transient Rectangle2D rect1;
156:
157:
158: private ValueDataset[] datasets = new ValueDataset[1];
159:
160:
161: private MeterNeedle[] seriesNeedle = new MeterNeedle[1];
162:
163:
164: protected static ResourceBundle localizationResources
165: = ResourceBundle.getBundle(
166: "org.jfree.chart.plot.LocalizationBundle");
167:
168:
172: protected double revolutionDistance = 360;
173:
174:
177: public CompassPlot() {
178: this(new DefaultValueDataset());
179: }
180:
181:
186: public CompassPlot(ValueDataset dataset) {
187: super();
188: if (dataset != null) {
189: this.datasets[0] = dataset;
190: dataset.addChangeListener(this);
191: }
192: this.circle1 = new Ellipse2D.Double();
193: this.circle2 = new Ellipse2D.Double();
194: this.rect1 = new Rectangle2D.Double();
195: setSeriesNeedle(0);
196: }
197:
198:
206: public int getLabelType() {
207:
208: return this.labelType;
209: }
210:
211:
218: public void setLabelType(int type) {
219:
220: if ((type != NO_LABELS) && (type != VALUE_LABELS)) {
221: throw new IllegalArgumentException(
222: "MeterPlot.setLabelType(int): unrecognised type.");
223: }
224: if (this.labelType != type) {
225: this.labelType = type;
226: notifyListeners(new PlotChangeEvent(this));
227: }
228: }
229:
230:
237: public Font getLabelFont() {
238:
239: return this.labelFont;
240: }
241:
242:
250: public void setLabelFont(Font font) {
251:
252: if (font == null) {
253: throw new IllegalArgumentException("Null 'font' not allowed.");
254: }
255: this.labelFont = font;
256: notifyListeners(new PlotChangeEvent(this));
257: }
258:
259:
266: public Paint getRosePaint() {
267: return this.rosePaint;
268: }
269:
270:
278: public void setRosePaint(Paint paint) {
279: if (paint == null) {
280: throw new IllegalArgumentException("Null 'paint' argument.");
281: }
282: this.rosePaint = paint;
283: notifyListeners(new PlotChangeEvent(this));
284: }
285:
286:
294: public Paint getRoseCenterPaint() {
295: return this.roseCenterPaint;
296: }
297:
298:
306: public void setRoseCenterPaint(Paint paint) {
307: if (paint == null) {
308: throw new IllegalArgumentException("Null 'paint' argument.");
309: }
310: this.roseCenterPaint = paint;
311: notifyListeners(new PlotChangeEvent(this));
312: }
313:
314:
322: public Paint getRoseHighlightPaint() {
323: return this.roseHighlightPaint;
324: }
325:
326:
334: public void setRoseHighlightPaint(Paint paint) {
335: if (paint == null) {
336: throw new IllegalArgumentException("Null 'paint' argument.");
337: }
338: this.roseHighlightPaint = paint;
339: notifyListeners(new PlotChangeEvent(this));
340: }
341:
342:
349: public boolean getDrawBorder() {
350: return this.drawBorder;
351: }
352:
353:
360: public void setDrawBorder(boolean status) {
361: this.drawBorder = status;
362: notifyListeners(new PlotChangeEvent(this));
363: }
364:
365:
373: public void setSeriesPaint(int series, Paint paint) {
374:
375: if ((series >= 0) && (series < this.seriesNeedle.length)) {
376: this.seriesNeedle[series].setFillPaint(paint);
377: }
378: }
379:
380:
388: public void setSeriesOutlinePaint(int series, Paint p) {
389:
390: if ((series >= 0) && (series < this.seriesNeedle.length)) {
391: this.seriesNeedle[series].setOutlinePaint(p);
392: }
393:
394: }
395:
396:
404: public void setSeriesOutlineStroke(int series, Stroke stroke) {
405:
406: if ((series >= 0) && (series < this.seriesNeedle.length)) {
407: this.seriesNeedle[series].setOutlineStroke(stroke);
408: }
409:
410: }
411:
412:
419: public void setSeriesNeedle(int type) {
420: setSeriesNeedle(0, type);
421: }
422:
423:
442: public void setSeriesNeedle(int index, int type) {
443: switch (type) {
444: case 0:
445: setSeriesNeedle(index, new ArrowNeedle(true));
446: setSeriesPaint(index, Color.red);
447: this.seriesNeedle[index].setHighlightPaint(Color.white);
448: break;
449: case 1:
450: setSeriesNeedle(index, new LineNeedle());
451: break;
452: case 2:
453: MeterNeedle longNeedle = new LongNeedle();
454: longNeedle.setRotateY(0.5);
455: setSeriesNeedle(index, longNeedle);
456: break;
457: case 3:
458: setSeriesNeedle(index, new PinNeedle());
459: break;
460: case 4:
461: setSeriesNeedle(index, new PlumNeedle());
462: break;
463: case 5:
464: setSeriesNeedle(index, new PointerNeedle());
465: break;
466: case 6:
467: setSeriesPaint(index, null);
468: setSeriesOutlineStroke(index, new BasicStroke(3));
469: setSeriesNeedle(index, new ShipNeedle());
470: break;
471: case 7:
472: setSeriesPaint(index, Color.blue);
473: setSeriesNeedle(index, new WindNeedle());
474: break;
475: case 8:
476: setSeriesNeedle(index, new ArrowNeedle(true));
477: break;
478: case 9:
479: setSeriesNeedle(index, new MiddlePinNeedle());
480: break;
481:
482: default:
483: throw new IllegalArgumentException("Unrecognised type.");
484: }
485:
486: }
487:
488:
495: public void setSeriesNeedle(int index, MeterNeedle needle) {
496:
497: if ((needle != null) && (index < this.seriesNeedle.length)) {
498: this.seriesNeedle[index] = needle;
499: }
500: notifyListeners(new PlotChangeEvent(this));
501:
502: }
503:
504:
511: public ValueDataset[] getDatasets() {
512: return this.datasets;
513: }
514:
515:
522: public void addDataset(ValueDataset dataset) {
523: addDataset(dataset, null);
524: }
525:
526:
532: public void addDataset(ValueDataset dataset, MeterNeedle needle) {
533:
534: if (dataset != null) {
535: int i = this.datasets.length + 1;
536: ValueDataset[] t = new ValueDataset[i];
537: MeterNeedle[] p = new MeterNeedle[i];
538: i = i - 2;
539: for (; i >= 0; --i) {
540: t[i] = this.datasets[i];
541: p[i] = this.seriesNeedle[i];
542: }
543: i = this.datasets.length;
544: t[i] = dataset;
545: p[i] = ((needle != null) ? needle : p[i - 1]);
546:
547: ValueDataset[] a = this.datasets;
548: MeterNeedle[] b = this.seriesNeedle;
549: this.datasets = t;
550: this.seriesNeedle = p;
551:
552: for (--i; i >= 0; --i) {
553: a[i] = null;
554: b[i] = null;
555: }
556: dataset.addChangeListener(this);
557: }
558: }
559:
560:
570: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
571: PlotState parentState,
572: PlotRenderingInfo info) {
573:
574: int outerRadius = 0;
575: int innerRadius = 0;
576: int x1, y1, x2, y2;
577: double a;
578:
579: if (info != null) {
580: info.setPlotArea(area);
581: }
582:
583:
584: RectangleInsets insets = getInsets();
585: insets.trim(area);
586:
587:
588: if (this.drawBorder) {
589: drawBackground(g2, area);
590: }
591:
592: int midX = (int) (area.getWidth() / 2);
593: int midY = (int) (area.getHeight() / 2);
594: int radius = midX;
595: if (midY < midX) {
596: radius = midY;
597: }
598: --radius;
599: int diameter = 2 * radius;
600:
601: midX += (int) area.getMinX();
602: midY += (int) area.getMinY();
603:
604: this.circle1.setFrame(midX - radius, midY - radius, diameter, diameter);
605: this.circle2.setFrame(
606: midX - radius + 15, midY - radius + 15,
607: diameter - 30, diameter - 30
608: );
609: g2.setPaint(this.rosePaint);
610: this.a1 = new Area(this.circle1);
611: this.a2 = new Area(this.circle2);
612: this.a1.subtract(this.a2);
613: g2.fill(this.a1);
614:
615: g2.setPaint(this.roseCenterPaint);
616: x1 = diameter - 30;
617: g2.fillOval(midX - radius + 15, midY - radius + 15, x1, x1);
618: g2.setPaint(this.roseHighlightPaint);
619: g2.drawOval(midX - radius, midY - radius, diameter, diameter);
620: x1 = diameter - 20;
621: g2.drawOval(midX - radius + 10, midY - radius + 10, x1, x1);
622: x1 = diameter - 30;
623: g2.drawOval(midX - radius + 15, midY - radius + 15, x1, x1);
624: x1 = diameter - 80;
625: g2.drawOval(midX - radius + 40, midY - radius + 40, x1, x1);
626:
627: outerRadius = radius - 20;
628: innerRadius = radius - 32;
629: for (int w = 0; w < 360; w += 15) {
630: a = Math.toRadians(w);
631: x1 = midX - ((int) (Math.sin(a) * innerRadius));
632: x2 = midX - ((int) (Math.sin(a) * outerRadius));
633: y1 = midY - ((int) (Math.cos(a) * innerRadius));
634: y2 = midY - ((int) (Math.cos(a) * outerRadius));
635: g2.drawLine(x1, y1, x2, y2);
636: }
637:
638: g2.setPaint(this.roseHighlightPaint);
639: innerRadius = radius - 26;
640: outerRadius = 7;
641: for (int w = 45; w < 360; w += 90) {
642: a = Math.toRadians(w);
643: x1 = midX - ((int) (Math.sin(a) * innerRadius));
644: y1 = midY - ((int) (Math.cos(a) * innerRadius));
645: g2.fillOval(x1 - outerRadius, y1 - outerRadius, 2 * outerRadius,
646: 2 * outerRadius);
647: }
648:
649:
650: for (int w = 0; w < 360; w += 90) {
651: a = Math.toRadians(w);
652: x1 = midX - ((int) (Math.sin(a) * innerRadius));
653: y1 = midY - ((int) (Math.cos(a) * innerRadius));
654:
655: Polygon p = new Polygon();
656: p.addPoint(x1 - outerRadius, y1);
657: p.addPoint(x1, y1 + outerRadius);
658: p.addPoint(x1 + outerRadius, y1);
659: p.addPoint(x1, y1 - outerRadius);
660: g2.fillPolygon(p);
661: }
662:
663:
664: innerRadius = radius - 42;
665: Font f = getCompassFont(radius);
666: g2.setFont(f);
667: g2.drawString("N", midX - 5, midY - innerRadius + f.getSize());
668: g2.drawString("S", midX - 5, midY + innerRadius - 5);
669: g2.drawString("W", midX - innerRadius + 5, midY + 5);
670: g2.drawString("E", midX + innerRadius - f.getSize(), midY + 5);
671:
672:
673: y1 = radius / 2;
674: x1 = radius / 6;
675: Rectangle2D needleArea = new Rectangle2D.Double(
676: (midX - x1), (midY - y1), (2 * x1), (2 * y1)
677: );
678: int x = this.seriesNeedle.length;
679: int current = 0;
680: double value = 0;
681: int i = (this.datasets.length - 1);
682: for (; i >= 0; --i) {
683: ValueDataset data = this.datasets[i];
684:
685: if (data != null && data.getValue() != null) {
686: value = (data.getValue().doubleValue())
687: % this.revolutionDistance;
688: value = value / this.revolutionDistance * 360;
689: current = i % x;
690: this.seriesNeedle[current].draw(g2, needleArea, value);
691: }
692: }
693:
694: if (this.drawBorder) {
695: drawOutline(g2, area);
696: }
697:
698: }
699:
700:
705: public String getPlotType() {
706: return localizationResources.getString("Compass_Plot");
707: }
708:
709:
715: public LegendItemCollection getLegendItems() {
716: return null;
717: }
718:
719:
724: public void zoom(double percent) {
725:
726: }
727:
728:
735: protected Font getCompassFont(int radius) {
736: float fontSize = radius / 10.0f;
737: if (fontSize < 8) {
738: fontSize = 8;
739: }
740: Font newFont = this.compassFont.deriveFont(fontSize);
741: return newFont;
742: }
743:
744:
751: public boolean equals(Object obj) {
752: if (obj == this) {
753: return true;
754: }
755: if (!(obj instanceof CompassPlot)) {
756: return false;
757: }
758: if (!super.equals(obj)) {
759: return false;
760: }
761: CompassPlot that = (CompassPlot) obj;
762: if (this.labelType != that.labelType) {
763: return false;
764: }
765: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
766: return false;
767: }
768: if (this.drawBorder != that.drawBorder) {
769: return false;
770: }
771: if (!PaintUtilities.equal(this.roseHighlightPaint,
772: that.roseHighlightPaint)) {
773: return false;
774: }
775: if (!PaintUtilities.equal(this.rosePaint, that.rosePaint)) {
776: return false;
777: }
778: if (!PaintUtilities.equal(this.roseCenterPaint,
779: that.roseCenterPaint)) {
780: return false;
781: }
782: if (!ObjectUtilities.equal(this.compassFont, that.compassFont)) {
783: return false;
784: }
785: if (!Arrays.equals(this.seriesNeedle, that.seriesNeedle)) {
786: return false;
787: }
788: if (getRevolutionDistance() != that.getRevolutionDistance()) {
789: return false;
790: }
791: return true;
792:
793: }
794:
795:
803: public Object clone() throws CloneNotSupportedException {
804:
805: CompassPlot clone = (CompassPlot) super.clone();
806: if (this.circle1 != null) {
807: clone.circle1 = (Ellipse2D) this.circle1.clone();
808: }
809: if (this.circle2 != null) {
810: clone.circle2 = (Ellipse2D) this.circle2.clone();
811: }
812: if (this.a1 != null) {
813: clone.a1 = (Area) this.a1.clone();
814: }
815: if (this.a2 != null) {
816: clone.a2 = (Area) this.a2.clone();
817: }
818: if (this.rect1 != null) {
819: clone.rect1 = (Rectangle2D) this.rect1.clone();
820: }
821: clone.datasets = (ValueDataset[]) this.datasets.clone();
822: clone.seriesNeedle = (MeterNeedle[]) this.seriesNeedle.clone();
823:
824:
825: for (int i = 0; i < this.datasets.length; ++i) {
826: if (clone.datasets[i] != null) {
827: clone.datasets[i].addChangeListener(clone);
828: }
829: }
830: return clone;
831:
832: }
833:
834:
842: public void setRevolutionDistance(double size) {
843: if (size > 0) {
844: this.revolutionDistance = size;
845: }
846: }
847:
848:
855: public double getRevolutionDistance() {
856: return this.revolutionDistance;
857: }
858:
859:
866: private void writeObject(ObjectOutputStream stream) throws IOException {
867: stream.defaultWriteObject();
868: SerialUtilities.writePaint(this.rosePaint, stream);
869: SerialUtilities.writePaint(this.roseCenterPaint, stream);
870: SerialUtilities.writePaint(this.roseHighlightPaint, stream);
871: }
872:
873:
881: private void readObject(ObjectInputStream stream)
882: throws IOException, ClassNotFoundException {
883: stream.defaultReadObject();
884: this.rosePaint = SerialUtilities.readPaint(stream);
885: this.roseCenterPaint = SerialUtilities.readPaint(stream);
886: this.roseHighlightPaint = SerialUtilities.readPaint(stream);
887: }
888:
889: }