カテゴリー別アーカイブ: プログラミング

Programing

都道府県を知ろう

1都1道2府43県

また、古い話で恐縮です。かつては車や新幹線などの交通機関が充実してないので、世界地図、日本地図への親しみが強かったように思います。

名曲「コンドルは飛んで行く」にあるように狭い大地に縛られることなく、自由に飛んでいきたいという欲求は誰にでもあります。郷愁をそそる名曲は『コンドルは飛んでいく』、『El Condor Pasa』で検索してお聞きください。

江戸時代のお伊勢参りのような無銭旅行もままならず、交通機関が充実しても即、自由に国内旅行を楽しむことは少なく、遠方への旅行ができるようになったのは、経済力が付いた後のことであり、そんなに時間は経っておりません。

今では、地図による予備知識の吸収に重きが置かれなくなったせいもあり、県の位置を正確に把握している人は昔より少ないように感じられます。そのぶん、生きた情報に触れる機会が多くなっています。

そこで、インターネットを利用して、県の位置、県名、県庁所在地名を始め、詳細な関連情報を取得するツールをJavaScriptで作ってみました。

使い方

小学生の地理の勉強に、47都道府県の場所と県名と県庁所在地名を覚えるには、地図上をクリックします。クリックする前に考えていたものと同じかどうかを確認します。県名の前に表示される2桁の番号は沖縄を0とし、北海道を46とする整理番号です。

同一箇所をさらにクリックすると県庁所在地名が表示され、もう一度クリックすると『〇〇県の詳細情報を表示するページにジャンプしますか?』という画面に切り替わります。OKの入力で別ページにジャンプし、戻るボタンをクリックすれば、当該ページに戻ってくることができます。

切り替えボタンをクリックすると47都道府県の県名が一斉に表示・非表示になります。

締めくくりの前に

因みに、県名と県庁所在地名が異なる県は、北海道と東京都を含めると19都道県になります。埼玉県はさいたま市が県庁所在地ですが、漢字とひらがなであり異なるものとしてカウントされています。

北国の人々は西国の地理に明るくなく、同様に西国の人々には北国の地理に暗いとされています。ゲーム感覚で地理や歴史、風土などについて調べてみましょう。

ソースコードは別の機会に、JavaScriptプログラミングの開発を解説する形で他も含めて一気に公開する予定です。

ちょっぴり高度な処理へ

2020年から小学校において、プログラミング教育が本格化します。

子供向けプログラミング言語としてScratch(スクラッチ)、Viscuit(ビスケット)があり、それらを使って県の特長や特産物の絵を表示するプログラムを小学2年生女子生徒が開発しています。頼もしいことです。

簡易言語では指定された県から詳細情報を引き出すことには難儀します。プログラミングを先導する先生にはJavaScriptのエッセンスは理解していただきたいものです。

動作例

 

 

 

JavaScript無名関数の文法

困った体験

最近、JavaScriptで無名関数を使い、思わぬ落とし穴にはまりそうになった体験談についてまとめます。古くからある言語は行単位で記述する習わしでした。

文の終わりは行末であるから、セミコロンのような文の終わりを明示しなくてもよかったのです。

新しい言語は行ではなく、ブロックという考え方に変わり、文の終わりにはセミコロンで区切るようになっています。

その名残りなのか、セミコロンを省略しても文法エラーとして扱わない温情ゆたかなブラウザが多いです。

4つの例題で確認

4つの例題を1つのファイルのまとめました。それぞれの特徴を記します。check0は無名関数の文法に則った記述です。check1は無名関数の終わりを示す’}’の後のセミコロンを省略したもの。check2は’}’の後の2つの改行コードを削除したもの。最後のcheck3は’}’の後にセミコロンを挿入したものです。

<!DOCTYPE html check0.html>
<html>
<script>
calc = function (a, b) {//結果を求め、戻り値を返す
	return a + b;
};						//<==== セミコロン
						//<<< 無名関数の動作確認 >>>
alert(calc(1, 2));		//3が出力される
alert(calc(10, 20));	//30が出力される
</script>
</html>
//----------------------------------------------------
<!DOCTYPE html check1.html>
<html>
<script>
calc = function (a, b) {//結果を求め、戻り値を返す
	return a + b;
}						//<==== セミコロン忘れ

alert(calc(1, 2));		//3が出力される
alert(calc(10, 20));	//30が出力される
</script>
</html>
//----------------------------------------------------
<!DOCTYPE html check2.html>
<html>
<script>
calc = function (a, b) {//結果を求め、戻り値を返す
	return a + b;
}alert(calc(1, 2));		//3が出力される(この行でエラー)
alert(calc(10, 20));	//30が出力される
</script>
</html>
//----------------------------------------------------
<!DOCTYPE html check3.html>
<html>
<script>
calc = function (a, b) {//結果を求め、戻り値を返す
	return a + b;
};alert(calc(1, 2));	//3が出力される(エラーは起きない)
alert(calc(10, 20));	//30が出力される
</script>
</html>

