訂餐系統之地圖訂餐


  離上一篇博客已經是1月有余了,這中間清楚的認識到自己幾年的編程下來(大學4年,入行3載),能拿出手的東西真的屈指可數,也許真的是,一直以來都是為實現功能來寫代碼,所以終無大的突破!每每得空便思考自己到底差在哪?是不了解框架?於是學習EF;是不了解node.js?安裝后,寫了些demo; 是沒學習新知識?於是學了下WFP... 然而,了解這些東西並沒讓自己有所收獲,反而越發不知所措了。思考還在繼續,”探索“還在繼續... 於是有一天看到了設計模式,看到《大話設計模式》 才明白自己差在哪,差在對面向對象的理解!於是才開始正式的去了解什么是面向對象,為了加深理解,又細細看了《你必須知道的.net(第二版)》中的OO大原則,OO大智慧,OO之美...而后,再一次對《大話設計模式》中內容細細品味...這時,我覺得自己才開始真正的沉淀了!羅嗦了半天,下面開始今天的正文吧!

  所謂地圖訂餐就用戶直接在地圖上確定自己的位置后,搜索出附近商家后,直接選擇商家開始訂餐。目前市面上也有很多這樣的功能,下面我就把自己實現方式介紹下吧。上篇博客《訂餐系統之按距離[根據經緯度]排序、搜索》中所提到的功能,在地圖訂餐中就是一個應用。

  注:以下代碼都是百度地圖的API。google API變成3.0后,語法變了太多了,大部分項目都變成百度地圖了。

商家定位

   所謂定位,就是在地圖上標注商家的位置,成為用戶搜索的來源,如圖1:

                                                                            圖1

   定們代碼比較簡單,js部分代碼如下:

<script type="text/javascript">
    var gzoom = 15;
    var marker = null;
    var map = new BMap.Map("map_canvas"); // 創建地圖實例
    map.enableScrollWheelZoom();
    var myGeo = new BMap.Geocoder();
    var _lat = parseFloat($("#hidLat").val());
    var _lng = parseFloat($("#hidLng").val());
    var initpoint = new BMap.Point(_lng, _lat); // 創建點坐標
    //圖標
    var myIcon = new BMap.Icon("http://www.ihangjing.com/images/marker50.png", new BMap.Size(20, 34), { anchor: new BMap.Size(10, 0) });
    marker = new BMap.Marker(initpoint, { icon: myIcon });
    map.addOverlay(marker);
    marker.enableDragging();
    marker.setTitle("拖動修改位置");
    map.centerAndZoom(initpoint, gzoom); // 初始化地圖,設置中心點坐標和地圖級別
    map.addControl(new BMap.NavigationControl());  //縮放工具

    EventWrapper.addListener(map, "click", function(e) {
        map.clearOverlays();
        initpoint = e.point;
        map.removeOverlay(marker);
        marker = new BMap.Marker(initpoint, { icon: myIcon });
        map.addOverlay(marker);
        marker.enableDragging();
        marker.openInfoWindow(infoWindow);
        setLatLng(initpoint);

        marker.addEventListener("dragend", function(e) {
            initpoint = e.point;
            marker = new BMap.Marker(initpoint);
            this.openInfoWindow(infoWindow);
            setLatLng(initpoint);
        });
        marker.addEventListener("dragstart", function(e) {
            map.closeInfoWindow()
        });
    });

    marker.addEventListener("dragend", function(e) {
        initpoint = e.point;
        marker = new BMap.Marker(initpoint);
        this.openInfoWindow(infoWindow);
        setLatLng(initpoint);
    });

    marker.addEventListener("dragstart", function(e) {
        map.closeInfoWindow()
    });

    var opts = {
        width: 250,     // 信息窗口寬度  
        height: 50    // 信息窗口高度
    }

    var winhtml = " <div><p>確定地圖位置后,點擊按鈕“確認位置”進行保存</p>";
    winhtml += "<p style=\" float:right; padding-right:10px;\"><input type=\"button\" value=\"確認位置\" onclick='map.closeInfoWindow()' /></p></div>";

    var infoWindow = new BMap.InfoWindow(winhtml, opts);  // 創建信息窗口對象

    function setLatLng(point) {
        document.getElementById("hidLat").value = point.lat;
        document.getElementById("hidLng").value = point.lng;
        return true;
    }

    function setPlace() {
        var hfcity = $("#hfcity").val();
        var address = document.getElementById("keyaddress").value.trim();
        var local = new BMap.LocalSearch(hfcity, {
            renderOptions: {
                map: map,
                autoViewport: true,
                selectFirstResult: false
            }
        });
        local.search(address);
    }
