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

最近我們就有一個需求,需要標識出一些熱門場所的人流的熱力圖情況,同時需要給出該熱門場所的邊界輪廓。經過查看百度地圖和高德地圖的開發者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 + "®ion=" + 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接口穩定性太差。
后記
①百度地圖會涉及到功能接口配額的問題

主要會涉及到上面的地點檢索配額,如果只是個人簡單使用的,可以注冊個人開發者,基本配額就夠使用了
②高德地圖沒有找到配額相關的數據,畢竟走的非正規手段吧
③百度地圖和高德地圖對於一些位置的邊界數據不同
有些地點只會在其中一個能夠獲取到(高德地圖能夠返回數據的情況下)
④高德地圖在檢索位置時,能夠支持全拼音輸入,也能檢索出來(感覺這個厲害,但是中文多音字處理不知道會怎么樣)
參考資料:
