欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Cesium调用高德地图服务实现搜索地点定位详解

程序员文章站 2022-05-15 17:16:03
...

一、需求分析

需要做一个类似于高德地图的搜索可以参考高德地图,用户输入地点,下拉列表自动弹出少量的相应地点,点击内容地点可以直接选择定位并且添加Cesium的广告牌(图标)和标注,点击标注可以弹出详细信息,点击搜索则通过输入地点查找出全部内容并且显示图片,搜索到的内容因为较多,所以内容增加分页功能。

二、后台接口SpringBoot+高德地图Web服务

高德服务地址:https://lbs.amap.com/api/webservice/summary/

用的高德搜索POI输入提示

Cesium调用高德地图服务实现搜索地点定位详解

Controller

	/**
	 * 查询输入位置数据
	 * @param id
	 * @return
	 * @author ygc
	 * @throws IOException 
	 */
	@RequestMapping("/queryInputPositionData")
	@SecurityParameter
	@ResponseBody
	@Transactional(propagation=Propagation.SUPPORTS)
	public Result  queryInputPositionData(@RequestBody RequestInfo requestInfo,HttpServletResponse response) throws IOException {	
		response.setContentType("text/html;charset=UTF-8");   
	    response.setHeader("Content-type", "application/json;charset=UTF-8");
	    if(RedisPool.checkToken(requestInfo.getToken())==false){
	    	return RedisTool.msgResult();
	    }
		Map<?, ?> map= (Map<?, ?>) requestInfo.getData();
		String position=(String)map.get("position");
		Result temp=sysuserService.queryInputPositionDataByPosition(position,requestInfo.getUid());
		return temp; 
	}
	/**
	 * 查询输入搜索数据
	 * @param id
	 * @return
	 * @author ygc
	 * @throws IOException 
	 */
	@RequestMapping("/querySearchPositionData")
	@SecurityParameter
	@ResponseBody
	@Transactional(propagation=Propagation.SUPPORTS)
	public Result  querySearchPositionData(@RequestBody RequestInfo requestInfo,HttpServletResponse response) throws IOException {	
		response.setContentType("text/html;charset=UTF-8");   
	    response.setHeader("Content-type", "application/json;charset=UTF-8");
	    if(RedisPool.checkToken(requestInfo.getToken())==false){
	    	return RedisTool.msgResult();
	    }
		Map<?, ?> map= (Map<?, ?>) requestInfo.getData();
		String position=(String)map.get("position");
		Integer currentPage=(Integer)map.get("currentPage");
		Result temp=sysuserService.querySearchPositionDataByPosition(position,requestInfo.getUid(),currentPage);
		return temp; 
	}

Service

	public Result queryInputPositionDataByPosition(String position, String uid) {
		   String key="68c16866958532b7b092f23a52ff6862";
		   BufferedReader in = null;  
	        String url="https://restapi.amap.com/v3/assistant/inputtips?output=json&keywords="+position+"&key="+key;
	        try {  
			URL Url = new URL(url); 
	            // 打开和URL之间的连接  
	            URLConnection connection = Url.openConnection();  
	            // 设置通用的请求属性  
	            connection.setConnectTimeout(5000);  
	            connection.setReadTimeout(5000);  
	            // 建立实际的连接  
	            connection.connect();  
	            // 定义 BufferedReader输入流来读取URL的响应  
	            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));  
	            StringBuffer sb = new StringBuffer();  
	            String line;  
	            while ((line = in.readLine()) != null) {  
	                sb.append(line);  
	            }  
	    		JSONObject userJson = JSONObject.parseObject(sb.toString());
				com.alibaba.fastjson.JSONArray tips = userJson.getJSONArray("tips");


