« 2007年3月 | メイン | 2007年5月 »

ISO6709Tool for PHP

PHP用のISO6709パーサ&シリアライザも作成しました。LGPLライセンスに基づいて、ご使用ください。
We also open ISO6709 tools for PHP. Please use it based on the LGPL license.

<?php

// =============================================================================
//
// ISO6709; ISO6709 (Latitude, Longitude and Altitude format)
//          Parser and Serializer for PHP
//           Copyright (C) 2007 Satoru Takagi, SVG Map Consortium
//
// History:
// 2007.04.26 First release
// =============================================================================
//
// 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
//
// =============================================================================

class ISO6709 {
var $latitude;
var $longitude;
var $altitude;

function ISO6709( $in ){
  $sec = 0.0;
  $min = 0.0;
  $deg = 0.0;
  ereg('([+-])([0-9][0-9])(([0-9][0-9])|())(([0-9][0-9])|())(([\.][0-9]+)|())([+-])([01][0-9][0-9])(([0-9][0-9])|())(([0-9][0-9])|())(([\.][0-9]+)|())(([+-][0-9]*\.?[0-9]*)|())(CRS(.*)|())[/]' , $in , $arr);
  if ( $arr != null ){
   if ( $arr[7] != null ){
    $sec = floatval( $arr[7] . $arr[10] );
    $min = intval( $arr[4] );
    $deg = intval( $arr[2] );
   } else if ( $arr[4] != null ){
    $min = floatval( $arr[4] . $arr[10] );
    $deg = intval( $arr[2] );
   } else {
    $deg = floatval( $arr[2] . $arr[10] );
   }
   $this -> latitude = floatval( $arr[1] . ( $deg + ( $min + $sec / 60.0 ) / 60.0 ) );
   
   $sec = 0.0;
   $min = 0.0;
   $deg = 0.0;
   if ( $arr[18] != null ){
    $sec = floatval( $arr[18] . $arr[21] );
    $min = intval( $arr[15] );
    $deg = intval( $arr[13] );
   } else if ( $arr[15] != null ){
    $min = floatval( $arr[15] . $arr[21] );
    $deg = intval( $arr[13] );
   } else {
    $deg = floatval( $arr[13] . $arr[21] );
   }
   $this -> longitude = floatval( $arr[12] . ( $deg + ( $min + $sec / 60.0 ) / 60.0 ) );
   
   if ( $arr[24] != null ){
    $this -> altitude = floatval( $arr[24] );
   }
  }
}

function toString( $format , $fractions ) {
  if ( $fractions == null ){
   $fractions = 6;
  }
  if ( $format == null ){
   $format = "d";
  }
  $alat = abs($this -> latitude);
  $alon = abs($this -> longitude);
  if ( $this -> latitude > 0 ){
   $str = "+";
  } else {
   $str = "-";
  }
  $str = $str . $this -> getLatOrLonStr( $alat , $format , $fractions , 2 );
  if ( $this -> longitude > 0 ){
   $str = $str . "+";
  } else {
   $str = $str . "-";
  }
  $str = $str . $this -> getLatOrLonStr( $alon , $format , $fractions , 3 );
  if ( $this -> altitude != null ){
   $aalt = abs( $this -> altitude );
   if ( $this -> altitude > 0 ){
    $str = $str . "+";
   } else {
    $str = $str . "-";
   }
   $str = $str . $aalt;
  }
  $str = $str . "/";
  return ( $str );
}

function getLatOrLonStr ( $latOrLon , $format , $fractions , $degLength ){
  if ( $fractions == 0){
   $rex = '([0-9]+)';
  } else {
   $rex = '(([0-9]+[\.][0-9]{0,'. $fractions .'})|([0-9]+))';
  }
  $latOrLon += 1000.0;
  if ( $format =="d" ){
   ereg( $rex , $latOrLon , $ret );
   return ( substr( $ret[1] ,  4 - $degLength  ) );
  } else {
   $deg = floor( $latOrLon );
   $min = ( $latOrLon - $deg ) * 60.0;
   if ( $format =="dm" ){
    ereg( $rex , $min + 100.0 , $ret );
    return ( substr( $deg , 4 - $degLength ) . substr( $ret[1] , 1 ) );
   } else {
    $sec = ( $min - floor( $min ) ) * 60.0;
    ereg( $rex , $sec + 100.0 , $ret );
    $min = floor ( 100.0 + $min );
    return ( substr( $deg , 4 - $degLength ) . substr( $min , 1 ) . substr( $ret[1] , 1 ) );
   }
  }
}
}