</script>
View Code

 

商家設置配送范圍

   所謂設置配送范圍,就在地圖上通過標注,繪制出一個多邊形區域,如圖2:

                                  圖2

  以下為繪制多邊形,js部分代碼。頁面中只多了幾個HiddenField,用來保存坐標、原多邊形信息以及一個用於放地圖的div。

<script type="text/javascript">
    var gzoom = 15;
    var points = new Array();
    var markers = new Array();
    var map = new BMap.Map("map"); // 創建地圖實例
    map.enableScrollWheelZoom();     
    var myPolygon = null;
    var count = 0;
    var _lat = parseFloat($("#hidLat").val());
    var _lng = parseFloat($("#hidLng").val());
    var initpoint = new BMap.Point(_lng, _lat);  // 創建點坐標

    map.centerAndZoom(initpoint, gzoom); // 初始化地圖,設置中心點坐標和地圖級別
    map.addControl(new BMap.NavigationControl());  //縮放工具

    var myIcon = new BMap.Icon("http://www.ihangjing.com/images/marker50.png", new BMap.Size(20, 34), { anchor: new BMap.Size(10, 0) });
    marker = new BMap.Marker(initpoint, { icon: myIcon });
    map.addOverlay(marker);

    //map.addEventListener("click", mapclick);
    EventWrapper.addListener(map, "click", mapclick);

    //繪制多邊形
    function drawPolygon() {
        if (myPolygon) {
            map.removeOverlay(myPolygon);
        }
        points.length = 0;
        //從marksers填充pints數組
        for (i = 0; i < markers.length; i++) {
            points.push(markers[i].getPosition());
        }
        //  points.push(markers[0].getPosition());

        myPolygon = new BMap.Polygon(points, { strokeColor: "red", strokeWeight: 1, strokeOpacity: 1 });
        map.addOverlay(myPolygon);
    }

    function mapclick(target) {
        //flag == 0表示點擊的是標注
        //flag > 0表示點擊的是地圖
        var flag = target.pixel.x;
        if (target && flag > 0) {
            count++
            var myIcon = new BMap.Icon("http://www.ihangjing.com/images/mm_20_red.png", new BMap.Size(14, 22), { anchor: new BMap.Size(7, 22) });
            var _marker = new BMap.Marker(target.point, { icon: myIcon });
            map.addOverlay(_marker);
            markers.push(_marker);
            _marker.enableDragging();
            _marker.setTitle("point" + count + " lat:" + target.point.lat);

            _marker.addEventListener("dragging", function(e) {
                drawOverlay();
            });
            _marker.addEventListener("dragend", function(e) {
                drawOverlay();
            });
            _marker.addEventListener("dragstart", function(e) {
                drawOverlay();
            });

            // Click listener to remove a marker
            _marker.addEventListener("click", function() {
                var n = 0;
                for (n = 0; n < markers.length; n++) {
                    if (markers[n] == _marker) {
                        map.removeOverlay(markers[n]);
                        break;
                    }
                }
                // 指定從數組中移除元素的開始位置,這個位置是從 0 開始計算的。 
                // 刪除從標n開始的,一個元素
                markers.splice(n, 1);
                if (markers.length == 0) {
                    count = 0;
                }
                else {
                    count = markers.length;
                    drawOverlay();
                }
            });
            drawPolygon();
        }
    }

    function drawOverlay() {

        drawPolygon();
    }
    
    //獲取每個點的坐標,以: lat1,lng1|lat2,lng2..保存
    function GetPolygon() {
        var html = "";
        for (var i = 0; i < points.length; i++) {
            html +=  points[i].lat + "," + points[i].lng + "|";
        }
        var temp = html.replace(/\|$/, "");
        $("#hidPolygon").val(temp);
        if (html == "") {
            alert("請設定配置范圍");
            return false;
        }
        return true;
    }

    //初始化多邊形
    function initPolygon() {
        var hidPolygon = $("#hidPolygon").val();
        if (hidPolygon == "") {
            return;
        }
        var oldPolygon = hidPolygon.split('|');
        for (var i = 0; i < oldPolygon.length; i++) {
            count++;
            var latlng = oldPolygon[i].split(',');
            var _mypoint = new BMap.Point(latlng[1], latlng[0]);
            var myIcon = new BMap.Icon("http://www.ihangjing.com/images/mm_20_red.png", new BMap.Size(14, 22), { anchor: new BMap.Size(7, 22) });
            var _marker = new BMap.Marker(_mypoint, { icon: myIcon });
            map.addOverlay(_marker);
            markers.push(_marker);
            _marker.enableDragging();
            _marker.setTitle("point" + count + " lat:" + _mypoint.lat);

            count++;
            addinitmarker(_marker);
        }
        drawPolygon();
    }


    function addinitmarker(_marker_init) {

        _marker_init.addEventListener("dragging", function(e) {
            drawOverlay();
        });
        _marker_init.addEventListener("dragend", function(e) {
            drawOverlay();
        });

        _marker_init.addEventListener("dragstart", function(e) {
            drawOverlay();
        });
        // Click listener to remove a marker
        EventWrapper.addListener(_marker_init, "click", function() {
            var n = 0;
            for (n = 0; n < markers.length; n++) {
                if (markers[n] == _marker_init) {
                    map.removeOverlay(markers[n]);
                    break;
                }
            }
            // 指定從數組中移除元素的開始位置,這個位置是從 0 開始計算的。 
            // 刪除從標n開始的,一個元素
            markers.splice(n, 1);
            if (markers.length == 0) {
                count = 0;
            }
            else {
                count = markers.length;
                drawOverlay();
            }
        });
    }
    initPolygon();;

