« 2007年4月 | メイン | 2007年6月 »

SVGを用いたauの地図サービス

EZガイドマップ

EzgmSVGを用いたコンシューマ向け携帯電話上の地図プラットホーム(EZガイドマップ)と、それを用いたサービス(複数)の開始が 本日(2007.5.22)KDDIの新製品発表会で発表されました。(筆者は、その関係者として発表会に参加してきました。)

EZガイドマップは、発表された15機種のうち、10機種で提供されます。

リリース時で、九つの「EZガイドマップ」対応サイトが公表されており、発表会ではその中で七つのサービスの説明が掲示されていました。

Rel_3その資料によると、インクリメントP、ALPS MAPPING、昭文社の3社の背景図提供会社の名前が出ていました。(今のところ、SVG Mapコンソーシアムメンバーであるゼンリンの名前が無かったのは残念ですが)
#これで、日本の地図提供を行っている会社・機関の大半が、SVG Mapを提供できることが明らかになったわけです。

そして、SVG MapコンソーシアムメンバーであるSECは、EZガイドマップで協業しています。

.

災害時ナビ

Dnavi_2さらに、同時に発表された災害時ナビも同じくSVGによる地図プラットホームが活用されているとのことです。(災害時ナビは12機種で提供)こちらのほうは、アジア航測が協業しています。

.

EZガイドマップの説明では、SVGによるオープン性が説明されており、今後のオープンな展開と、実効性のある標準化への貢献も期待できます。もちろん、これは国際的な取り組みとしても期待できるでしょう。
地図サービスのためのオープンスタンダードで具体的かつ現実的なプラットホームとして、SVG Mapが有望であることが、いよいよ明らかになってきたと言えるでしょう。

SVG Mapコンソーシアムは、その構成メンバーが今回のサービスに強く関わっており、今後、このプラットホームの支援を進めていく予定です。

タイリングされた地図のコンテナの例

<image>によるSVGのインポートに対応していないビューアがありますので、ラスターデータのタイルをインポートしてタイリングするコンテナSVGデータを タイリングされた地図データのサンプルとして投稿します。

Firefoxでは、地理座標は認識しませんが、タイリングされた地図を見ることができます。

orthocontainer2.svgをダウンロード  (銀座周辺です)

このデータは、国土交通省のオルソ化空中写真ダウンロードシステムが提供するWMSインターフェースのURLを用いて、オルソ写真データをインポートし、タイリングしています。

電子国土SVG Mapコンテナビルダー

電子国土ラボ が公開されました。電子国土SVGデータは、30秒区切りのタイルデータになっています。そのため、このデータはタイリングして使用すると便利です。

そこで、タイリングされた地図データの仕様に基づいた、コンテナSVGファイルを生成するプログラムを公開します。

ダウンロードした電子国土データのディレクトリを指定すると、そのディレクトリ内にある地図タイルデータをタイリングするコンテナSVGデータを生成します。

LGPLに基づいてご使用ください。

MakeCJContainer.java

// The basic Cyber Japan SVG Map mesh data container for tiling
//
// Copyright (C) 2007 by Satoru Takagi All rights reserved.
//  http://www.svg-map.com/
//
// Usage : java MakeCJContainer SearchDirectory
// Output: SearchDirectory.svg
//
// =============================================================================
//
// This software is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by  the  Free
// Software Foundation; either version 2.1 of the License, or (at  your  option)
// any later version.
//
// This library is distributed in the hope that it will be  useful,  but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for  more
// details.
//
// You should have received a copy of the  GNU  Lesser  General  Public  License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// =============================================================================
import java.io.*;
import java.util.*;

