パブリックタグ登録申請サンプルサイト ソース解説

場所情報コード入力・確認ページ3

概要

入力時に使用される次の機能について解説する。

解説

緯度・経度の10進・60進切り替え

緯度:<input type="text" name="latitude">
      <input type="hidden" name="data[latitude]" value="<?php h(_d($data,'latitude'))?>""><br>
経度:<input type="text" name="longitude">
      <input type="hidden" name="data[longitude]" value="<?php h(_d($data,'longitude'))?>""><br>
<input type="checkbox" name="deg60" value="1" id="deg60" onclick="display_latlng()" >
<label for="deg60">60進(緯度:ddmmss.ss 経度:dddmmss.ss)で入力</label><br>
10進・60進の切り替えを実現するために、入力用のtextフィールドのほかにhiddenフィールド、切り替え指示用のcheckboxを用意する。
function display_latlng(){
    if(document.getElementById('deg60').checked){
        document.getElementsByName('latitude')[0].value = dec_to_60(document.getElementsByName('data[latitude]')[0].value,2);
        document.getElementsByName('longitude')[0].value = dec_to_60(document.getElementsByName('data[longitude]')[0].value,3);
    }else{
        document.getElementsByName('latitude')[0].value = document.getElementsByName('data[latitude]')[0].value;
        document.getElementsByName('longitude')[0].value = document.getElementsByName('data[longitude]')[0].value;
    }
}
function dec_to_60(deg,w){
    var ji = Math.floor(deg);
    var hun = Math.floor((deg-ji)*60);
    var byou = ((deg-ji)*60-hun)*60;
    var byou_n = Math.floor(byou);
    var byou_z = Math.floor((byou-byou_n)*100);
    ji = ('000'+ji).substr(-w);
    hun = ('00'+hun).substr(-2);
    byou = ('00'+byou_n).substr(-2)+'.'+('00'+byou_z).substr(-2);
    return ji+hun+byou;
}
function dec_from_60(n,w){
    n = ''+n;
    var ji = parseInt(n.substr(0,w));
    var hun = parseInt(n.substr(w,2));
    var byou = parseFloat(n.substr(w+2,5));
    if(isNaN(ji)||isNaN(hun)||isNaN(byou)||hun>60||hun<0||byou>60||byou<0){
        return false;
    }
    return ji+hun/60+byou/3600;
}
緯度・経度の60進表記を10新表記に変換する関数dec_from_60および10進表記を60進表記に変換する関数dec_to_60を用意する。 なお、緯度と経度で桁数が異なる(緯度はddmmss.ss、経度はdddmmss.ss)ので、第2引数で2桁か3桁かを選択できるようにする。 display_latlngは、hiddenフィールドに入力された数値をtextフィールドに表示する関数であり、ページが読み込まれた時や地図や端末の現在地を入力した時に実行する。
    display_latlng();
    document.getElementsByName('latitude')[0].onchange = function(){
        var latitude;
        if(document.getElementById('deg60').checked){
            latitude = dec_from_60(this.value,2);
            if(!latitude)latitude = '';
        }else{
            latitude = this.value;
        }
        document.getElementsByName('data[latitude]')[0].value=latitude;
    }
    document.getElementsByName('longitude')[0].onchange = function(){
        var longitude;
        if(document.getElementById('deg60').checked){
            longitude = dec_from_60(this.value,3);
            if(!longitude)longitude = '';
        }else{
            longitude = this.value;
        }
        document.getElementsByName('data[longitude]')[0].value=longitude;
    }
ページ読み込み時の初期化関数(今回の例ではinit())に、textフィールドに値が入力された場合の処理を記載する。

水平位置精度・標高精度の絶対精度・相対精度への分割

水平位置精度・標高精度は2桁の数値をとるが、10の位は絶対精度、1の位は相対精度である。これを2つのセレクトボックスに分割して選択させる。
<input type="hidden" name="data[horizontal_accuracy]" value="<?php h(_d($data,'horizontal_accuracy'))?>"">
測定精度:
絶対精度:
<select id="horizontal_absolute_accyracy" onchange="absolute_accyracy_change('horizontal');">
    <option value='0</option>
    <option value='10高精度</option>
    <option value='20中精度</option><option value='30低精度</option>
    <option value='99精度不明</option>
