地圖小區景點邊界輪廓實現

冷星1024發表於2018-09-27

經常的我們在使用地圖功能時,會發現在選擇一個小區或者一個熱門景點的時候,地圖上面會給出其邊界輪廓,能夠方便我們知道其範圍大小,有時候在我們使用地圖元件的時候,也會面臨著類似的需求。比如在地圖上面標識出一個商場範圍內的熱力圖,一個熱門景點的遊覽情況等。那麼,我們該如何利用地圖功能來實現這類效果呢,今天我們一起來探討一下。

效果截圖

最近我們就有一個需求,需要標識出一些熱門場所的人流的熱力圖情況,同時需要給出該熱門場所的邊界輪廓。經過檢視百度地圖和高德地圖的開發者API文件,發現並沒有這類公共介面提供我們使用。目前地圖能夠提供我們使用的,基本只能是一些行政區劃的邊界範圍,這個在我之前的文章中也有寫過,大家可以參照《仿鏈家地圖找房的簡單實現》

那麼現在面臨的需求該如何實現呢?

通過檢視地圖功能的介面呼叫情況和在網上查詢相關資料,最終我們找到了下面這個“不算是方法的方法”。

  • 使用了地圖的相關API介面獲取相關資料
  • API介面不是官方給出的,所以也就面臨著穩定性的問題,可能隨時被關(高德的只能簡單參考,本身就存在較大缺陷,後面會說)

實現思路

  • 通過地圖的POI查詢服務獲取到興趣點id

    那麼什麼是POI呢?

    檢索服務提供某一特定地區的興趣點位置查詢服務(POI:Point of Interest,感興趣點)

    相關的官方文件請參照以下地址:

  • 通過興趣點id獲取該興趣點的詳細資訊

    這裡面需要用到的相關API就需要我們檢視地圖的執行過程,找到對應的API了。(也希望各個地圖官方能夠給出官方的方法吧)

PS:地圖功能的使用情況在本篇不做說明,具體申請相關Key的過程請分別參照官網說明即可。

下面我們來分別給出百度地圖和高德地圖的實現方法:

百度地圖實現

閒話休談,我們們直接上碼