動作結果

check0 文法エラーなし
check1 文法エラーなし。18行の}の後、行末まで有効なコードがないのでセミコロンを自動補完
strict版check1 文法エラー発生
check2 文法エラー発生。30行、}の後に有効なコードがあるのでセミコロンは自動挿入されない
check3 文法エラーなし

厳密な文法チェック法

check1において、厳密な文法チェックをするには、”use strict”指定にします。すると「Uncaught ReferenceError: calc is not defined at check1.html:5」のエラーが出力されるようになります。以下に改訂されたcheck1を示します。

<!DOCTYPE html check1.html>
<html>
<script>
"use strict";
calc = function (a, b) {//結果を求め、戻り値を返す
	return a + b;
}

alert(calc(1, 2));		//3が出力される
alert(calc(10, 20));	//30が出力される
</script>
</html>

さいごに

曖昧なプログラミングでも文法エラーにしないで、期待通りに実行できることはありがたいことですが、check2のように冗長度をなくした時に、突然、エラーが検出されることがあります。

改行コードを削除して、ソースコードの解析されにくさをねらったときに、エラーが起きた場合には、厳格さを欠いたプログラミングを疑ってみましょう。

無名関数の定義は代入文と同等です。「代入文はセミコロンで締めくくる」と覚えておけば、セミコロンを忘れることが少なくなりそうです。

曖昧さを放っておくとセキュリティホールの原因になることがあります。できれば、曖昧な表現をなくしましょう。

 

時計と歯車の研究

理系の研究の一助に

2017年6月2日、日本の準天頂衛星「みちびき2号」の打ち上げ成功が伝えられました。何でもかんでも米国におんぶにだっこでは、経済的に優位を築いても難癖をつけられて譲歩を迫られてきました。多かれ少なかれこの傾向は果てしなく続くことでしょう。

そんな折、みちびき2号の打ち上げ成功は米軍のGPSから独立する糸口になることでしょう。一説によると位置検出の誤差は6㎝とも言われています。安定した打ち上げを行い、的確な運用には様々な分野の知識の結集が必要です。

ここでは紀元前はるか昔から存在する時計に目を向けてみます。

requestAnimationFrameの利用

スクリプト言語のJavaScriptにおいて、一定間隔で処理をするにはsetTimeout関数を使いますがここでは、requestAnimationFrame関数を使いました。

この関数はウインドウが非アクティブになると動作が低下ないし停止するので、メモリなどのコンピュータリソースの消費を抑えることができます。反面、バックグラウンドにおいて動作させたいときには不向きです。

Start Animationボタンをクリックして開始します。思うような動作にならない場合は、ブラウザをChromeかOperaに変えてみてください。

歯車の基本

歯車の歴史をたどるのも一つの楽しみかも知れません。向きを変えたり、減速、増速、間欠動作、反転、回転から直線運動など様々な使い方があります。

ここでは二つの歯車がかみ合ったとき、密着する歯数が同じになると想定しています。図面上に正しく歯数を表現することが難しいことから単純な円で表しています。

JavaScriptのSVG要素を用いた動作例を次に示しますが、右端の振り子は味付けの調味料です。冗長をお許しください。秒針の回転を、1/10、1/6の順に1/60に落とし、さらに分針の回転を、1/4、1/3の順に1/12に落としています。フクシアピンク色の#8、#4、#0の歯車が時分秒針に対応します。

動作例

 

 

縦書きを高級化する

縦書きの高級化・一般化

漢字文化圏では、縦書きがなくなることは少ないと思われます。かつて、大型コンピュータが普及し、事務計算、技術計算として重宝され始めました。手書きだった給与明細書の氏名欄がカタカナ表記になり、気の早い向きには漢字が消滅して、日本語はすべてカタカナになると論じていました。



そのような状況下でも、個人で手に入いるパソコンで漢字が使えるようになり、漢字処理の技術が確立しました。連綿と続いた文化は簡単には消え去るものではありません。その意味で、縦書きも高級化・一般化してこそ、晴れて市民権を得ると言えます。

その一つというにはチッポケですが、和歌の楽しみ方として、遅延表示をしながら音楽と画像を協同表現してみました。試みを以下に掲げます。

再読み込みボタンをクリックし、再実行して『桜と富士山の画像』の透過度が少しずつ変化する様に注目ください。