</select>
相対精度:
<select id="horizontal_relative_accuracy" onchange="relative_accuracy_change('horizontal');"></select><br>
精度を保持するhiddenフィールドと、絶対精度・相対精度を選択するselectを用意する。 hiddenフィールドには、data[*_accuracy]というname、各selectにはidとして*_relative_accuracy、*_absolute_accyracyをidを設定する。 絶対精度には、選択肢をoptionタグによりあらかじめ用意し、相対精度は絶対制度に応じて選択肢を変化させるため、空のselectを用意する。
function absolute_accyracy_change(direction){
    var sel_ra = document.getElementById(direction+'_relative_accuracy');
    var val_ra = sel_ra.value;
    var sel_aa = document.getElementById(direction+'_absolute_accyracy');
    var val_aa = sel_aa.value;
    sel_ra.innerHTML = '';
    if(val_ra == '')val_ra = '0';
    if(val_aa == 99 || val_aa == 0){
        sel_ra.setAttribute('disabled','disabled');
        document.getElementsByName('data['+direction+'_accuracy]')[0].value = val_aa == 99? 99 : '';
    }else{
        sel_ra.removeAttribute('disabled','disabled');
        ['','特に高精度','高精度','中精度','低精度'].map(function(str,i){
            if(direction == 'altitude' && i == 3) return;
            if(i>(val_aa/10+1)) return;
            var opt = document.createElement('option');
            opt.innerText = str;
            opt.value = i;
            if(val_ra == i)opt.selected = true;
            sel_ra.appendChild(opt);
        });
        document.getElementsByName('data['+direction+'_accuracy]')[0].value = parseInt(val_ra)+parseInt(val_aa);
    }
}
function accuracy_change(direction){
    var accuracy = document.getElementsByName('data['+direction+'_accuracy]')[0];
    var absolute_accyracy = document.getElementById(direction+'_absolute_accyracy');
    var value = accuracy.value
    if(accuracy.value == 99) absolute_accyracy.value = 99;
    else if(accuracy.value > 0) absolute_accyracy.value = Math.floor(accuracy.value/10)*10;
    else accuracy.value = 0;
    absolute_accyracy_change(direction);
    document.getElementById(direction+'_relative_accuracy').value = value - absolute_accyracy.value;
    accuracy.value = value;
}
function relative_accuracy_change(direction){
    document.getElementsByName('data['+direction+'_accuracy]')[0].value =
        parseInt(document.getElementById(direction+'_absolute_accyracy').value) + parseInt(document.getElementById(direction+'_relative_accuracy').value);
}
hiddenフィールドがほかの部分のJavaScriptから変更された場合やページ読み込み時に実行されるaccuracy_change()、絶対精度のselectが変更されたときに呼び出されるabsolute_accyracy_change()、相対精度のselectが変更されたときに呼び出されるrelative_accuracy_change()の3つの関数を用意する。 引数directionは、文字列'horizontal'はたは'altitude'のいずれかである。

accuracy_change(direction)が呼び出されると、hiddenフィールドから値を読み出し、変数に入れておく。次に 10の位を絶対精度のselectにセットし、absolute_accyracy_change(direction)を呼び出す。 すると相対精度のselectに選択肢が用意されるので、その後にhiddenフィールドの値の1の位を相対精度の値としてセットする。 この過程でhiddenフィールドの値が破壊される場合があるので、最初に読みだしておいた値を書き戻す。

absolute_accyracy_change(direction)が呼び出されると、絶対精度の値をもとに相対精度の選択肢を変化させる。 相対精度は、絶対精度が高精度の場合は「特に高精度」「高精度」、中精度の場合には「特に高精度」「高精度」「中精度」など選択肢が変化する。 また、絶対精度が空白の場合は精度不明の場合には相対精度は選択させない。 このように場合分け及び値の演算(相対精度は絶対精度の値/10+1より大きい)により選択肢を決定し、それを相対精度の欄にセットする。 最後にhiddenフィールドに新たに選択された絶対精度および相対精度の値から求めた値をセットする。

relative_accyracy_change(direction)が呼び出されると、hiddenフィールドに選択された絶対精度および相対精度の値から求めた値をセットする。

修正申請入力画面の入力項目選択チェックボックス

修正申請の入力画面では、各項目の先頭にチェックボックスがあり、変更したい項目を選択すると、対応すると入力欄が入力可能となる。 また、チェックされた項目だけがサーバーに送信される。この部分HTMLタグを出力するPHPコードは下記のようになる。 これらのチェックボックスは<dt>タグによって囲まれ、'modify'クラスが設定されていること、また入力欄は<dd>タグによって囲まれていることに注意する。
<dt class=" pubtag <?php echo in_array('name',$error_params)?'error':''?>""><input type="checkbox" class="modify" >名称</dt>
<dd><input type="text" name="data[name]" value="<?php h(_d($data,'name'))?>""></dd>
チェックボックスは、登録申請時の入力画面では、次のCSSを出力することにより非表示とされている。
input.modify{
    display: none;
}
また、修正申請時の入力画面では、次のJavaScriptが出力され、チェックボックスによる操作が有効化されている。
function init(){
    var modify = document.getElementsByClassName('modify');
    for(var i=0; i<modify.length; i++){
        modify[i].onclick = toggle_modify;
        toggle_modify2(modify[i]);
    }
}
function toggle_modify(){
    toggle_modify2(this);
}
function toggle_modify2(element){
    var tagnames = ['input','select','textarea'];
    for(var j=0; j<tagnames.length; j++){
        var ii = element.parentNode.nextSibling.nextSibling.getElementsByTagName(tagnames[j]);
        for(var i=0; i<ii.length; i++){
            if(element.checked){
                ii[i].removeAttribute('disabled','disabled');
            }else{
                ii[i].setAttribute('disabled','disabled');
            }
        }
    }
}

