Chart.js 2本縦線を引いて間を塗りつぶす。

グラフに任意に1本の縦線しか書き足さない場合は、
chart.js 任意の点でグラフに縦線を描画する - Oboe吹きプログラマの黙示録
のとおり。

では、2本引く場合は、chart.js 任意の点でグラフに縦線を描画する - Oboe吹きプログラマの黙示録
を例に、、
単にインデックスの配列で2点を渡して CANVAS で stroke() の実行を2回にするだけ。

var originalLineDraw = Chart.controllers.line.prototype.draw;
Chart.helpers.extend(Chart.controllers.line.prototype, {
   draw: function(){
      originalLineDraw.apply(this, arguments);
      var chart = this.chart;
      var ctx = chart.chart.ctx;
      if (chart.config.data.lineAtIndex){
         for(i in chart.config.data.lineAtIndex){
            var xaxis = chart.scales['x-axis-0'];
            var yaxis = chart.scales['y-axis-0'];
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(xaxis.getPixelForValue(undefined
, chart.config.data.lineAtIndex[i]), yaxis.top);
            ctx.strokeStyle = '#ff0000';
            ctx.lineWidth = 1;
            ctx.lineTo(xaxis.getPixelForValue(undefined
, chart.config.data.lineAtIndex[i]), yaxis.bottom);
            ctx.stroke();
            ctx.restore();
         }
      }
   }
});

var graph = {
   labels: [ "0", "1", "2", "3", "4", "5", "6", "7", ],
   datasets: [{
      // 省略
   },{
      // 省略
   }]
};

Chart.plugins.register({
   beforeDraw: function(c){
      var ctx = c.chart.ctx;
      ctx.fillStyle = "rgba(255, 255, 255, 1)";
      ctx.fillRect(0, 0, c.chart.width, c.chart.height);
   }
});
var canvas = document.getElementById("myChart");
var ctx = canvas.getContext('2d');
var myChart = new Chart(ctx, {
    type: 'line',
    data: graph,
    options: options
});
/***** 配列で指定 *************/
graph.lineAtIndex = [ 2, 4 ];

myChart.update();

f:id:posturan:20180602104613j:plain

次に間を塗りつぶすように修正する

var originalLineDraw = Chart.controllers.line.prototype.draw;
Chart.helpers.extend(Chart.controllers.line.prototype, {
   draw: function(){
      originalLineDraw.apply(this, arguments);
      var chart = this.chart;
      var ctx = chart.chart.ctx;
      if (chart.config.data.lineAtIndex){
         for(i in chart.config.data.lineAtIndex){
            var xaxis = chart.scales['x-axis-0'];
            var yaxis = chart.scales['y-axis-0'];
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(xaxis.getPixelForValue(undefined, chart.config.data.lineAtIndex[i]), yaxis.top);
            ctx.strokeStyle = '#ff0000';
            ctx.lineWidth = 1;
            ctx.lineTo(xaxis.getPixelForValue(undefined, chart.config.data.lineAtIndex[i]), yaxis.bottom);
            ctx.stroke();
            ctx.restore();
         }
         var xaxis = chart.scales['x-axis-0'];
         var yaxis = chart.scales['y-axis-0'];
         var x = xaxis.getPixelForValue(undefined, chart.config.data.lineAtIndex[0]);
         var w = xaxis.getPixelForValue(undefined, chart.config.data.lineAtIndex[1]) - x;
         ctx.save();
         ctx.fillStyle = "rgba(255, 230, 230, 0.6)";
         ctx.fillRect(x, yaxis.top, w, yaxis.height);
         ctx.restore();
      }
   }
});

chart.js のデータセットコントローラの書き方しだいで、こんなに変わってくる。
f:id:posturan:20180602105056j:plain

chart.js 任意の点でグラフに縦線を描画する

線グラフ Chart.js で、任意のポイントでグラフに縦の線を描画する。
この方法、まだ完全に理解できてないけど、なんとなくこんな方法になるのかと。。。
f:id:posturan:20180601215142j:plain

データセットコントローラとやらを設定するみたい、
Chart.controllers.line.prototype 
そして
datasets と並ぶ属性として、lineAtIndex で縦線を引きたいインデックスを指定する。

var graph = {
   labels: [ "0", "1", "2", "3", "4", "5", "6", "7", ],
   datasets: [{
      label: "Algon",
      lineTension: 0,
      backgroundColor: "rgba(255, 255, 255, 1)",
      borderColor: "rgba(0, 0, 255, 1)",
      borderCapStyle: 'round',
      borderDash: [4, 10],
      borderDashOffset: 1.0,
      borderJoinStyle: "round",
      pointBorderColor: "rgba(0, 0, 255, 1)",
      pointBackgroundColor: "rgba(0, 0, 255, 1)",
      pointBorderWidth: 4,
      pointHoverRadius: 10,
      pointHoverBackgroundColor: "rgba(0, 0, 255, 1)",
      pointHoverBorderColor: "rgba(255, 240, 15, 1)",
      pointHoverBorderWidth: 4,
      pointRadius: 1,
      pointHitRadius: 10,
      fill: false,
      spanGaps: false,
      data: []
   }],
};
var options = {
   responsive: true,
   title:{ display:true,
      text:'Line Chart sample'
   },
   scales: {
      xAxes: [{ display: true,
         scaleLabel: { display: true, labelString: 'Days' }
      }],
      yAxes: [{ display: true,
         scaleLabel: { display: true, labelString: 'Value' },
         ticks: { min: 0, max: 100, stepSize: 20 }
      }]
   }
};
/********************* 任意の縦線を引く為の処理 ***********************/
var originalLineDraw = Chart.controllers.line.prototype.draw;
Chart.helpers.extend(Chart.controllers.line.prototype, {
   draw: function(){
      originalLineDraw.apply(this, arguments);
      var chart = this.chart;
      var ctx = chart.chart.ctx;
      var index = chart.config.data.lineAtIndex;
      if (index){
         var xaxis = chart.scales['x-axis-0'];
         var yaxis = chart.scales['y-axis-0'];
         ctx.save();
         ctx.beginPath();
         ctx.moveTo(xaxis.getPixelForValue(undefined, index), yaxis.top);
         ctx.strokeStyle = '#ff0000';
         ctx.lineWidth = 1;
         ctx.lineTo(xaxis.getPixelForValue(undefined, index), yaxis.bottom);
         ctx.stroke();
         ctx.restore();
      }
   }
});

$(function(){
   Chart.plugins.register({
      beforeDraw: function(c){
         var ctx = c.chart.ctx;
         ctx.fillStyle = "rgba(255, 255, 255, 1)";
         ctx.fillRect(0, 0, c.chart.width, c.chart.height);
      }
   });
   var canvas = document.getElementById("myChart");
   var ctx = canvas.getContext('2d');
   var myChart = new Chart(ctx, {
       type: 'line',
       data: graph,
       options: options,
   });
   var data = [43, 36, 17, 28, 51, 66, 73 ];
   data.forEach(function(e){
      graph.datasets[0].data.push(e);
   });
   /*** 3番目に 縦線を引く ***/
   graph.lineAtIndex = 2;

   myChart.update();

});

GenericBuilder の強化

Java データエンティティなど、データを表現するインスタンス生成する時に威力を発揮する
ものを作っていた。。これを強化しようと思う。
  (自分はエンティティという単語、DBに限定してしまうような表現が嫌い)
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/util/GenericBuilder.java

これは生成対象のクラスが、setter を持っていることが条件で with メソッド

public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value)

により、setter を with の引数として BiConsumer に渡すことができた。
setter メソッドさえあれば、、、
メソッド参照、Object::setXxxx を BiConsumer とできたので、この with を簡潔に書けた。