</script>
View Code

 

  設置好商家的位置,范圍后,用戶就可以在地圖上定好自己的位置,開始搜索商家。搜索分兩種方式:1.以當前用戶地址為中心,搜索N公里內的商家;2.搜索當前坐標在商家設置的多邊形內的商家。以下的地圖拉框搜索以及按配送范圍搜索就分別是用這種方式來實現的。

 

地圖拉框搜索

   所謂拉框搜索,就是在地圖上拉出一個框,只搜索這個框內的商家。當然拉出的是一個長方形,搜索是轉換成一個圓,按半徑搜索。如圖3:

                           圖3

  
  拉框部分主要用了api之RectangleZoom.js 只是修改了完成時的事件,這個是從一個論壇中認識的朋友那得來的---程序員真是大多數是好人吶!在548行左右,代碼如下:

 var startlng = Math.min(northEastPoint.lng,southWestPoint.lng);
                  var endlng = Math.max(northEastPoint.lng,southWestPoint.lng);
                  var startlat = Math.min(northEastPoint.lat,southWestPoint.lat);
                  var endlat = Math.max(northEastPoint.lat,southWestPoint.lat);
                  var startlgt = endlng - (endlng - startlng)/2;
                  var startlat = endlat - (endlat - startlat)/2;
                  var radius = Math.max((endlng - startlng)/2,(endlat - startlat)/2)*111;
                  GetMapInfo('1',startlgt,startlat,radius);//這里調用自己獲取數據方法
修改部分代碼

   然后再加上API之RichMarker_min.js,這個使用比較簡單,基本代碼如下:

//地圖上添加marker
//參數說明:togoname , picture,address,reviewcont,BookPercapita(人均).shopid
function createMarker(index, togoname, picture, address, reviewcont, BookPercapita, shopid, point) {
    var infohtml = "";

    var htm1 = "<div class='RichmarkerContainer'  title=\"" + togoname + "\"><h4 class='Richmarker' id=\"h4_" + parseInt(index) + "\"><span class=\"numberL\">" + parseInt(index) + "</span></h4><lable id=\"name_" + parseInt(index) + "\" class='markerlabel'></lable></div>";
    var shopmarker = new BMapLib.RichMarker(htm1, point);

    var temptipinfo = getInfohtml(index, togoname, picture, address, reviewcont, BookPercapita, shopid);
    $("#tipcontainer").append("<div id=\"click_list_infohtml_" + index + "\">" + temptipinfo + "</div>");

    shopmarker.addEventListener("click", function(e) {
        var shopinfohtml = getInfohtml(index, togoname, picture, address, reviewcont, BookPercapita, shopid);
        var shopinfoWindow = new BMap.InfoWindow(shopinfohtml);
        map.openInfoWindow(shopinfoWindow,point);
    });
    allmarkers.push(shopmarker);
    points.push(point);

    return shopmarker;
}
生成圖標代碼

  最終形成了,現在的界面

按配送范圍搜索

  這部分其實主要就是用一個射線法,判斷點是否在多邊形內,這是我頭頭寫的,不過不怎么准確,在這里我也帖下代碼,也希望哪個朋友有更精確的方法提出來。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Reflection;
using System.Text;

using System.Drawing;
using System.Drawing.Drawing2D;

public static class hjmap
{
    const double INFINITY = 1e10;
    const double ESP = 1e-5;
    const int MAX_N = 1000;

    public struct XPoint
    {
        public double x, y;