if (isset($_GET["position"])) {
$req = str_replace( " " , "+" , $_GET["position"] );
} else {
$req = "+35.5+139.5+100.0/";
}

$pos = new ISO6709($req);
$latitude = $pos -> latitude;
$longitude = $pos -> longitude;
$altitude = $pos -> altitude;

echo "<font color=red>Example: iso6709.php?position=+3530.224+13924.448+48.2/</font><br><br>";
echo "<font color=blue>input:$req = lat:$latitude long:$longitude alt:$altitude</font><br><br>";

echo 'toString(d):' .  $pos -> toString( "d" , 6 ) . '<br>';
echo 'toString(dm):' . $pos -> toString( "dm" , 6 ) . '<br>';
echo 'toString(dms):' . $pos -> toString( "dms" , 6 ) . '<br>';

?>

Shape to SVG Map converter

SVG Map Profileの最も基本的な部分(空間参照系メタデータの埋め込み)のサンプルプログラムを公開します。ShapeファイルからSVG Mapコンテンツを生成します。
GeoTools を使用していますので、少しの修正で他の形式からの変換もできるでしょう。
またまた つたないコードですがご勘弁を・・
LGPLライセンスに基づいて、ご使用ください。

(2007.5.9 CRSのURIを改訂)

We post the sample code of Shape to SVG Map Converter which used GeoTools. This code includes the most basic part of SVG Map Profile (Coordinate Reference System metadata). Please use it based on the LGPL license.

// =============================================================================
// The basic SVG Map exporter program
//
// Copyright (C) 2007 by Satoru Takagi All rights reserved.
//  http://blog.svg-map.com/
//
// Usage: java Shape2SvgMapBasic shapefile.shp
//
// This program uses geoTools2.3.0
// =============================================================================
//
// 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.io.IOException;
import java.net.URL;

import org.geotools.data.*;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.feature.*;

import com.vividsolutions.jts.geom.*;