//	    		String tips = (String) userJson.get("tips");
//	            System.out.println(sb.toString()); 
	     	    Result result=new Result();
	            result.setUid(uid);
	            result.setCode(200); 
			    result.setData(tips);   
			    result.setResult("成功"); 
			    result.setMessage("查询输入位置数据"); 
			    return result;
	        } catch (Exception e1) { 
	        	e1.printStackTrace();
	        	throw new RuntimeException(e1);	  
	        }  
	        // 使用finally块来关闭输入流  
	        finally {  
	            try {  
	                if (in != null) {  
	                    in.close();  
	                }  
	            } catch (Exception e2) {  
	                e2.printStackTrace();  
	            }  
	        }

	}

	public Result querySearchPositionDataByPosition(String position, String uid, Integer currentPage) {
		   String key="68c16866958532b7b092f23a52ff6862";
		   BufferedReader in = null;  
		   int pageSize=10;
		   String url="https://restapi.amap.com/v3/place/text?keywords="+position+"&output=json&offset="+pageSize+"&page="+currentPage+"&key="+key+"&extensions=all";
//	        String url="https://restapi.amap.com/v3/assistant/inputtips?output=json&keywords="+position+"&key="+key;
	        try {  
			URL Url = new URL(url); 
	            // 打开和URL之间的连接  
	            URLConnection connection = Url.openConnection();  
	            // 设置通用的请求属性  
	            connection.setConnectTimeout(5000);  
	            connection.setReadTimeout(5000);  
	            // 建立实际的连接  
	            connection.connect();  
	            // 定义 BufferedReader输入流来读取URL的响应  
	            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));  
	            StringBuffer sb = new StringBuffer();  
	            String line;  
	            while ((line = in.readLine()) != null) {  
	                sb.append(line);  
	            }  
	    		JSONObject userJson = JSONObject.parseObject(sb.toString());
	    		System.out.println(userJson);
	    		JSONObject json=new JSONObject();
	    		if(currentPage==1) {
		    		Integer resultCount=Integer.valueOf(userJson.get("count").toString());
					int pageCount=(resultCount%pageSize==0)?resultCount/pageSize:(resultCount/pageSize+1);	
		    		System.out.println("resultCount:"+resultCount);
					System.out.println("pageCount:"+pageCount);
					json.put("pageCount", pageCount);
					json.put("resultCount",resultCount);
	    		}
				com.alibaba.fastjson.JSONArray pois = userJson.getJSONArray("pois");
				System.out.println("pageIndex:"+currentPage);
//	    		String tips = (String) userJson.get("tips");
//	            System.out.println(sb.toString()); 
			
				json.put("pageIndex", currentPage);
				json.put("pageSize", pageSize);
//				pois.add(json);
//				System.out.println(pois);
	     	    Result result=new Result();
	            result.setUid(uid);
	            result.setCode(200); 
			    result.setData(pois);   
			    result.setResult(json.toString()); 
			    result.setMessage("查询输入位置数据"); 
			    return result;
	        } catch (Exception e1) { 
	        	e1.printStackTrace();
	        	throw new RuntimeException(e1);	  
	        }  
	        // 使用finally块来关闭输入流  
	        finally {  
	            try {  
	                if (in != null) {  
	                    in.close();  
	                }  
	            } catch (Exception e2) {  
	                e2.printStackTrace();  
	            }  
	        }

	}

三、前端实现React+Antd+Dva+Cesium

Html+antd用的AutoComplete自动完成插件

import React, {Component} from "react";
import { Menu, Dropdown,Divider,Button } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import DataSource from '@/assets/svg/DataSource.js'
import Measure from '@/assets/svg/Measure.js'
import Plot from '@/assets/svg/Plot.js'
import Remove from '@/assets/svg/Remove.js'
import { Input, AutoComplete,Icon,Select,Pagination  } from 'antd';
import { SelectProps } from 'antd/es/select';
import styles from '@/components/search/SearchPosition.css';
import config from '../../../public/config';
import { connect } from 'react-redux';
import * as Cesium from 'cesium';
import { removesDuplicates } from '@/components/utils/CommonTool';
const { Option, OptGroup } = AutoComplete;
/**
 * @author 于公成
 * @date 2020/6/30
 * @Description: 搜索位置
 */
let flag=this
class SearchPosition extends  React.Component{

  constructor(props) {
    super(props);

    this.state = {
      dataSource:[],
      data:{
        position:undefined
      },
      status:false,
      pageIndex:1,
      pageCount:1,
      resultCount:undefined
    };

  }

