[C#/Google ] IP、經緯度、中文地址互轉

[C#/Google ] IP、經緯度、中文地址互轉

現在開發地理資訊系統, 很難說以後不會遇到IP、經緯度、中文地址三者的互轉

最近做這方面的研究,把找到的方法都整理一下

 

 

1. 由IP取得經緯度和英文縣市(出處:Visual Studio 2010妙用錦囊)

Visual Studio 2010妙用錦囊使用的API已換新位址:http://freegeoip.net/static/index.html,而且只有英文結果

限制:每小時1000個查詢,超過的話會收到403禁止的訊息

Sample Code:


 string myIP = "210.59.250.101";
        protected void Page_Load(object sender, EventArgs e)
        {


            WebClient wc = new WebClient();
            //wc.Encoding = Encoding.UTF8;
            //wc.Headers.Add("Accept-Language:zh-tw");
            string format = "json";//csv 或 xml
            //取得原始結果
            string json = wc.DownloadString("http://freegeoip.net/" + format + "/" + this.myIP);
            
            //如果IP為空字串的話,預設會自動偵測抓Request的IP
            //利用Json.net解析
            JObject obj = JsonConvert.DeserializeObject<JObject>(json);
            //取得經緯度
            string lat = obj["latitude"].ToString();//緯度
            string lng = obj["longitude"].ToString();
            //取得英文縣市名稱
            string city = obj["city"].ToString();//縣市名稱
            Response.Write("縣市:"+city+"<hr/>");
            Response.Write("經度:"+lng+",緯度:"+lat);
        }

Google官方文件表示,使用IP來偵測使用者位置只能估計出約略結果,利用W3C制定的Geolocation API(HTML 5功能)才會得到比較完整精確的結果

以下是HTML5 Geolocation API範例(這篇圖文教學介紹的滿詳細:HTML5 Geolocation API 簡介)

使用限制:要注意部份瀏覽器才支援(因為是Html5功能),且javascript限定


<script type="text/javascript">

            window.onload = function () {

                //防呆
                if (window.navigator.geolocation) {
                    window.navigator.geolocation.getCurrentPosition(success, error,
                  {
                      enableHighAccuracy: true, //取得高精確度的位置資訊
                      maximumAge: 0//馬上取得位置資訊
                  });
                } else {
                    alert("瀏覽器不支援Html5的Geolocation API");
                }
              
            }
            function success(position)
            {
                alert("緯度:" + position.coords.latitude+",經度:"+position.coords.longitude);
            }
            function error(e)
            {
                alert(e.message);

            }

        </script>

另外,雖然Google官方文件出現Google Gears Geolocation的範例,但其實Google Gears API已經無法使用了

 

 

 

2.經緯度轉中文地址(出處:Google Geocoding API),使用限制在API裡有講

Sample Code:


using System;
using System.Net;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Linq;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Collections.Generic;

namespace CSASPNETIPtoLocation
{
    public partial class Default : System.Web.UI.Page
    {

       
        protected void Page_Load(object sender, EventArgs e)
        {
             
            double lat = Convert.ToDouble("25.0392");//緯度
            double lng = Convert.ToDouble("121.525");//經度
            string result = latLngToChineseAddress(lat, lng);
            Response.Write(result);
            
        }


        /// <summary> 
        /// 經緯度取得行政區 
        /// </summary> 
        /// <returns></returns> 
        public static string latLngToChineseDistrict(params double[] latLng)
        {
            string result = string.Empty;//要回傳的字串 
            string url =
                 "http://maps.googleapis.com/maps/api/geocode/json?latlng=" + string.Join(",", latLng) + "&sensor=true";
            string json = String.Empty;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            //指定語言,否則Google預設回傳英文 
            request.Headers.Add("Accept-Language", "zh-tw");
            using (var response = request.GetResponse())
            {
                using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                {
                    json = sr.ReadToEnd();
                }
            }
            GoogleGeocodingAPI.RootObject rootObj = JsonConvert.DeserializeObject<GoogleGeocodingAPI.RootObject>(json);
             
            result = rootObj.results[0].address_components
                .Where(c => c.types[0] == "locality" && c.types[1] == "political").FirstOrDefault().long_name;

            return result;

        }
         
        /// <summary> 
        /// 經緯度轉中文地址:https://developers.google.com/maps/documentation/geocoding/?hl=zh-TW#ReverseGeocoding 
        /// </summary> 
        /// <param name="latLng"></param> 
        public static string latLngToChineseAddress(params double[] latLng)
        {
            string url =
                 "http://maps.googleapis.com/maps/api/geocode/json?latlng=" + string.Join(",", latLng) + "&sensor=true";
            string json = String.Empty;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            //指定語言,否則Google預設回傳英文 
            request.Headers.Add("Accept-Language", "zh-tw");
            using (var response = request.GetResponse())
            {
                using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                {
                    json = sr.ReadToEnd();
                }
            }
            GoogleGeocodingAPI.RootObject rootObj = JsonConvert.DeserializeObject<GoogleGeocodingAPI.RootObject>(json);
             
            return rootObj.results[0].formatted_address;
              
        }




    }
}


namespace GoogleGeocodingAPI
{
    public class AddressComponent
    {
        public string long_name { get; set; }
        public string short_name { get; set; }
        public List<string> types { get; set; }
    }
    public class Location
    {
        public double lat { get; set; }
        public double lng { get; set; }
    }
    public class Northeast
    {
        public double lat { get; set; }
        public double lng { get; set; }
    }
    public class Southwest
    {
        public double lat { get; set; }
        public double lng { get; set; }
    }
    public class Viewport
    {
        public Northeast northeast { get; set; }
        public Southwest southwest { get; set; }
    }
    public class Geometry
    {
        public Location location { get; set; }
        public string location_type { get; set; }
        public Viewport viewport { get; set; }
    }
    public class Result
    {
        public List<AddressComponent> address_components { get; set; }
        public string formatted_address { get; set; }
        public Geometry geometry { get; set; }
        public bool partial_match { get; set; }
        public List<string> types { get; set; }
    }
    public class RootObject
    {
        public List<Result> results { get; set; }
        public string status { get; set; }
    }

}

執行結果:

image

中文地址轉成經緯度

出處:Google Geocoding API, 使用限制在API裡

先自訂一個GoogleAPIUtilities.cs類別

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;

namespace Console_ConvertShopAddrTo_latlng.Utilities
{
    public class GoogleAPIUtilities
    {
        /// <summary>  
        /// 把地址轉成Json格式,這樣回傳字串裡才有緯經度  
        /// 因為使用到Geocoding API地理編碼技術,請注意使用限制:https://developers.google.com/maps/documentation/geocoding/?hl=zh-tw#Limits  
        /// </summary>  
        /// <param name="address">地址全名(含縣市)</param>  
        /// <returns></returns>  
        public static string ConvertAddressToJsonString(string address)
        {
            //申請API Key,能夠呼叫的額度會多一些
            string GoogleAPIKey = ConfigurationManager.AppSettings["GoogleAPIKey"];
            string url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + HttpUtility.UrlEncode(address, Encoding.UTF8) + "&key=" + GoogleAPIKey; 
            string result = "";//回傳結果 
            using (WebClient client = new WebClient())
            {
                //指定語言,否則Google預設回傳英文   
                client.Headers[HttpRequestHeader.AcceptLanguage] = "zh-TW";
                //不設定的話,會回傳中文亂碼
                client.Encoding = Encoding.UTF8;
                #region POST method才會用到
                /*
                client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
                byte[] response = client.UploadValues("https://maps.googleapis.com/maps/api/geocode/json", new NameValueCollection()
                {
                        { "address", HttpUtility.UrlEncode(address, Encoding.UTF8)},
                        { "key", GoogleAPIKey }
                });
                result = Encoding.UTF8.GetString(response);
                */
                #endregion
                result = client.DownloadString(url);
            }//end using

            return result; 
        }//end method
         
        /// <summary>  
        /// 傳入Geocoding API產生的Json字串,取得經緯度  
        /// </summary>  
        /// <param name="json"></param>  
        /// <returns></returns>  
        public static double[] 傳入GeocodingAPI產生的Json字串取得經緯度(string json)
        {

            //將Json字串轉成物件  
            GoogleGeocodingAPI.RootObject rootObj = JsonConvert.DeserializeObject<GoogleGeocodingAPI.RootObject>(json);

            //回傳結果
            double[] latLng = new double[2];
            //防呆
            if (rootObj.status=="OK")
            {
                //從results開始往下找 
                double lat = rootObj.results[0].geometry.location.lat;//緯度  
                double lng = rootObj.results[0].geometry.location.lng;//經度   
                //緯度
                latLng[0] = lat;
                //經度
                latLng[1] = lng;
            }//end if 
            else
            {//todo寫Log

            }//end else 
            return latLng; 
        }//end method 
    }
}


/// <summary>
/// Json Parse資料用
/// </summary>
namespace GoogleGeocodingAPI
{ 
    public class AddressComponent
    { 
        public string long_name { get; set; } 
        public string short_name { get; set; } 
        public List<string> types { get; set; } 
    }


    public class Location
    { 
        public double lat { get; set; } 
        public double lng { get; set; } 
    }


    public class Northeast
    { 
        public double lat { get; set; } 
        public double lng { get; set; } 
    }


    public class Southwest
    { 
        public double lat { get; set; } 
        public double lng { get; set; } 
    }


    public class Viewport
    { 
        public Northeast northeast { get; set; } 
        public Southwest southwest { get; set; } 
    }


    public class Geometry
    { 
        public Location location { get; set; } 
        public string location_type { get; set; } 
        public Viewport viewport { get; set; } 
    }


    public class Result
    { 
        public List<AddressComponent> address_components { get; set; } 
        public string formatted_address { get; set; } 
        public Geometry geometry { get; set; } 
        public bool partial_match { get; set; } 
        public List<string> types { get; set; } 
    }
     
    public class RootObject
    { 
        public List<Result> results { get; set; } 
        public string status { get; set; } 
    }
     
}

在Console專案中的使用方式↓

using Console_ConvertShopAddrTo_latlng.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string address = "台北市大安區信義路4段88號";
            //取得json字串
            string json =    GoogleAPIUtilities.ConvertAddressToJsonString(address);
            Console.WriteLine(json);
            //取得緯度、經度
            double[] latLng =   GoogleAPIUtilities.傳入GeocodingAPI產生的Json字串取得經緯度(json); 
            Console.WriteLine($"緯度:{latLng[0]},經度:{latLng[1]}");
        }
      
    }
}

以上的功能

 

image

由於目前找到的方法 IP直接轉地址只能得到英文結果

如果要中文的話

就照 IP→經緯度→地址 的順序使用Google API的話,就可以得到中文地址

不過要注意因為用IP來偵測使用者位置只能粗略得到經緯度,所以即時由IP得到的地址,也只是粗估,大概到縣市為止還正確之後的道路幾號門牌就不太準了