地图找房的实例
1.功能說明:
- 在房源信息類網(wǎng)站中,為了方便用戶查找房源,推出了地圖找房的功能,房源數(shù)據(jù)展示更加的直觀。
- 例如:鏈家網(wǎng):上海地圖找房_上海小區(qū)地圖_上海房產(chǎn)地圖(上海鏈家)
?
?
BMapGLLib:
- BMapGLLib是基于百度地圖JSAPI GL版的JavaScript開源工具庫。
- 代碼地址:GitHub - huiyan-fe/BMapGLLib: 百度地圖JSAPI GL版JavaScript開源工具庫
?
?靜態(tài)頁面:
<!DOCTYPE html> <html> <head><meta name="viewport" content="initial-scale=1.0, user-scalable=no"/><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>地圖找房 - 地圖搜索 </title><style type="text/css">html {height: 100%}body {height: 100%;margin: 0px;padding: 0px}#container {height: 100%}.district {width: 84px;height: 84px;line-height: 16px;font-size: 12px;display: flex;flex-direction: column;justify-content: center;border: 1px solid transparent;border-radius: 50%;overflow: hidden;text-align: center;font-family: PingFangSC-Semibold;color: #fff;background: #00ae66 !important;box-sizing: border-box;}.district i {font-size: 12px;color: hsla(0, 0%, 100%, .7);line-height: 12px;margin-top: 4px;font-style: normal;}#platform > div > div > div {background: none !important;}</style><script type="text/javascript" src="jquery-3.6.0.min.js"></script><script type="text/javascript"src="http://api.map.baidu.com/api?v=1.0&type=webgl&ak=XdBGuSs0p90SNRM3dtpGGbvEygzfQyte"></script><script src="http://mapopen.bj.bcebos.com/github/BMapGLLib/RichMarker/src/RichMarker.min.js"></script> </head> <body> <div id="container"></div> <script type="application/javascript">function showInfo(map) {let bound = map.getBounds(); //可視范圍矩形坐標,其中sw表示矩形區(qū)域的西南角,參數(shù)ne表示矩形區(qū)域的東北角let zoom = map.getZoom(); //縮放級別console.log(bound);// $.ajax({// url: "/house/search",// data: {// maxLongitude: bound.ne.lng,// minLongitude: bound.sw.lng,// maxLatitude: bound.ne.lat,// minLatitude: bound.sw.lat,// zoom: zoom// },// success: function (data) {// showMapMarker(data, map);// }// });//測試效果:let data = [{"name":"徐匯","price":"1028.43","total":"6584","longitude":121.43676,"latitude":31.18831},{"name":"黃浦","price":"1016.19","total":"7374","longitude":121.49295,"latitude":31.22337},{"name":"長寧","price":"1008.34","total":"4380","longitude":121.42462,"latitude":31.22036},{"name":"靜安","price":"1005.34","total":"8077","longitude":121.4444,"latitude":31.22884},{"name":"普陀","price":"1026.14","total":"5176","longitude":121.39703,"latitude":31.24951},{"name":"金山","price":"1099.67","total":"6","longitude":121.34164,"latitude":30.74163},{"name":"松江","price":"1017.71","total":"14","longitude":121.22879,"latitude":31.03222},{"name":"青浦","price":"1038.11","total":"751","longitude":121.12417,"latitude":31.14974},{"name":"奉賢","price":"1108.63","total":"35","longitude":121.47412,"latitude":30.9179},{"name":"浦東","price":"1030.22","total":"8294","longitude":121.5447,"latitude":31.22249},{"name":"嘉定","price":"1041.45","total":"1620","longitude":121.2655,"latitude":31.37473},{"name":"寶山","price":"1050.65","total":"102","longitude":121.4891,"latitude":31.4045},{"name":"閔行","price":"1027.15","total":"941","longitude":121.38162,"latitude":31.11246},{"name":"楊浦","price":"1007.78","total":"2747","longitude":121.526,"latitude":31.2595},{"name":"虹口","price":"1025.81","total":"4187","longitude":121.48162,"latitude":31.27788}];showMapMarker(data, map);}//顯示覆蓋物function showMapMarker(data, map) {for (let vo of data) {let html = "<div class=\"district\">" + vo.name + "<span>" + vo.price + "萬</span><i>" + vo.total + "套</i></div>";let marker = new BMapGLLib.RichMarker(html, new BMapGL.Point(vo.longitude, vo.latitude));map.addOverlay(marker);}}//清除覆蓋物function clearMapMarker(map) {let markers = map.getOverlays(); //獲取到地圖上所有的覆蓋物for (let marker of markers) { //循環(huán)將其刪除map.removeOverlay(marker);}}$(function () {//地圖默認位置,上海市l(wèi)et defaultX = 121.48130241985999;let defaultY = 31.235156971414239;let defaultZoom = 12; //默認縮放比例let map = new BMapGL.Map("container"); // 創(chuàng)建地圖實例let point = new BMapGL.Point(defaultX, defaultY); // 創(chuàng)建點坐標map.centerAndZoom(point, defaultZoom); // 初始化地圖,設置中心點坐標和地圖級別map.enableScrollWheelZoom(true); //開啟鼠標滾輪縮放//顯示比例尺map.addControl(new BMapGL.ScaleControl({anchor: BMAP_ANCHOR_BOTTOM_RIGHT}));map.addEventListener("dragstart", () => { //拖動開始事件clearMapMarker(map)});map.addEventListener("dragend", () => { //拖動結束事件showInfo(map)});map.addEventListener("zoomstart", () => { //縮放開始事件clearMapMarker(map)});map.addEventListener("zoomend", () => { //縮放結束事件showInfo(map)});//初始顯示數(shù)據(jù)showInfo(map);}); </script> </body> </html>?搭建MongoDB環(huán)境:
?application.properties文件中的配置:
spring.application.name = itcast-baidumap server.port = 8899#springboot MongoDB配置 spring.data.mongodb.username=house spring.data.mongodb.password=******** spring.data.mongodb.authentication-database=admin spring.data.mongodb.database=house spring.data.mongodb.port=27017 spring.data.mongodb.host=192.168.31.81 spring.data.mongodb.auto-index-creation=true構造數(shù)據(jù):
為了數(shù)據(jù)真實性,我們從網(wǎng)上抓取一些小區(qū)的數(shù)據(jù),小區(qū)的位置數(shù)據(jù)可以通過百度地圖API數(shù)據(jù)
?
?
案例以上海為例,其有16個行政區(qū)
構造完成的數(shù)據(jù)如下:
?商圈數(shù)據(jù)是行政區(qū)的下級分類:
?構造完成后有198條數(shù)據(jù)
?
小區(qū)數(shù)據(jù)抓取網(wǎng)上的數(shù)據(jù)。
抓取完成后,共有20510條數(shù)據(jù)(建議使用提供的數(shù)據(jù)即可,無需重新抓取)
?
由于房源數(shù)據(jù)僅僅是展現(xiàn)數(shù)量,所以就不再進行數(shù)據(jù)抓取,按照小區(qū)數(shù)據(jù)進行構造即可
每個小區(qū)隨機構造1~20個房源數(shù)據(jù)
最終構造完成有106094條數(shù)據(jù)。
?
MongoDB的聚合操作:
- MongoDB的聚合操作是以管道的形式完成的,在一個管道處理完畢后將結果傳遞給下一個管道處理
- 常用的聚合操作有:
?MongoDB的聚合操作符:
?
實現(xiàn)搜索:
實現(xiàn)搜索的關鍵點:
通過矩形的對角的坐標可以確定矩形搜索范圍。
需要按照不同的地圖比例尺進行分組聚合查詢,通過按照行政區(qū)、商圈或小區(qū)名進行分組,聚合數(shù)量、價格等數(shù)據(jù)。
響應數(shù)據(jù):
?pom文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><!--通過繼承的方式集成SpringBoot--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version></parent><groupId>cn.itcast</groupId><artifactId>itcast-baidumap</artifactId><version>1.0-SNAPSHOT</version><!-- 集中定義依賴版本號 --><properties><mongo.version>4.0.3</mongo.version><lombok.version>1.18.4</lombok.version></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.2</version></dependency><!--springdata對于mongodb支持--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional><version>${lombok.version}</version></dependency><dependency><groupId>us.codecraft</groupId><artifactId>webmagic-core</artifactId><version>0.7.3</version></dependency><dependency><groupId>us.codecraft</groupId><artifactId>webmagic-extension</artifactId><version>0.7.3</version></dependency></dependencies><build><plugins><!-- java編譯插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.2</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build></project>實現(xiàn)service:
package cn.itcast.baidumap.service;import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.NumberUtil; import cn.itcast.baidumap.pojo.BusinessCircle; import cn.itcast.baidumap.pojo.Community; import cn.itcast.baidumap.pojo.District; import cn.itcast.baidumap.pojo.House; import cn.itcast.baidumap.vo.HouseResultVo; import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.geo.Box; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.*; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Service;import java.util.ArrayList; import java.util.Collections; import java.util.List;/*** @Description:* @Author : Jerry* @create : 2022-07-19 18:50*/ @Service public class HouseSearchService {@Autowiredprivate MongoTemplate mongoTemplate;/*** 地圖找房搜索服務* @param maxLongitude 最大經(jīng)度* @param minLongitude 最小經(jīng)度* @param maxLatitude 最大緯度* @param minLatitude 最小緯度* @param zoom 地圖縮放比例值* @return*/public List<HouseResultVo> search(Double maxLongitude,Double minLongitude,Double maxLatitude,Double minLatitude,Double zoom) {//收集聚合查詢條件List<AggregationOperation> operationList = new ArrayList<>();//在可視范圍內(nèi)搜索Box box = new Box(new double[]{maxLongitude,maxLongitude},new double[]{minLongitude,minLatitude});MatchOperation matchOperation = Aggregation.match(Criteria.where("location").within(box));operationList.add(matchOperation);int type;GroupOperation groupOperation;//根據(jù)地圖得縮放比例進行分組if(zoom < 13.5){ //2公里以上//按照行政區(qū)分組groupOperation = Aggregation.group("districtCode");type = 1;}else if(zoom < 15.5){ //200米以上//按照商圈分組groupOperation = Aggregation.group("businessCircleCode");type = 2;}else{ //200米以下//按照小區(qū)分組groupOperation = Aggregation.group("communityId");type = 3;}groupOperation = groupOperation.count().as("total").avg("price").as("price");operationList.add(groupOperation);//生成最終的聚合條件Aggregation aggregation = Aggregation.newAggregation(operationList);//執(zhí)行查詢AggregationResults<HouseResultVo> aggregationResults = this.mongoTemplate.aggregate(aggregation, House.class, HouseResultVo.class);List<HouseResultVo> houseResultVoList = aggregationResults.getMappedResults();if(CollUtil.isEmpty(houseResultVoList)){return Collections.emptyList();}//填充數(shù)據(jù)switch (type){case 1:{//查詢行政區(qū)數(shù)據(jù)for(HouseResultVo houseResultVo:houseResultVoList){District district = this.queryDistrictByCode(Convert.toInt(houseResultVo.getCode()));houseResultVo.setName(district.getName());houseResultVo.setLongitude(district.getLocation().getX());houseResultVo.setLatitude(district.getLocation().getY());//價格保留兩位小數(shù)houseResultVo.setPrice(NumberUtil.roundStr(houseResultVo.getPrice(),2));}break;}case 2:{//查詢商圈數(shù)據(jù)for(HouseResultVo houseResultVo:houseResultVoList){BusinessCircle businessCircle = this.queryBusinessCircleByCode(Convert.toInt(houseResultVo.getCode()));houseResultVo.setName(businessCircle.getName());houseResultVo.setLongitude(businessCircle.getLocation().getX());houseResultVo.setLatitude(businessCircle.getLocation().getY());//價格保留兩位小數(shù)houseResultVo.setPrice(NumberUtil.roundStr(houseResultVo.getPrice(),2));}break;}case 3:{//查詢商圈數(shù)據(jù)for(HouseResultVo houseResultVo:houseResultVoList){Community community = this.queryCommunityById(new ObjectId(houseResultVo.getCode()));houseResultVo.setName(community.getName());houseResultVo.setLongitude(community.getLocation().getX());houseResultVo.setLatitude(community.getLocation().getY());//價格保留兩位小數(shù)houseResultVo.setPrice(NumberUtil.roundStr(houseResultVo.getPrice(),2));}break;}default: return Collections.emptyList();}return houseResultVoList;}/*** 根據(jù)code查詢行政區(qū)數(shù)據(jù)* @param code* @return*/private District queryDistrictByCode(Integer code){Query query = Query.query(Criteria.where("code").is(code));return this.mongoTemplate.findOne(query,District.class);}/*** 根據(jù)code查詢商圈數(shù)據(jù)* @param code* @return*/private BusinessCircle queryBusinessCircleByCode(Integer code){Query query = Query.query(Criteria.where("code").is(code));return this.mongoTemplate.findOne(query,BusinessCircle.class);}/*** 根據(jù)code查詢小區(qū)數(shù)據(jù)* @param id* @return*/private Community queryCommunityById(ObjectId id){return this.mongoTemplate.findById(id,Community.class);}}controller:
package cn.itcast.baidumap.controller;import cn.itcast.baidumap.service.HouseSearchService; import cn.itcast.baidumap.vo.HouseResultVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;import java.util.List;/*** @Description:* @Author : Jerry* @create : 2022-07-19 18:40*/ @RequestMapping("house/search") @RestController public class HouseSearchController {@Autowiredprivate HouseSearchService houseSearchService;@GetMappingpublic List<HouseResultVo> search(@RequestParam("maxLongitude") Double maxLongitude,@RequestParam("minLongitude") Double minLongitude,@RequestParam("maxLatitude") Double maxLatitude,@RequestParam("minLatitude") Double minLatitude,@RequestParam("zoom") Double zoom){return this.houseSearchService.search(maxLongitude,minLongitude,maxLatitude,minLatitude,zoom);}}實現(xiàn)效果:根據(jù)縮放的程度,顯示不同的尋房的信息。
?
?
總結
 
                            
                        