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; 029 030import org.jfree.chart.axis.CategoryAxis; 031import org.jfree.chart.axis.ValueAxis; 032import org.jfree.chart.entity.EntityCollection; 033import org.jfree.chart.plot.CategoryPlot; 034import org.jfree.chart.plot.PlotOrientation; 035import org.jfree.data.category.CategoryDataset; 036import org.jfree.data.general.DatasetUtilities; 037import org.jfree.data.Range; 038import org.jfree.util.ShapeUtilities; 039 040import java.awt.geom.AffineTransform; 041 042/** 043 * HybsLineRenderer は、org.jfree.chart.renderer.category.LineAndShapeRenderer を 044 * 拡張したカスタマイズクラスです。 045 * これは、描画に対して、予め制限を設けて、処理速度の向上を図っています。 046 * 047 * @og.rev 3.8.9.2 (2007/07/28) 新規作成 048 * 049 * @version 0.9.0 2001/05/05 050 * @author Kazuhiko Hasegawa 051 * @since JDK1.1, 052 */ 053public class HybsLineRenderer extends LineAndShapeRenderer implements HybsDrawItem { 054 private static final long serialVersionUID = 519020100801L ; 055 056 private transient ValueMarkOverColors overColors ; // 4.0.3.0 (2008/01/07) マーカーラインでShapeを切り替える時の色指定 057 058 private Color[] shapeColors ; // 4.0.3.0 (2008/01/07) データ毎にShapeを切り替える時の色指定 059 private double visibleLimit = Double.NEGATIVE_INFINITY; 060 private int dynamicOCNo = -1; // 4.1.1.0 (2008/02/04) 動的なマーカーラインの基準シリーズ番号 061 private String shapeScale ; // 4.1.1.0 (2008/02/04) shapeの大きさの倍率 062 private boolean isLastVisible ; // 4.1.2.0 (2008/03/12) 6.0.2.5 (2014/10/31) refactoring 063 private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ; // 4.3.1.1 (2008/08/23) 064 065 private Color[] categoryColor ; // 6.0.2.1 (2014/09/26) categoryカラー配列 066 067 /** 068 * Creates a renderer with both lines and shapes visible by default. 069 */ 070 public HybsLineRenderer() { 071 super(true, true); 072 } 073 074 /** 075 * Creates a new renderer with lines and/or shapes visible. 076 * 077 * @param lines draw lines? 078 * @param shapes draw shapes? 079 */ 080 public HybsLineRenderer( final boolean lines, final boolean shapes ) { 081 super(lines,shapes); 082 } 083 084 /** 085 * データ毎にShapeを切り替える時の色の繰返しパターンを指定します。 086 * 087 * HybsLine でのみ使用可能です。 088 * これは、データそのものが、繰返し性のある場合に、その繰返し性に対応した 089 * 形状のShape を表示させる場合に使用します。 090 * 繰返しShapeの形状は、JFreeChart のシリーズ毎の繰返し標準形状を使用します。 091 * 現在のバージョンでは、10個までの繰返しに対応可能です。 092 * 繰返し色を、指定した分だけ、順に使用されていきます。 093 * 094 * 指定文字列は、java.awt.Color クラスのstatic フィールド名で指定します。 095 * BLACK , BLUE , CYAN , DARK_GRAY , GRAY , GREEN , LIGHT_GRAY , 096 * MAGENTA , ORANGE , PINK , RED , WHITE , YELLOW , (PURPLE) が指定できます。 097 * また、#XXXXXX形式の16bitRGB表記 でも指定可能です。 098 * (独自メソッド) 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 * 119 * @og.rev 4.1.1.0 (2008/02/04) 新規追加 120 * 121 * @param scale shapeの大きさの倍率 122 */ 123 protected void setShapeScale( final String scale ) { 124 shapeScale = scale; 125 } 126 127 /** 128 * マーカーラインの超過時のShape色管理クラスを設定します。 129 * 130 * 動的なマーカーラインを使用する場合は、引数のシリーズデータが 131 * マーカーラインの最下位閾値に相当します。これは、グラフ化されますが、 132 * Shape は自動的に削除されます。 133 * 逆に、最上位のデータ(シリーズ=0)のShape は必ず付けます。 134 * (独自メソッド) 135 * 136 * @og.rev 4.1.0.1(2008/01/19) 新規追加 137 * @og.rev 4.1.1.0 (2008/02/04) 動的なオーバーカラー 138 * 139 * @param vmoc マーカーラインの超過時のShape色管理クラス 140 * @param dyOCNo 動的なマーカーラインの基準シリーズ番号(dynamicOverColorNo) 141 */ 142 protected void setValueMarkOverColors( final ValueMarkOverColors vmoc, final int dyOCNo ) { 143 overColors = vmoc; 144 dynamicOCNo = dyOCNo; // 6.0.2.5 (2014/10/31) refactoring 145 } 146 147 /** 148 * 表示下限値(これ以下のデータは未表示)の値(double)を指定します。 149 * 150 * HybsLine でのみ使用可能です。 151 * この設定値以下のデータは、存在しない扱いとします。 152 * Lineを引くとき、このデータと、存在しているデータ間にラインは引かれません。 153 * 何も指定しない場合は、設定しません。 154 * (独自メソッド) 155 * 156 * @og.rev 4.0.3.0 (2008/01/07) 新規追加 157 * 158 * @param limit 表示下限値(これ以下のデータは未表示) 159 */ 160 protected void setVisibleLimit( final double limit ) { 161 visibleLimit = limit; 162 } 163 164 /** 165 * itemLabelVisible 時に、最後の値のみ表示するかどうか[true:有効/false:無効]を指定します。 166 * 167 * これは、itemLabelVisible 属性に、"last" という設定値を指定した場合は、 168 * 最後のみラベル表示します。 169 * このメソッドでは、true が指定された場合は、"last" 属性が有効になったと 170 * 判断します。 171 * (独自メソッド。HybsDrawItem より継承) 172 * 173 * @og.rev 4.1.2.0 (2008/03/12) 新規追加 174 * 175 * @param flag 最後の値のみ表示するかどうか[true:有効/false:無効] 176 */ 177 @Override 178 public void setItemLabelLastVisible( final boolean flag ) { 179 isLastVisible = flag; // 6.0.2.5 (2014/10/31) refactoring 180 } 181 182 /** 183 * categoryカラー配列を設定します。 184 * 185 * これは、HybsJDBCCategoryDataset クラスで、カテゴリカラーを指定した場合に、 186 * そこから取り出した値をセットすることで、Hybs***Renderer に設定して使います。 187 * Hybs***Renderer 側では、このカラー配列を使用して、getItemPaint(int,int) を 188 * オーバーライドして使います。 189 * (独自メソッド。HybsDrawItem より継承) 190 * 191 * @og.rev 6.0.2.1 (2014/09/26) 新規追加 192 * 193 * @param cateColor categoryカラー配列(可変長引数) 194 */ 195 @Override 196 public void setCategoryColor( final Color... cateColor ) { 197 // 6.0.2.5 (2014/10/31) refactoring 198 if( cateColor != null ) { categoryColor = cateColor.clone(); } // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。 199 } 200 201 /** 202 * カテゴリ別のColorオブジェクトを返します。 203 * 204 * Returns the paint used to color data items as they are drawn. 205 * <p> 206 * The default implementation passes control to the 207 * <code>lookupSeriesPaint()</code> method. You can override this method 208 * if you require different behaviour. 209 * 210 * @param row the row (or series) index (zero-based). 211 * @param column the column (or category) index (zero-based). 212 * 213 * @return カテゴリ別のColorオブジェクト 214 */ 215 @Override 216 public Paint getItemPaint( final int row, final int column ) { 217 // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method 218 return categoryColor == null ? super.getItemPaint( row,column ) : categoryColor[column]; 219 } 220 221 /** 222 * drawItem と同等の機能を持った、高速版メソッドです。 223 * 224 * @og.rev 4.0.3.0 (2008/01/07) shapeColors 属性追加 225 * @og.rev 4.1.1.0 (2008/02/04) 繰返しshapeの開始番号(shapeStartNo)追加 226 * @og.rev 4.1.1.0 (2008/02/04) seriesColors 属性は、色(Paint)のみ切り替えるようにする。 227 * @og.rev 4.1.1.0 (2008/02/04) ラベルブレイク機能の追加(HybsCategoryAxis) 228 * @og.rev 4.1.1.0 (2008/02/04) 動的なマーカーライン 229 * @og.rev 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加 230 * @og.rev 4.1.2.0 (2008/03/12) ラベルのアンダーライン時にItemLavelを表示しない 231 * 232 * @param g2 Graphics2Dオブジェクト 233 * @param state CategoryItemRendererStateオブジェクト 234 * @param dataArea Rectangle2Dオブジェクト 235 * @param plot CategoryPlotオブジェクト 236 * @param domainAxis CategoryAxisオブジェクト 237 * @param rangeAxis ValueAxisオブジェクト 238 * @param dataset CategoryDatasetオブジェクト 239 * @param serNo シリアル番号 240 */ 241 @Override 242 public void drawItem2( final Graphics2D g2, final CategoryItemRendererState state, 243 final Rectangle2D dataArea, final CategoryPlot plot, final CategoryAxis domainAxis, 244 final ValueAxis rangeAxis, final CategoryDataset dataset, final int serNo ) { 245 246 final int clmCount = dataset.getColumnCount(); 247 int rowCount = dataset.getRowCount(); 248 final RectangleEdge domEdge = plot.getDomainAxisEdge(); 249 final RectangleEdge rangeEdge = plot.getRangeAxisEdge(); 250 251 final boolean isShape = getBaseShapesVisible() ; 252 253 HybsCategoryAxis hybsAxis = null; 254 if( domainAxis instanceof HybsCategoryAxis ) { 255 hybsAxis = (HybsCategoryAxis)domainAxis; 256 hybsAxis.setItemLabelLastVisible( isLastVisible ); // 6.0.2.5 (2014/10/31) refactoring 257 } 258 259 // データ毎にShapeを切り替える時の色の繰返しパターン 260 final int shpCnt = ( shapeColors == null ) ? 1 : shapeColors.length; 261 262 // Shape の形状を指定できる。任意ではなく、表示順の開始位置の指定 263 int shapeNo = 0 ; 264 265 // shapeの大きさの倍率 266 AffineTransform transform = null; 267 if( shapeScale != null ) { 268 final double scale = Double.parseDouble( shapeScale ); 269 transform = AffineTransform.getScaleInstance( scale, scale ); 270 } 271 272 // 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加 273 final Stroke baseStroke = getBaseStroke(); 274 if( baseStroke != null ) { g2.setStroke( baseStroke ); } 275 276 // トリッキー:row == serNo を処理したいがために、1回余分にループをまわす。 277 for( int row=0; row<=rowCount; row++ ) { 278 if( row == serNo ) { continue; } // Mis Add 2007/07/23 279 if( row >= rowCount ) { 280 if( serNo >= 0 ) { 281 row = serNo; 282 rowCount = -1; // 終了条件 283 } 284 else { 285 break; 286 } 287 } 288 289 // 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加 290 final Stroke serStroke = getSeriesStroke( row ); 291 if( serStroke != null ) { g2.setStroke( serStroke ); } 292 293 final Paint rowPaint = lookupSeriesPaint( row ); 294 Shape rowShape = lookupSeriesShape( shapeNo ); 295 296 // shapeの大きさの倍率 297 if( transform != null ) { 298 rowShape = transform.createTransformedShape(rowShape); 299 } 300 301 Paint linePaint = rowPaint ; 302 // 4.1.1.0 (2008/02/04) 動的なマーカーライン(row==dynamicOCNoのデータで判定) 303 boolean shapeFlag = false; 304 if( row == dynamicOCNo ) { // 使わない場合は、-1 なので、マッチしない。 305 if( overColors != null ) { 306 linePaint = overColors.getDynamicColor(); 307 } 308 } 309 else { 310 // 動的なマーカーライン使用時(dynamicOCNo >= 0)は、row == 0 で、Shape を使います。 311 if( isShape || row == serNo || dynamicOCNo >= 0 && row == 0 ) { // 6.9.7.0 (2018/05/14) PMD Useless parentheses. 312 shapeFlag = true; 313 shapeNo++ ; // Shape 形状の変更は、使用した場合のみ 314 } 315 } 316 317 double v0 = 0; 318 double x0 = 0; 319 double y0 = 0; 320 321 final boolean isLabelsVisible = isSeriesItemLabelsVisible( row ); // 6.0.2.5 (2014/10/31) refactoring 322 323 int clmSeq = 0; // カラムの繰返し制御(Shape色の順番表示用) 324 for( int column=0; column<clmCount; column++ ) { 325 // nothing is drawn for null... 326 final Number v1Num = dataset.getValue( row,column ); 327 if( v1Num == null ) { continue; } 328 final double v1 = v1Num.doubleValue(); 329 final double x1 = domainAxis.getCategoryMiddle( column,clmCount,dataArea,domEdge ); 330 final double y1 = rangeAxis.valueToJava2D( v1,dataArea,rangeEdge ); 331 332 // Line の描画 333 if( column > 0 && v0 >= visibleLimit && v1 >= visibleLimit ) { 334 final Line2D line = new Line2D.Double( x0,y0,x1,y1 ); 335 g2.setPaint( linePaint ); 336 g2.draw( line ); 337 } 338 339 // Shape の描画 340 if( shapeFlag ) { 341 // ラベルブレイク処理 342 if( hybsAxis != null && hybsAxis.isLabelBreak( column ) ) { 343 clmSeq = 0; // 初期化 344 } 345 346 final int adrs = clmSeq%shpCnt; 347 clmSeq++ ; 348 Paint paint = ( shapeColors == null ) ? rowPaint : shapeColors[adrs]; 349 // 4.1.1.0 (2008/02/04) 動的なマーカーライン(row==dynamicOCNoのデータで判定) 350 if( overColors != null ) { 351 if( dynamicOCNo >= 0 ) { 352 paint = overColors.getColor( v1,dataset.getValue( dynamicOCNo,column ) ); 353 } 354 else { 355 paint = overColors.getColor( v1 ); 356 } 357 } 358 g2.setPaint(paint); 359 final Shape shape = ShapeUtilities.createTranslatedShape( rowShape,x1,y1 ); 360 g2.fill(shape); 361 g2.setPaint(rowPaint); // 色を戻す。 362 // 4.3.1.0 (2008/08/09) add an item entity, if this information is being collected 363 final EntityCollection entities = state.getEntityCollection(); 364 if( entities != null ) { 365 addItemEntity( entities, dataset, row, column, shape ); 366 } 367 } 368 369 // ItemLabel の描画 370 // 山形 0-1-0 nega=fale , x= 0 上中 371 // 右坂 0-1-2 nega=true , x=10 下右 372 // 谷形 1-0-1 nega=true , x= 0 下中 373 // 左坂 2-1-0 nega=fale , x=10 上右 374 // 6.9.7.0 (2018/05/14) PMD These nested if statements could be combined 375// if( isLabelsVisible ) { // 6.0.2.5 (2014/10/31) refactoring 376// // 4.1.2.0 (2008/03/12) アンダースコアの場合は、表示しない。 377// if( hybsAxis != null && hybsAxis.isViewItemLabel( column ) ) { 378 if( isLabelsVisible // 6.0.2.5 (2014/10/31) refactoring 379 // 4.1.2.0 (2008/03/12) アンダースコアの場合は、表示しない。 380 && hybsAxis != null && hybsAxis.isViewItemLabel( column ) ) { 381 double v2 = v0 ; // 仮設定(最後のcolumnとnull の場合) 382 if( column+1 < clmCount ) { 383 final Number v2Num = dataset.getValue( row,column+1 ); 384 if( v2Num != null ) { 385 v2 = v2Num.doubleValue(); 386 } 387 } 388 final boolean nega = v1<v2 ; 389 final double lblx = nega && v0<v1 || !nega && v0>v1 ? x1 + 10 : x1 ; // 6.9.7.0 (2018/05/14) PMD Useless parentheses. 390 391 drawItemLabel( g2,PlotOrientation.VERTICAL,dataset,row,column,lblx,y1,nega ); 392// } 393 } 394 395 v0 = v1; // Lineを消す処理に、過去の値が必要 396 x0 = x1; 397 y0 = y1; 398 } 399 } 400 } 401 402 /** 403 * このオブジェクトと指定されたオブジェクトを比較します。 404 * 405 * @og.rev 4.3.1.1 (2008/08/23) 新規追加 406 * @og.rev 5.1.9.0 (2010/08/01) findbug対応 407 * 408 * @param anObject 比較されるオブジェクト 409 * 410 * @return 指定されたオブジェクトが等しい場合は true、そうでない場合は false 411 */ 412 @Override 413 public boolean equals( final Object anObject ) { 414 // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method 415 return super.equals( anObject ) && hsCode == ((HybsLineRenderer)anObject).hsCode; 416 } 417 418 /** 419 * このオブジェクトのハッシュコードを返します。 420 * 421 * @og.rev 4.3.1.1 (2008/08/23) 新規追加 422 * @og.rev 5.1.9.0 (2010/08/01) findbug対応 423 * 424 * @return このオブジェクトのハッシュコード値 425 */ 426 @Override 427 public int hashCode() { return hsCode ; } 428 429 /** 430 * 指定されたデータセットからのアイテムをすべて表示するために、要求する値の範囲を返します。 431 * 432 * @param dataset カテゴリDataset 433 * 434 * @return Rangeオブジェクト 435 */ 436 @Override 437 public Range findRangeBounds( final CategoryDataset dataset ) { 438 // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method 439 return dataset instanceof HybsDataset ? ((HybsDataset)dataset).getRange() : DatasetUtilities.findRangeBounds(dataset); 440 } 441}