public class MakeCJContainer {
public final static double svgMeshHeight = 400.0; // set mesh Height for SVG

public static double svgMeshWidth;
    public static String target;
static boolean firstTile = false;
static double tA, tB , tC , tD , tE , tF; // transform matrix for CRS

// Main
    public static void main( String argv[] ){
        try {
   // Prepare an output SVG Map file
   FileOutputStream osFile = new FileOutputStream( argv[0] + ".svg" );
   Writer fos = new BufferedWriter(new OutputStreamWriter( osFile , "UTF-8" ));

            MakeCJContainer.target = ".svg";
            MakeCJContainer top
                = new MakeCJContainer( new File( "."+ File.separator + argv[0] + File.separator) , fos );
   fos.close();
        }
        catch( ArrayIndexOutOfBoundsException e ) {  // Parameter error
            System.err.println("Usage:java MakeCJContainer searchname");
            System.exit(-1);
        }
        catch( Exception e ) {   // Other Errors
            System.err.println("Error....");
         e.printStackTrace();
            System.exit(-1);
        }
    }

// Constructor
    public MakeCJContainer( File dir ,  Writer out ) throws Exception {
    
        try {
         searchSubDirs( dir , out );
        }
     catch( Exception e ) {
            throw e;
        }
     out.write("</svg>\n");
    }

// Generation of image element of SVG Map mesh
    public void writeImageElement( File aFile ,  Writer out ) {
     String name;
     int mStart , mEnd;
  if( aFile.getName().indexOf( target ) >= 0 ){
   name = aFile.getPath();
   mStart = name.lastIndexOf(File.separator) + 1;
   mEnd = name.indexOf( "-all" );
   try {
    CyberJpMesh mesh = new CyberJpMesh( name.substring(mStart , mEnd));
   
    if (firstTile==false){
     printSvgHeader( mesh.longitude , mesh.latitude , CyberJpMesh.span , CyberJpMesh.span , out);
     firstTile=true;
    }
    calcTransform( mesh.longitude , mesh.latitude + CyberJpMesh.span );
       out.write("<image xlink:href=\"" + name.replace('\\' , '/') +"\" "
    + "y=\"" + transY + "\" " + "x=\"" + transX + "\" "
    + "width=\"" + svgMeshWidth + "\" " + "height=\"" + svgMeshHeight + "\" />\n");
   } catch (Exception e ) {
   
   }
  }
    }

// Recurrent search for subdirectory
    public void searchSubDirs(File dir ,  Writer out ) throws Exception {
     File[] files;
     String[] names;
     names = dir.list();
     int fileLength = names.length;
     files = new File[ fileLength ];
        try {
         for( int i=0; i< fileLength; i++ ){
          files[i] = new File( dir.getPath() + File.separator + names[i] );
              if( files[i].isDirectory() ) {
              searchSubDirs( files[i] , out );
             } else {
              writeImageElement( files[i] , out);
             }
         }
        }
        catch( Exception e ) {
            throw e;
        }
    
    }


// Coordinate Trnasformer
private double transX , transY;

private void calcTransform( double x , double y){
  transX = tA * x + tC * y + tE;
  transY = tB * x + tD * y + tF;
}


// Prepare transform matrix for CRS using first mesh and write SVG header.
public void printSvgHeader(double x , double y , double w , double h ,  Writer out) throws Exception{
  tD = - svgMeshHeight / h;
  svgMeshWidth =  svgMeshHeight * Math.cos( y * Math.PI / 180.0 ) * w / h ;
  tA = - tD * Math.cos( y * Math.PI / 180.0 );
  tB = 0.0;
  tC = 0.0;
 
  tE = - tA * x;
  tF = - tD * ( y + h );
 
 
  out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  out.write("<svg  xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"" + 0.0 + " " + 0.0 + " " + w * tA + " " + (- h * tD) + "\">\n");
  out.write("<metadata>\n");
  out.write("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:crs=\"http://www.ogc.org/crs\" xmlns:svg=\"http://www.w3.org/2000/svg\">\n");
  out.write("<rdf:Description>");
  out.write("<crs:CoordinateReferenceSystem rdf:resource=\"http://purl.org/crs/84\"  svg:transform=\"matrix(" + tA + "," + tB + "," + tC + "," + tD + "," + tE + "," + tF + ")\" />\n");
  out.write("</rdf:Description>\n");
  out.write("</rdf:RDF>\n");
  out.write("</metadata>\n");
}

}

CyberJpMesh.java

// 電子国土SVGのメッシュコードを入出力します
// 2007/04/06 Satoru Takagi

import java.util.*;
import java.io.*;

public class CyberJpMesh{

public String mesh , latMesh , longMesh;
public int latNo , longNo;
public double latitude , longitude;

public static double span = 30.0 / (60.0 * 60.0); // 30sec

public static void main( String argv[] ) {
  if (argv.length == 2 ){
   CyberJpMesh jm = new CyberJpMesh(Double.parseDouble(argv[0]) , Double.parseDouble(argv[1]));
   System.out.println( "mesh3:" + jm );
  } else {
   CyberJpMesh jm = new CyberJpMesh( argv[0]);
   System.out.println( "lat:" + jm.latitude + " long:" + jm.longitude );
  }
}

public CyberJpMesh(double lati , double longi){
  latitude = lati;
  longitude = longi;
 
  latNo = (int)(latitude * 60.0 * 60.0 * 100.0);
  longNo = (int)(longitude * 60.0 * 60.0 * 100.0);
 
  latMesh = Integer.toString(latNo);
  longMesh = Integer.toString(longNo);
  mesh = longMesh + "-" + latMesh;
 
}

public CyberJpMesh(String meshNo){
  mesh = meshNo;
  int delim = mesh.indexOf("-");
  if ( delim < 0 ) {
   throw new NumberFormatException();
  }
  longMesh=mesh.substring(0,delim);
  latMesh=mesh.substring(delim+1);
 
  latNo = Integer.parseInt(latMesh);
  longNo = Integer.parseInt(longMesh);
 
  latitude = (double)latNo / (60.0 * 60.0 * 100);
  longitude = (double)longNo / (60.0 * 60.0 * 100);
 
}

public String toString(){
  return (mesh);
}

}