  componentDidMount(){

  }
  renderOption=(item)=>{
    let str=[];
    if(item.phones!=null){
      for(let i=0;i<item.phones.length;i++){
        if(item.phones[i]!=null){
            str.push(<img style={{width:'70px',height:'70px'}} src={item.phones[i].url}/>);
        }
      }
      return (

          <Option  key={item.name+item.id} position={item.location} text={item.name}>
            <div className="global-search-item">
            <span style={{color:'#999999',fontSize:'3px',float:'right'}}>{str.map((item)=>
                <span>
              {item}
            </span>
            )}</span>{item.name}<br/>{item.district}
            </div>
          </Option>

      );
    }else{
      return (
        <Option key={item.name+item.id} position={item.location} text={item.name}>
          <div className="global-search-item">
            {item.name}<br/><span style={{color:'#999999',fontSize:'3px'}}>{item.district}{str}</span>
          </div>
        </Option>


      );

    }

  }
   onSelect=(value,option)=> {
      let flag=this;
      let position=option.props.position;
      let lon=position.split(",")[0];
      let lat=position.split(",")[1];
      let viewer=  this.props.viewer.ceViewer;
     viewer.entities.removeById(1)
     viewer.entities.add({
       id:1,
       name : 'test',
       position : Cesium.Cartesian3.fromDegrees(Number(lon),Number(lat), 0),
       color : Cesium.Color.fromCssColorString('#000000'),

       //圆
      //  ellipse: {
      //    semiMinorAxis: 50,
      //    semiMajorAxis: 50,
      //    height: 10,
      //    outlineColor: Cesium.Color.RED,
      //    zIndex:0,
      //    //rotation : Cesium.Math.toRadians(45),//旋转角
      //    material: new Cesium.ImageMaterialProperty({
      //      image: '../img/标记.png\',',
      //      // //指定图像在每个方向上重复的次数,默认为Cesium.Cartesian2(1.0, 1.0),{Cartesian2}类型
      //      // repeat: Cesium.Cartesian2(1.0, 1.0),
      //      // // 默认为false,当图像具有透明性时设置为true(例如,当png具有透明部分时)
      //      // transparent: true,
      //      // color: Cesium.Color.WHITE.withAlpha(0.5),  //透明度0.5
      //    })
      //  },
       //点样式
       // point : {
       //   pixelSize : 25,
       //   color : Cesium.Color.RED,
       //   outlineColor : Cesium.Color.WHITE,
       //   // outlineWidth : 5
       // },
       //立广告牌
       billboard :{
         image:'../img/标记.png',
           show : true, // default
           width : 50, // default: undefined
           height : 50 // default: undefined
       },
       //字体标签样式
       label : {
         // text : option.props.text,
         text : option.props.text,
         font : '14pt',
         // color : Cesium.Color.BLUE,
         style: Cesium.LabelStyle.FILL_AND_OUTLINE,
         outlineWidth : 200,
         //垂直位置
         // verticalOrigin : Cesium.VerticalOrigin.BUTTON,
         //中心位置
         // pixelOffset : new Cesium.Cartesian2(0,0),
         eyeOffset: new Cesium.Cartesian3(0, 0, -10),

       }
     });


     // viewer. entities.removeById(1)
     // let pinBuilder = new Cesium.PinBuilder();
     // let bluePin = viewer.entities.add({
     //   id:'1',
     //   name: "test",
     //   position: Cesium.Cartesian3.fromDegrees(Number(lon),Number(lat), 0),
     //   billboard: {
     //     image:'../img/标记.png',
     //     // image: pinBuilder.fromColor(Cesium.Color.ROYALBLUE, 48).toDataURL(),
     //     verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
     //   },
     // });
     // Cesium.when.all(
     //   [bluePin],
     //   (pins)=> {
     //     viewer.zoomTo(pins);
     //   }
     // );
     // let loactionEntity;
     // if(loactionEntity)
     // viewer.entities.remove(loactionEntity);
     //  loactionEntity = new Cesium.Entity({
     //   id : 'flyTmp',
     //   position : Cesium.Cartesian3.fromDegrees(Number(lon),Number(lat), 2000.0),
     //   point : {
     //     pixelSize : 10,
     //     color : Cesium.Color.WHITE.withAlpha(0.9),
     //     outlineColor : Cesium.Color.WHITE.withAlpha(0.9),
     //     outlineWidth : 1
     //   }
     // });
     // viewer.entities.add(loactionEntity);
     // viewer.camera.flyTo(loactionEntity,{

     viewer.camera.flyTo({
     // viewer.flyTo(loactionEntity,{
        destination : Cesium.Cartesian3.fromDegrees(Number(lon),Number(lat), 5000.0),
        // orientation : {
        //   heading : Cesium.Math.toRadians(0.0),
        //   pitch : Cesium.Math.toRadians(-25.0),
        //   roll : 0.0
        // }
      });
    //添加点击事件
     let handler =new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
     handler.setInputAction(function (movement) {
       let pick = viewer.scene.pick(movement.position);
       // if (Cesium.defined(pick) && (pick.id.id === cid)) {
       if (Cesium.defined(pick)) {
         // window.open('www.baidu.com');
         flag.markWin(null,position);
       }
     }, Cesium.ScreenSpaceEventType.LEFT_CLICK);



    // console.log('onSelect', value,option);
    //  let position=option.props.position;
    //  let lon=position.split(",")[0];
    //  let lat=position.split(",")[1];
    //  let viewer=  this.props.viewer.ceViewer;
    //  viewer.camera.flyTo({
    //    destination : Cesium.Cartesian3.fromDegrees(Number(lon),Number(lat), 5000.0),
    //    // orientation : {
    //    //   heading : Cesium.Math.toRadians(0.0),
    //    //   pitch : Cesium.Math.toRadians(-25.0),
    //    //   roll : 0.0
    //    // }
    //  });

  }
  markWin=(e,position)=>{
    alert("当前坐标:"+position)
  }
  //查询输入位置
  handleSearch=(position)=> {
    let data=this.state.data;
    data.position=position
    this.setState({
      data:data
    });
    this.props.dispatch(
      {
        type:"query/querySecret",
        payload: {
          url:config.dataUrl+ "/userinfo/queryInputPositionData",
          data: {
            position:position
          }
        },
        callback:(result)=>{
          if(result.code==200){
            let list=[];
            // let list2=removesDuplicates(result.data,result.data.name);
            result.data.map((item)=>{
              if(item.location[0]!=null ){
                list.push(
                  {
                    name:item.name,
                    address:item.address,
                    location:item.location,
                    district:item.district,
                    id:item.id
                  }
                );
              }
            })
             let position=this.state.data.position;
            this.setState({
              dataSource:list,
              status:false,

            });
          }
        }
      }
    )

  };

