You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
410 lines
14 KiB
410 lines
14 KiB
import React, { useEffect, useState } from 'react';
|
|
import { connect } from 'react-redux';
|
|
import { render } from 'react-dom';
|
|
import { message, Tooltip } from 'antd';
|
|
import FireAddForm from '../components/alarm-add'
|
|
import LoacationSelect from '../components/location-select';
|
|
import moment from 'moment'
|
|
import './gis.less'
|
|
const MAPDOMID = 'fs-amap-container-fire';
|
|
let map = null;
|
|
|
|
const tabs = [
|
|
{ name: '应急物资', tab: 'xfyjwz', className: 'emergency_button' },
|
|
{ name: '消防救援', tab: 'xfjy', className: 'emergency_button' },
|
|
{ name: '人民武装部', tab: 'rmwzb', className: 'emergency_button' },
|
|
{ name: '医疗救援', tab: 'yljy', className: 'emergency_button' },
|
|
{ name: '应急避难场所', tab: 'yjbns', className: 'emergency_button' },
|
|
]
|
|
|
|
function Map(props) {
|
|
const { dispatch, actions, emergencyList, propTab, setLongitudeLatitude } = props;
|
|
const [delay, setDelay] = useState(true)
|
|
const [tab, setTab] = useState('xfyjwz')
|
|
const [visible, setVisible] = useState(false)
|
|
const [level, setLevel] = useState('I')
|
|
const [alarmData, setAlarmData] = useState()
|
|
const [locationVisible, setLocationVisible] = useState(false)
|
|
const [location, setLocation] = useState({})
|
|
const [alarmList, setAlarmList] = useState()
|
|
|
|
//查询告警列表数据
|
|
const getData = () => {
|
|
dispatch(actions?.firecontrol?.getFireAlarmList()).then(res => {
|
|
if (res?.payload?.data) {
|
|
setAlarmList(res?.payload?.data)
|
|
}
|
|
})
|
|
}
|
|
|
|
// 初始化GIS 组件销毁清空定时器
|
|
useEffect(() => {
|
|
loadMap();
|
|
getData()
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (alarmData) renderAlarmInfowindow(alarmData)
|
|
}, [level])
|
|
|
|
|
|
useEffect(() => {
|
|
if (propTab == 'overview') getData()
|
|
}, [propTab])
|
|
|
|
const setMapInitFit = () => {
|
|
map.setZoom(10.3)
|
|
map.setCenter([116.054664, 28.538966])
|
|
map.setPitch(22.9)
|
|
map.setRotation(0)
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!delay && (alarmList?.length > 0 || emergencyList['xfjy'])) {
|
|
map.clearMap()
|
|
map && renderMarkers()
|
|
map && renderAlarms()
|
|
setMapInitFit()
|
|
}
|
|
}, [delay, alarmList, emergencyList, propTab])
|
|
|
|
const renderMarkers = () => {
|
|
const data = emergencyList['xfjy']
|
|
|
|
data?.map((x, index) => {
|
|
var marker = new AMap.Marker({
|
|
position: new AMap.LngLat(x.location?.split(',')[0], x.location?.split(',')[1]),
|
|
// 将一张图片的地址设置为 icon
|
|
icon: '/assets/images/homepage/fire/rescue_icon.png',
|
|
// 设置了 icon 以后,设置 icon 的偏移量,以 icon 的 [center bottom] 为原点
|
|
offset: new AMap.Pixel(-13, -30),
|
|
zooms: [3, 19],
|
|
zIndex: 12,
|
|
});
|
|
marker.setTitle(x.teamName);
|
|
map.add(marker);
|
|
marker.setLabel({
|
|
zIndex: 13,
|
|
direction: 'right',
|
|
offset: new AMap.Pixel(-90, -40), //设置文本标注偏移量
|
|
content: "<div class='rescu_team_marker'>" + x.teamName + "</div>", //设置文本标注内容
|
|
});
|
|
})
|
|
|
|
}
|
|
|
|
// 地图初始化
|
|
const loadMap = () => {
|
|
// 图片图层 实现瓦片地图中国地图样式 bounds 第一个点为左下角 第二个点为右上角
|
|
const imageLayer = new AMap.ImageLayer({
|
|
url: '/assets/images/map.svg',
|
|
bounds: new AMap.Bounds(
|
|
[115.800221, 28.265659],
|
|
[116.334849, 28.973298],
|
|
),
|
|
zooms: [3, 14],
|
|
});
|
|
|
|
map = new AMap.Map(MAPDOMID, {
|
|
center: [116.054664, 28.538966],
|
|
zoomEnable: true,
|
|
dragEnable: true,
|
|
viewMode: '3D',
|
|
pitch: 22.9,
|
|
labelzIndex: 130,
|
|
zoom: 10.3,
|
|
cursor: 'pointer',
|
|
mapStyle: 'amap://styles/4eb48d1ef0a024c73376fd2256d0b5a5',
|
|
layers: [
|
|
AMap.createDefaultLayer(),
|
|
imageLayer,
|
|
],
|
|
});
|
|
|
|
|
|
map.on('complete', () => {
|
|
// map.setZoom(17.4)
|
|
// map.setCenter([115.922069, 28.554867])
|
|
// map.setPitch(47.30)
|
|
// map.setRotation(1.7000)
|
|
setTimeout(() => {
|
|
setDelay(false)
|
|
}, 1000);
|
|
|
|
});
|
|
|
|
map.on('click', (e) => {
|
|
if (!e && !e.lnglat) {
|
|
return
|
|
}
|
|
|
|
const zoom = map.getZoom();
|
|
const pitch = map.getPitch();
|
|
const center = map.getCenter();
|
|
const Rotation = map.getRotation();
|
|
console.log('zoom' + zoom)
|
|
console.log('pitch' + pitch)
|
|
console.log('center' + center)
|
|
console.log('Rotation' + Rotation)
|
|
console.log('e.lnglat' + e.lnglat)
|
|
})
|
|
};
|
|
|
|
const renderAlarms = () => {
|
|
alarmList
|
|
?.filter(s => s.state == 1)
|
|
?.map((x, index) => {
|
|
var marker = new AMap.Marker({
|
|
position: new AMap.LngLat(x.longitude, x.latitude),
|
|
// 将一张图片的地址设置为 icon
|
|
icon: '/assets/images/homepage/communtity/markeralarm.png',
|
|
// 设置了 icon 以后,设置 icon 的偏移量,以 icon 的 [center bottom] 为原点
|
|
offset: new AMap.Pixel(-13, -30),
|
|
zooms: [3, 19],
|
|
zIndex: 14,
|
|
});
|
|
marker.setTitle(x.location);
|
|
map.add(marker);
|
|
|
|
// var circle = new AMap.Circle({
|
|
// center: [x.longitude, x.latitude],
|
|
// radius: 5000, //半径
|
|
// borderWeight: 3,
|
|
// strokeColor: "#AE0000",
|
|
// strokeOpacity: 1,
|
|
// strokeWeight: 6,
|
|
// strokeOpacity: 0.8,
|
|
// fillOpacity: 0.2,
|
|
// // strokeStyle: 'dashed',
|
|
// strokeDasharray: [10, 10],
|
|
// // 线样式还支持 'dashed'
|
|
// fillColor: 'rgba(243, 0, 0, 0.15)',
|
|
// zIndex: 50,
|
|
// })
|
|
|
|
// map.add(circle);
|
|
|
|
let infowindow = new AMap.InfoWindow({
|
|
isCustom: true, //使用自定义窗体
|
|
content: `<div id="map-content" class="fire-gis-infowindow fire-gis-infowindow-alarm">
|
|
<div style="height:${360}px;" id="alarmcontentid${x.location}"></div></div>`,
|
|
offset: new AMap.Pixel(233, 260)
|
|
});
|
|
|
|
marker.on('click', () => {
|
|
let position = marker.getPosition ? marker.getPosition() : marker.getCenter();
|
|
infowindow.open(map, position);
|
|
map.setCenter(position)
|
|
// map.setZoom(17.4)
|
|
// map.setPitch(47.30)
|
|
// map.setRotation(1.7000)
|
|
setAlarmData(x)
|
|
setTimeout(() => {
|
|
if (document.getElementById(`alarmcontentid${x.location}`)) {
|
|
renderAlarmInfowindow(x)
|
|
}
|
|
}, 50)
|
|
setLongitudeLatitude({ longitude: x.longitude, latitude: x.latitude })
|
|
})
|
|
})
|
|
}
|
|
|
|
//驾车路线规划
|
|
const drawDrivings = (location1, location2, alarmInfo, rescueInfo) => {
|
|
var driving = new AMap.Driving({});
|
|
// 根据起终点经纬度规划驾车导航路线
|
|
driving.search(new AMap.LngLat(location1[0], location1[1]), new AMap.LngLat(location2[0], location2[1]), function (status, result) {
|
|
// result 即是对应的驾车导航信息,相关数据结构文档请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingResult
|
|
if (status === 'complete') {
|
|
if (result.routes && result.routes.length) {
|
|
// 绘制第一条路线,也可以按需求绘制其它几条路线
|
|
drawRoute(result.routes[0])
|
|
props.routeCallback({ routes: result.routes[0] })
|
|
|
|
}
|
|
} else {
|
|
}
|
|
});
|
|
// driving.clear() //清除路线
|
|
}
|
|
|
|
function drawRoute(route) {
|
|
var path = parseRouteToPath(route)
|
|
|
|
var routeLine = new AMap.Polyline({
|
|
path: path,
|
|
isOutline: true,
|
|
outlineColor: 'rgba(118, 38, 63, 1)',
|
|
borderWeight: 2,
|
|
strokeWeight: 5,
|
|
strokeOpacity: 0.9,
|
|
strokeColor: 'rgba(118, 38, 63, 1)',
|
|
lineJoin: 'round'
|
|
})
|
|
|
|
map.add(routeLine);
|
|
|
|
// 调整视野达到最佳显示区域
|
|
map.setFitView([routeLine], false, [150, 150, 400, 400])
|
|
// setTimeout(() => {
|
|
// map.setZoom(map.getZoom() - 1)
|
|
// }, 1000);
|
|
|
|
}
|
|
|
|
//计算两点距离
|
|
function computeDis(p1, p2) {
|
|
return Math.round(p1.distance(p2));
|
|
}
|
|
|
|
// 解析DrivingRoute对象,构造成AMap.Polyline的path参数需要的格式
|
|
// DrivingResult对象结构参考文档 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DriveRoute
|
|
function parseRouteToPath(route) {
|
|
var path = []
|
|
for (var i = 0, l = route.steps.length; i < l; i++) {
|
|
var step = route.steps[i]
|
|
|
|
for (var j = 0, n = step.path.length; j < n; j++) {
|
|
path.push(step.path[j])
|
|
}
|
|
}
|
|
return path
|
|
}
|
|
|
|
const renderAlarmInfowindow = (x) => {
|
|
return x && render(<div>
|
|
<div className='gis_exit' onClick={() => {
|
|
map.setCenter([116.054664, 28.538966])
|
|
map.setZoom(10.3)
|
|
map.setPitch(22.9)
|
|
map.setRotation(0)
|
|
map.clearInfoWindow();
|
|
}} />
|
|
|
|
<div style={{ marginTop: 50 }}>
|
|
<div className='gis_item'>
|
|
<div className='gis_title'>事件时间</div><div className='gis_text'>{moment(x?.createTime).format('YYYY-MM-DD HH:mm:ss')}</div>
|
|
</div>
|
|
<div className='gis_item'>
|
|
<div className='gis_title'>地址信息</div><div className='gis_text'>
|
|
<Tooltip placement="top" title={x?.location}>
|
|
<span style={{ color: '#FFF' }}>{x?.location?.length > 20 ? x?.location?.substring(0, 20) + '...' : x?.location}</span>
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
<div className='gis_item'>
|
|
<div className='gis_title'>警情类型</div><div className='gis_text'>{x?.type}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{
|
|
propTab == 'overview' && <div className='alarm_confirm'>
|
|
<div className='hande_button'
|
|
onClick={() => {
|
|
map.clearInfoWindow();
|
|
if (emergencyList['xfjy']?.length > 0) {
|
|
const list = emergencyList['xfjy'].sort((b, a) => computeDis(
|
|
new AMap.LngLat(x.longitude, x.latitude),
|
|
new AMap.LngLat(b.location?.split(',')[0], b.location?.split(',')[1])
|
|
) - computeDis(
|
|
new AMap.LngLat(x.longitude, x.latitude),
|
|
new AMap.LngLat(a.location?.split(',')[0], a.location?.split(',')[1])
|
|
))
|
|
const location = list[0]?.location
|
|
drawDrivings([x.longitude, x.latitude], location.split(','), x, list[0])
|
|
|
|
props.alarmOk({
|
|
alarmInfo: { ...x },
|
|
rescueInfo: list[0]
|
|
})
|
|
setTimeout(() => {
|
|
drawDrivings([x.longitude, x.latitude], location.split(','), x, list[0])
|
|
}, 200);
|
|
}
|
|
}}
|
|
>一键护航</div>
|
|
</div>}
|
|
</div>,
|
|
document.getElementById(`alarmcontentid${x.location}`));
|
|
}
|
|
|
|
const renderLeftTop = () => {
|
|
return <div className='home_left_add' onClick={() => { setVisible(true) }} />
|
|
}
|
|
|
|
const onFinish = (values) => {
|
|
setLocation({})
|
|
|
|
dispatch(actions.firecontrol.addFireAlarm(
|
|
{
|
|
...values,
|
|
state: 1,
|
|
location: location?.location,
|
|
longitude: location?.lnglat?.split(',')[0],
|
|
latitude: location?.lnglat?.split(',')[1],
|
|
}
|
|
)).then(res => {
|
|
getData()
|
|
message.success('新增上报信息成功')
|
|
setVisible(false)
|
|
})
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{/* 延缓加载遮罩 */}
|
|
{delay && <div className='map_delay'><div className='delay_img' /></div>}
|
|
{/* 地图容器 */}
|
|
<div className="gis" id={MAPDOMID} />
|
|
{/* 底部按钮 */}
|
|
{props.propTab == 'item' && tabs.map((s, index) => {
|
|
return <>
|
|
<div className={s.className ? `${s.className} ${s.className}${index + 1}` : 'water-gis-button' + (index + 1)}
|
|
onClick={() => {
|
|
setTab(s.tab)
|
|
s.className ? props.changeEmengencyTab(s.tab) : props.changeTab(s.tab)
|
|
if (s.tab == 'emergency') {
|
|
setTab('yjwz')
|
|
props.changeEmengencyTab('yjwz')
|
|
}
|
|
}}
|
|
>
|
|
<div className={
|
|
s.className ? `button_img_${(index + 1)} ${tab == s.tab ? 'button_img_' + (index + 1) + '_select' : ''}` :
|
|
`button_img ${tab == s.tab ? 'button_img_select' : ''}`} />
|
|
<div>{s.name}</div>
|
|
{s.className && <div className='dotbg'>{emergencyList[s.tab]?.length}</div>}
|
|
</div>
|
|
</>
|
|
})}
|
|
{/* 左上角图例 */}
|
|
{props.propTab == 'overview' && renderLeftTop()}
|
|
{/* 四周遮罩 */}
|
|
<div className='gis-left'></div>
|
|
<div className='gis-right'></div>
|
|
<div className='gis-top'></div>
|
|
<div className='gis-bottom'></div>
|
|
|
|
{/* 新增告警 */}
|
|
<FireAddForm
|
|
visible={visible}
|
|
onCancel={() => setVisible(false)}
|
|
locationClick={() => setLocationVisible(true)}
|
|
location={location}
|
|
onFinish={onFinish}
|
|
/>
|
|
|
|
{/* 位置选点 */}
|
|
<LoacationSelect
|
|
locationVisible={locationVisible}
|
|
onCancel={() => setLocationVisible(false)}
|
|
locationSelect={(location) => {
|
|
setLocation(location)
|
|
setLocationVisible(false)
|
|
}}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default connect()(Map);
|
|
|