2015年1月 : してログ

XYZタイルが正しく作成できているか確認するのに便利な、タイルのインデックスを表示するサービスを作ってみました。 単純に、受け渡されたXYZを表示する256×256ドットの画像を作って返すだけというものです。 OpenLayers では、下記のようにタイルレイヤを追加してください。

var ixlayer = new OpenLayers.Layer.XYZ(
	'インデックスレイヤー', 
	'http://landhere.info/services/tile/iximg.php?x=${x}&y=${y}&z=${z}'
);
map.addLayer(ixlayer);

ソースコードも掲載しておきますので、ご利用ください。 描画用フォントが必要なので、適当なものを用意して、$font にパスを入れて下さい。 半透明にしたい場合は、PHP でも、OpenLayers でもお好きな方で対応してください。

<?php

$z = $_REQUEST['z'];
$x = $_REQUEST['x'];
$y = $_REQUEST['y'];

$png = imagecreatetruecolor(256,256);

$white = imagecolorallocate($png, 0xFF, 0xFF, 0xFF);
$black = imagecolorallocate($png, 0x00, 0x00, 0x00);
$font = 'yourfont.ttf';
$text = "z={$z},x={$x},y={$y}";

$ix = 10;
$iy = 20;

imagefttext($png,10,0,$ix,$iy,$white,$font,$text);

imagerectangle($png,0,0,255,255,$white);

header('Content-type: image/png');
imagepng($png);

?>

[PHP] max_input_varsの罠

PHP 2015122

項目数の多いフォームの値が受け取れない場合、max_input_vars の設定を疑ってみてください。 PHP 5.3.9 から導入されたもので、この設定値を超える数の GET や POST の値を捨ててしまう、という動きをします。 内容からして DoS 攻撃を回避する目的のものですのですし、デフォルトの 1000 個というのは、通常は問題にならない設定だと思います。

しかし、現在関わっているプロジェクトでは、数百~千レコードくらいのテーブルを、すべてフォーム要素に展開するという恐ろしい仕様なのです。 仕様を把握した瞬間に、これは無いな~と変更を打診しましたがゴリ押しされてしまい、仕方なく実装してみたところ、軽くこの制限を超えてしまったようです。 これに気づくのに、かなりムダな時間を要してしまったのが痛いですが、今回は内部のシステムですので max_input_vars の値を大幅に増やして対応しました。 ちなみに、この設定は無効にできないらしいです。 それにしても、まだシステムエンジニア的な嗅覚は持っていたようで、少し安心しました。

テーブルの定義(データ型、フィールド長、カラムの定義順、デフォルト値、NOT NULLなど)を一括して取得する SQL 文です。 テーブル名は、relname = 'table_name' の部分で指定します。 前の記事の方法より、こちらのほうが簡単で他の情報も取得できます。

select
	*
from
	information_schema.columns
where
	table_catalog = current_database()
		and
	table_name = 'table_name'
order by
	ordinal_position
;

テーブルを構成する、各フィールドのデータ型を取得するSQL文は、下記のようなります。 テーブル名は、relname = 'table_name' の部分で指定します。 プログラム内でこれらを取得することで、入力値の桁数や、データ型のチェックに役立ちます。

select
	at.attname,
	format_type(at.atttypid, at.atttypmod)
from
	pg_attribute as at
		left join pg_type as tp on (at.atttypid = tp.oid)
where
	at.attnum > 0 and
	at.attrelid = (select relfilenode from pg_class where relname = 'table_name')
order by
	at.attnum
;

Android や iOS の GPS アプリで高度(Altitude)が表示されるものがありますが、アプリによって高度の数値が違う場合があります。 実際の海抜より数十メートルほど大きい値を示しているアプリは、GPS の生の高度を表示しています。 GPS が出す高度値は、地球の形を近似した楕円体(WGS84楕円体)からの高さ(楕円体高)を表していて、日本付近では約40mほどずれた値になります。 これを標高に直すには、その地点に応じたジオイド高を減算してあげる必要があります。 このジオイド高は、衛星などによる観測によって得られた値で、その地点ごとに違うことになります。 専門的なアプリで無い限り、このジオイド高による補正が行われていないため、先のようなずれが生じます。

いくつかインストールした限りでは、下記のアプリケーションでジオイド高による補正がされているようです。

  • GPS Test(Android)
    このアプリは、設定の中に「Adjust altitude」という項目があり、ジオイド高による補正をオン・オフできます。実際に、どのくらい差があるのか確かめることができます。
  • My Altitude(iOS)
    デバイスのセンサー値(Device Sensor)とダウンロードした NOAA の標高データ(Data File)から、現在地の標高を表示できます。ただし、センサー値を補正した表示は行えないようです。
  • Commander Compass Lite(iOS)
    特に説明は無いのですが、右上の3行目が標高を表しているようです。
    センサー値っぽいので削除

レイヤーの重ねあわせ順を制御するのに layer.setZIndex は良い結果を生みません。 そのような場合は、map.setIndex を使用します。 この関数を使用すると、レイヤーのインデックスが振り直されることに注意してください。 例えば、レイヤーを再背面に移動したい場合は、setIndex(Layer, -1) のようにコールしますが、インデックス番号0に Layer が配置され、それ以降のレイヤーは1ずつ後ろへシフトされます。 このため、インデックスは任意の番号を与えることができません。

実装上は、レイヤーリストやレイヤーツリーとして別途管理しておき、レイヤーの追加や削除が行われたときは、すべてのレイヤーについて順番に map.setIndex(Layer, -1) をすると良いです。