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: fireChangeEvent();
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: fireChangeEvent();
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: fireChangeEvent();
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: fireChangeEvent();
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: fireChangeEvent();
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: fireChangeEvent();
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: if ((needle != null) && (index < this.seriesNeedle.length)) {
497: this.seriesNeedle[index] = needle;
498: }
499: fireChangeEvent();
500: }
501:
502:
509: public ValueDataset[] getDatasets() {
510: return this.datasets;
511: }
512:
513:
520: public void addDataset(ValueDataset dataset) {
521: addDataset(dataset, null);
522: }
523:
524:
530: public void addDataset(ValueDataset dataset, MeterNeedle needle) {
531:
532: if (dataset != null) {
533: int i = this.datasets.length + 1;
534: ValueDataset[] t = new ValueDataset[i];
535: MeterNeedle[] p = new MeterNeedle[i];
536: i = i - 2;
537: for (; i >= 0; --i) {
538: t[i] = this.datasets[i];
539: p[i] = this.seriesNeedle[i];
540: }
541: i = this.datasets.length;
542: t[i] = dataset;
543: p[i] = ((needle != null) ? needle : p[i - 1]);
544:
545: ValueDataset[] a = this.datasets;
546: MeterNeedle[] b = this.seriesNeedle;
547: this.datasets = t;
548: this.seriesNeedle = p;
549:
550: for (--i; i >= 0; --i) {
551: a[i] = null;
552: b[i] = null;
553: }
554: dataset.addChangeListener(this);
555: }
556: }
557:
558:
568: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
569: PlotState parentState,
570: PlotRenderingInfo info) {
571:
572: int outerRadius = 0;
573: int innerRadius = 0;
574: int x1, y1, x2, y2;
575: double a;
576:
577: if (info != null) {
578: info.setPlotArea(area);
579: }
580:
581:
582: RectangleInsets insets = getInsets();
583: insets.trim(area);
584:
585:
586: if (this.drawBorder) {
587: drawBackground(g2, area);
588: }
589:
590: int midX = (int) (area.getWidth() / 2);
591: int midY = (int) (area.getHeight() / 2);
592: int radius = midX;
593: if (midY < midX) {
594: radius = midY;
595: }
596: --radius;
597: int diameter = 2 * radius;
598:
599: midX += (int) area.getMinX();
600: midY += (int) area.getMinY();
601:
602: this.circle1.setFrame(midX - radius, midY - radius, diameter, diameter);
603: this.circle2.setFrame(
604: midX - radius + 15, midY - radius + 15,
605: diameter - 30, diameter - 30
606: );
607: g2.setPaint(this.rosePaint);
608: this.a1 = new Area(this.circle1);
609: this.a2 = new Area(this.circle2);
610: this.a1.subtract(this.a2);
611: g2.fill(this.a1);
612:
613: g2.setPaint(this.roseCenterPaint);
614: x1 = diameter - 30;
615: g2.fillOval(midX - radius + 15, midY - radius + 15, x1, x1);
616: g2.setPaint(this.roseHighlightPaint);
617: g2.drawOval(midX - radius, midY - radius, diameter, diameter);
618: x1 = diameter - 20;
619: g2.drawOval(midX - radius + 10, midY - radius + 10, x1, x1);
620: x1 = diameter - 30;
621: g2.drawOval(midX - radius + 15, midY - radius + 15, x1, x1);
622: x1 = diameter - 80;
623: g2.drawOval(midX - radius + 40, midY - radius + 40, x1, x1);
624:
625: outerRadius = radius - 20;
626: innerRadius = radius - 32;
627: for (int w = 0; w < 360; w += 15) {
628: a = Math.toRadians(w);
629: x1 = midX - ((int) (Math.sin(a) * innerRadius));
630: x2 = midX - ((int) (Math.sin(a) * outerRadius));
631: y1 = midY - ((int) (Math.cos(a) * innerRadius));
632: y2 = midY - ((int) (Math.cos(a) * outerRadius));
633: g2.drawLine(x1, y1, x2, y2);
634: }
635:
636: g2.setPaint(this.roseHighlightPaint);
637: innerRadius = radius - 26;
638: outerRadius = 7;
639: for (int w = 45; w < 360; w += 90) {
640: a = Math.toRadians(w);
641: x1 = midX - ((int) (Math.sin(a) * innerRadius));
642: y1 = midY - ((int) (Math.cos(a) * innerRadius));
643: g2.fillOval(x1 - outerRadius, y1 - outerRadius, 2 * outerRadius,
644: 2 * outerRadius);
645: }
646:
647:
648: for (int w = 0; w < 360; w += 90) {
649: a = Math.toRadians(w);
650: x1 = midX - ((int) (Math.sin(a) * innerRadius));
651: y1 = midY - ((int) (Math.cos(a) * innerRadius));
652:
653: Polygon p = new Polygon();
654: p.addPoint(x1 - outerRadius, y1);
655: p.addPoint(x1, y1 + outerRadius);
656: p.addPoint(x1 + outerRadius, y1);
657: p.addPoint(x1, y1 - outerRadius);
658: g2.fillPolygon(p);
659: }
660:
661:
662: innerRadius = radius - 42;
663: Font f = getCompassFont(radius);
664: g2.setFont(f);
665: g2.drawString("N", midX - 5, midY - innerRadius + f.getSize());
666: g2.drawString("S", midX - 5, midY + innerRadius - 5);
667: g2.drawString("W", midX - innerRadius + 5, midY + 5);
668: g2.drawString("E", midX + innerRadius - f.getSize(), midY + 5);
669:
670:
671: y1 = radius / 2;
672: x1 = radius / 6;
673: Rectangle2D needleArea = new Rectangle2D.Double(
674: (midX - x1), (midY - y1), (2 * x1), (2 * y1)
675: );
676: int x = this.seriesNeedle.length;
677: int current = 0;
678: double value = 0;
679: int i = (this.datasets.length - 1);
680: for (; i >= 0; --i) {
681: ValueDataset data = this.datasets[i];
682:
683: if (data != null && data.getValue() != null) {
684: value = (data.getValue().doubleValue())
685: % this.revolutionDistance;
686: value = value / this.revolutionDistance * 360;
687: current = i % x;
688: this.seriesNeedle[current].draw(g2, needleArea, value);
689: }
690: }
691:
692: if (this.drawBorder) {
693: drawOutline(g2, area);
694: }
695:
696: }
697:
698:
703: public String getPlotType() {
704: return localizationResources.getString("Compass_Plot");
705: }
706:
707:
713: public LegendItemCollection getLegendItems() {
714: return null;
715: }
716:
717:
722: public void zoom(double percent) {
723:
724: }
725:
726:
733: protected Font getCompassFont(int radius) {
734: float fontSize = radius / 10.0f;
735: if (fontSize < 8) {
736: fontSize = 8;
737: }
738: Font newFont = this.compassFont.deriveFont(fontSize);
739: return newFont;
740: }
741:
742:
749: public boolean equals(Object obj) {
750: if (obj == this) {
751: return true;
752: }
753: if (!(obj instanceof CompassPlot)) {
754: return false;
755: }
756: if (!super.equals(obj)) {
757: return false;
758: }
759: CompassPlot that = (CompassPlot) obj;
760: if (this.labelType != that.labelType) {
761: return false;
762: }
763: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
764: return false;
765: }
766: if (this.drawBorder != that.drawBorder) {
767: return false;
768: }
769: if (!PaintUtilities.equal(this.roseHighlightPaint,
770: that.roseHighlightPaint)) {
771: return false;
772: }
773: if (!PaintUtilities.equal(this.rosePaint, that.rosePaint)) {
774: return false;
775: }
776: if (!PaintUtilities.equal(this.roseCenterPaint,
777: that.roseCenterPaint)) {
778: return false;
779: }
780: if (!ObjectUtilities.equal(this.compassFont, that.compassFont)) {
781: return false;
782: }
783: if (!Arrays.equals(this.seriesNeedle, that.seriesNeedle)) {
784: return false;
785: }
786: if (getRevolutionDistance() != that.getRevolutionDistance()) {
787: return false;
788: }
789: return true;
790:
791: }
792:
793:
801: public Object clone() throws CloneNotSupportedException {
802:
803: CompassPlot clone = (CompassPlot) super.clone();
804: if (this.circle1 != null) {
805: clone.circle1 = (Ellipse2D) this.circle1.clone();
806: }
807: if (this.circle2 != null) {
808: clone.circle2 = (Ellipse2D) this.circle2.clone();
809: }
810: if (this.a1 != null) {
811: clone.a1 = (Area) this.a1.clone();
812: }
813: if (this.a2 != null) {
814: clone.a2 = (Area) this.a2.clone();
815: }
816: if (this.rect1 != null) {
817: clone.rect1 = (Rectangle2D) this.rect1.clone();
818: }
819: clone.datasets = (ValueDataset[]) this.datasets.clone();
820: clone.seriesNeedle = (MeterNeedle[]) this.seriesNeedle.clone();
821:
822:
823: for (int i = 0; i < this.datasets.length; ++i) {
824: if (clone.datasets[i] != null) {
825: clone.datasets[i].addChangeListener(clone);
826: }
827: }
828: return clone;
829:
830: }
831:
832:
840: public void setRevolutionDistance(double size) {
841: if (size > 0) {
842: this.revolutionDistance = size;
843: }
844: }
845:
846:
853: public double getRevolutionDistance() {
854: return this.revolutionDistance;
855: }
856:
857:
864: private void writeObject(ObjectOutputStream stream) throws IOException {
865: stream.defaultWriteObject();
866: SerialUtilities.writePaint(this.rosePaint, stream);
867: SerialUtilities.writePaint(this.roseCenterPaint, stream);
868: SerialUtilities.writePaint(this.roseHighlightPaint, stream);
869: }
870:
871:
879: private void readObject(ObjectInputStream stream)
880: throws IOException, ClassNotFoundException {
881: stream.defaultReadObject();
882: this.rosePaint = SerialUtilities.readPaint(stream);
883: this.roseCenterPaint = SerialUtilities.readPaint(stream);
884: this.roseHighlightPaint = SerialUtilities.readPaint(stream);
885: }
886:
887: }