public class Shape2SvgMapBasic {
double  boundHeight = 400.0; // SVG Height
double  projCenter;
String fillColor = "green";
String strokeColor = "black";
double strokeWidth = 0.01;
// CRS:84 (WGS84 Long-Lat) This URI(URN) is ambiguous...
String CRS = "http://purl.org/crs/84";
double[] affineParam = new double[6];

public static void main(String[] args) {
  Shape2SvgMapBasic s2sm = new Shape2SvgMapBasic();
  try {
   // Prepare an output SVG Map file
   FileOutputStream osFile = new FileOutputStream( args[0] + ".svg" );
   Writer fos = new BufferedWriter(new OutputStreamWriter( osFile , "UTF-8" ));
   
   // Shape file
   URL shapeURL = (new File(args[0])).toURL();
   
   s2sm.parse(  shapeURL , fos );
   fos.close();
   
  }catch(Exception e){
   e.printStackTrace();
  }
}


public void parse( URL shapeURL , Writer out ) throws Exception {
  double maxX, minX, maxY, minY;
 
  // Initialize shape file reader
  ShapefileDataStore readStore = new ShapefileDataStore(shapeURL);
  FeatureSource source = readStore.getFeatureSource();

 
  // get envelope
  Envelope env = source.getBounds();
  maxX = env.getMaxX();
  minX = env.getMinX();
  maxY = env.getMaxY();
  minY = env.getMinY();
 
  // Make affine parameters for CRS metadata of SVG Map
  // Equirectangular Projection (Standard parallel = Center of map)
  //
  // SVG_X = a * GEO_X + c * GEO_Y + e
  // SVG_Y = b * GEO_X + d * GEO_Y + f
  affineParam[1] = 0.0; // b
  affineParam[2] = 0.0; // c
 
  affineParam[3] = - boundHeight / ( maxY - minY ); // d
 
  projCenter = ( maxY + minY ) / 2.0;
  affineParam[0] = - affineParam[3] * Math.cos( projCenter * Math.PI / 180.0 ); // a
 
  affineParam[4] = -affineParam[0] * minX; // e
  affineParam[5] = -affineParam[3] * maxY; // f
 
  // Make a SVG ViewBox
  double origX , origY , cWidth , cHeight;
  cWidth  = Math.abs( ( maxX - minX ) * affineParam[0] );
  cHeight = Math.abs( ( maxY - minY ) * affineParam[3] );
  origX = affineParam[0] * minX + affineParam[4];
  origY = affineParam[3] * maxY + affineParam[5];
 
  // get FeatureCollection
  FeatureCollection fsShape = source.getFeatures();
 
 
  // Start SVG Map output
  // SVG header etc.
  out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  out.write("<svg  xmlns=\"http://www.w3.org/2000/svg\" ");
  out.write("xmlns:xlink=\"http://www.w3.org/1999/xlink\" ");
  out.write("viewBox=\"" + origX + " " + origY + " " + cWidth + " " + cHeight + "\" >\n");
 
  // metadata for SVG Map
  out.write("<metadata>\n");
 
  // CRS metadata
  out.write(" <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" ");
  out.write("xmlns:crs=\"http://opengis.org/xmldtds/transformations.dtd\" xmlns:svg=\"http://www.w3.org/2000/svg\">\n");
  out.write("  <rdf:Description>\n");
  out.write("   <crs:CoordinateReferenceSystem rdf:resource=\"" + CRS + "\"  ");
  out.write("svg:transform=\"matrix(" + affineParam[0] + "," + affineParam[1] + "," + affineParam[2]
   + "," + affineParam[3] + "," + affineParam[4] + "," + affineParam[5] +")\" />\n");
  out.write("  </rdf:Description>\n");
  out.write(" </rdf:RDF>\n");
  out.write("</metadata>\n");
 
  // Icon for Point
  out.write("<defs>\n");
  out.write(" <g id=\"p0\" >\n");
  out.write("<circle cx=\"0.0\" cy=\"0.0\" r=\"" + (cWidth / 300) + "\" stroke=\"none\" />\n");
  out.write(" </g>\n");
  out.write("</defs>\n");
 
  // Main data body
  FeatureIterator reader = fsShape.features();
  Feature oneFeature;
  Geometry oneGeom;
  Coordinate[] coord;
  while (reader.hasNext()) {
   oneFeature = reader.next();
   oneGeom = oneFeature.getDefaultGeometry();
   parseGeometry(oneGeom , out );
  }
  reader.close();
  out.write("</svg>\n");
}
 
private void parseGeometry(Geometry geom , Writer out ) throws Exception {
  Coordinate[] coord;
  Coordinate oneCrd = new Coordinate();
  if (geom instanceof Polygon ){
   out.write("<path ");
   out.write("fill=\"" + fillColor + "\" fill-rule=\"evenodd\" d=\"M");
   coord = (((Polygon)geom).getExteriorRing()).getCoordinates();
   for ( int i = 0 ; i < coord.length ; i++ ){
    if ( i!= 0 ){
     out.write( "L" );
    }
    oneCrd = transform2D(coord[i] );
    out.write( oneCrd.x + "," + oneCrd.y + " " );
   }
   out.write ("Z ");
   
   for ( int j = 0 ; j < ((Polygon)geom).getNumInteriorRing() ; j++ ){
    coord = (((Polygon)geom).getInteriorRingN(j)).getCoordinates();
    for ( int i = 0 ; i < coord.length ; i++ ){
     if ( i!= 0 ){
      out.write( "L" );
     } else {
      out.write( "M" );
     }
     oneCrd = transform2D(coord[i] );
     out.write( oneCrd.x + "," + oneCrd.y + " " );
    }
    out.write ("Z ");
   }
   
   out.write ("\"/>\n");
  } else if (geom instanceof LineString ){
   out.write("<polyline ");
   out.write("fill=\"none\" stroke-width=\"" + strokeWidth + "\" stroke=\"" + strokeColor + "\" points=\"");
   coord = ((LineString)geom).getCoordinates();
   for ( int i = 0 ; i < coord.length ; i++ ){
    oneCrd = transform2D(coord[i] );
    out.write( oneCrd.x + "," + oneCrd.y + " " );
   }
   out.write ("\"/>\n");
  } else if (geom instanceof Point ){
   oneCrd = transform2D(((Point)geom).getCoordinate() );
   out.write("<use ");
   out.write("xlink:href=\"#p0\" fill=\"" + fillColor + "\"");
   out.write(" x=\"" + oneCrd.x + "\"" + " y=\"" + oneCrd.y + "\"/>\n");
  } else if (geom instanceof GeometryCollection ){
   out.write("<g>\n");
   for ( int j = 0 ; j < ((GeometryCollection)geom).getNumGeometries() ; j++){
    Geometry childGeom = ((GeometryCollection)geom).getGeometryN(j);
    parseGeometry(childGeom , out);
   }
   out.write("</g>\n");
  } else if (geom instanceof Geometry ){
   out.write("<!-- Type: Other Geometry...." + geom + "-->\n");
  } else if (geom instanceof Object){
   out.write("<!-- Type: Other Object...." + geom + "-->\n");
  }
}

private  Coordinate transform2D( Coordinate inCrd ){
  Coordinate outCrd = new Coordinate(
   affineParam[0] * inCrd.x + affineParam[2] * inCrd.y + affineParam[4] ,
   affineParam[1] * inCrd.x + affineParam[3] * inCrd.y + affineParam[5] );
  return (outCrd);
}
}

地理的な座標による空間参照

SVG Mapプロファイル 1.0(案)

SVG Mapプロファイル 1.0において最も重要な情報は、SVGデータの座標を地理座標と関連付けるための情報です。それは、座標参照系(Coordinate Reference System)メタデータとして記述されます。

SVG Mapプロファイル1.0における座標参照系メタデータの記述規則はSVG1.1仕様(Geographic Coordinate Systems)に基づいていますが、小型コンピュータへの実装容易性と同時に、仕様の曖昧さを解消するために、新たな限定を行うプロファイルを定義します。
SVG Mapプロファイル1.0のデータは必ず座標参照系メタデータを持たなければなりません。それは、以下のRDF/XMLによる言明によって記述されなければなりません。

