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