電子国土ラボ

国土交通省国土地理院と共同研究実施各社が行っている、「SVGコンテンツを利用した電子国土の実用的な普及戦略に関する研究」 の成果を提供するためのラボサイトが公開されました。

電子国土ラボ

同サイトでは、現在試作検証中のSVG形式の電子国土(1/25000の電子地図)データに関する情報が提供されています。 5月18日時点で、2次メッシュ区画で50枚弱が提供されています。

電子国土データの内容に関しては、電子国土ポータルサイトもご覧ください。

また、SVG Mapコンソーシアムのメンバーの数社が、共同研究の実施会社に含まれており、SVG Mapコンソーシアムの活動へのフィードバックなどが期待できます。

タイリングされた地図データ

インターネット上の地図サービスでは、サーバ上のデータベースで端末の表示領域に応じた地図を動的に生成したり、クライアント上のECMA Scriptプログラムによって、タイル分割された地図を組み合わせたりして、自在にスクロールできる地図提供が近年流行しています。

しかし、サーバ上でデータベースを運用するのはコストがかかりますし、ECMA Scriptによるタイル合成では、クライアント上にそのサービス専用のプログラムをロードしますので、例えば、サービスAとサービスBを重ね合わせて(マッシュアップして)使う といったことを行おうとすると、かなりのプログラム知識が必要になります。 スクロール自在な地図が単なる地図ファイルの配信と標準のビューアだけで実現できれば、これらの問題の多くを解消できるでしょう。

そこで、SVG Mapコンソーシアムでは、SVG形式のタイル分割された単なる地図ファイルを用いて、自在に地図のスクロール表示を行う仕組みを SVG Mapプロファイルとして規定します。

この仕組みは、SVGの基本仕様を応用して実現されます。その方法を以下に説明します。

タイリングされた地図データ

個々の地図のタイルのファイルは、図のようにそれらを纏める(タイリングする)SVGファイルによって、一枚の地図に合成されることにします。このように、他のファイルをインポートするSVGファイルのことをコンテナSVGファイルと呼ぶことにします。

Tile2_3

ここで、ファイルをインポートする仕様は、SVGが元々持っている、<image>(またはSVG1.2では<animation>)要素によって実現できます。コンテナSVGファイルの例を次に示します。

Container.svg
<svg ... viewBox="20 110 120 85">
<metadata ... />
<image   x="0"   y="0" width="100" height="70" xlink:href="0_0.svg"/>
<image x="100"   y="0" width="100" height="70" xlink:href="1_0.svg"/>
<image x="200"   y="0" width="100" height="70" xlink:href="2_0.svg"/>
<image   x="0"  y="70" width="100" height="70" xlink:href="0_1.svg"/>
<image x="100"  y="70" width="100" height="70" xlink:href="1_1.svg"/>
<image x="200"  y="70" width="100" height="70" xlink:href="2_1.svg"/>
<image   x="0" y="140" width="100" height="70" xlink:href="0_2.svg"/>
<image x="100" y="140" width="100" height="70" xlink:href="1_2.svg"/>
<image x="200" y="140" width="100" height="70" xlink:href="2_2.svg"/>
</svg>

SVG仕様によれば、このような記述によって、地図タイルを張り合わせて(タイリングして)表示することが可能です。すなわち、W3Cが規定したオリジナルのSVG仕様の範囲で、タイリングされた地図の表示が可能です。(これは仕様上の話で、実際に可能かどうかはビューアの実装に依存します。例えば、FirefoxのSVGビューアでは今のところまだ<image>要素ではsvgの参照ができませんので、SVGのタイルの表示はできません。(タイルがjpeg形式であればできるようです))

参考:今後ご説明する、SVG Mapの主要な相互運用性を提供するハイパーレイヤリング機能でも、ファイルのインポート・コンテナSVGファイルは重要な要素です。タイリングでは、インポートしたデータを並べて表示しますが、レイヤリングは、データを重ねます。複数のデータをインポートして表示することに変わりはありません。