参照音楽:
【サイト名】フリー音楽素材 H/MIX GALLERY
【管理者】 秋山裕和
【アドレス】http://www.hmix.net/

サンプル表示


和歌を朗読のように
時間差で表示します
©TacM,2017 Ver0.01

 

縦書き遅延表示

縦書きのその後

詩、俳句、和歌などは縦書きが似合います。カラオケから縦書きの歌詞が流れると安らぎを感じます。

ブログやホームページにおいて縦書きをサポートするツール、h2vR.jsがリリースされ、多くの愛好家に使われています。

かつてから、縦書きで宮内庁が主催する歌会始の朗読のように、ちはやぶる……などと伸ばして遅延表示ができないかを検討しましたが、難解であるとの結論で保留していました。

setTimeout関数で処理を遅らせて非同期処理を試みましたが、思うようにはなりませんでした。

誤動作の内容

時間を遅らせて再度、innerHTMLにてデータを送ると、中間の進行中状態が表示されずに最後の状態だけが表示されたり、横書きになったりして期待した結果になりません。

試行錯誤を繰り返していると、縦書きと横書きを交互に切り替える方法を運営・管理サイトでJavaScriptコードを公開しており、切り替えた後にその都度、h2vR.switcher( … )関数を呼んでいることが分かりました。

遅延表示は切り替えではないのですが、縦書きの新しいデータを設定し直した後に、h2vR.switcher( … )関数を呼んだところ、こちらが期待した動作になりました。

以下に現代語訳と動作例を掲げます。

すでに実行中ですので、ロードボタンをクリックして、初めから表示してください。

現代語訳

わが家の庭にある、小さな竹林に吹く風の音が、かすかに聞こえるこの夕べかな。

実行例


©TacM,2017 Ver0.01 2017-04-08


和歌を朗読のように
時間差で表示します

 

Svgを用いた小作品

品質向上に寄与

商売を始めるにあたり、資金や労力が用意できれば十分というわけではありません。書類には表すことのできない、秘伝、ノウハウなど人知を超えたものが成功・不成功を決定づけることがあります。

JavaScriptを用いてHTML文書を作るにも、ツールを使って価値の高い文書に近づけます。

このたび、Svg要素で矢印付き円弧を含む図形を作成するための小さな道具を作りましたのでお知らせします。

Svgとは

Svg(Scalable Vector Graphics)はテキストを含めて、図形を表示するにあたり、すべて文字で表現するので、図形を拡大しても画面が粗くなることがありません。曲線や直線を表すには、豊富なコマンドを使います。コマンドはHTMLリファレンスマニュアルにしたがって作りますが思い通りになるとは限りません。

矢印の角度や長さが適切でないと寸胴のようで醜く、誤差で矢印が逆向きになることがあります。そこで動作を確認できるサポートツールを作りました。

矢印付き円弧を作る

矢印付きの円弧はpaint.netなどを使うと作成可能ですが、容易ではありません。今回のツールは描画するためのコマンドを仮に作り表示させます。

そのとき、そのコマンドをファイルに出力します。そして、満足のいく結果が得られたときに、ファイルにセーブしたコマンドをソースに反映します。console.logデバッグ文にてファイル出力を代替します。

コマンドをsvg要素のなかに設置すれば、JavaScriptを使わずにきれいな図形を描くことができます。上の図は今回のツールを用いて円弧に矢印を付けています。

サンプルコード

<html>
<head>
<svg width="500px" height="500px" version="2.0" xmlns="http://www.w3.org/2000/svg"  style="border:1px solid pink;">
<text x="5" y="16" font-family="sans-serif" font-size="12px" fill="navy">&copy;2017 TacM, Ver 0.01</text>
<marker viewBox="0 -3 10 10" refX="5" refY="0" orient="auto-start-reverse" markerWidth="10" markerHeight="10" id="mk1">
  <polygon points="10,0, 0,3 0,-3"/>
</marker>
<path id="cm0" d="M 430 246 A 180 180 0 0 0 343.46410161513774 96.11542731880104"
 fill="none" stroke="orange"	marker-start="url(#mk1)" marker-end="url(#mk1)"/>
<path id="cm1" d="M 336.53589838486226 92.11542731880104 A 180 180 0 0 0 82.22340883183918 184.67760371823599"
 fill="none" stroke="orange"	marker-start="url(#mk1)" marker-end="url(#mk1)"/>
<path id="cm2" d="M 79.48724768523383 192.19514468452323 A 180 180 0 0 0 214.80409700790372 426.5708028315297"
 fill="none" stroke="orange"	marker-start="url(#mk1)" marker-end="url(#mk1)"/>
