2014年11月28日 星期五

如何改變Google Map 的Mark顏色

  公司突然有種特殊的需求,希望從手機上瀏覽到資料庫中客戶們的位置,目的透過手機的定位裝置,找到附近的客戶們,並加上不同的顏色區分,有此需求的朋友們,可以參考以下的作法,如果你們有更好的想好也歡迎給我些意見,互相交流技術想法

步驟一: 先建立顯示地圖div
    <div data-role="page" id="geolocation_map">//頁面
        <div id="geolocation_header" data-role="header"> //表頭
            <a data-role="button" data-rel="back">back</a>
            <h3>google Map</h3>
            <a href="#geolocation_menus" data-position="right"  data-role="button" data-icon="grid"  >選單</a>
        </div>
        <div id="geolocation_conent" data-role="content"> //表身
            <div id="geolocation_canvas">gelocation</div> //顯示地圖
            <a href="#geolocation_changeColor" data-ajax="false" id="geolocation_showDialog" data-rel="dialog" data-transition="slidedown">Open dialog</a>
            <input type="hidden" id="geolocation_userID" runat="server" />
        </div>
      <div id="geolocation_footer" data-position="fixed" data-role="footer" data-id="gelocation_footerPage"> //表尾
            <div data-role="navbar">
                <ul>
                    <li><a href="#geolocation_map">搜尋附近牙醫</a></li>
                    <li><a href="#setParameter_page">參數設定</a></li>
                </ul>
            </div>
        </div>      
    </div>

步驟二: 為了可以顯示在手機裝置上(寬度和高度 不宜超大),所以使用javasScript 自動偵測

//自動縮放畫面的size
function resize_canvas() {
    var canvas = $('#geolocation_canvas').width();
    if ($('#geolocation_canvas').width() <= window.innerWidth)
    {
        $('#geolocation_canvas').css("width", window.innerWidth);
    }
    if ($('#geolocation_canvas').height() <= window.innerHeight) {
        $('#geolocation_canvas').css("height", window.innerHeight);
    }
}

步驟四: 呼叫目前的位置 
 navigator.geolocation.getCurrentPosition( function (position) {), geo_error);
  function geo_error(err) {
    if (err.code == 1) {
        alert( "Error: Access is denied!");
    } else if (err.code == 2) {
        alert( "Error: Position is unavailable!");
    }
  }
    目前所有瀏覽器(電腦版,僅IE9正常抓到位置),因他Chrom safari 等只能在行動設備開啟wifi定位偵測