init()はページ読み込み時に実行されるよう設定された関数であり、この中で各チェックボックスが操作された際のイベントハンドラtoggle_modifyを設定するとともに、チェックされているか否かに応じて入力欄を有効化・無効化する関数toggle_modify2()を呼び出す。

toggle_modify()は操作されたcheckboxを引数としてtoggle_modify2()を呼び出している。

toggle_modify2(element)は、引数elementで与えられたチェックボックスに対して処理を行う。 チェックボックス'element'を囲っているdlは、element.parentNodeである。また、それに続くddは、element.parentNode.nextSibling.nextSiblingである。 その中から指定するタグをリストアップするには、例えばinputタグであればelement.parentNode.nextSibling.nextSibling.getElementsByTagName('input')とする。 このようにしてリストアップされたすべての入力欄(input、select、textarea)に対して、'element'のチェックが外れた状態であれば、属性disableを設定する。 すると、入力や値の選択が不可となるほかに、フォームの情報をサーバーに送信する際に、送信する対象外となる。'element'のチェックがされた状態であれば、属性disableを取り去る。

屋内・屋外など

階層および中間層を選択する部分のHTMLは、以下のようになる。APIへのアクセスに使用されるlevel_codeはhiddenフィールドに格納され、その値を設定するためにkaisoおよびkaisuuの2つのセレクトボックスが使用される。 kaisoで'0'が選択された場合は、屋内を表し、kaisuuの値をhiddenフィールドに設定する。それ以外が選択された場合は、kaisoの値をそのまま設定する。 中間層は、セレクトボックスで選択した値がそのまま使用される。
<input type="hidden" name="data[level_code]" value="">
<select name="kaiso" >
    <option value="" ></option>
    <option value="0" >屋内</option>
    <option value="999" >屋外</option>
    <option value="998" >屋上</option>
    <option value="997" >海底、湖底、川底</option>
</select><br>
<div id="okunai">
    階数:
    <select name="kaisuu" >
        <option value="200" >200階</option>
        ...略...
        <option value="1">1階</option>
        ...略...
        <option value="-50">地下50階</option>
    </select>
    <select name="data[middle_floor]" >
        <option value="0" >整数階</option><option value="0.5" >中間階</option>
    </select>
</div>
この部分のPHPコードを示すと下記のようになる。ただし、make_select()はselectを容易に出力するために作成した関数である。
<input type="hidden" name="data[level_code]" value="<?php h(_d($data,'level_code'))?>"">
<?php echo make_select('kaiso',$kaiso_list,!isset($data['level_code'])||$data['level_code']==''?'':($data['level_code']<989?'0':$data['level_code'])) ?><br>
<div id="okunai">
    階数:
    <?php echo make_select('kaisuu',get_kaisuu_list(),isset($data['level_code'])&&$data['level_code']<989?$data['level_code']:1);   ?>
    <?php echo make_select('data[middle_floor]',array('0'=>'整数階','0.5'=>'中間階'),_d($data,'middle_floor')) ?>
</div>
また、JavaScriptは次のようになる。ただし、init()はページ読み込み時に自動実行されるよう設定された関数である。
function init(){
        toggle_okunai();
    document.getElementsByName('kaiso')[0].onchange = function(){
        toggle_okunai();
        if(this.value=='0'){
            document.getElementsByName('data[level_code]')[0].value =
                document.getElementsByName('kaisuu')[0].value;
        }else{
            document.getElementsByName('data[level_code]')[0].value = this.value;
            document.getElementsByName('data[middle_floor]')[0].value = 0;
        }
    }
    document.getElementsByName('kaisuu')[0].onchange = function(){
        document.getElementsByName('data[level_code]')[0].value = this.value;
    }
}
function toggle_okunai(){
    if(document.getElementsByName('kaiso')[0].value=='0'){
        document.getElementById('okunai').style.display = 'block';
    }else{
        document.getElementById('okunai').style.display = 'none';
    }
}
document.getElementsByName('kaiso')[0].onchangeはkaisoが変更された際に実行される処理を設定している。 document.getElementsByName('kaisuu')[0].onchangeはkaisuuが変更された際の処理を設定する。 toggle_okunaiはkaisoが変更された際、およびinit()から呼び出される関数だが、kaisoの値が0の場合には階数の入力欄を表示し、それ以外の場合には非表示としている。

任意項目・必須項目の表示切り替え

任意項目・必須項目は、それぞれ次のように<dl>タグを用いて表記している。
<dl id="required"></dl>
<dl id="optional"></dl>
これに対し、次のCSSを適用することで、ページ読み込み時には必須項目のみを表示させる。
dl#optional{
    display: none;
}
また、必須項目と任意項目の表示を切り替えるには、次のJavaScript関数を用意し、切り替えボタンが押されたときに実行されるようにする。
function toggle_required_optional(){
    var required = document.getElementById('required');
    var optional = document.getElementById('optional');
    var btn = document.getElementById('btn_toggle_required_optional');
    if(required.style.display == 'none'){
        required.style.display = 'block';
        optional.style.display = 'none';
        btn.value = '任意項目入力';
    }else{
        required.style.display = 'none';
        optional.style.display = 'block';
        btn.value = '必須項目入力';
    }
}