Cesium调用高德地图服务实现搜索地点定位详解
程序员文章站
2022-05-15 17:16:03
...
一、需求分析
需要做一个类似于高德地图的搜索可以参考高德地图,用户输入地点,下拉列表自动弹出少量的相应地点,点击内容地点可以直接选择定位并且添加Cesium的广告牌(图标)和标注,点击标注可以弹出详细信息,点击搜索则通过输入地点查找出全部内容并且显示图片,搜索到的内容因为较多,所以内容增加分页功能。
二、后台接口SpringBoot+高德地图Web服务
高德服务地址:https://lbs.amap.com/api/webservice/summary/
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.输入地点下拉列表
2.点击内容定位
3.点击搜索详细内容
4.详细内容分页
5.点击标注弹出信息