步驟五: 初始化GoogleMap
//設定目前座落的位置,並找尋附近範圍經緯度的值
function setInitMap(lat, lng, range) {
    try{
        var mapOptions = {
            center: new google.maps.LatLng(lat, lng),
            zoom: 17, //地圖比例大小
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map(document.getElementById("geolocation_canvas"),
            mapOptions);
        var myLatlng = new google.maps.LatLng(lat, lng); //設置中心點
        setMarket("current", "", "", "green", "目前位置", "", myLatlng, map); //繪製目前的座標位置
        getNeighbor(lat, lng, range, map); //搜尋附近鄰近的診所的座標
    } catch (e) {
        alert(e.message);
    }
}

//算出附近四座標範圍
function getNeighbor(lat, lng, range, map) {
    $.ajax({
       //取回json格式,符合範圍內的經緯度的診所資料
        url: "Service.svc/computeNextBy?lat=" + lat + "&lng=" + lng + "&range=" + range,
        dataType: "json",
        async: true,
        success: function (data) {
            if (!!(data) == true) {
               //取得座標值,如果已拜訪過的診所/未拜訪診所等狀況,呈現不同顏色
                $.each(data, function (index, value) {
                    var myLatlng = new google.maps.LatLng(value["lanLng"]["Lat"], value["lanLng"]["Lng"]);
                    if (value["status"] == 0 && value["isCustomer"] == false) {
                        //非客戶_未拜訪
                        setMarket(value["clinicNo"], value["isCustomer"], value["status"], "red", value["name"], value["address"], myLatlng, map);
                    } else if (value["status"] == 1 && value["isCustomer"] == false) {
                        //非客戶_有拜訪
                        setMarket(value["clinicNo"], value["isCustomer"], value["status"], "yellow", value["name"], value["address"], myLatlng, map);
                    } else if (value["status"] == 0 && value["isCustomer"] == true) {
                        //客戶_未拜訪
                        setMarket(value["clinicNo"], value["isCustomer"], value["status"], "purple", value["name"], value["address"], myLatlng, map);
                    }
                    else if (value["status"] == 1 && value["isCustomer"] == true) {
                        //客戶_有拜訪
                        setMarket(value["clinicNo"], value["isCustomer"], value["status"], "blue", value["name"], value["address"], myLatlng, map);
                    }                   
                });
            }
        },
        error: function (xhr, status) {
        }
    });
}
//改變狀態時,將標籤的的顏色改變
function setMarket(clinicNo, isCustomer, isVisited, color, title, addr, myLatlng, map) {
    var strMakerLink = "";
    switch (color) {
        case "red":
            strMakerLink = 'http://maps.google.com/mapfiles/ms/icons/red-dot.png';
            break;
        case "blue":
            strMakerLink = 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png';
            break;
        case "yellow":
            strMakerLink = 'http://maps.google.com/mapfiles/ms/icons/yellow-dot.png';
            break;
        case "purple":
            strMakerLink = 'http://maps.google.com/mapfiles/ms/icons/purple-dot.png';
            break;
        case "green":
            strMakerLink = 'http://maps.google.com/mapfiles/ms/icons/green-dot.png';
            break;
        default:
            strMakerLink = 'http://maps.google.com/mapfiles/ms/icons/red-dot.png';
            break;
    }   
 //建立地圖google座標
    var marker = new google.maps.Marker({
        position: myLatlng,
        title: title,
        icon: strMakerLink,
    })

//google座標放進地圖
    marker.setMap(map);
    google.maps.event.addListener(marker, 'click', function () {       
        callCustomData(clinicNo);
        $("#geolocation_showDialog").click(); //點選座標後,跳出視窗
        return false;
    });
}

步驟六: 查詢範圍內的診所(WCF的函式)
/// <summary>
    /// 算出附近鄰近四個經緯度
    /// </summary> 
    public List<Clinic> computeNextBy(double doubleLat, double doubleLng, double doubleRange)
    {
        try
        {
            //先取此座標附近的範圍~ 四角形座標範圍
            Dictionary<string, Marker> dicClinicRange = getNeighborRange(doubleLat, doubleLng, doubleRange);           
            //取其左上和右下的兩點座標, 找出中間範圍所有診所座標           
            Marker markerSource= dicClinicRange["left_top"];
            Marker markerTarget = dicClinicRange["right_bottom"];
            double minLat, maxLat, minLng, maxLng;
            //座標(經緯)並非數字大小決定位置,但sql查詢範圍條件的 between 是以小到大
            if(markerSource.Lat <= markerTarget.Lat)
            {
                minLat=markerSource.Lat;
                maxLat = markerTarget.Lat;
            }else{
                minLat = markerTarget.Lat;
                maxLat = markerSource.Lat;
            }
            if (markerSource.Lng <= markerTarget.Lng)
            {
                minLng = markerSource.Lng;
                maxLng = markerTarget.Lng;
            }
            else
            {
                minLng = markerTarget.Lng;
                maxLng = markerSource.Lng;
            }
            List<Clinic> liNeighborClinics = getNeighborLocation(minLat,minLng, maxLat, maxLng, doubleLat, doubleLng); //從資料庫中取回落在查詢範圍內的經緯度的客戶
            return liNeighborClinics;
        }
        catch (Exception ex) {
            throw new Exception(ex.Message);
        }
    }
    /// <summary>
    /// 計算出四角形的個別座標 (就能找到自已附近範圍的客戶)
    public Dictionary<string, Marker> getNeighborRange(double doubleLat, double doubleLng, double doubleRange)
    {
        try
        {
            Dictionary<string, Marker> dicMarkers = new Dictionary<string, Marker>();
            Marker leftTop = new Marker();
            Marker rightTop = new Marker();
            Marker leftBottom = new Marker();
            Marker rightBottom = new Marker();
            leftTop.Lat = doubleLat;
            leftTop.Lng = doubleLng;
            /*
             台灣地區:
                兩地經緯度相差 0.5度:距離相差約 50公里
                兩地經緯度相差 0.1度:距離相差約 10公里
                兩地經緯度相差 0.05度:距離相差約 5公里
                兩地經緯度相差 0.01度:距離相差約 1公里
             */
            double dLng = 0.01 * 0.001 * doubleRange;
            double dLat = 0.01 * 0.001 * doubleRange;

            leftTop.Lat = doubleLat + dLat;
            leftTop.Lng = doubleLng - dLng;
            dicMarkers["left_top"] = leftTop;

            rightTop.Lat = doubleLat + dLat;
            rightTop.Lng = doubleLng + dLng;
            dicMarkers["right_top"] = rightTop;

            leftBottom.Lat = doubleLat - dLat;
            leftBottom.Lng = doubleLng - dLng;
            dicMarkers["left_bottom"] = leftBottom;

            rightBottom.Lat = doubleLat - dLat;
            rightBottom.Lng = doubleLng + dLng;
            dicMarkers["right_bottom"] = rightBottom;
            return dicMarkers;
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }

呈現樣式如下





補充說明: 如何將資料庫的客戶的地址全部轉換成經緯度
步驟一: 呼叫Google API 的網址http://maps.google.com/maps/api/geocode/json?sensor=false&address=住址


//將地址轉換成google 的經緯度值
public decimal[] getLocation(string address)
        {
            decimal[] strLatIng = new decimal[2];
            var url = String.Format("http://maps.google.com/maps/api/geocode/json?sensor=false&address={0}", address);
            string result = String.Empty;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            using (var response = request.GetResponse())
            {
                using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                {
                    result = sr.ReadToEnd();
                    GeoResponse geoResponse = JsonConvert.DeserializeObject<GeoResponse>(result);

                    if (geoResponse.Status == "OK")
                    {
                        int count = geoResponse.Results.Length;
                        if (count > 0)
                        {
                            strLatIng[0] = geoResponse.Results[0].Geometry.Location.Lat;
                            strLatIng[1] = geoResponse.Results[0].Geometry.Location.Lng;
                        }
                    }
                }
            }
            return strLatIng;
        }

    public class GeoLocation
    {
        public decimal Lat
        {
            get;
            set;
        }

        public decimal Lng
        {
            get;
            set;
        }
    }

    public class GeoGeometry
    {
        public GeoLocation Location
        {
            get;
            set;
        }
    }

    public class GeoResult
    {
        public GeoGeometry Geometry
        {
            get;
            set;
        }
    }

    public class GeoResponse
    {
        public string Status
        {
            get;
            set;
        }

        public GeoResult[] Results
        {
            get;
            set;
        }
    }

步驟二: 更新資料欄位的函式
  public void updateAllClinicGelocation()
        {
                string strConnection = ConfigurationManager.ConnectionStrings["TCS"].ConnectionString;
                SqlConnection sqlCon = new SqlConnection();
                sqlCon.ConnectionString = strConnection;
                sqlCon.Open();
                string strCommand = "SELECT 機構代號,名稱,地址 from tclinic with (nolock) where (緯度 is null or 緯度=0)  and (經度 is null or  經度=0) ";
                SqlCommand sqlCmd = new SqlCommand(strCommand, sqlCon);
                SqlDataReader sqlReader = sqlCmd.ExecuteReader(CommandBehavior.CloseConnection);
                if (sqlReader.HasRows)
                {
                    SqlConnection sqlCon2 = new SqlConnection();
                    sqlCon2.ConnectionString = strConnection;
                    sqlCon2.Open();

                    while (sqlReader.Read())
                    {
                        decimal[] deciLocation = new decimal[2];
                        deciLocation = getLocation(sqlReader["地址"].ToString());                      if (deciLocation[0] > 0 && deciLocation[1] > 0)
                        {
                            SqlCommand sqlUpdateCmd = new SqlCommand("update tclinic  set 緯度=@緯度, 經度=@經度 where 機構代號=@機構代號", sqlCon2);
                            SqlParameter spUpdateClinicNo = new SqlParameter("@機構代號", sqlReader["機構代號"].ToString());
                            SqlParameter spClinicLat = new SqlParameter("@緯度", deciLocation[0]);
                            SqlParameter spClinicIng = new SqlParameter("@經度", deciLocation[1]);
                            sqlUpdateCmd.Parameters.Add(spUpdateClinicNo);
                            sqlUpdateCmd.Parameters.Add(spClinicLat);
                            sqlUpdateCmd.Parameters.Add(spClinicIng);
                            sqlUpdateCmd.ExecuteNonQuery();
                            Thread.Sleep(1500); //記得加上這一行,以免伺服器過忙碌
                        }
                    }
                    sqlCon2.Close();
                }

步驟三: 使用AwaitAsync執行非同步執行作業,同時更新畫面值
private async void button1_Click(object sender, EventArgs e)
        {         
            timer1.Enabled = true; // 啟用Timer
            timer1.Start();
            timer1.Interval = 5000; // The time per tick.           
            timer1.Tick += new EventHandler(timer1_Tick);

            WorkTemp workObj = new WorkTemp();
            await workObj.DoWork();
        }

        private async void timer1_Tick(object sender, EventArgs e)
        {
            WorkTemp workObj = new WorkTemp();
            await workObj.DoComplete();         
            progressBar1.Value = workObj.theValue;
            label1.Text = "進度:" + workObj.theValue.ToString();
        }

    public class WorkTemp
    {
        public int theValue; //傳回資料庫的完成度
        public async Task DoWork()
        {
            await Task.Run(async () =>
            {
                updateAllClinicGelocation(); //更新資料庫的經緯度.
            });   
        }

   public async Task DoComplete()
        {
            await Task.Run(async () =>
            {
                computePercent(); //計算已更新經緯度的資料庫的百分比
            });
        }
}

示範程式畫面:可以在畫面顯示完程度進度









Button1_click:啟動workObj.DoWork()
Timer: 每隔秒去觸發workObj.DoComplete()