ビューアの実装要求仕様

さて、ここでSVG Mapプロファイルとしての新たな ビューア側の仕様を規定します。

要求仕様1: まず大前提の条件として、SVG Mapプロファイル準拠のビューアは、<image>要素によって、外部のSVGファイルのインポート表示ができなければなりません。

更に、クライアントのSVG Mapビューアは、上記のコンテナSVGデータに対応したロジックをもっている必要があります。そのロジックとは、以下のようなものです。

先のようなコンテナSVGをビューアで表示した場合、何の工夫もされていないSVGビューアでは、image要素が参照している全てのタイルデータをダウンロードしようとしてしまうでしょう。上の例ではまだ9個しかないので、ビューアの動作に問題は出ないかもしれません。しかし、例えば100x100にタイル分割されたデータを表示しようとした場合、問題が起きるかもしれません。

要求仕様2: そこで、SVG Map profileに準拠したビューアは、<image>要素の領域(w,y,width,heightで指定されている)がビューアのビューボックスに入ったときに、その<image>要素が参照しているデータを動的に取得するロジックを少なくとも実装していなければならないこととします。

その仕組みを下の図で説明します。

タイリングされた地図の表示ロジックの説明図 (swfアニメーションです。右クリックで"新しいウィンドを開く"とすると良いでしょう。)

緑色のタイルがロードされていなければならないタイルデータです。
最初は、コンテナのviewBox="20 110 120 85" なので、左下の4枚(0_1.svg, 1_1.svg, 0_2.svg, 1_2.svg)が取得されます。 ここで、利用者が右上の方向にスクロール操作を行ったとします。すると、ビューアのビューボックスが移動していき、次々と(0_0.svg,1_0.svg)、(2_0.svg,2_1.svg)がロードされます。ここで、黄色のタイルは、一旦ロードされたものの、viewBoxから外れたために、不要となったデータです。このようなデータをメモリの許す限り常に保持し続けるか、すぐに捨て去るのかといった処理の工夫は、ビューアの実装に依存します。

ここで規定した仕様では、例えば、全部のタイルを表示してしまうような広いエリアの表示を行おうとすれば、結局全てのタイルの読み込みが始まってしまうでしょう。次回以降では、拡大・縮小表示を可能にするプロファイルの説明を予定しています。

座標参照系のPURL

地理的な座標による空間参照 のページを更新しましたとおり、座標参照系のURIを確固としたものに改訂しました。  それは、http://purl.org/crs/84 です。

SVG MapではWGS 84を基にした座標参照系を共通の座標参照系とすることで、シンプルで広汎な相互運用性を得ようと考えています。 この座標参照系は、OGCのWMSでは"CRS:84"と呼ばれています。

一方、SVG(1.1以降)の仕様では、そのグラフィックス(地図)が使用している座標参照系を、コンテンツ自身にメタデータとして記述することになっています。したがって、先の"共通の座標参照系を使っている"ということを 各コンテンツに記述すれば良いことになります。SVG(1.1以降)の仕様では、それはRDF (RDF/XML)によって記述・言明されなければなりません。(参照) 

RDFで、"共通の座標参照系"を言明するには、それがURIとして識別できなければなりません。そのため、、"共通の座標参照系"である"CRS:84"座標参照系のための確固としたURIは、とても重要です。

WMSでの名称"CRS:84"は、URIとしては不適切です。そこでOGCは、独自にURNによる座標参照系識別子を提唱しているようですが、以下の問題がありました。

  • (NID:ogc)はURNとして正式に登録されていない(2007-04-27 現在)(参照
  • シンプルな仕様書へのアクセスがURNではできない

一方、PURL(Persistent Uniform Resource Locator)は、永続的なURL(URI)の保証を目的に作られたものです。詳しくは、purlのホームページwikipedia:purlをご覧ください。 日本語の資料はあまり多くありません。PURLは基本的にWWWのためのURLなので、WWWブラウザで簡単にアクセスすることができます。

PURLはセマンティックWebに関係する仕様のネームスペースとして しばしば用いられており、有名なところではDublin CoreRSS1.0などがPURLを用いています。

そこで、座標参照系のためのドメインをPURLに登録しました。
http://purl.org/crs  です。

そして、CRS:84のための、PURLを登録しました。
http://purl.org/crs/84  です。

今後、SVG Mapコンソーシアムでは、http://purl.org/crs/84 を 共通の座標参照系として推奨します。

検索

注目エントリー

2008年8月

          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

最近のトラックバック