001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.hayabusa.io; 017 018import org.jfree.chart.renderer.category.LineAndShapeRenderer; 019import org.jfree.chart.renderer.category.CategoryItemRendererState; 020import org.jfree.ui.RectangleEdge; 021 022import java.awt.Graphics2D; 023import java.awt.Shape; 024import java.awt.Color; 025import java.awt.Paint; 026import java.awt.Stroke; 027import java.awt.geom.Line2D; 028import java.awt.geom.Rectangle2D; 029import java.io.IOException; 030import java.io.ObjectOutputStream; 031import java.io.ObjectInputStream; 032 033import org.jfree.chart.axis.CategoryAxis; 034import org.jfree.chart.axis.ValueAxis; 035import org.jfree.chart.entity.EntityCollection; 036import org.jfree.chart.plot.CategoryPlot; 037import org.jfree.chart.plot.PlotOrientation; 038import org.jfree.data.category.CategoryDataset; 039import org.jfree.data.general.DatasetUtilities; 040import org.jfree.data.Range; 041import org.jfree.util.ShapeUtilities; 042 043import java.awt.geom.AffineTransform; 044 045/** 046 * HybsLineRenderer は、org.jfree.chart.renderer.category.LineAndShapeRenderer を 047 * 拡張したカスタマイズクラスです。 048 * これは、描画に対して、予め制限を設けて、処理速度の向上を図っています。 049 * 050 * @og.rev 3.8.9.2 (2007/07/28) 新規作成 051 * 052 * @version 0.9.0 2001/05/05 053 * @author Kazuhiko Hasegawa 054 * @since JDK1.1, 055 */ 056public class HybsLineRenderer extends LineAndShapeRenderer implements HybsDrawItem { 057 private static final long serialVersionUID = 519020100801L ; 058 059 private transient ValueMarkOverColors overColors = null; // 4.0.3.0 (2008/01/07) マーカーラインでShapeを切り替える時の色指定 060 061 private Color[] shapeColors = null; // 4.0.3.0 (2008/01/07) データ毎にShapeを切り替える時の色指定 062 private double visibleLimit = Double.NEGATIVE_INFINITY; 063 private int dynamicOCNo = -1; // 4.1.1.0 (2008/02/04) 動的なマーカーラインの基準シリーズ番号 064 private String shapeScale = null; // 4.1.1.0 (2008/02/04) shapeの大きさの倍率 065 private boolean isItemLabelLastVisible = false; // 4.1.2.0 (2008/03/12) 066 private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ; // 4.3.1.1 (2008/08/23) 067 068 /** 069 * Creates a renderer with both lines and shapes visible by default. 070 */ 071 public HybsLineRenderer() { 072 super(true, true); 073 } 074 075 /** 076 * Creates a new renderer with lines and/or shapes visible. 077 * 078 * @param lines draw lines? 079 * @param shapes draw shapes? 080 */ 081 public HybsLineRenderer( final boolean lines, final boolean shapes ) { 082 super(lines,shapes); 083 } 084 085 /** 086 * データ毎にShapeを切り替える時の色の繰返しパターンを文字列配列で指定します。 087 * 088 * HybsLine でのみ使用可能です。 089 * これは、データそのものが、繰返し性のある場合に、その繰返し性に対応した 090 * 形状のShape を表示させる場合に使用します。 091 * 繰返しShapeの形状は、JFreeChart のシリーズ毎の繰返し標準形状を使用します。 092 * 現在のバージョンでは、10個までの繰返しに対応可能です。 093 * 繰返し色を、指定した分だけ、順に使用されていきます。 094 * 095 * 指定文字列は、java.awt.Color クラスのstatic フィールド名で指定します。 096 * BLACK , BLUE , CYAN , DARK_GRAY , GRAY , GREEN , LIGHT_GRAY , 097 * MAGENTA , ORANGE , PINK , RED , WHITE , YELLOW , (PURPLE) が指定できます。 098 * また、#XXXXXX形式の16bitRGB表記 でも指定可能です。 099 * 100 * @og.rev 4.0.3.0 (2008/01/07) 新規追加 101 * 102 * @param colors データ毎の色の繰返しパターン(文字列配列) 103 * @see java.awt.Color#BLACK 104 */ 105 protected void setShapeColors( final Color[] colors ) { 106 shapeColors = colors; 107 } 108 109 /** 110 * shapeの大きさを倍率指定で変更します(初期値:null)。 111 * 112 * ラインチャートのShape(各グラフのポイントのマーカー)の大きさは、通常は、 113 * 自動設定されます。 114 * この大きさを、倍率指定で、変更可能です。 115 * 指定は、double 型です。 116 * 初期値は、null は、スケール変更しません(自動設定のままの大きさ) 117 * 118 * @og.rev 4.1.1.0 (2008/02/04) 新規追加 119 * 120 * @param scale shapeの大きさの倍率 121 */ 122 protected void setShapeScale( final String scale ) { 123 shapeScale = scale; 124 } 125 126 /** 127 * マーカーラインの超過時のShape色管理クラスを設定します。 128 * 129 * 動的なマーカーラインを使用する場合は、引数のシリーズデータが 130 * マーカーラインの最下位閾値に相当します。これは、グラフ化されますが、 131 * Shape は自動的に削除されます。 132 * 逆に、最上位のデータ(シリーズ=0)のShape は必ず付けます。 133 * 134 * @og.rev 4.1.0.1(2008/01/19) 新規追加 135 * @og.rev 4.1.1.0 (2008/02/04) 動的なオーバーカラー 136 * 137 * @param vmoc マーカーラインの超過時のShape色管理クラス 138 * @param dynamicOverColorNo 動的なマーカーラインの基準シリーズ番号 139 */ 140 protected void setValueMarkOverColors( final ValueMarkOverColors vmoc, 141 final int dynamicOverColorNo ) { 142 overColors = vmoc; 143 dynamicOCNo = dynamicOverColorNo; 144 } 145 146 /** 147 * 表示下限値(これ以下のデータは未表示)の値(double)を指定します。 148 * 149 * HybsLine でのみ使用可能です。 150 * この設定値以下のデータは、存在しない扱いとします。 151 * Lineを引くとき、このデータと、存在しているデータ間にラインは引かれません。 152 * 何も指定しない場合は、設定しません。 153 * 154 * @og.rev 4.0.3.0 (2008/01/07) 新規追加 155 * 156 * @param limit 表示下限値(これ以下のデータは未表示) 157 */ 158 protected void setVisibleLimit( final double limit ) { 159 visibleLimit = limit; 160 } 161 162 /** 163 * itemLabelVisible 時に、最後の値のみ表示するかどうか[true:有効/false:無効]を指定します。 164 * 165 * これは、itemLabelVisible 属性に、"last" という設定値を指定した場合は、 166 * 最後のみラベル表示します。 167 * このメソッドでは、true が指定された場合は、"last" 属性が有効になったと 168 * 判断します。 169 * 170 * @og.rev 4.1.2.0 (2008/03/12) 新規追加 171 * 172 * @param flag 最後の値のみ表示するかどうか[true:有効/false:無効] 173 */ 174 public void setItemLabelLastVisible( final boolean flag ) { 175 isItemLabelLastVisible = flag; 176 } 177 178 /** 179 * drawItem と同等の機能を持った、高速版メソッドです。 180 * 181 * @og.rev 4.0.3.0 (2008/01/07) shapeColors 属性追加 182 * @og.rev 4.1.1.0 (2008/02/04) 繰返しshapeの開始番号(shapeStartNo)追加 183 * @og.rev 4.1.1.0 (2008/02/04) seriesColors 属性は、色(Paint)のみ切り替えるようにする。 184 * @og.rev 4.1.1.0 (2008/02/04) ラベルブレイク機能の追加(HybsCategoryAxis) 185 * @og.rev 4.1.1.0 (2008/02/04) 動的なマーカーライン 186 * @og.rev 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加 187 * @og.rev 4.1.2.0 (2008/03/12) ラベルのアンダーライン時にItemLavelを表示しない 188 * 189 * @param g2 Graphics2Dオブジェクト 190 * @param state CategoryItemRendererStateオブジェクト 191 * @param dataArea Rectangle2Dオブジェクト 192 * @param plot CategoryPlotオブジェクト 193 * @param domainAxis CategoryAxisオブジェクト 194 * @param rangeAxis ValueAxisオブジェクト 195 * @param dataset CategoryDatasetオブジェクト 196 * @param serNo シリアル番号 197 */ 198 public void drawItem2( final Graphics2D g2, final CategoryItemRendererState state, 199 final Rectangle2D dataArea, final CategoryPlot plot, final CategoryAxis domainAxis, 200 final ValueAxis rangeAxis, final CategoryDataset dataset, final int serNo ) { 201 202 int clmCount = dataset.getColumnCount(); 203 int rowCount = dataset.getRowCount(); 204 RectangleEdge domEdge = plot.getDomainAxisEdge(); 205 RectangleEdge rangeEdge = plot.getRangeAxisEdge(); 206 207 boolean isShape = getBaseShapesVisible() ; 208 209 HybsCategoryAxis hybsAxis = null; 210 if( domainAxis instanceof HybsCategoryAxis ) { 211 hybsAxis = (HybsCategoryAxis)domainAxis; 212 hybsAxis.setItemLabelLastVisible( isItemLabelLastVisible ); 213 } 214 215 // データ毎にShapeを切り替える時の色の繰返しパターン 216 int shpCnt = ( shapeColors == null ) ? 1 : shapeColors.length; 217 218 // Shape の形状を指定できる。任意ではなく、表示順の開始位置の指定 219 int shapeNo = 0 ; 220 221 // shapeの大きさの倍率 222 AffineTransform transform = null; 223 if( shapeScale != null ) { 224 double scale = Double.parseDouble( shapeScale ); 225 transform = AffineTransform.getScaleInstance( scale, scale ); 226 } 227 228 // 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加 229 Stroke baseStroke = getBaseStroke(); 230 if( baseStroke != null ) { g2.setStroke( baseStroke ); } 231 232 // トリッキー:row == serNo を処理したいがために、1回余分にループをまわす。 233 for( int row=0; row<=rowCount; row++ ) { 234 if( row == serNo ) { continue; } // Mis Add 2007/07/23 235 if( row >= rowCount ) { 236 if( serNo >= 0 ) { 237 row = serNo; 238 rowCount = -1; // 終了条件 239 } 240 else { 241 break; 242 } 243 } 244 245 // 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加 246 Stroke serStroke = getSeriesStroke( row ); 247 if( serStroke != null ) { g2.setStroke( serStroke ); } 248 249 Paint rowPaint = lookupSeriesPaint( row ); 250 Shape rowShape = lookupSeriesShape( shapeNo ); 251 252 // shapeの大きさの倍率 253 if( transform != null ) { 254 rowShape = transform.createTransformedShape(rowShape); 255 } 256 257 Paint linePaint = rowPaint ; 258 // 4.1.1.0 (2008/02/04) 動的なマーカーライン(row==dynamicOCNoのデータで判定) 259 boolean shapeFlag = false; 260 if( row == dynamicOCNo ) { // 使わない場合は、-1 なので、マッチしない。 261 if( overColors != null ) { 262 linePaint = overColors.getDynamicColor(); 263 } 264 } 265 else { 266 // 動的なマーカーライン使用時(dynamicOCNo >= 0)は、row == 0 で、Shape を使います。 267 if( isShape || row == serNo || ( dynamicOCNo >= 0 && row == 0 ) ) { 268 shapeFlag = true; 269 shapeNo++ ; // Shape 形状の変更は、使用した場合のみ 270 } 271 } 272 273 double v0 = 0; 274 double x0 = 0; 275 double y0 = 0; 276 277 boolean isItemLabelsVisible = isSeriesItemLabelsVisible( row ); 278 279 int clmSeq = 0; // カラムの繰返し制御(Shape色の順番表示用) 280 for( int column=0; column<clmCount; column++ ) { 281 // nothing is drawn for null... 282 Number v1Num = dataset.getValue( row,column ); 283 if(v1Num == null) { continue; } 284 double v1 = v1Num.doubleValue(); 285 double x1 = domainAxis.getCategoryMiddle( column,clmCount,dataArea,domEdge ); 286 double y1 = rangeAxis.valueToJava2D( v1,dataArea,rangeEdge ); 287 288 // Line の描画 289 if( column > 0 && v0 >= visibleLimit && v1 >= visibleLimit ) { 290 Line2D line = new Line2D.Double( x0,y0,x1,y1 ); 291 g2.setPaint( linePaint ); 292 g2.draw( line ); 293 } 294 295 // Shape の描画 296 if( shapeFlag ) { 297 // ラベルブレイク処理 298 if( hybsAxis != null && hybsAxis.isLabelBreak( column ) ) { 299 clmSeq = 0; // 初期化 300 } 301 302 int adrs = clmSeq%shpCnt; 303 clmSeq++ ; 304 Paint paint = ( shapeColors == null ) ? rowPaint : shapeColors[adrs]; 305 // 4.1.1.0 (2008/02/04) 動的なマーカーライン(row==dynamicOCNoのデータで判定) 306 if( overColors != null ) { 307 if( dynamicOCNo >= 0 ) { 308 paint = overColors.getColor( v1,dataset.getValue( dynamicOCNo,column ) ); 309 } 310 else { 311 paint = overColors.getColor( v1 ); 312 } 313 } 314 g2.setPaint(paint); 315 Shape shape = ShapeUtilities.createTranslatedShape( rowShape,x1,y1 ); 316 g2.fill(shape); 317 g2.setPaint(rowPaint); // 色を戻す。 318 // 4.3.1.0 (2008/08/09) add an item entity, if this information is being collected 319 EntityCollection entities = state.getEntityCollection(); 320 if( entities != null ) { 321 addItemEntity( entities, dataset, row, column, shape ); 322 } 323 } 324 325 // ItemLabel の描画 326 // 山形 0-1-0 nega=fale , x= 0 上中 327 // 右坂 0-1-2 nega=true , x=10 下右 328 // 谷形 1-0-1 nega=true , x= 0 下中 329 // 左坂 2-1-0 nega=fale , x=10 上右 330 if( isItemLabelsVisible ) { 331 // 4.1.2.0 (2008/03/12) アンダースコアの場合は、表示しない。 332 if( hybsAxis != null && hybsAxis.isViewItemLabel( column ) ) { 333 double v2 = v0 ; // 仮設定(最後のcolumnとnull の場合) 334 if( column+1 < clmCount ) { 335 Number v2Num = dataset.getValue( row,column+1 ); 336 if( v2Num != null ) { 337 v2 = v2Num.doubleValue(); 338 } 339 } 340 boolean nega = ( v1<v2 ) ; 341 double lblx = ( nega && v0<v1 || !nega && v0>v1 ) ? x1 + 10 : x1 ; 342 343 drawItemLabel( g2,PlotOrientation.VERTICAL,dataset,row,column,lblx,y1,nega ); 344 } 345 } 346 347 v0 = v1; // Lineを消す処理に、過去の値が必要 348 x0 = x1; 349 y0 = y1; 350 } 351 } 352 } 353 354 /** 355 * この文字列と指定されたオブジェクトを比較します。 356 * 357 * 親クラスで、equals メソッドが実装されているため、警告がでます。 358 * 359 * @og.rev 5.1.8.0 (2010/07/01) findbug対応 360 * 361 * @param object Object 362 * 363 * @return Objectが等しい場合は true、そうでない場合は false 364 */ 365// @Override 366// public boolean equals( final Object object ) { 367// return super.equals( object ); 368// } 369 370 /** 371 * このオブジェクトのハッシュコードを取得します。 372 * 373 * @og.rev 5.1.8.0 (2010/07/01) findbug対応 374 * 375 * @return ハッシュコード 376 */ 377// public int hashCode() { return super.hashCode() ; } 378 379 /** 380 * このオブジェクトと指定されたオブジェクトを比較します。 381 * 382 * @og.rev 4.3.1.1 (2008/08/23) 新規追加 383 * @og.rev 5.1.9.0 (2010/08/01) findbug対応 384 * 385 * @param anObject 比較されるオブジェクト 386 * 387 * @return 指定されたオブジェクトが等しい場合は true、そうでない場合は false 388 */ 389 @Override 390 public boolean equals( final Object anObject ) { 391// return this == anObject; 392 if( super.equals( anObject ) ) { 393 return hsCode == ((HybsLineRenderer)anObject).hsCode; 394 } 395 return false; 396 } 397 398 /** 399 * このオブジェクトのハッシュコードを返します。 400 * 401 * @og.rev 4.3.1.1 (2008/08/23) 新規追加 402 * @og.rev 5.1.9.0 (2010/08/01) findbug対応 403 * 404 * @return このオブジェクトのハッシュコード値 405 */ 406 @Override 407 public int hashCode() { return hsCode ; } 408 409 /** 410 * 指定されたデータセットからのアイテムをすべて表示するために、要求する値の範囲を返します。 411 * 412 * @param dataset カテゴリDataset 413 * 414 * @return Rangeオブジェクト 415 */ 416 @Override 417 public Range findRangeBounds( final CategoryDataset dataset ) { 418 if( dataset instanceof HybsJDBCCategoryDataset2 ) { 419 return ((HybsJDBCCategoryDataset2)dataset).getRange(); 420 } 421 else { 422 return DatasetUtilities.findRangeBounds(dataset); 423 } 424 } 425 426 /** 427 * シリアライズ用のカスタムシリアライズ書き込みメソッド 428 * 429 * @og.rev 4.1.0.1(2008/01/19) 新規追加 430 * @serialData 一部のオブジェクト(ValueMarkOverColors)は、シリアライズされません。 431 * 432 * @param strm ObjectOutputStreamオブジェクト 433 * @throws IOException 入出力エラーが発生した場合 434 */ 435 private void writeObject( final ObjectOutputStream strm ) throws IOException { 436 strm.defaultWriteObject(); 437 } 438 439 /** 440 * シリアライズ用のカスタムシリアライズ読み込みメソッド 441 * 442 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。 443 * 444 * @og.rev 4.1.0.1(2008/01/19) 新規追加 445 * @serialData 一部のオブジェクト(ValueMarkOverColors)は、読み込まれません。 446 * 447 * @param strm ObjectInputStreamオブジェクト 448 * @throws IOException シリアライズに関する入出力エラーが発生した場合 449 * @throws ClassNotFoundException クラスを見つけることができなかった場合 450 */ 451 private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException { 452 strm.defaultReadObject(); 453 } 454}