</html>
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>百度地圖DEMO</title>
    <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=你申請的AK"></script>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript">
    $(document).ready(function() {

        var queryHouseOutline = function(hid, callback) {
            var baseURL = 'http://map.baidu.com/?reqflag=pcmap&coord_type=3&from=webmap&qt=ext&ext_ver=new&l=18';
            var url = baseURL + "&uid=" + hid;
            callback && (window.queryHouseOutlineCallback = callback);
            $.ajax({
                type: "get",
                async: false,
                url: url,
                dataType: "jsonp",
                jsonpCallback: "queryHouseOutlineCallback",
                success: function(datas) {}
            });
        };

        /**
         * 模糊查詢小區資訊, 無返回值
         * @param {} house  小區名稱
         * @param {} city   所屬城市名稱
         * @param {} ak     百度地圖AK
         * @param {} callback   回撥函式,該函式可以接收到請求的返回值
         */
        var queryHouse = function(house, city, ak, callback) {
            var baseURL = 'http://api.map.baidu.com/place/v2/search?output=json&scope=2';
            var url = baseURL + "&q=" + house + "&region=" + city + "&ak=" + ak;
            callback && (window.queryHouseCallback = callback);
            $.ajax({
                type: "get",
                async: false,
                url: url,
                dataType: "jsonp",
                jsonpCallback: "queryHouseCallback",
                success: function(datas) {}
            });
        };

        /**
         * 墨卡託座標轉百度座標
         * @param {} coordinate
         * @return {}
         */
        var coordinateToPoints = function(map, coordinate) {
            var points = [];
            if (coordinate) {
                var arr = coordinate.split(";");
                if (arr) {
                    for (var i = 0; i < arr.length; i++) {
                        var coord = arr[i].split(",");
                        if (coord && coord.length == 2) {
                            var mctXY = new BMap.Pixel(coord[0], coord[1]);
                            var project = map.getMapType().getProjection();
                            var point = project.pointToLngLat(mctXY);
                            points.push(new BMap.Point(point.lng, point.lat));
                        }
                    }
                }
            }
            return points;
        };
        /**
         * 墨卡託座標解析
         * @param {} mocator
         * @return {}
         */
        var parseGeo = function(mocator) {
            if (typeof mocator != 'string') {
                return {};
            }
            var t = mocator.split("|");
            var n = parseInt(t[0]);
            var i = t[1];
            var r = t[2];
            var o = r.split(";");
            if (n === 4) {
                for (var a = [], s = 0; s < o.length - 1; s++) {
                    "1" === o[s].split("-")[0] && a.push(o[s].split("-")[1]);
                }
                o = a;
                o.push("");
            }
            var u = [];
            switch (n) {
                case 1:
                    u.push(o[0]);
                    break;
                case 2:
                case 3:
                case 4:
                    for (var s = 0; s < o.length - 1; s++) {
                        var l = o[s];
                        if (l.length > 100) {
                            l = l.replace(/(-?[1-9]\d*\.\d*|-?0\.\d*[1-9]\d*|-?0?\.0+|0|-?[1-9]\d*),(-?[1-9]\d*\.\d*|-?0\.\d*[1-9]\d*|-?0?\.0+|0|-?[1-9]\d*)(,)/g,
                                "$1,$2;");
                            u.push(l);
                        } else {
                            for (var c = [], d = l.split(","), f = 0; f < d.length; f += 2) {
                                var p = d[f];
                                var h = d[f + 1];
                                c.push(p + "," + h);
                            }
                            u.push(c.join(";"))
                        }
                    }
                    break;
                default:
                    break;
            }

            if (u.length <= 1) {
                u = u.toString();
            }

            var result = {
                type: n,
                bound: i,
                points: u
            };
            return result;
        };


        var map = new BMap.Map("allmap"); // 建立Map例項
        map.centerAndZoom("北京", 19);
        map.addControl(new BMap.MapTypeControl()); //新增地圖型別控制元件

        map.enableScrollWheelZoom(false); //開啟滑鼠滾輪縮放

        /**
         * 第一個引數是城市名,第二引數是小區名 
         */
        var showArea = function(city, area) {
            queryHouse(area, city, "你申請的AK", function(data) {
                if (data.message == 'ok') {
                    var houses = data.results;
                    if (houses && houses.length > 0) {
                        var house = houses[0];
                        queryHouseOutline(house.uid, function(houseOutline) {
                            var geo = houseOutline.content.geo;
                            if (!geo) {
                                var location = house.location;
                                var point = new BMap.Point(location.lng, location.lat);
                                map.centerAndZoom(point, 19);
                                var marker = new BMap.Marker(point);
                                marker.setAnimation(BMAP_ANIMATION_BOUNCE);
                                map.addOverlay(marker);
                            } else {
                                map.clearOverlays();
                                var geoObj = parseGeo(geo);
                                //邊界點
                                var points = coordinateToPoints(map, geoObj.points);
                                var ply = new BMap.Polygon(points, {
                                    strokeWeight: 2,
                                    strokeColor: "#F01B2D",
                                    strokeOpacity: 0.9,
                                    fillColor: "transparent"
                                }); //建立多邊形覆蓋物
                                map.addOverlay(ply); //新增覆蓋物
                                map.setViewport(ply.getPath()); //調整視野 
                            }
                        });
                    }
                }
            });
        };

        showArea($('#cityId').val(), $('#areaId').val());

        $('#showBtn').click(function() {
            debugger;
            showArea($('#cityId').val(), $('#areaId').val());
        });

        $("#areaId").keydown(function(e) {
            if (event.keyCode == "13") {
                showArea($('#cityId').val(), $('#areaId').val());
            }
        })
    });
    </script>
</head>

<body>
    <table>
        <tr>
            <td>城市:</td>
            <td>
                <input id="cityId" type="text" value="北京" />
            </td>
            <td>小區:</td>
            <td>
                <input id="areaId" type="text" value="故宮博物院" />
            </td>
            <td>
                <button id="showBtn">顯示</button>
            </td>
        </tr>
    </table>
    <div id="allmap" style="width: 90vw; height: 90vh;"></div>
</body>

</html>
複製程式碼

相關的程式碼註釋都有所新增,參照即可。其中需要注意的是百度地圖獲取到的座標點需要進行轉換成百度地圖識別的點位形式。 另外,邊界的描畫使用到的是地圖的Polygon功能,相關內容請參照