public class Foo{
   public int value;
   public void setValue(int value){
        this.value = value;
   }
}
/////////
GenericBuilder.of(Foo::new).with(Foo::setValue, 16).build();

のように書けた。
しかし、setter を持っていないクラスでフィールドに値をセットするには

Throwable な BiConsumer
(自作 → https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/function/ThrowableBiConsumer.java
を使って、以下のようにしないとならない。

(例)Foo というクラス

public class Foo{
   public int value;
}

Foo の value に int 値 16 をセットして生成

Foo foo = GenericBuilder.of(Foo::new)
.with(ThrowableBiConsumer.of((t, u)->t.getClass().getDeclaredField("value").set(t, u)), 16)
.build();

では、対象フィールドが、public ではなく、private では以下のように長くなってしまう。

Foo foo = GenericBuilder.of(Foo::new)
.with(ThrowableBiConsumer.of((t, u)->{
   Field f = t.getClass().getDeclaredField("value");
   f.setAccessible(true);
   f.set(t, u);
}), 16)
.build();

これは、少し辛い。
新しい関数型インターフェースを定義する。

package org.yipuran.util;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.function.BiConsumer;
/**
 * Fieldsetter
 */
@FunctionalInterface
public interface Fieldsetter<T, U> extends Serializable{
   String get(T t, U u) throws Exception;
   public static <T, U> BiConsumer<T, U> of(Fieldsetter<T, U> function){
      return (t, u)->{
         try{
            Field f = t.getClass().getDeclaredField(function.get(t, u));
            f.setAccessible(true);
            f.set(t, u);
         }catch(Throwable ex){
            throw new RuntimeException(ex);
         }
      };
   }
}

これならば、

Foo foo = GenericBuilder.of(Foo::new)
.with(Fieldsetter.of((t, u)->"value"), 16)
,build();

これで生成クラスが setter を持っていなくても、private フィールドでも
GenericBuilder でビルドできる。
しかしリフレクションが走る分の遅さはある。

この Fieldsetter.of 他にも役立ちそうな気がする。

Wicket URLからページバージョン番号を除外する

Wicket のページは、そのまま使用するとステートフルでページセッションの番号が
ブラウザのURLに付与されてしまう。
  ?1  → 2回目の表示、 → ?2
昔からこの解決方法は以下 stackoverflow で示唆されてきているが、敢えてここに書くと。。
stackoverflow.com

↑ ↑ ↑
コンストラクタは、1つのみにすべきで

import org.apache.wicket.core.request.handler.BookmarkableListenerRequestHandler;
import org.apache.wicket.core.request.handler.ListenerRequestHandler;
import org.apache.wicket.core.request.mapper.MountedMapper;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;

public class NoVersionMapper extends MountedMapper{
   public NoVersionMapper(String mountPath, final Class<? extends IRequestablePage> pageClass){
      super(mountPath, pageClass, new PageParametersEncoder());
   }
   @Override
   protected void encodePageComponentInfo(Url url, PageComponentInfo info){
   }
   @Override
   public Url mapHandler(IRequestHandler requestHandler){
      if (requestHandler instanceof ListenerRequestHandler
            || requestHandler instanceof BookmarkableListenerRequestHandler){
         return null;
      }else{
         return super.mapHandler(requestHandler);
      }
   }
}

mountPath を指定しないコンストラクタを用意してそれを使ってしまうと、
全てのURLで同じ Page を指してしまい良くない。・・そういう要求ケースもあるかもしれないが考えにくい。

この NoVersionMapper を WebApplation の init() で

getRootRequestMapperAsCompound().add(new NoVersionMapper("/", HomePage.class));

のように指定していくわけだが、サイトの全ページに対処するとなると全Page クラスでこれを呼びだすのも
ちょっとたいへん。init() の書き方を各々工夫することに委ねられるだろう。
(注意)
ただし!以下が後から発覚! "/" に対しては
NoVersionMapper を書くべきでない。

oboe2uran.hatenablog.com

Wicket8 StatelessAjax*** は正式リリースには存在しない

Wicket 8.0.0 が出て気がついた。
Wicket-stuff にあった wicketstuff-stateless 8.0.0-M2 これは、Wicket 8.0.0 には入らなかった。。
stateless と statefull 両方混在するサイトを作る場合もあるので、
以下

StatelessAjaxFormSubmitBehavior
StatelessAjaxEventBehavior
StatelessAjaxFormComponentUpdatingBehavior
StatelessOnChangeAjaxBehavior
StatelessAjaxButton
StatelessAjaxFallbackLink
StatelessAjaxSubmitLink
StatelessEncoder

は、欲しかった。
とくに、AjaxFormSubmitBehavior → StatelessAjaxFormSubmitBehavior が欲しかったのだが、
Wicket の Component の getStatelessHint() で true を返すということが、
Behavior で実装して効果を持てないの?!
AjaxSubmitLink を使えばいんですが、どうしても StatelessAjaxFormSubmitBehavior が欲しいなら
あまり良くないけど、これで取ってくるしかないか。

<dependency>
      <groupId>org.wicketstuff</groupId>
      <artifactId>wicketstuff-stateless</artifactId>
      <version>8.0.0-M2</version>
</dependency>

整理する

自分が作ったOSSgithub に整理する。maven-repository も配置したgithubにブランチとして置いたので
かなり自由に取得して使える可能性は高くなった。

URL 対象
https://github.com/yipuran/jacob JSONを返す目的のWeb Application
https://github.com/yipuran/yipuran-core Java core application
https://github.com/yipuran/yipuran-mybatis mybatis利用の為のライブラリ
https://github.com/yipuran/yipuran-gsonhelper Google gson を簡易に利用する為のライブラリ
https://github.com/yipuran/yipuran-compress tar and gzip 機能
https://github.com/yipuran/yipuran-wicketcustom Wicket カスタマイズ

まだまだ、ドキュメントがちゃんと書けてない部分が沢山あるが、Wiki に少しずつ書いていきたい。

悩んだのは、Java祝日計算」、これ、10年以上前に、sourceforge.jp というサイトで公開して
sourceforge.jp → osdn.jp に変わり、
Javaしか考えてなかったものの、JavaScript 版も作成して、
国民の休日の特殊な考え方の制度ができた時も対応して、、、
山の日も対応して、、、
次の天皇誕生日の変更も対応して、、、
今に至る。。。
書いた自分が知らないうちに、書籍に紹介までされてしまってたので驚いた。。
 (それも気がついたのは最近、、、技術習得は本出るの待ってると遅いので最近買わないなぁ。。。)
 翔泳社 2013年に出版
 タイトル:現場で使えるJavaライブラリ
 竹添直樹 (著), 島本多可子 (著), 小津美夕紀 (著), 亀井隆司 (著)
まあ、別に書いた本人はかまわない。。。
好きに使ってくれて構わないし好きに変更して使ってくれて構わないし、そのかわり責任は一切持たない。
10年以上前に書いた時は、たいしたプログラムじゃないと思ってたし、ダサいなあと感じてた。。
でも "Java祝日計算" をググってみると、結構、自分の書いたものが目につくではないですか。。。
こうなってくると。。
この「Java祝日計算」のソースを、いいかげん、GitHub に移そうかどうしようか迷ってる。。。
移したい気もするけど、長年、osdn.jp に置いたのどうする。。。

GitHub READEMEからWikiへのリンクを張る

GitHub READEME.md を編集
編集中のリポジトリ内への内部リンクは、
  [表示文字列](相対パス)
の書式なので以下のように書くが、

[詳細説明](/doc/detail.md)

READEME.md から、編集中のリポジトリWiki ページは、

[to Wiki](../wiki)

とすると、実際は404ページへ遷移になり失敗する。

以下の様に、もう1つ PATH階層を上にしないとならない。すると成功する

[to Wiki Page](../../wiki)