第10回(応用編-2)

グラフを書く-2


 前回は、簡単な棒グラフを書いたところで終わりにしましたので、今回は、もう少し複雑なグラフを書いてみることにします。
 前回使った、幅・高さともに1ピクセルで、透明:img001.gif、白:img002.gif、赤:img003.gif、青:img004.gif の4ファイルに加え、黒:img005.gif、緑:img006.gif の2ファイルを用意しました。

積み上げ縦棒グラフ

 縦棒グラフの、積み上げグラフの例です。ここをクリックしてください。
<HTML>
<HEAD>
<SCRIPT TYPE="text/javascript">
    function disp()
    {
        var i,datA=0,datB;
        var Dat=new Array();
        for(i=0;i<=7;i++)
        {
            Dat[i]=eval(document.form01.elements[i].value);
            if(i%2==1)
                datA=Math.max(datA,Dat[i]+Dat[i-1]);
        }
        datA=Math.ceil(datA*0.1)*10;
        document.write("<TABLE cellspacing='5'><TR valign='bottom' align='center'>");
        document.write("<TD HEIGHT='100' align='right' valign='top'>"+datA+"</TD>");
        document.write("<TD align='right' rowspan='3'><IMG src='img005.gif' WIDTH='10' HEIGHT='2'><BR>");
        document.write("<IMG src='img005.gif' WIDTH='2' HEIGHT='147'><BR>");
        document.write("<IMG src='img005.gif' WIDTH='10' HEIGHT='2'><BR>");
        document.write("<IMG src='img005.gif' WIDTH='2' HEIGHT='147'><BR>");
        document.write("<IMG src='img005.gif' WIDTH='10' HEIGHT='2'></TD>");
        for(i=0;i<=7;i++)
        {
            datB=Math.floor((Dat[i]/datA)*300);
            if(i%2==0)
                document.write("<TD rowspan='3'><IMG src='img003.gif' WIDTH='30' HEIGHT='"+datB+"'><BR>");  
            else
                document.write("<IMG src='img004.gif' WIDTH='30' HEIGHT='"+datB+"'></TD>");
        }
        document.write("</TR><TR><TD HEIGHT='100' align='right'>"+datA/2+"</TD>");
        document.write("</TR><TR><TD HEIGHT='100' align='right' valign='bottom'>0</TD>");
        document.write("</TR><TR valign='bottom' align='center'><TD></TD><TD></TD>
            <TD>1月</TD><TD>2月</TD><TD>3月</TD><TD>4月</TD></TR></TABLE>");
        document.close();
    }
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="form01">
■色別の売れた数量推移グラフ(縦棒)<P>
<TABLE>
<TR align="center"><TD></TD><TD>赤</TD><TD>青</TD></TR>
<TR><TD>1月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
<TR><TD>2月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
<TR><TD>3月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
<TR><TD>4月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
</TABLE>
<INPUT TYPE="button" VALUE="グラフ表示" onClick="disp()"><P>
</FORM>
</BODY>
</HTML>
 フォーム form01 のテキストボックスの値をはじから読んで配列 Dat に入れます。このとき、赤と青の計 Dat[0] + Dat[1] というような、積み上がる高さの、最大値の下一桁を切り上げた値を datA として、この値の棒の長さを300ピクセルにし、各値の datA に対する比率の長さの棒を書いています。偶数番目の Dat のときは、表のセルを作ってから赤色棒 img003.gif を書き改行、奇数番目の Dat のときは、青色棒 img004.gif を書いてからセルを閉じます。
 グラフの棒を書くのは、それほど面倒ではありませんが、左側に目盛りを表示するための処理の方が複雑になっています。目盛りの数値と目盛り線を表示するために、テーブルのセルのサイズ、水平位置、垂直位置を指定しています。

積み上げ横棒グラフ

 一般的に、横方向にした方が簡単です。ここをクリックしてください。
<HTML>
<HEAD>
<SCRIPT TYPE="text/javascript">
    function disp()
    {
        var i,datA=0,datB;
        var Dat=new Array();
        var Lab=new Array("1月","2月","3月","4月");
        for(i=0;i<=7;i++)
        {
            Dat[i]=eval(document.form01.elements[i].value);
            if(i%2==1)
                datA=Math.max(datA,Dat[i]+Dat[i-1]);
        }
        datA=Math.ceil(datA*0.1)*10;
        document.write("<TABLE cellspacing='5'>");
        for(i=0;i<=7;i++)
        {
            datB=Math.floor((Dat[i]/datA)*300);
            if(i%2==0)
                document.write("<TR><TD>"+Lab[i/2]+"</TD><TD><IMG src='img003.gif' WIDTH='"+datB+"' HEIGHT='30'>");  
            else
                document.write("<IMG src='img004.gif' WIDTH='"+datB+"' HEIGHT='30'> "+Dat[i-1]+" / "+Dat[i]+"</TD></TR>");
        }
        document.write("<TR><TD></TD><TD><IMG src='img005.gif' WIDTH='2' HEIGHT='6'><IMG src='img005.gif'
            WIDTH='148' HEIGHT='2' align='top'><IMG src='img005.gif' WIDTH='2' HEIGHT='6'><IMG src='img005.gif'
            WIDTH='148' HEIGHT='2' align='top'><IMG src='img005.gif' WIDTH='2' HEIGHT='6'></TD></TR>");
        document.write("<TR><TD></TD><TD>0<IMG src='img001.gif' WIDTH='140' HEIGHT='1'>"+datA/2+"<IMG src='img001.gif'
            WIDTH='140' HEIGHT='1'>"+datA+"</TD></TR>");
        document.write("</TABLE>");
        document.close();
    }
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="form01">
■色別の売れた数量推移グラフ(横棒)<P>
<TABLE>
<TR align="center"><TD></TD><TD>赤</TD><TD>青</TD></TR>
<TR><TD>1月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
<TR><TD>2月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
<TR><TD>3月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
<TR><TD>4月</TD><TD><INPUT TYPE="text"></TD><TD><INPUT TYPE="text"></TD></TR>
</TABLE>
<INPUT TYPE="button" VALUE="グラフ表示" onClick="disp()"><P>
</FORM>
</BODY>
</HTML>
 値を棒の右に表示しました。基本的に、やっていることは上の縦棒グラフと同じです。偶数番目の Dat のときは、表の行とセルを作って、項目名 Lab を書き、次のセルに赤色棒 img003.gif を書きます。奇数番目の Dat のときは、青色棒 img004.gif を書いた後に各値を書いて、セルを閉じます。

積み上げ面グラフ

 積み上げ面グラフを書くのは、棒グラフと比べるとけっこう大変です。赤と青だけではさみしいので、緑も追加しました。12のテキストボックスに入力するのがちょっと面倒だと思うので、適当な初期値を入力してあります。 ここをクリックしてください。
<HTML>
<HEAD>
<SCRIPT TYPE="text/javascript">
    var datA=0,win01;
    function disp01(dr,db,dg,r,b,g,i)
    {
        var datR,datB,datG;
        datR=Math.floor(((r+dr*i)/datA)*300);
        datB=Math.floor(((b+db*i)/datA)*300);
        datG=Math.floor(((g+dg*i)/datA)*300);
        win01.document.write("<IMG src='img003.gif' WIDTH='"+datR+"' HEIGHT='1'>");
        win01.document.write("<IMG src='img004.gif' WIDTH='"+datB+"' HEIGHT='1'>");
        win01.document.write("<IMG src='img006.gif' WIDTH='"+datG+"' HEIGHT='1'><BR>");
    }

    function disp()
    {
        var i,j,datB,dr,db,dg;
        var Dat=new Array();
        for(i=0;i<=11;i++)
        {
            Dat[i]=eval(document.form01.elements[i].value);
            if(i%3==2)
            datA=Math.max(datA,Dat[i]+Dat[i-1]+Dat[i-2]);
        }
        datA=Math.ceil(datA*0.1)*10;
        win01=window.open("","","width=600,height=400");
        win01.document.write("<TABLE cellpadding='0' cellspacing='0'>");
        win01.document.write("<TR><TD>1月</TD>");
        win01.document.write("<TD rowspan='4'><IMG src='img001.gif' WIDTH='1' HEIGHT='25'><BR>");  
        for(j=0;j<3;j++)
        {
            dr=(Dat[j*3+3]-Dat[j*3])/50;
            db=(Dat[j*3+4]-Dat[j*3+1])/50;
            dg=(Dat[j*3+5]-Dat[j*3+2])/50;
            for(i=0;i<50;i++)
                disp01(dr,db,dg,Dat[j*3],Dat[j*3+1],Dat[j*3+2],i);
        }
        win01.document.write("<IMG src='img001.gif' WIDTH='1' HEIGHT='25'></TD>");
        for(j=0;j<=3;j++)
        {
            win01.document.write("<TD>"+Dat[j*3]+" / "+Dat[j*3+1]+" / "+Dat[j*3+2]+"</TD>");
            if(j<3)
                win01.document.write("</TR><TR><TD>"+(j+2)+"月</TD>");
        }
        win01.document.write("</TR></TABLE>");
        win01.document.close();
    }
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="form01">
■色別の売れた数量推移グラフ(積み上げ面)<P>
<TABLE>
<TR align="center"><TD></TD><TD>赤</TD><TD>青</TD><TD>緑</TD></TR>
<TR><TD>1月</TD><TD><INPUT TYPE="text" VALUE="20"></TD><TD><INPUT TYPE="text" VALUE="45"></TD>
<TD><INPUT TYPE="text" VALUE="22"></TD></TR>
<TR><TD>2月</TD><TD><INPUT TYPE="text" VALUE="65"></TD><TD><INPUT TYPE="text" VALUE="40"></TD>
<TD><INPUT TYPE="text" VALUE="15"></TD></TR>
<TR><TD>3月</TD><TD><INPUT TYPE="text" VALUE="50"></TD><TD><INPUT TYPE="text" VALUE="20"></TD>
<TD><INPUT TYPE="text" VALUE="10"></TD></TR>
<TR><TD>4月</TD><TD><INPUT TYPE="text" VALUE="94"></TD><TD><INPUT TYPE="text" VALUE="36"></TD>
<TD><INPUT TYPE="text" VALUE="26"></TD></TR>
</TABLE>
<INPUT TYPE="button" VALUE="グラフ表示" onClick="disp()"><P>
</FORM>
</BODY>
</HTML>
 ここからは、比較的簡単な横方向のグラフにします。グラフは、win01 という別ウインドウに表示しています。面の色部分を書く関数を disp01 として分けたのですが、Netscape Navigator で元のウインドウに document.write で書き出してしまうと、そこから disp01 が認識できないようです。Internet Explorer では、他の例と同じように、元のウインドウに書くこともできます。
 この例では、1項目の幅を50ピクセルにしています。例えば、赤の1月の「売れた数量」 Dat[0] が100、2月の「売れた数量」 Dat[3] が150とすれば、1月から2月の間の50ピクセルで50増えているので、1月のところに100の長さの線を書いた後、長さを1ずつ増やしながら2月のところまで書きます。なお、実際には他のグラフと同様に、3色の計の最大 datA が300ピクセルになるようにしていますので、長さは調整されています。赤の1ピクセル増えるごとの値の増加は dr で、i 番目に引く線は、r+dr*i に、datA*300 を乗じた長さになります。青と緑についても同様に計算して、赤の右に書いています。
 2月まで書いた後は、2月と3月の間で同じように計算し、3月のところまで書いていきます。

計算式をグラフにする

 x と y の関係の計算式をグラフにします。
 ● サイン曲線
 交流の電圧などでなじみがある、サイン曲線です。ここをクリックしてください。
<HTML>
<HEAD>
<SCRIPT TYPE="text/javascript">
    function disp()
    {
        var i,start,end,wd;
        start=eval(document.form01.txt01.value);
        end=eval(document.form01.txt02.value);
        for(i=start;i<=end;i++)
        {
            wd=Math.ceil(Math.sin((Math.PI/180)*i)*180);
            if(wd>=0)
            {
                document.write("<IMG SRC='img001.gif' WIDTH='180' HEIGHT='1'>");
                document.write("<IMG SRC='img003.gif' WIDTH='"+wd+"' HEIGHT='1'><BR>");
            }
            else
            {
                document.write("<IMG SRC='img001.gif' WIDTH='"+(180+wd)+"' HEIGHT='1'>");
                document.write("<IMG SRC='img003.gif' WIDTH='"+(-wd)+"' HEIGHT='1'><BR>");  
            }
        }
        document.close();
    }
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="form01">
■ サイン曲線を書く(面)<P>
開始角:<INPUT TYPE="text" NAME="txt01" VALUE="0"> 
終了角:<INPUT TYPE="text" NAME="txt02" VALUE="360"><P><HR>
<INPUT TYPE="button" VALUE="表示" onClick="disp()"><P>
</FORM>
</BODY>
</HTML>
 x の値は、i で、フォームに入力した開始角(start)から終了角(end)まで増加させます。Math.sin( ) は引数をラジアンで指定しますので、i にπ/180 を乗じたものを引数として y とし、これを180倍して、表示する大きさの wd としています。wd が0以上のときは、長さ180の透明な線 img001.gif を書いてから、長さ wd の赤い線 img003.gif を書き、wd が負のときは、wd に 180を加えた長さの透明な線を書いてから、-wdの長さの赤い線を書いています。
 面でなく、線で書くこともできます。ここをクリックしてください。
<HTML>
<HEAD>
<SCRIPT TYPE="text/javascript">
    function disp()
    {
        var i,start,end,y;
        start=eval(document.form01.txt01.value);
        end=eval(document.form01.txt02.value);
        for(i=start;i<=end;i++)
        {
            y=Math.ceil(Math.sin((Math.PI/180)*i)*180)+180;
            document.write("<IMG SRC='img001.gif' WIDTH='"+y+"' HEIGHT='1'>");  
            document.write("<IMG SRC='img003.gif' WIDTH='1' HEIGHT='1'><BR>");
        }
    document.close();
    }
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="form01">
■ サイン曲線を書く(線-1)<P>
開始角:<INPUT TYPE="text" NAME="txt01" VALUE="0"> 
終了角:<INPUT TYPE="text" NAME="txt02" VALUE="360"><P><HR>
<INPUT TYPE="button" VALUE="表示" onClick="disp()"><P>
</FORM>
</BODY>
</HTML>
 このように、単純に y の長さの透明な線を書き、その右に1ピクセルの赤い点を書くと、当然なのですが、傾きが大きい部分では、線の幅が確保できずに点線のようになってしまいます。そこで、一つ前の y からの増減に1ピクセル加えた長さの赤い線を書くように改善したのが次の例です。ここをクリックしてください。
<HTML>
<HEAD>
<SCRIPT TYPE="text/javascript">
    function disp()
    {
        var i,start,end,y,wd;
        start=eval(document.form01.txt01.value);
        end=eval(document.form01.txt02.value);
        for(i=start;i<=end;i++)
        {
            y=Math.ceil(Math.sin((Math.PI/180)*i)*180);
            wd=Math.abs(Math.ceil(Math.sin((Math.PI/180)*(i-1))*180)-y)+1;
            if(y>0)
            {
                document.write("<IMG SRC='img001.gif' WIDTH='185' HEIGHT='1'>");
                document.write("<IMG SRC='img005.gif' WIDTH='1' HEIGHT='1'>");
                document.write("<IMG SRC='img001.gif' WIDTH='"+(y)+"' HEIGHT='1'>");
                document.write("<IMG SRC='img003.gif' WIDTH='"+wd+"' HEIGHT='1'><BR>");
            }
            else
            {
                document.write("<IMG SRC='img001.gif' WIDTH='"+(185+y)+"' HEIGHT='1'>");
                document.write("<IMG SRC='img003.gif' WIDTH='"+wd+"' HEIGHT='1'>");
                if(y<-4)
                {
                    document.write("<IMG SRC='img001.gif' WIDTH='"+(-y-wd)+"' HEIGHT='1'>");
                    document.write("<IMG SRC='img005.gif' WIDTH='1' HEIGHT='1'><BR>");
                }
                else
                document.write("<BR>");
            }
        }
        document.close();
    }
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME="form01">
■ サイン曲線を書く(線-2)<P>
開始角:<INPUT TYPE="text" NAME="txt01" VALUE="0"> 
終了角:<INPUT TYPE="text" NAME="txt02" VALUE="360"><P><HR>
<INPUT TYPE="button" VALUE="表示" onClick="disp()"><P>
</FORM>
</BODY>
</HTML>
 i のときの y の値と、i-1 のときの y の値の差に、1加えた wd の長さの赤い線を書くようにして、線が点線にならないようにしています。ついでに、y=0 のところに黒線も入れてみました。
 今回の例のサイン曲線以外でも、x と y の関係が計算式になっていれば、グラフにすることができるはずです。