  • 座標参照系プロパティ
    crs:CoordinateReferenceSystem
    このSVGコンテンツが用いる空間参照系を言明します。
    定義域:SVGコンテンツ
    値域:CRS型のインスタンス

    このプロパティの名前空間:
    xmlns:crs="http://opengis.org/xmldtds/transformations.dtd"
    この語彙はOGCが過去に勧告した"Recommended Definition Data for Coordinate Reference Systems and Coordinate Transformations"から引用されたものです。

  • CRS型のインスタンス
    以下のCRS型のインスタンスのみを推奨します。
    urn:ogc:def:crs:OGC:1.3:CRS84
    http://purl.org/crs/84       (2007.5.9更新)

    このリソースは、一般にWGS-84と呼ばれるもの、ただし第一パラメータ(X)が経度、第二パラメータ(Y)が緯度 単位はそれぞれ"度"の2次元座標系を持つ地球の座標参照系です。この座標参照系は、OGCWMS1.3.0のために定義した座標参照系"CRS:84"(Annex B3)と等価です。

    また、上記のURN表記方法は 同じくOGCの"URNs of definitions in ogc namespace"に記述されているものです。 (2007.5.9削除)
    .
    注1:現在のところ、URNのNIDとして"ogc"は登録されていません。したがって、ogcの仕様を元に規定した、urn表現による空間参照系には、正当性の面で問題がありました。そこで、SVG Mapコンソーシアムでは、purl(persistent URL)を用いることにしました。そして、CRS:84座標参照系のためのURL(URI)として、http://purl.org/crs/84 を登録しました。現在のところ、これが唯一の推奨する座標参照系です。(2007.5.9更新)
    .
    注2:同じWGS 84空間参照系であるEPSG:4326は第一パラメータ(X)が緯度、第二パラメータ(Y)が経度であり、SVG Mapが使用するCRS:84とはパラメータの順番が逆です。
    .
    注3:purlはwebブラウザでアクセス可能なURIです。CRS:84のためのドキュメントが同URL(http://purl.org/crs/84)から取得できます。(2007.5.9更新)
    .
     このCRSの限定は、CRSを一つにすることにより、利用者端末への実装を容易にしつつ、相互運用性を向上することができるという利点によるものです。例えば複数の地図の重ね合わせを行うとき、空間参照系の違いを事実上考慮する必要がなくなります。また、ここで限定したWGS-84座標系は、インターネット上の大多数の地理情報で用いられています。さらに、多くの携帯端末が実装しつつあるGPSが扱うネイティブの空間参照系でもあります。したがって、大多数のユースケースでは、この限定による不都合は起きないでしょう。
    .
  • SVG座標変換プロパティ
    svg:transform
    SVGコンテンツの座標値と空間参照系で示される座標値との間の変換式を与えます。
    定義域: CRS型のインスタンス
    値域: SVG transform属性で使用できる文字列(アフィン変換パラメータとして一般化できる)

    座標変換はSVGのレンダリングシステムに内蔵されている一次のアフィン変換のみを許容します。

    変換式は以下の通りです。
     SVG_X:SVGデータのX座標
     SVG_Y:SVGデータのY座標
     Geo_X:空間座標の第一パラメータ CRS型のインスタンスの制約により、それは経度です
     Geo_Y:空間座標の第二パラメータ CRS型のインスタンスの制約により、それは緯度です

     a , b , c , d , e , f = SVG transform(a,b,c,d,e,f)パラメータの該当値

     SVG_X = a * Geo_X + c * Geo_Y + e
     SVG_Y = b * Geo_X + d * Geo_Y + f

    このプロパティが言明されていない場合は、その値がmatrix(1,0,0,1,0,0)であるとみなします。
    .
  • SVG Mapの図法
     SVG座標変換プロパティが一次のアフィン変換のみであることから、SVG Mapで使用できる図法は正距円筒図法(及びその標準緯線を赤道に取った派生形であるPlate Caree)に限定されます。なお、更に派生形としてアフィン変換の非対角成分が"0"でない図法(正距円筒図法を回転させた図法)を用いることも可能です。
     この限定は、多くのユースケースでは実用的な図法を提供しますが、高緯度地域の地図などにおいて不都合な場合がありえます。しかしながら、SVGのレンダリングシステムにあらかじめ内蔵されている機能を流用でき、しかも単純な一次変換であるので、利用者端末への実装を容易にし、スケーラビリティ(特に小型端末への)を向上させるでしょう。
    .
  • サンプル
    <?xml version="1.0" encoding="UTF-8"?>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <metadata>
      <rdf:RDF
       xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
       xmlns:crs="http://opengis.org/xmldtds/transformations.dtd"
       xmlns:svg="http://www.w3.org/2000/svg">
       <rdf:Description>
        <crs:CoordinateReferenceSystem
         rdf:resource="http://purl.org/crs/84"
         svg:transform="matrix(15.3631,0.0,0.0,-18.6994,-1889.2916,849.9202)"/>
       </rdf:Description>
      </rdf:RDF>
    </metadata>
    <circle cx="258.1401" cy="185.1558" r="10.0" stroke="none" fill="green"/> </g>
    </svg>

    この例で、空間参照系のメタデータは以下のようなグラフで表現されます。
    <This SVG Document> crs:CoordinateReferenceSystem <http://purl.org/crs/84>.
    <http://purl.org/crs/84> svg:transform "matrix(15.3631,0.0,0.0,-18.6994,-1889.2916,849.9202)".

    SVG座標変換プロパティのパラメータは以下の通りです。
    a=15.3631
    b=0.0
    c=0.0
    d=-18.6994
    e=-1889.2916
    f=849.9202

    ここで、<circle>図形の中心のSVG座標(258.1401,185.1558)を地理座標に換算すると以下になります。
    258.1401 =  15.3631 * Geo_X - 1889.2916
    185.1558 = -18.6994 * Geo_Y + 849.9202

    Geo_X(経度) = 139.7694°
    Geo_Y(緯度) =  35.5500°

SVG Map Profile 1.0 概要

SVG Mapプロファイル 1.0(案) 2008.7.28更新

○概要:
この仕様書は、SVG Mapプロファイル1.0を記述します。

Svgmapstructure_5

SVG Mapプロファイルは別に掲げる要求事項を満たす地図サービスを実現するプロファイルです。SVG Map仕様の大部分はW3Cが勧告したSVGそのものです。しかし、地図プラットホームとしての要求を満たすために、いくらか不足している部分がありました。そのため、SVG Mapプロファイルは、SVG1.1Tiny仕様に基づいていますが、地図サービスへの適用性を高めるために以下の限定と拡張を行っています。

検索

注目エントリー

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            

最近のトラックバック