2010年12月28日火曜日

formatedAddressの細部取得

/// <summary>
/// 住所を表します。
/// </summary>
class Address
{
    /// <summary>
    /// JavaScript によって、Goole Map の Geocoder から取得した住所情報から Address インスタンスを生成します。
    /// Address クラスのインスタンスは、このメソッドだけが生成可能です。
    /// </summary>
    /// <param name="address">Geocoder から取得した住所文字列。</param>
    /// <returns></returns>
    public static Address FromGeocoderAddress( string address )
    {
        if( String.IsNullOrEmpty( address ) ) { return null; }
 
        // 整形済みの住所は常に存在している必要がある
        var formatedAddress = Address.GetAdressValue( address, Address.TagFormatedAddress );
        if( String.IsNullOrEmpty( formatedAddress ) ) { return null; }
 
        return new Address()
        {
            FormatedAddress = formatedAddress,
            Country         = Address.GetAdressValue( address, Address.TagCountry ),
            ProvinceState   = Address.GetAdressValue( address, Address.TagProvinceState ),
            City            = Address.GetAddressValueCity( address )
        };
    }
 
    /// <summary>
    /// JavaScript によって、Goole Map の Geocoder から取得した住所情報から、一つの住所区分を取得します。
    /// 住所情報となる文字列は、"country:日本;administrative_area_level_1:東京都; ...etc" という形式となります。
    /// </summary>
    /// <param name="address">Geocoder から取得した住所情報の文字列。</param>
    /// <param name="tag">取得する値の種別を示すタグ名。</param>
    /// <returns>取得した住所区分。</returns>
    private static string GetAdressValue( string address, string tag )
    {
        return Address.GetAdressValue( address, tag, true );
    }
 
    /// <summary>
    /// JavaScript によって、Goole Map の Geocoder から取得した住所情報から、一つの住所区分を取得します。
    /// 住所情報となる文字列は、"country:日本;administrative_area_level_1:東京都; ...etc" という形式となります。
    /// </summary>
    /// <param name="address">Geocoder から取得した住所情報の文字列。</param>
    /// <param name="tag">取得する値の種別を示すタグ名。</param>
    /// <param name="isLastIndexOf">tag を先頭から検索する場合は true、末尾からの場合は false。</param>
    /// <returns>取得した住所区分。</returns>
    private static string GetAdressValue( string address, string tag, bool isIndexOf )
    {
        int index = ( isIndexOf ? address.IndexOf( tag ) : address.LastIndexOf( tag ) );
        if( index == -1 ) { return null; }
 
        int begin = index + tag.Length;
        index = address.IndexOf( ";", begin );
        if( index == -1 ) { return null; }
 
        int length = index - begin;
        return address.Substring( begin, length );
    }
 
    /// <summary>
    /// 都市情報を取得します。
    /// </summary>
    /// <param name="address">Geocoder から取得した住所情報の文字列。</param>
    /// <returns></returns>
    private static string GetAddressValueCity( string address )
    {
        // 例えば住所の都市部分が A 市 B 町 6-2 の場合、{ "2", "6", "B 町", "A 市" } のように格納されていて、
        // "A 市" が TagCity、それ以降が全て TagSubCity になるので、TagCity と最後に登場する TagSubCity で取得する。
        //
        var city = String.Format( "{0}{1}", Address.GetAdressValue( address, Address.TagCity ), Address.GetAdressValue( address, Address.TagSubCity, false ) );
        if( String.IsNullOrEmpty( city ) )
        {
            city = String.Format( "{0}{1}", Address.GetAdressValue( address, Address.TagProvinceState2 ), Address.GetAdressValue( address, Address.TagProvinceState3 ) );
        }
 
        return city;
    }
}

formatted_addressの取得サンプル

<script type="text/javascript"><!--
google_ad_client = "ca-pub-7939613401472758";
/* getAddressByLatLng top */
google_ad_slot = "2186761428";
google_ad_width = 678;
google_ad_height = 15;
//--></script></div>
<div id="map" style="width: 680px; height: 340px;">&nbsp;</div>
<script type='text/javascript'><!--
    var map = new google.maps.Map(document.getElementById("map"), {
        zoom: 12,
        center: new google.maps.LatLng(35.712036115726725, 139.60383224487302),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    });
    google.maps.event.addListener(map, 'click', function(mouseEvent) {
        getAddress(mouseEvent.latLng);
    });

    function getAddress(latlng) {
        var geocoder = new google.maps.Geocoder();
        geocoder.geocode({
            latLng: latlng
        }, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                if (results[0].geometry) {
                    var address = results[0].formatted_address.replace(/^日本, /, '');
                    address = replace(/^日本, /, '');
                    new google.maps.InfoWindow({
                        content: address + "<br>(Lat, Lng) = " + latlng
                    }).open(map, new google.maps.Marker({
                        position: latlng,
                        map: map
                    }));
                }
            } else if (status == google.maps.GeocoderStatus.ERROR) {
                alert("サーバとの通信時に何らかのエラーが発生!");
            } else if (status == google.maps.GeocoderStatus.INVALID_REQUEST) {
                alert("リクエストに問題アリ!geocode()に渡すGeocoderRequestを確認せよ!!");
            } else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
                alert("短時間にクエリを送りすぎ!落ち着いて!!");
            } else if (status == google.maps.GeocoderStatus.REQUEST_DENIED) {
                alert("このページではジオコーダの利用が許可されていない!・・・なぜ!?");
            } else if (status == google.maps.GeocoderStatus.UNKNOWN_ERROR) {
                alert("サーバ側でなんらかのトラブルが発生した模様。再挑戦されたし。");
            } else if (status == google.maps.GeocoderStatus.ZERO_RESULTS) {
                alert("見つかりません");
            } else {
                alert("えぇ~っと・・、バージョンアップ?");
            }
        });
    }
//--></script>

2010年12月18日土曜日

メーラーの文字コードが ISO-2022-JP ではイケない件

ウェブサイトで「スペイン語が文字化けしますっ」と言われて &oacute; とか入れれば 大丈夫ですね~という対応で文字化けを救った後の話題です。

メーラーでは、いわゆる特殊文字で「①(マルイチ)」とか入っているとまずいので、 「ISO-2022-JP で送りましょうねー」という暗黙の了解の元にソースを書いているのですが、 &oacute; はメチャメチャ文字化けする訳です。

しかし、受け取り手がみんなUTF-8では無い訳ですからぢゃあUTF-8ねという事もできません。・・・ハテ

文字化けする条件

なんだかんだ見て行くと西欧なら iso-885901 で間に合いそうだが、韓国語だと EUC-KR だし、中国語だと Big5 と GB2312 になる。受け取り手のメーラーを予想してコード変換は無意味な感じがするので、古いコードを捨てよう。

ちなみにXOOPSだと
    $xoopsMailer->charSet = "UTF-8";
    $xoopsMailer->Encoding = "base64";
とやらないとデフォでiso-2022-jp/7bitになる。最新ソースは未調査

ちなみにガラケー用には ISO-2022-JP にしておかないと化けますよ、多分。