この手順は、メルカトル図法の簡易版を実装する例を元に説明します。この例のコード一式は、以下のファイルにあります。
新しい投影クラスの定義
-
新しいクラスを定義するには、まずパッケージ ilog.views.maps.projection にある投影ライブラリーをインポートする必要があります。次のセクションの例で使用されるソース・コード一式は、メルカトルの例のファイルにあります。
-
投影クラスは、パッケージにあるすべての投影図法の基本クラスである IlvProjection クラスを拡張する必要があります。
import ilog.views.maps.projection.*; import ilog.views.maps.*; class MercatorProjection extends IlvProjection
コンストラクターの記述
-
スーパークラス
IlvProjection
のコンストラクターを呼び出す必要があります。MercatorProjection() { super(true, true, IlvProjection.CONFORMAL); }
このコンストラクターは、次の 3 つの引数をとります。- 最初の引数は
boolean
値で、投影が非球体楕円体をサポートするかどうかを指定します。この例では、投影がこれらの楕円体の方程式をサポートするので、この引数はtrue
に設定されます。 - 2 番目の引数は
boolean
値で、投影が逆メソッドをサポートするかどうかを示します。この例では投影が逆メソッドをサポートするので、この引数はtrue
に設定されます。 - 3 番目の引数は
int
値で、投影の幾何学プロパティーを示します。この例では、メルカトル図法が正角のため、この引数はIlvProjection.CONFORMAL
です。
順方向投影の記述
メルカトル図法に
forward()
メソッドを記述する前に、IlvProjection.forward()
メソッドについて理解しておく必要があります。IlvProjection.forward()
public メソッドは、ユーザーがデータを投影するために呼び出します。このメソッドは投影計算をするデータを準備し、それを適切に縮尺します。その後、投影のサブクラス (ここでは Mercator クラス) で定義されている eforward
または sForward
protected メソッドのいずれかに呼び出しをリダイレクトします。ほとんどの場合、forward
メソッドはオーバーライドしないでください。 forward メソッドは、以下の処理を行います。
球体からのデータの投影
sforward()
protected メソッドは、球形の投影を実装します。適切な縮尺は実際にはメソッド
IlvProjection.forward()
で実行されるため、sForward()
メソッドは常に球体の半径を 1 とみなします。この例では、メルカトル図法は赤道に正接になっている円筒への球形の投影です。球形の半径を 1 で、経度をラジアンで表すとしているため、
x
座標は経度と同じです。この場合、ll
の x
値を変更する必要はありません。メルカトル図法では両極近くの領域を表示することができないため、緯度が
PI/2
に近すぎる場合には例外 IlvToleranceConditionException がスローされます。-
方程式を使用して、投影済みデータの
y
座標を計算します。protected void sForward(IlvCoordinate ll) throws IlvToleranceConditionException { if (Math.abs(Math.abs(ll.y) - Math.PI / 2D) <= 1e-10D) throw new IlvToleranceConditionException(); ll.y = Math.log(Math.tan(Math.PI / 4D + .5D * ll.y)); }
楕円体からのデータの投影
eforward()
protected メソッドは、データが非球形の楕円から投影されている場合に、IlvProjection.forward()
メソッドがコールします。-
投影に
eForward()
メソッドを実装する必要はありません。データを非球体の楕円体から投影し、その投影図法がこの種類の楕円体をサポートしない場合、forward()
メソッドから例外 IlvUnsupportedProjectionFeature. この場合、任意の球体の楕円体を使用するか、またはIlvEllipsoid
クラスの適切な変換メソッドを使用して該当する球体を作成することができます。eForward()
メソッドはsForward()
メソッドよりも若干複雑ですが、getEllipsoid().getE()
が0
を返す場合、両者の方程式は同じです。protected void eForward(IlvCoordinate ll) throws IlvToleranceConditionException { if (Math.abs(Math.abs(ll.y) - Math.PI / 2D) <= 1e-10D) throw new IlvToleranceConditionException(); double e = Math.sqrt(getEllipsoid().getES()); double sinphi = e * Math.sin(ll.y); ll.y = Math.tan (.5D * (Math.PI/2D - ll.y)) / Math.pow((1D - sinphi) / (1D + sinphi), .5D * e); ll.y = -Math.log(ll.y); }
逆投影の記述
メルカトル図法に
inverse()
メソッドを記述する前に、 inverse メソッドについて理解しておく必要があります。IlvProjection.inverse()
メソッドは、反転するデータを準備し、それを適切なオフセットで処理します。ほとんどの場合、IlvProjection.inverse()
メソッドをオーバーライドする必要はないはずです。このメソッドで、次のことを行います。
-
デカルト座標で生成されたオフセットを抑制し、これらの座標をメートルに変換します。
-
座標を地理値に戻し、値 1 の半長軸で標準の楕円に適用します。
-
楕円体が球体であるかどうかによって、
sInverse()
メソッドまたはeInverse()
メソッドを呼び出します。 -
中央子午線の値を経度に追加し、経度の縮小が使用されている場合 (デフォルト値) は経度を
[-PI;PI]
範囲に調整します。 -
地心座標の場合に、緯度を変換します。
球体への逆投影
球体への逆投影は、 sInverse メソッド経由で実行されます。
-
sInverse()
メソッドを実装する必要はありません。inverse()
メソッドをサポートしない投影に対してIlvProjection.inverse()
メソッドを呼び出すと、例外 IlvUnsupportedProjectionFeature がスローされます。 -
sInverse()
メソッドはすべての順方向メソッドと同様に IlvToleranceConditionException 例外をスローすることがあります。しかし、メルカトル図法の逆方程式はすべての可能な値に対して定義されているので、このメソッドは例外を発しません。 -
sForward()
メソッドと同様に、投影図法ではx
値は変更しないため、逆方程式はy
値にのみ適用されます。protected void sInverse(IlvCoordinate xy) { xy.y = Math.PI/2D - 2D * Math.atan(Math.exp(-xy.y)); }
楕円体への逆投影
楕円体への逆投影は、 eInverse メソッド経由で実行されます。
このメソッドでは、楕円の半長軸の値を 1 とします。
-
メルカトル図法の特定のケースでは、このメソッドの楕円体への実装が球体への実装よりも複雑になります。反復が必要になり、失敗する可能性もあります。これは、非球体の楕円体からメルカトル図法を単純分析する逆方程式がないことが原因です。
protected void eInverse(IlvCoordinate xy) throws IlvToleranceConditionException { double ts = Math.exp(- xy.y); double e = Math.sqrt(getEllipsoid().getES()); double eccnth = .5D * e; double Phi = Math.PI/2D - 2D * Math.atan(ts); int i = 15; double dphi; do { double con = e * Math.sin (Phi); dphi = Math.PI/2D - 2D * Math.atan(ts * Math.pow((1D - con) / (1D + con), eccnth)) - Phi; Phi += dphi; } while ((Math.abs(dphi) > 1e-10D) && (--i != 0)); if (i <= 0) throw new IlvToleranceConditionException("non-convergent inverse phi2"); xy.y = Phi; }