<path id="cm3" d="M 222.68255903200136 427.9599882528652 A 180 180 0 0 0 430 254.0"
 fill="none" stroke="orange"	marker-start="url(#mk1)" marker-end="url(#mk1)"/>
	<path id="ln0" d="M 250 250 L 350 76.8" fill="none" stroke="green"/>
	<path id="ln1" d="M 250 250 L 62.06147584281834 181.59597133486622" fill="none" stroke="green"/>
	<path id="ln2" d="M 250 250 L 215.27036446661393 446.9615506024416" fill="none" stroke="green"/>
	<path id="ln3" d="M 250 250 L 450 250.0" fill="none" stroke="green"/>
<path d="M 0 250 L 500 250" stroke="aliceblue" fill="none" stroke-width="1"/>
<path d="M 250 0 L 250 500" stroke="aliceblue" fill="none" stroke-width="1"/>
</svg>
</head>
<body onLoad="rose1()"><BR>
開始角度<input id="step1" type="number" min="0" max="360" value="0" style="width:52px;"/>
終了角度<input id="step2" type="number" min="0" max="360" value="60" style="width:52px;"/>
矢印円径<input id="step3" type="number" min="40" max="120" value="72" style="width:52px;"/>
仕切り長<input id="step4" type="number" min="40" max="120" value="90" style="width:52px;"/>
<input id="msg3" type="button" onClick="display('step1', 'step2', 'step3', 'step4')" value="描画"
style="font-weight:bold; background-color:cyan; border-color:red; border-width:1px"/>
</body>
<script type="text/javascript" charset="Shift_JIS">
const COM1="M "; //
const COM2="A "; //
const ZX=250;
const ZY=250;

var count=0;
var RADIUS=72;
var LENGTH=90;
function rose1(){
	console.log("Start SvgArcGen.");
}
function display(target1, target2, target3, target4){   //描画ボタンが押下されたとき起動
 if(count >= 4) alert("終わっています");
 else{													//開始
  if(count==0){
	cm0.setAttribute("d", "");ln0.setAttribute("d", "");//#1
	cm1.setAttribute("d", "");ln1.setAttribute("d", "");//#2
	cm2.setAttribute("d", "");ln2.setAttribute("d", "");//#3
	cm3.setAttribute("d", "");ln3.setAttribute("d", "");//#4
  }
  var tg1 = document.getElementById(target1).value;     //ステッパー値を算出
  var tg2 = document.getElementById(target2).value;     //ステッパー値を算出
  RADIUS = document.getElementById(target3).value;      //ステッパー値を算出
  LENGTH = document.getElementById(target4).value;      //ステッパー値を算出
  var x1 = ZX+RADIUS * Math.cos(tg1 * Math.PI / 180);
  var y1 = ZY-RADIUS * Math.sin(tg1 * Math.PI / 180);
  var x2 = ZX+RADIUS * Math.cos(tg2 * Math.PI / 180);
  var y2 = ZY-RADIUS * Math.sin(tg2 * Math.PI / 180);

  var f2 = (tg2 - tg1) < 180 ? 0 : 1;

  var xb1 = 4 * Math.sin(tg1 * Math.PI / 180);
  var yb1 = 4 * Math.cos(tg1 * Math.PI / 180);
  var xb2 = 4 * Math.sin(tg2 * Math.PI / 180);
  var yb2 = 4 * Math.cos(tg2 * Math.PI / 180);

    var vx = ZX+LENGTH * Math.cos(tg2 * Math.PI / 180);
    var vy = ZY-LENGTH * Math.sin(tg2 * Math.PI / 180);
    //console.log("LX="+vx+" LY="+vy);

    var md1 = COM1 + (x1-xb1) + " " + (y1-yb1) + " ";
    var md2 = COM2 + RADIUS + " "+ RADIUS + " 0 ";
    var md3 = md1 + md2 + f2 + " 0 " + (x2+xb2) + " " + (y2+yb2);

    md2 = COM1 + ZX + " "+ ZY + " L " + vx + " " + vy;
    console.log("cm"+count+" : "+md3+"  ln"+count+" : "+md2);
	switch(count){
	case 0:	cm0.setAttribute("d", md3);ln0.setAttribute("d", md2);break;	//#1
	case 1:	cm1.setAttribute("d", md3);ln1.setAttribute("d", md2);break;	//#2
	case 2:	cm2.setAttribute("d", md3);ln2.setAttribute("d", md2);break;	//#3
	case 3:	cm3.setAttribute("d", md3);ln3.setAttribute("d", md2);break;	//#4
	}
	step1.setAttribute("value", tg2);										//開始角度
	var tg3 = (tg2 > 260) ? 360 : Number(tg2) + 100;
	step2.setAttribute("value", tg3);										//終了角度
	++count;
 }
}
</script>
</html>