  //点击搜索地点
  SearchPosition=(query)=>{

    let data=this.state.data;
    let position=data.position
    console.log(query)
    this.props.dispatch(
      {
        type:"query/querySecret",
        payload: {
          url:config.dataUrl+ "/userinfo/querySearchPositionData",
          data: {
            position:position,
            currentPage:1
          }
        },
        callback:(result)=>{
          if(result.code==200){

            let list=[];
            // let list2=removesDuplicates(result.data,result.data.name);
            result.data.map((item)=>{
              if(item.location[0]!=null ){
                list.push(
                  {
                    name:item.name,
                    address:item.address,
                    location:item.location,
                    district:item.cityname+item.adname,
                    id:item.id,
                    phones:item.photos
                  }
                );
              }
            })
            let position=this.state.data.position;
            let pageData=JSON.parse(result.result);
            this.setState({
              dataSource:list,
              status:true,
              pageIndex:1,
              pageCount:pageData.pageCount,
              resultCount:pageData.resultCount
            });
          }
        }
      }
    )

  }
  InputText=(info)=>{
      console.log(info)
  }
  onChange=(info)=>{
    console.log(info)
  }
  changePage=(type,info)=>{
    let data=this.state.data;
    let position=data.position

    let flag=this;
    let page=this.state.pageIndex;
    if(type=="indexPage"){

      this.props.dispatch(
        {
          type:"query/querySecret",
          payload: {
            url:config.dataUrl+ "/userinfo/querySearchPositionData",
            data: {
              position:position,
              currentPage:1
            }
          },
          callback:(result)=>{
            if(result.code==200){
              let list=[];
              // let list2=removesDuplicates(result.data,result.data.name);
              result.data.map((item)=>{
                if(item.location[0]!=null ){
                  list.push(
                    {
                      name:item.name,
                      address:item.address,
                      location:item.location,
                      district:item.cityname+item.adname,
                      id:item.id,
                      phones:item.photos
                    }
                  );
                }
              })
              let position=this.state.data.position;
              this.setState({
                dataSource:list,
                status:true,
                pageIndex:1
              });
            }
          }
        }
      )
    }
    if(type=="upPage"){
      page=page-1;
      flag.props.dispatch(
        {
          type:"query/querySecret",
          payload: {
            url:config.dataUrl+ "/userinfo/querySearchPositionData",
            data: {
              position:position,
              currentPage:page
            }
          },
          callback:(result)=>{
            if(result.code==200){
              let list=[];
              // let list2=removesDuplicates(result.data,result.data.name);
              result.data.map((item)=>{
                if(item.location[0]!=null ){
                  list.push(
                    {
                      name:item.name,
                      address:item.address,
                      location:item.location,
                      district:item.cityname+item.adname,
                      id:item.id,
                      phones:item.photos
                    }
                  );
                }
              })
              let position=this.state.data.position;
              flag.setState({
                dataSource:list,
                status:true,
                pageIndex:page
              });

              // flag.setState({
              //   currentPage:page-1
              // })
            }
          }
        }
      )
    }
    if(type=="downPage"){
      page=page+1;
      flag.props.dispatch(
        {
          type:"query/querySecret",
          payload: {
            url:config.dataUrl+ "/userinfo/querySearchPositionData",
            data: {
              position:position,
              currentPage:page
            }
          },
          callback:(result)=>{
            if(result.code==200){
              let list=[];
              // let list2=removesDuplicates(result.data,result.data.name);
              result.data.map((item)=>{
                if(item.location[0]!=null ){
                  list.push(
                    {
                      name:item.name,
                      address:item.address,
                      location:item.location,
                      district:item.cityname+item.adname,
                      id:item.id,
                      phones:item.photos
                    }
                  );
                }
              })
              let position=this.state.data.position;
              flag.setState({
                dataSource:list,
                status:true,
                pageIndex:page
              });
              // flag.setState({
              //   currentPage:page+1
              // })
            }
          }
        }
      )
    }

  }