高德地圖實現

</html>
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>高德地圖DEMO</title>
    <script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.10&key=你申請的AK"></script>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript">
    $(document).ready(function() {
        var map = new AMap.Map('allmap', {
            zoom: 19,
            center: [116.397428, 39.90923]
        }); // 建立Map例項

        /**
         * 第一個引數是城市名,第二引數是小區名 
         */
        var showArea = function(city, area) {
            queryHouse(area, city, "你申請的AK", function(data) {
                console.error(data)
                if (data.status == 1) {
                    var houses = data.pois;
                    if (houses && houses.length > 0) {
                        var house = houses[0];
                        queryHouseOutline(house.id, function(houseOutline) {
                            console.error("get outline success");

                            var pathPoints = houseOutline.data.spec.mining_shape.shape;
                            
                            var tmpPath = pathPoints.split(";");

                            var points = [];
                            tmpPath.forEach(function(value, index, array) {
                                points.push(value.split(","))
                            });

                            map.clearMap();

                            var ply = new AMap.Polygon({
                                map: map,
                                path: points,
                                strokeColor: "#F01B2D",
                                fillColor: "transparent"
                            }); //建立多邊形覆蓋物

                            map.setFitView(); //調整最佳顯示
                        });
                    }
                }
            });
        };

        var queryHouseOutline = function(hid, callback) {
            var baseURL = 'https://www.amap.com/detail/get/detail';
            $.ajax({
                type: "get",
                data: {
                    id: hid
                },
                url: baseURL,
                dataType: "json",
                success: function(datas) {
                    callback(datas)
                }
            });
        };

        /**
         * 模糊查詢小區資訊, 無返回值
         * @param {} house  小區名稱
         * @param {} city   所屬城市名稱
         * @param {} ak     高德地圖AK
         * @param {} callback   回撥函式,該函式可以接收到請求的返回值
         */
        var queryHouse = function(house, city, ak, callback) {
            var baseURL = 'http://restapi.amap.com/v3/place/text?&keywords=' + house + '&city=' + city + '&output=json&offset=20&page=1&key=' + ak;
            callback && (window.queryHouseCallback = callback);
            $.ajax({
                type: "get",
                async: false,
                url: baseURL,
                dataType: "jsonp",
                jsonpCallback: "queryHouseCallback",
                success: function(datas) {}
            });
        };

        showArea($('#cityId').val(), $('#areaId').val());

        $('#showBtn').click(function() {
            showArea($('#cityId').val(), $('#areaId').val());
        });

        $("#areaId").keydown(function(e) {
            if (event.keyCode == "13") {
                showArea($('#cityId').val(), $('#areaId').val());
            }
        })
    });
    </script>
</head>

<body>
    <table>
        <tr>
            <td>城市:</td>
            <td>
                <input id="cityId" type="text" value="北京" />
            </td>
            <td>小區:</td>
            <td>
                <input id="areaId" type="text" value="故宮博物院" />
            </td>
            <td>
                <button id="showBtn">顯示</button>
            </td>
        </tr>
    </table>
    <div id="allmap" style="width: 90vw; height: 90vh;"></div>
</body>

</html>
複製程式碼

高德地圖的實現方式根據實際的效果來看,本身應該是做了API介面限制的處理,經常會出現獲取不到詳細資訊或者給出的詳細資訊中的邊界資訊資料不準確。 這裡只是作為一個對比參照,高德地圖不推薦來做這個需求,API介面穩定性太差。

後記

①百度地圖會涉及到功能介面配額的問題

配額截圖

主要會涉及到上面的地點檢索配額,如果只是個人簡單使用的,可以註冊個人開發者,基本配額就夠使用了

②高德地圖沒有找到配額相關的資料,畢竟走的非正規手段吧

③百度地圖和高德地圖對於一些位置的邊界資料不同

有些地點只會在其中一個能夠獲取到(高德地圖能夠返回資料的情況下)

④高德地圖在檢索位置時,能夠支援全拼音輸入,也能檢索出來(感覺這個厲害,但是中文多音字處理不知道會怎麼樣)


參考資料:

百度地圖小區邊界(輪廓)處理

高德地圖之python爬取POI資料及其邊界經緯度(根據關鍵字在城市範圍內搜尋)

相關文章