実行例

©2017 TacM, Ver 0.01
開始角度 終了角度 矢印円径 仕切り長

コマンドの作り方

サンプルコードの3,5行目に示すように、Svgのバージョンは2.0でorient属性のパラメータはauto-start-reverse指定でなければなりません。開始角度、終了角度、矢印円径、仕切り長を入力して描画ボタンをクリックすると下図のような円弧と仕切り線が描画されます。

次の開始角度は前回の終了角度がコピーされます。よって、2回目以降、連続した領域に円弧を描くには終了角度だけを入力して描画ボタンをクリックします。描画は4回で終了になります。

デバッガを起動しconsole.logで出力したメッセージをコマンドに使えるように、コピー&ペーストで取り込み、テキストエディタで編集しHTMLコードを作成します。

さいごに

このたびの例題では360度を最高4つの領域に分割するにとどまっています。せっかくの矢印が期待した向きにならない場合は、chrome, opera, firefoxなどのブラウザをお使いください。デバッガはどのブラウザにも付属されています。デバッガの使い方については別の機会に譲りますが、右クリックし「検証」から進みます。

 

地図を目次にするを改善

内外判定

地図を目次にする簡潔方法では、47都道府県をすべて長方形に見立てて、クリックした位置から詳細な情報にたどり着くために道筋を立てられるような内容でした。

地図画像の複数点をクリックして、その位置に応じた流れにするには、ある点が多角形に内包するかという問題に帰着します。今回はもう少し、複雑な領域をサポートしてみます。

《多角形に点が含まれるかどうか判定する》には、点と多角形の1辺が作る符号付き角度(a,b,c,d)の総和が 360 度であれば、その点は多角形の内部にあり、そうでなければ外にあると判定する簡便法があります。

多角形が単純な凸型ならば問題は起きませんが、左図のような凹部があると期待した動作にならないことが知られています。

これらの問題をクリアした、点から正の方向に伸ばした x 軸(半直線)が多角形境界線と交差する回数を数える方式(crossing Number Algorithm)があります。

この回数(cN)が奇数ならば、その点は多角形の内側にあり、偶数ならば外側にあると判定されます。Crossing Number Algorithm は Ray Casting Algorithm とも呼ばれています。

Ray Casting Algorithm

符号付き角度(a,b,c,d)の総和が 360 度であるかどうかで判定する方法では、九州・沖縄の地図を図のように簡略化して実装したときに、鹿児島県をクリックしたときに誤動作しました。

これらを解決するために、多くの研究者が技術を公開しており、二次元多角 形領域内部に点が含まれるかどうかを判定を参考にさせていただきました。

ここでは、九州・沖縄に限定して実装を試みました。1ヶ所で独立しているのは北海道と沖縄だけであり、この課題をクリアすれば47都道府県をカバーできるでしょう。

サンプルコード

<html>
ray casting algorithm<BR>
<body onload="starting()"><canvas id="kyu8" width="300" height="444" style="border: 1px orange solid"></canvas></body>
<audio id="sound3" src="http://aidesign.lolipop.jp/wp-content/uploads/2015/09/select06.mp3"></audio>
<script type="text/javascript" charset="Shift_JIS">
const KX=160;                                           // 表示位置x
const KY=400;                                           // 表示位置y
const pref=["長崎", "佐賀", "福岡", "大分", "熊本", "宮崎", "鹿児島", "沖縄"];              // # n 県名
const poly=[[ 10,  9,   96,  9,   96,100,   10,100],                                        // 0 4 長崎
            [ 96,  9,  170,  9,  170,100,   96,100],                                        // 1 4 佐賀
            [170,  9,  288,  9,  288,101,  214,101,  214,149,  129,149,  129,100,  170,100],// 2 8 福岡
            [214,101,  288,101,  288,198,  214,198],                                        // 3 4 大分
            [129,149,  214,149,  214,268,  129,268],                                        // 4 4 熊本
            [214,198,  288,198,  288,314,  214,314],                                        // 5 4 宮崎
            [129,268,  214,268,  214,314,  288,314,  288,357,  129,357],                    // 6 6 鹿児島
            [ 14,341,   79,341,   79,423,   14,423]];                                       // 7 4 沖縄
var img1 = new Image();                                 // 画像オブジェクト,ロードする
img1.src = "http://aidesign.lolipop.jp/wp-content/uploads/2017/03/Kyushu08.png";            // 九州・沖縄の地図
var ctx;                                                // コンテキスト
//------------------------------------------------------// 2017-03-20 Completed.
function starting(){                                    // draw-top、ページの読み込み時に実行される
  var canvas = document.getElementById('kyu8');         // canvasの要素を取得する
  if(canvas.getContext){                                // canvasオブジェクト
    ctx = canvas.getContext('2d');                      // canvasのコンテキスト
    canvas.addEventListener('click', clickfunc , false);// マウスイベントを取り付ける
    ctx.drawImage(img1, 0, 0, 300, 444);                // <<<<< 九州・沖縄の地図を描画 >>>>>
    ctx.fillStyle = "navy";                             // 現在地名の色
    ctx.fillText("\xA9TacM,2017 Ver0.01", KX, 430);     // copyright
  }
}                                                       // draw-bottom
function clickfunc(event){                              // クリックされた時の処理
  document.getElementById("sound3").play();             // キー押下音
  ctx.clearRect(KX, KY-30, 120, 34);                    // 前に表示した内容をクリアする
  ctx.font = "30px Gill Sans MT Bold";                  // set font.
  ctx.fillStyle = "red";                                // メッセージの色
  for(var i=0; i<poly.length; ++i){                     // 地点を検索する
    if(pointInPolygon(event.offsetX, event.offsetY, poly, i)){// judgeInclusion 検索地の左端・上端~右端・下端
      ctx.fillText(pref[i]+"県", KX, KY);               // 検索にヒットした
      return;                                           // breakはNG、returnの省略は不可
    }
  }
  ctx.fillText("該当なし", KX, KY);                     // 検索にヒットしない
  return;                                               // 戻り値のない関数は省略可能
}
function pointInPolygon(px, py, Array, m){  // px:event.offsetX, py:event.offsetY, Array:array-addr, m:index
  var crossingNumber=0;                     // cNが奇数ならば点Pは多角形Tの内側に、偶数ならば外側にあると判定
  for(var i=Array[m].length-2,j=0,yi,yj,counterClockwise; i>=0; j=i,i-=2){// Arrayテーブルの大きさ:8, 6 4 2 0
    yi=Array[m][i+1]-py;                    // <<<<< 点の多角形に対する内外判定 >>>>>
    yj=Array[m][j+1]-py;                    // role-1:上向きの辺。点Pがy軸方向について、始点と終点の間にある。
    if((yi>0)!=(yj>0) && (counterClockwise=sign(yj,yi))==sign(yj*(Array[m][i]-px), yi*(Array[m][j]-px))){
      crossingNumber+=counterClockwise;     // role-2:下向きの辺。点Pがy軸方向について、始点と終点の間にある。
    }                                       // role-3:ルール1,ルール2を確認することで、ルール3も確認できる。
  }                                         // role-4:辺は点pよりも右側にある。
  return crossingNumber%2==1;               // ray casting algorithm
}
function sign(a, b) {                       // 両者を比較して、-1 0 1を返す
    return (a > b) ? 1 : (a < b) ? -1 : 0;
}
</script>
</html>

 

実装例

福岡県は8角形、鹿児島県は6角形でありともに凹部を含んでいます。他県はすべて4角形です。本格的な処理に発展させるには38行と42行を拡張させるとよいでしょう。県内、県外の領域をクリックし、納得のいくメッセージが表示されるかを試してください。

ray casting algorithm

 

 

地図を目次にする簡潔方法

概要

以前に、地図を目次にするウィジェットの作り方で本格的な地図の利用法を掲載しましたが、今回は違う仕組みで簡単な地図目次を作ってみます。

仕組み

ここでは、canvas要素にaddEventListenerを用いてマウスイベントを取り付けて、クリック位置を管理します。マウスイベント関数では、地図上にあらかじめ定義された領域とクリックされたポイントを突き合わせてどの県都が選択されたかを決定します。

作成内容

関東地方、一都六県の地図の所定位置をクリックするとその県都の情報が表示されるものです。

地図を目次にする手順

①基準となる地図を用意する

例として最後の方に示す関東地方の地図(KantoArea1.png)を考えます。地図は自然な形で画面に収まるサイズが望ましいです。もう少し小さい方が更に引き立つと思われるますが、他に表示するものがないので横550px、縦520pxにしました。

➁クリック有効領域を地図にマークする

県や都の地図上の形は複雑ですが、長方形で代表することにします。下のような地図(KantoArea2.png)を作成します。オリジナルな地図(KantoArea1.png)を残しておくことが大切です。

③補助線付き地図とクリック位置が表示されるJavaScriptコードを作成します。このサンプルコードは栃木県から群馬県まで時計回りに、黄色に象られた長方形の左上と右下を順にクリックして、領域の範囲をテーブル化するためだけにあります。入力を失敗したときは再読み込みをしてやり直します。

<html>
<script type="text/javascript" charset="Shift_JIS">
const mesg=["左上", "右下"];
const pref=["栃木", "茨城", "千葉", "神奈川", "東京", "埼玉", "群馬"];
const point=[226, 46,374,173, 380, 58,505,294, 325,302,503,508,   // 画面上の位置
             174,235,305,321, 165,322,312,371, 141,383,303,483,  54, 81,212,206];
var img1 = new Image();                                 // 画像オブジェクト,ロードする
img1.src = "../material/KantoArea2.png";                // 1都6県
var m=n=0;                                              // n:0~6、訪問地番号
var ctx;
//------------------------------------------------------// 2017-03-11
function starting(){                                    // draw-top、ページの読み込み時に実行される
  var canvas = document.getElementById('east');         // canvasの要素を取得する
  if(canvas.getContext){                                // canvasオブジェクト
    ctx = canvas.getContext('2d');                      // canvasのコンテキスト
    canvas.addEventListener('click', clickfunc , false);// マウスイベントを取り付ける
    ctx.drawImage(img1, 0, 0, 550, 520);                // <<<<< 奥の細道全行程図を描画 >>>>>
  }
}                                                       // draw-bottom
function clickfunc (event) {                            // クリックされた時の処理
  document.getElementById("sound3").play();             // キー押下音
  m = Math.floor(n / 2);
  console.log("N="+n+" : "+pref[m]+" "+mesg[n%2]+" X="+event.offsetX+" Y="+event.offsetY);
  ++n;
  return;                                               // 戻り値のない関数は省略可能
}
</script>
<body onload="starting()"><canvas id="east" width="550" height="520" style="border: 0px #999 solid"></canvas></body>
<audio id="sound3" src="http://aidesign.lolipop.jp/wp-content/uploads/2015/09/select06.mp3"></audio>
</html>

④上の関東地方地図の右にデバッグ情報として出力されたクリック位置の値を参照してpointテーブルを編集します。

⑤次に、これまでの開発用コードを保存し、補助線なしの地図に切り替え、クリックされた都県を認識する目次の機能を追加します。

⑥デバッグ用ステートメントを削除するかコメント文にします。使わなくなったmesgテーブルや変数mなども削除します。完成版サンプルコードを以下に掲載します。27行の三項演算子で東京では都を、その他では県が付加されます。33行のreturnは省力できますが、29行のreturnを省略すると正しく動作しません。

<html>
<script type="text/javascript" charset="Shift_JIS">
const pref=["栃木", "茨城", "千葉", "神奈川", "東京", "埼玉", "群馬"];
const point=[233,  6,364,188, 370, 42,536,289, 345,297,532,514,   // 画面上の位置
             138,381,301,486, 134,322,338,372, 116,217,329,314,  66, 46,217,206];
var img1 = new Image();                                 // 画像オブジェクト,ロードする
img1.src = "../material/KantoArea1.png";                // 1都6県
var n=0;                                                // n:0~6、訪問地番号
var ctx;
//------------------------------------------------------// 2017-03-11 Completed.
function starting(){                                    // draw-top、ページの読み込み時に実行される
  var canvas = document.getElementById('east');         // canvasの要素を取得する
  if(canvas.getContext){                                // canvasオブジェクト
    ctx = canvas.getContext('2d');                      // canvasのコンテキスト
    canvas.addEventListener('click', clickfunc , false);// マウスイベントを取り付ける
    ctx.drawImage(img1, 0, 0, 550, 520);                // <<<<< 奥の細道全行程図を描画 >>>>>
  }
}                                                       // draw-bottom
function clickfunc (event) {                            // クリックされた時の処理
  document.getElementById("sound3").play();             // キー押下音
  //console.log("X="+event.offsetX+" Y="+event.offsetY);
  ctx.clearRect(210, 10, 220, 24);
  ctx.font = "18px Gill Sans MT Bold";                  // set font.
  ctx.fillStyle = "red";                                // メッセージの色
  for(var i=0; i<7; ++i){                               // 地点を検索する
    if(ck(event.offsetX, event.offsetY, i)){            // x,yをチェック  検索地の左端・上端~右端・下端
      var s = (i != 4) ? "県" : "都";                   // 東京ならば「都」
      ctx.fillText(pref[i]+s+"が選択されました", 210, 30);
      return;                                           // breakはNG
    }
  }
  ctx.fillText("該当する都県がありません", 210, 30);    // 検出にヒットしない
  return;                                               // 戻り値のない関数は省略可能
}
function ck(x, y, j){                                   // クリックされた位置の有効性をチェック
 if(x>=point[j*4] && x<=point[j*4+2] && y>=point[j*4+1] && y<=point[j*4+3])return true;//範囲内,左端・上端~右端・下端
 return false;                                          // 範囲外
}
</script>
<body onload="starting()"><canvas id="east" width="550" height="520" style="border: 0px #999 solid"></canvas></body>
<audio id="sound3" src="http://aidesign.lolipop.jp/wp-content/uploads/2015/09/select06.mp3"></audio>
</html>

さいごに

多角形で象られた複雑な領域をクリックして制御するデータを作成するツールが出回っていますが、ここでは単純な長方形だけをサポートしています。

長方形だけでは県境で誤差が出て正確さを欠きますが、それでも県庁所在地付近では正確に都県を選択することが可能です。まずは簡単な仕組みを理解して高みを目指しましょう。

詳細な都県情報を出力するには、上記コードの28行を拡張して都県が運営するホームページにジャンプするように編集します。

 

はいチーズ

新しいことへ挑戦

新しいことに挑戦することは楽しいです。モノは経年変化、消耗などいつかは終わりが来ます。そのぶん、新しいことに挑戦する人がいないとこの世は終わります。

むかし、「開けゴマ」という寓話がありました。恐らく、幻灯で見た「アリババと40人の盗賊」に現れたように記憶しています。

今は博物館にいかないと見られないものですが、幻灯機が60年前にはありました。視聴覚教育という言葉がない頃、夏休み、公民館で紙芝居を映像化したような幻灯機で童話をみる習慣がありました。

図は骨董商の買い取り案内です。今は製造販売が中止されていることから、お値打ちものと言えるでしょう。

はいチーズ

著名な景勝地は外国人観光客で埋め尽くされています。そこには自撮り棒でスマホにより写真を撮る姿を多く見ることができます。かつては三脚にカメラをセットして自動シャッターで撮ったものです。

スマホでは、「はいチーズ」、「撮ります」、「よそ行きの顔」などの発声に反応し、自動的に写真を撮影するアプリケーションがすでに発売されています。ここでは発声を動作に代えた《身振りシャッター》なるアプリを作成するアイデアについて述べることにします。

身振りシャッター

身振りシャッターはJavaScriptで作ることができます。三脚に設置されたスマホが「並んで!」というしぐさをした人物をとらえると「早く並んでください」と呼びかけます。

「皆さん、よろしいでしょうか!」、「撮るよー」、「撮影準備」、「よういできましたか」などの案内メッセージを流し続けているとき、『よういドン!』のような身振りを認識すると「ワンツースリー」、「3秒後にシャッターが押されます」などのメッセージが流れた後、撮影終了になるアプリケーションです。

作り方

アイデアが固まって開発に入るところです。

主な処理
発声
画像認識
動画中継
撮影
保存

 

動画を縁取り加工

Canvas動画を縁取る

Youtubeに投稿するような動画の上部をカットする技術はまだ、未開発です。今回、Canvas要素を使って描いた動画に縁取りを付けてみました。仕組みはハート型写真を作るで採用した重ね書き方法です。

CANVASで描いた画像にかたどるパターンを上書きします。先の記事で解説したように、表示させる部分は透明色、表示をカットする部分を白色のパターンを用意します。

JavaScriptのプログラミング例を以下に示します。

      for(var i = 0; i < NUM; i++){
        …                                      //省略
        ctx.beginPath();                        //更新した座標で円を描く
        ctx.fillStyle = 'rgb(' + r[i] + ',' + g[i] + ',' + b[i] + ')';
        ctx.arc(locX[i], locY[i], radius[i], 0, Math.PI*2.0, true);
        ctx.fill();								//表示を完成させる
      }
      ctx.drawImage(Img[ImageCounter], 0, 0);	//縁取り画像を表示する

応用例

HTMLのCanvas要素を用いて、以前、ダイナミック新年書簡2016で下図のような花火の動画を作りました。四角形では花火の雰囲気が出にくいので、その下の雲の形に象った動画を作ってみます。

fuchidori-1夜空をイメージした雲のなかで動画が展開します。表示する動画は全く、同じものですがフィルター効果で縁取りが生きてきます。

fuchidori

採用例

この手法で作った短い動画を93回2017箱根駅伝の往路と復路の間に挟み込みました。開始から1分20秒近辺で出現します。冬の花火をご覧いただけます。

最後に

このたびの技術の基本はCANVASとフィルター画像による重ね書きです。これを応用すれば、Youtubeに投稿するような動画を縁取ることも可能になると思われます。

AVI、MP4、WEBM、WMVなどの動画はCANVAS要素を使って表示することは容易です。2つの技術を融合すれば、多様な形状を縁取った動画を実現できそうです。