  render(){
    let dataSource=this.state.dataSource;
    console.log("dataSource:"+dataSource)
    let result;
    if(dataSource[0]!=undefined){
      result=dataSource.map(this.renderOption).concat([
        <Option    disabled key="all" className={this.state.status==false?styles.hide:styles.show}>
          <div>
            <span  onClick={this.changePage.bind(this,"indexPage")}>共找到{this.state.resultCount}条结果</span>
            <span style={{marginLeft:'70px'}}>{this.state.pageIndex}/{this.state.pageCount}页</span>
            <span style={{marginLeft:'20px',cursor:'pointer',borderStyle:'solid',borderWidth:'1px',width:20,height:20}} onClick={this.changePage.bind(this,"indexPage")}>首页</span>
            <span style={{cursor:'pointer',borderStyle:'solid',borderWidth:'1px',width:20,height:20}}  className={this.state.pageIndex==1?styles.hideInput:styles.showInput} onClick={this.changePage.bind(this,"upPage")}>上一页</span>
            <span style={{cursor:'pointer',borderStyle:'solid',borderWidth:'1px',width:20,height:20}} className={this.state.pageIndex==this.state.pageCount?styles.hideInput:styles.showInput} onClick={this.changePage.bind(this,"downPage")}>下一页</span>
          </div>
        </Option>,
      ]);;
      console.log(result)
    }

    // const options = dataSource
    //   .map(group => (
    //     <OptGroup key={group.title}>
    //       {group.children.map(opt => (
    //         <Option key={opt.title} value={opt.title}>
    //           {opt.title}
    //         </Option>
    //       ))}
    //     </OptGroup>
    //   ))
    //   .concat([
    //     <Option disabled key="all" className="show-all">
    //       <div onClick={this.changePage}>
    //         首页
    //       </div>
    //     </Option>,
    //   ]);
    return(
      <div className={styles.Search}>
        <div className="global-search-wrapper" style={{ width: 400 }}>
          <AutoComplete
            className="global-search"
            size="large"
            style={{ width: '100%' }}
            // dataSource={options}
            dataSource={result}
            // dataSource={this.state.dataSource==undefined?null:this.state.dataSource.map(this.renderOption)}
            onChange={this.onChange}
            onSelect={this.onSelect}
            onSearch={this.handleSearch}
            placeholder="请输入地名"
            optionLabelProp="text"
          >
            <Input    allowClear={true}     onChange={this.InputText} suffix={
                <Button
                  className="search-btn"
                  style={{ marginRight: -12 }}
                  size="large"
                  type="primary"
                  onClick={this.SearchPosition.bind(this)}
                >
                  <Icon type="search"     />
                </Button>
              }
            />
          </AutoComplete>
          {/*<div style={{backgroundColor:'white',width:500,height:200,position:'absolute',zIndex:200,top:'295px'}}>*/}
          {/*  <Pagination defaultCurrent={6} total={500}   />*/}
          {/*</div>*/}
        </div>

        <div id="cesiumContainer">
          <div style={{
            position: "absolute",
            width: "100px",
            height: "100px",
            zIndex: 1000,
            display: "none",
            background: "rgba(255, 255, 255, 0.5)",
            border: "2px solid greenyellow",
            borderRadius: "4px"
          }}></div>
        </div>
      </div>
    )
  }
}
function mapStateToProps(state) {
  return {
    verify:state.verify,
    user:state.user,
    viewer:state.viewer,
  };
}
export default connect(mapStateToProps)(SearchPosition);

四、实现效果

1.输入地点下拉列表

Cesium调用高德地图服务实现搜索地点定位详解

2.点击内容定位

Cesium调用高德地图服务实现搜索地点定位详解

3.点击搜索详细内容

Cesium调用高德地图服务实现搜索地点定位详解

4.详细内容分页

Cesium调用高德地图服务实现搜索地点定位详解

5.点击标注弹出信息

Cesium调用高德地图服务实现搜索地点定位详解