        public XPoint(double _x, double _y)
        {
            x = _x;
            y = _y;
        }
    };

    public struct LineSegment
    {
        public XPoint pt1, pt2;
    };

    /// 判斷點在多邊形內
    /// <summary>
    /// 如果點在多邊形內: 返回0
    /// 如果點在多邊形邊上: 返回1
    /// 如果點在多邊形外: 返回2
    /// </summary>
    /// <param name="polygon">多邊形頂點</param>
    /// <param name="point">當前點</param>
    /// <returns></returns>
    public static int InPolygon(XPoint[] polygon, XPoint point)
    {
        int n = polygon.Length;
        int count = 0;
        LineSegment line;
        line.pt1 = point;
        line.pt2.y = point.y;
        line.pt2.x = -INFINITY;
        for (int i = 0; i < n; i++)
        {
            //得到多邊形的一條邊
            LineSegment side;
            side.pt1 = polygon[i];
            side.pt2 = polygon[(i + 1) % n];
            if (IsOnline(point, side))
            {
                return 1;
            }
            // 如果side平行x軸則不作考慮
            if (Math.Abs(side.pt1.y - side.pt2.y) < ESP)
            {
                continue;
            }
            if (IsOnline(side.pt1, line))
            {
                if (side.pt1.y > side.pt2.y)
                    count++;
            }
            else if (IsOnline(side.pt2, line))
            {
                if (side.pt2.y > side.pt1.y)
                    count++;
            }
            else if (Intersect(line, side))
            {
                count++;
            }
        }
        if (count % 2 == 1)
        {
            return 0;
        }
        else
        {
            return 2;
        }
    }

    // 計算叉乘 |P0P1| × |P0P2|
    public static double Multiply(XPoint p1, XPoint p2, XPoint p0)
    {
        return ((p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y));
    }

    // 判斷線段是否包含點point
    public static bool IsOnline(XPoint point, LineSegment line)
    {
        return ((Math.Abs(Multiply(line.pt1, line.pt2, point)) < ESP) &&
        ((point.x - line.pt1.x) * (point.x - line.pt2.x) <= 0) &&
        ((point.y - line.pt1.y) * (point.y - line.pt2.y) <= 0));
    }

    // 判斷線段相交
    public static bool Intersect(LineSegment L1, LineSegment L2)
    {
        return ((Math.Max(L1.pt1.x, L1.pt2.x) >= Math.Min(L2.pt1.x, L2.pt2.x)) &&
        (Math.Max(L2.pt1.x, L2.pt2.x) >= Math.Min(L1.pt1.x, L1.pt2.x)) &&
        (Math.Max(L1.pt1.y, L1.pt2.y) >= Math.Min(L2.pt1.y, L2.pt2.y)) &&
        (Math.Max(L2.pt1.y, L2.pt2.y) >= Math.Min(L1.pt1.y, L1.pt2.y)) &&
        (Multiply(L2.pt1, L1.pt2, L1.pt1) * Multiply(L1.pt2, L2.pt2, L1.pt1) >= 0) &&
        (Multiply(L1.pt1, L2.pt2, L2.pt1) * Multiply(L2.pt2, L1.pt2, L2.pt1) >= 0));
    }

    /// 判斷點在多邊形內
    /// <summary>
    /// 如果點在多邊形內: 返回0
    /// 如果點在多邊形邊上: 返回1
    /// 如果點在多邊形外: 返回2
    /// </summary>
    /// <param name="polygon">定點集合,以30.192353,120.164766|30.192712,120.175851|30.189668,120.170192的方式存在</param>
    /// <param name="point">當前點</param>
    /// <returns></returns>
    public static int InPolygon(string _lat, string _lng, string _polygon)
    {
        XPoint mypoint = new XPoint();
        mypoint.x = Convert.ToDouble(_lat);
        mypoint.y = Convert.ToDouble(_lng);

        string Polygon = _polygon;

        string[] PolygonArray = Polygon.Split('|');
        hjmap.XPoint[] curvePoints = new hjmap.XPoint[PolygonArray.Length - 1];

        for (int i = 0; i < PolygonArray.Length - 1; i++)
        {
            string[] point_value = PolygonArray[i].Split(',');
            curvePoints[i] = new hjmap.XPoint((Convert.ToDouble(point_value[0])), (Convert.ToDouble(point_value[1])));
        }

        int rs = InPolygon(curvePoints, mypoint);
        return rs;
    }
}
判斷點是否在多邊形內

    以上便是我們系統中地圖訂餐部分的內容了,也有做這塊的朋友,大家可以交流下。

  成為一名優秀的程序員!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM