添加搜索显示隐藏具体的车辆,船舶,游艇数据

This commit is contained in:
xwj
2026-01-28 13:13:02 +08:00
parent 6a23132628
commit 3cd0eeb9f9
5 changed files with 958 additions and 210 deletions

View File

@@ -1,32 +1,275 @@
<template>
<div class="ship-type-box" id="ship-type-box">
<ul>
<li v-for="(item,index) in shipTypes" :key="index">
<div :style="'background-color:'+ item.color"></div>
<span>{{ language === 'zh' ? item.name : item.tran }}</span>
</li>
</ul>
<!-- 筛选搜索按钮 -->
<div class="filter-btn" @click="toggleFilter">
<!-- {{ isShowFilter ? '' : '+' }} -->
<img v-if="isShowFilter" src="@/util/map/img/search-fold.png" alt="筛选搜索" />
<img v-else src="@/util/map/img/search-ex.png" alt="筛选搜索" />
</div>
<!-- 筛选搜索弹窗 -->
<div v-if="isShowFilter" class="filter-popup">
<div class="filter-item">
<el-checkbox v-model="checkedTypes.ship" @change="handleTypeChange('ship')">
<!-- {{ language === 'zh' ? '船舶' : 'Ship' }} -->
</el-checkbox>
<div class="filter-title">{{ language === 'zh' ? '船舶' : 'Ship' }}</div>
<el-select
v-model="searchValues.ship"
:placeholder="language === 'zh' ? '搜索MMSI' : 'Search Ship Name/MMSI'"
:disabled="!checkedTypes.ship"
filterable
clearable
remote
:remote-method="handleShipSearch"
:loading="false"
@change="handleShipSelect"
class="ship-select"
no-match-text="没有匹配结果"
>
<el-option
v-for="ship in shipOptions"
:key="ship.shipId"
:label="ship.mmsi"
:value="ship.mmsi"
>
<div class="ship-option-content">
<div class="ship-mmsi">MMSI: {{ ship.mmsi }}</div>
</div>
</el-option>
</el-select>
</div>
<div class="filter-item">
<el-checkbox v-model="checkedTypes.car" @change="handleTypeChange('car')">
<!-- {{ language === 'zh' ? '车辆' : 'Car' }} -->
</el-checkbox>
<div class="filter-title"> {{ language === 'zh' ? '车辆' : 'Car' }}</div>
<el-select
style="width: 300px"
v-model="searchValues.car"
:placeholder="language === 'zh' ? '搜索车牌号' : 'Search Car Number'"
:disabled="!checkedTypes.car"
filterable
clearable
no-match-text="请输入车牌号超过4位才搜索"
remote
:remote-method="handleCarSearch"
:loading="false"
@change="handleCarSelect"
class="car-select"
>
<el-option
v-for="car in carOptions"
:key="car.carNum"
:label="car.carNum"
:value="car.carNum"
>
<div class="car-option-content">
<div class="car-num">{{ car.carNum }}</div>
<div class="car-company">{{ car.companyName || '' }}</div>
</div>
</el-option>
</el-select>
</div>
<div class="filter-item">
<el-checkbox v-model="checkedTypes.yacht" @change="handleTypeChange('yacht')">
<!-- {{ language === 'zh' ? '游艇' : 'Yacht' }} -->
</el-checkbox>
<div class="filter-title"> {{ language === 'zh' ? '游艇' : 'Yacht' }}</div>
<el-select
v-model="searchValues.yacht"
:placeholder="language === 'zh' ? '搜索游艇名/MMSI' : 'Search Yacht Name/MMSI'"
:disabled="!checkedTypes.yacht"
filterable
clearable
remote
:remote-method="handleYachtSearch"
:loading="false"
@change="handleYachtSelect"
class="yacht-select"
no-match-text="没有匹配结果"
>
<el-option
v-for="yacht in yachtOptions"
:key="yacht.shipId"
:label="yacht.mmsi"
:value="yacht.mmsi"
>
<div class="yacht-option-content">
<div class="yacht-mmsi">MMSI: {{ yacht.mmsi }}</div>
</div>
</el-option>
</el-select>
</div>
</div>
</div>
</template>
<script lang="ts">
import {Vue, Component} from 'vue-property-decorator'
import {Vue, Component, Prop} from 'vue-property-decorator'
import bus from '@/util/bus'
import storeUtil from '@/util/store'
@Component({})
export default class MapBottomShipType extends Vue {
private language = window.localStorage.getItem('language')
private shipTypes: any[] = [
{
name: '货船',
tran: 'Cargo Ship',
color: '#BD3154'
},
{
name: '游艇',
tran: 'Fishing vessel',
color: '#FFC24D'
private isShowFilter = false
// 接收父组件传递的车辆数据
@Prop({
type: Array,
default: () => []
})
private carData!: any[]
// 接收父组件传递的船舶数据
@Prop({
type: Array,
default: () => []
})
private shipData!: any[]
// 勾选状态
private checkedTypes: { [key: string]: boolean } = {
ship: true,
car: true,
yacht: true
}
// 搜索值
private searchValues: { [key: string]: string } = {
ship: '',
car: '',
yacht: ''
}
// 车辆搜索选项
private carOptions: any[] = []
// 船舶搜索选项
private shipOptions: any[] = []
// 游艇搜索选项
private yachtOptions: any[] = []
// 控制车辆下拉框是否显示
private carSelectVisible = false
// 切换筛选弹窗显示状态
private toggleFilter() {
this.isShowFilter = !this.isShowFilter
}
// 处理类型勾选变化
private handleTypeChange(type: string) {
const isChecked = this.checkedTypes[type]
// 取消勾选时,清空并禁用搜索框
if (!isChecked) {
this.searchValues[type] = ''
// 触发搜索,清空结果
this.handleSearch(type)
// 如果有打开的弹窗或轨迹,重置地图
this.resetMap()
}
// 触发全局事件,通知地图更新显示
bus.$emit('filterTypeChange', {
type,
isChecked
})
}
// 处理搜索
private handleSearch(type: string) {
const value = this.searchValues[type]
// 车牌号需要超过4位才搜索
if (type === 'car' && value.length < 4) {
return
}
// 触发全局事件,通知地图更新搜索结果
bus.$emit('filterSearch', {
type,
value
})
}
// 处理车辆搜索 - 当输入超过4位时触发
private handleCarSearch(query: string) {
// 车牌号需要超过4位才搜索
if (query.length < 4) {
this.carOptions = []
return
}
// 模糊搜索车辆数据
this.carOptions = this.carData.filter((car: any) => {
const carNum = car.carNum || ''
return carNum.includes(query)
})
}
// 处理车辆选择
private handleCarSelect(value: string) {
// 触发搜索事件,更新地图显示
this.handleSearch('car')
}
// 处理船舶搜索
private handleShipSearch(query: string) {
// 过滤出船舶数据shipTypeName !== '其他'
const shipData = this.shipData.filter((ship: any) => {
return ship.shipTypeName !== '其他'
})
console.log(shipData,'船舶数据')
// 模糊搜索船舶数据匹配船名或MMSI
this.shipOptions = shipData.filter((ship: any) => {
const mmsi = ship.mmsi || ''
return mmsi.includes(query)
})
}
// 处理船舶选择
private handleShipSelect(value: string) {
// 触发搜索事件,更新地图显示
this.handleSearch('ship')
}
// 处理游艇搜索
private handleYachtSearch(query: string) {
// 过滤出游艇数据shipTypeName === '其他'
const yachtData = this.shipData.filter((ship: any) => {
return ship.shipTypeName === '其他'
})
console.log(yachtData,'游艇数据')
// 模糊搜索游艇数据匹配船名或MMSI
this.yachtOptions = yachtData.filter((yacht: any) => {
const mmsi = yacht.mmsi || ''
return mmsi.includes(query)
})
}
// 处理游艇选择
private handleYachtSelect(value: string) {
// 触发搜索事件,更新地图显示
this.handleSearch('yacht')
}
// 重置地图
private resetMap() {
const mapFun = storeUtil.getMapFun()
const mapInstance = storeUtil.getMapInstance()
if (mapFun && mapInstance) {
// 这里需要根据实际情况调用地图工具的方法来重置地图
// 例如关闭弹窗、擦除轨迹、取消聚焦等
console.log('重置地图')
}
}
]
public mounted() {
window.addEventListener('setItemEvent', (e: any) => {
@@ -45,33 +288,65 @@ export default class MapBottomShipType extends Vue {
align-items: center;
height: 2.5rem;
z-index: 999;
right: 1.25rem;
bottom: 1.25rem;
left: 1.1rem;
bottom: 3.8rem;
border-radius: 0.25rem;
background-color: #FFFFFF;
color: #333333;
padding: 0 0 0 1.25rem;
}
ul {
width: 100%;
.filter-btn {
width: 2.5rem;
height: 2.5rem;
display: flex;
margin-right: 0.625rem;
padding: 0 0.3125rem;
line-height: 0.625rem;
justify-content: space-between;
justify-content: center;
font-size: 1.5rem;
cursor: pointer;
color: white;
border-radius: 0.25rem;
transition: all 0.3s;
}
.filter-title{
color: #2c469b;
margin-right: 0.5rem;
font-weight: 600;
}
li {
:first-child {
display: inline-block;
border-radius: 0.5625rem;
width: 0.5625rem;
height: 0.5625rem;
margin-right: 0.25rem;
.filter-popup {
position: absolute;
bottom: 3rem;
left: 0;
background-color: white;
border-radius: 0.25rem;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
padding: 1rem;
width: 25rem;
z-index: 1000;
}
.filter-item {
display: flex;
align-items: center;
margin-bottom: 0.75rem;
&:last-child {
margin-bottom: 0;
}
font-size: 0.75rem;
display: inline-block;
margin-right: 0.625rem;
.el-checkbox {
margin-right: 1rem;
font-size: 0.875rem;
}
.el-select {
flex: 1;
}
.ship-select, .yacht-select {
flex: 1;
}
.el-input {
flex: 1;
::v-deep .el-input__inner {
height: 2rem;
font-size: 0.875rem;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -44,6 +44,7 @@ import WKT from 'ol/format/WKT'
import {Circle, Fill, Stroke, Style, Text} from 'ol/style.js'
import unByKey from 'ol/Observable.js'
// 地图全局配置对象,存储所有地图相关的实例和状态
const shipMap = new Object({
map: null, //地图
view: null, //地图的资源
@@ -73,15 +74,20 @@ const shipMap = new Object({
__selectPort: null,//点击港口
})
// 地图工具覆盖物数组
let mapToolOver = []
// 地图功能方法集合提供对外API
const shipFun = new Object({})
// 设置地图实例
shipFun.setmap = function (map) {
shipMap.map = map
}
var __pathData = {};
var __pathDrawData = {};
var __pathArray = {};
// 路径数据存储对象
var __pathData = {}; // 原始路径数据
var __pathDrawData = {}; // 绘制用路径数据
var __pathArray = {}; // 路径ID数组
// 示例边界线数据ABC线用于判断船舶位置
const abcLineData = [
[
11822129.922245655,
@@ -110,18 +116,20 @@ const abcLineData = [
]
//地图
shipMap.view = new View({
projection: 'EPSG:3857', //使用这个坐标系
center: transform([120, 36], 'EPSG:4326', 'EPSG:3857'), //定义中心位置
zoom: 6, //定义等级
minZoom: 3, //最小等级
maxZoom: 18 //最大等级
// rotation:120
projection: 'EPSG:3857', // 使用Web墨卡托投影
center: transform([120, 36], 'EPSG:4326', 'EPSG:3857'), // 中心点设置为东经120°北纬36°
zoom: 6, // 初始缩放级别
minZoom: 3, // 最小缩放级别
maxZoom: 18 // 最大缩放级别
// rotation:120 // 旋转角度(注释掉)
})
// 初始化OSM基础图层
shipMap.layer = new TileLayer({
source: new OSM(), //资源
zIndex: 1
})
// 定义EPSG:3395投影墨卡托投影
var projection_3395 = new Projection({
code: 'EPSG:3395',
extent: [-20026376.39, -15496570.74, 20026376.39, 18764656.23],
@@ -129,6 +137,7 @@ var projection_3395 = new Projection({
axisOrientation: 'neu'
})
// 注册EPSG:3395投影定义
proj4.defs(
'EPSG:3395',
'+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'
@@ -137,7 +146,12 @@ register(proj4)
var seaMapPath = 'http://cm191.myships.com/ChartMap'
/**
* 获取海图数据源
* @returns {XYZ} 海图XYZ数据源
*/
function getSeaMapSource() {
// 获取EPSG:3857投影的范围
var projectionExtent = get('EPSG:3857').getExtent()
var height = getHeight(projectionExtent)
var width = getWidth(projectionExtent)
@@ -145,27 +159,35 @@ function getSeaMapSource() {
var maxResolution = Math.max(width / tileSize[0], height / tileSize[1])
var length = 18 + 1
var resolutions = new Array(length)
// 生成各级分辨率数组
for (var z = 0; z < length; ++z) {
resolutions[z] = maxResolution / Math.pow(2, z)
}
// 创建瓦片网格
var tilegrid = new TileGrid({
origin: [projectionExtent[0], projectionExtent[3]],
tileSize: tileSize,
resolutions: resolutions
})
// 监听分辨率变化,刷新路径
shipMap.view.on('change:resolution', function (event) {
freshPath();
});
/**
* 刷新路径数据
*/
function freshPath() {
for (var pathId in __pathData) {
var data = __pathData[pathId];
// 生成绘制用路径数据
var re = __makePathData(data, shipMap.map, pathId);
var old_re = __pathDrawData[pathId];
// 如果路径数据发生变化
if (!old_re || re.length != old_re.length) {
__pathDrawData[pathId] = re;
var ids = __pathArray[pathId];
// 移除旧的路径要素
if (ids && ids.length > 0) {
ids.forEach(function (item, index) {
var f = shipMap.sourceList.pathLineSource.getFeatureById(item);
@@ -176,15 +198,23 @@ function getSeaMapSource() {
}
})
}
// 绘制新的路径
__drawPathData(re, pathId);
}
}
}
/**
* 数字补零函数
* @param {number} num - 补零后的总长度
* @param {string|number} val - 要补零的值
* @returns {string} 补零后的字符串
*/
function padLeft(num, val) {
return (new Array(num).join('0') + val).slice(-num)
}
// 创建并返回XYZ数据源
return new XYZ({
cacheSize: 256,
projection: projection_3395,
@@ -193,20 +223,25 @@ function getSeaMapSource() {
if (!xyz) {
return ''
}
// 计算瓦片URL参数
var z = xyz[0] - 2
if (z <= -1) return ''
var x = Math.abs(xyz[1])
var y = Math.abs(xyz[2])
// 格式化参数
x = 'C' + padLeft(8, x.toString(16))
y = 'R' + padLeft(8, y.toString(16))
z = 'L' + padLeft(2, z)
// 返回完整的瓦片URL
return seaMapPath + '/' + z + '/' + y + '/' + x + '.jpg'
}
})
}
// 初始化海图数据源
shipMap.seaView = getSeaMapSource()
// 创建海图图层
shipMap.seaLayer = new TileLayer({
preload: Infinity,
source: shipMap.seaView,
@@ -217,21 +252,24 @@ shipMap.sateView = new XYZ({
url:
'http://mt{0-1}.google.cn/maps/vt?lyrs=s@198&hl=zh-CN&gl=CN&&x={x}&y={y}&z={z}'
})
// 创建卫星图图层
shipMap.sateLayer = new TileLayer({
preload: Infinity,
source: shipMap.sateView,
zIndex: 1
})
// 初始化天地图图层组
shipMap.tdtLayer = new Group({
layers: [
// 天地图矢量底图
new TileLayer({
source: new XYZ({
crossOrigin: 'anonymous',
url: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=f68226212bdd3ffed1b0bb884f81ddda'
})
}),
// 天地图矢量注记
new TileLayer({
source: new XYZ({
crossOrigin: 'anonymous',
@@ -240,7 +278,7 @@ shipMap.tdtLayer = new Group({
})
]
})
//分布图
// 初始化船舶分布图
shipMap.distributeLayer = new TileLayer({
preload: Infinity,
visible: true,
@@ -259,7 +297,7 @@ shipMap.distributeLayer = new TileLayer({
})
//船舶
// 初始化船舶数据源
shipMap.sourceList.shipSource = new SourceVector()
shipMap.layerList.shipLayer = new Vector({
source: shipMap.sourceList.shipSource,
@@ -267,8 +305,9 @@ shipMap.layerList.shipLayer = new Vector({
style: (f, r) => shipStyle.newshipStyle(f, r, shipMap.map),
zIndex: 21
})
//船舶
// 初始化车辆数据源
shipMap.sourceList.carSource = new SourceVector()
// 创建车辆图层
shipMap.layerList.carLayer = new Vector({
source: shipMap.sourceList.carSource,
name: 'carLayer',
@@ -277,28 +316,31 @@ shipMap.layerList.carLayer = new Vector({
})
// 港资
// 初始化港口数据
shipMap.sourceList.portSource = new SourceVector()
// 创建港口图层
shipMap.layerList.portLayer = new Vector({
source: shipMap.sourceList.portSource,
zIndex: 15
})
// 泊位图层资
// 初始化泊位数据
shipMap.sourceList.berthSource = new SourceVector()
// 创建泊位图层
shipMap.layerList.berthLayer = new Vector({
source: shipMap.sourceList.berthSource,
zIndex: 17
})
//轨迹
// 初始化轨迹路径数据源
shipMap.sourceList.pathLineSource = new SourceVector()
// 创建轨迹路径图层
shipMap.layerList.pathLineLayer = new Vector({
source: shipMap.sourceList.pathLineSource,
zIndex: 4
})
// 轨迹上的点
// 初始化轨迹点数据源
shipMap.sourceList.pathPointSource = new SourceVector()
shipMap.layerList.pathPointLayer = new Vector({
source: shipMap.sourceList.pathPointSource,
@@ -306,20 +348,24 @@ shipMap.layerList.pathPointLayer = new Vector({
})
// 地图上显示的点
// 初始化地图标记点数据源
shipMap.sourceList.PointSource = new SourceVector()
// 创建地图标记点图层
shipMap.layerList.PointLayer = new Vector({
source: shipMap.sourceList.PointSource,
zIndex: 5
})
// 定义台风警戒线样式
var lineStyle = new Style({
stroke: new Stroke({
color: '#FF0000',
width: 1
})
});
// 初始化台风警戒线数据源
shipMap.sourceList.jjxSource = new SourceVector();
// 创建台风警戒线图层
shipMap.layerList.jjxLayer = new Vector({
source: shipMap.sourceList.jjxSource,
style: lineStyle,
@@ -327,7 +373,9 @@ shipMap.layerList.jjxLayer = new Vector({
zIndex: 3
});
// 初始化台风数据源
shipMap.sourceList.taifeng = new SourceVector();
// 创建台风图层
shipMap.layerList.taifengLayer = new Vector({
source: shipMap.sourceList.taifeng,
style: function (feature, res) {
@@ -355,11 +403,13 @@ var max = [50.9, 17.1, 24.4, 32.6, 41.4, 1000];
function getTyphoonStyle(f, r) {
var maxWindSpeed = f.get('maxWindSpeed');
var level = 0;
// 确定台风等级
for (var i = 0; i <= 5; i++) {
if (min[i] <= maxWindSpeed && maxWindSpeed <= max[i]) {
level = i;
}
}
// 根据等级设置颜色
var color = '';
switch (level) {
case 0:
@@ -384,6 +434,7 @@ function getTyphoonStyle(f, r) {
break;
}
// 创建主样式
var s = new Style({
fill: new Fill({
color: color
@@ -399,6 +450,7 @@ function getTyphoonStyle(f, r) {
})
})
});
// 在高分辨率下显示预报信息
if (r < 3000) {
var ntext = new Text({
textAlign: "left",
@@ -418,6 +470,7 @@ function getTyphoonStyle(f, r) {
s.setText(ntext);
}
// 创建时间文本样式
const style2 = new Style({
text: new Text({
textAlign: "left",
@@ -438,6 +491,12 @@ function getTyphoonStyle(f, r) {
return [s, style2];
}
/**
* 获取台风预报点样式
* @param {Feature} f - 台风要素
* @param {number} r - 分辨率
* @returns {Array<Style>} 样式数组
*/
function getTyphoonFutureStyle(f, r) {
var maxWindSpeed = f.get('maxWindSpeed');
@@ -529,6 +588,12 @@ function getTyphoonFutureStyle(f, r) {
return [s, style2];
}
/**
* 获取台风路径线样式
* @param {Feature} f - 台风要素
* @param {number} r - 分辨率
* @returns {Array<Style>} 样式数组
*/
function getTyphoonStyleLine(f, r) {
var ss = [];
var s = new Style({
@@ -584,7 +649,7 @@ function getTyphoonStyleLine(f, r) {
return ss;
}
//船舶选择框
// 船舶选择框数据源和图层
shipMap.sourceList.selectSource = new SourceVector()
shipMap.layerList.selectLayer = new Vector({
source: shipMap.sourceList.selectSource,
@@ -662,8 +727,7 @@ shipMap.layerList.circlePathLayer = new Vector({
source: shipMap.sourceList.circlePath,
zIndex: 10
})
//我的修改
//eez线
// EEZ线专属经济区线图层用户添加
shipMap.sourceList.eezSource = new XYZ({
cacheSize: 256,
tileUrlFunction: function (xyz, obj1, obj2) {
@@ -882,76 +946,105 @@ shipMap.layerList.pLayer.setZIndex(6);
})
})
//监听返回zoom
// 设置选中的港口
shipFun.getSelectPortFun = function (selectPort) {
shipMap.__selectPort = selectPort
}
//九方温度
/**
* 加载九方温度图层
* @param {string} url - 温度数据URL
*/
shipFun.loadTemp = function (url) {
// 如果气象图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.iLayer)) {
shipMap.map.addLayer(shipMap.layerList.iLayer)
}
// 如果行政区划图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.vtLayer)) {
shipMap.map.addLayer(shipMap.layerList.vtLayer)
}
// 设置温度图层参数
mapClient.colorMapTileLayer.setOption({
url: url,
factor: Factor.Temp,
})
// 刷新气象图层
sourceImageCanvas.refresh()
}
// 加载气压
shipFun.loadPressure = function (url) {
// 如果气象图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.iLayer)) {
shipMap.map.addLayer(shipMap.layerList.iLayer)
}
// 如果行政区划图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.vtLayer)) {
shipMap.map.addLayer(shipMap.layerList.vtLayer)
}
// 气压色斑图
// 设置气压图层参数
mapClient.colorMapTileLayer.setOption({
url: url,
factor: Factor.Pressure,
})
// 刷新气象图层
sourceImageCanvas.refresh()
}
// 加载风场
/**
* 加载风场图层
* @param {string} url - 风场数据URL
*/
shipFun.loadWind = function (url) {
// 如果气象图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.iLayer)) {
shipMap.map.addLayer(shipMap.layerList.iLayer)
}
// 如果行政区划图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.vtLayer)) {
shipMap.map.addLayer(shipMap.layerList.vtLayer)
}
// 设置风场图层参数
mapClient.colorMapTileLayer.setOption({
url: url,
factor: Factor.Wind,
})
// 刷新气象图层
sourceImageCanvas.refresh()
}
// 加载洋流
/**
* 加载洋流图层
* @param {string} url - 洋流数据URL
*/
shipFun.loadSeacurrents = function (url) {
// 如果气象图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.iLayer)) {
shipMap.map.addLayer(shipMap.layerList.iLayer)
}
// 如果行政区划图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.vtLayer)) {
shipMap.map.addLayer(shipMap.layerList.vtLayer)
}
// 气压色斑图
// 设置洋流图层参数
mapClient.colorMapTileLayer.setOption({
url: url,
factor: Factor.SeaCurrents,
})
// 刷新气象图层
sourceImageCanvas.refresh()
}
//加载洋流粒子
/**
* 加载洋流粒子动画
* @param {string} url - 洋流粒子数据URL
*/
shipFun.loadNineParticleSeacurrents = function (url) {
// 如果粒子图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.pLayer)) {
shipMap.map.addLayer(shipMap.layerList.pLayer)
}
// 粒子动画
// 配置粒子动画参数
mapClient.particleAnimationLayer.setConfig({
lineWidth: 0.95,
speedFactor: 1.8,
@@ -964,32 +1057,42 @@ shipFun.loadNineParticleSeacurrents = function (url) {
1.0: 'rgba(255,255,255,0.5)',
}
})
// 设置粒子动画数据源
mapClient.particleAnimationLayer.setOption({
url: url,
type: 'image',
factor: Factor.SeaCurrents,
})
}
//加载风粒子
/**
* 加载风粒子动画
* @param {string} url - 风粒子数据URL
*/
shipFun.loadNineParticleWind = function (url) {
// 如果粒子图层不存在,添加到地图
if (!hasLayerInMap(shipMap.layerList.pLayer)) {
shipMap.map.addLayer(shipMap.layerList.pLayer)
}
// 粒子动画
// 配置粒子动画参数
mapClient.particleAnimationLayer.setConfig({
lineWidth: 0.95,
speedFactor: 0.2,
fadeOpacity: 0.85,
particlesNumber: 15000
lineWidth: 0.95, // 线条宽度
speedFactor: 0.2, // 速度因子
fadeOpacity: 0.85, // 衰减透明度
particlesNumber: 15000 // 粒子数量
})
// 设置粒子动画数据源
mapClient.particleAnimationLayer.setOption({
url: url,
type: 'image',
factor: Factor.Wind,
})
}
// 加载等值线
/**
* 加载等值线图层
* @param {string} url - 等值线数据URL
*/
shipFun.loadIsobar = function (url) {
// 如果等值线图层不存在,创建并添加到地图
if (!hasLayerInMap(shipMap.layerList.isobarLayer)) {
shipMap.layerList.isobarLayer = new Vector({
source: new SourceVector({
@@ -998,6 +1101,7 @@ shipFun.loadIsobar = function (url) {
format: new GeoJSON(),
}),
style: function (feature) {
// 设置等值线文本
if (shipStyle.isobarStyle && shipStyle.isobarStyle.getText()) {
shipStyle.isobarStyle.getText().setText(feature.get('values_'))
}
@@ -1009,32 +1113,50 @@ shipFun.loadIsobar = function (url) {
}
}
//隐藏台风警戒线
/**
* 隐藏台风警戒线
*/
shipFun.hidejjx = function () {
shipMap.layerList.jjxLayer.setVisible(false)
}
//显示台风警戒线
/**
* 显示台风警戒线
*/
shipFun.showjjx = function () {
shipMap.layerList.jjxLayer.setVisible(true)
}
//清除九方气象
/**
* 清除九方气象图层
*/
shipFun.clearJfWeather = function () {
shipMap.map.removeLayer(shipMap.layerList.iLayer)
shipMap.map.removeLayer(shipMap.layerList.vtLayer)
}
/**
* 清除九方气象等值线图层
*/
shipFun.clearJfWeatherIsobar = function () {
if (hasLayerInMap(shipMap.layerList.isobarLayer)) {
shipMap.map.removeLayer(shipMap.layerList.isobarLayer)
}
}
/**
* 清除九方粒子图层
*/
shipFun.clearJfParticle = function () {
if (hasLayerInMap(shipMap.layerList.pLayer)) {
shipMap.map.removeLayer(shipMap.layerList.pLayer)
}
}
/**
* 显示港口
* @param {Object} portInfo - 港口信息
*/
shipFun.showPort = function (portInfo) {
// 坐标转换从WGS84到Web墨卡托
let coor = transform(
[portInfo.lon, portInfo.lat],
'EPSG:4326',
@@ -1058,7 +1180,12 @@ shipFun.showPort = function (portInfo) {
shipMap.sourceList.portSource.addFeature(f)
}
/**
* 显示泊位
* @param {Object} berthInfo - 泊位信息
*/
shipFun.showBerth = function (berthInfo) {
// 坐标转换从WGS84到Web墨卡托
let coor = transform(
[berthInfo.decLongitude, berthInfo.decLatitude],
'EPSG:4326',
@@ -1073,23 +1200,37 @@ shipFun.showBerth = function (berthInfo) {
berthLon: berthInfo.decLongitude,
berthLat: berthInfo.decLatitude,
})
f.setStyle(shipStyle.berthStyle(f))
f.set('geom', geom)
f.setId('berth-' + berthInfo.berthId)
shipMap.sourceList.berthSource.addFeature(f)
f.setStyle(shipStyle.berthStyle(f)) // 设置泊位样式
f.set('geom', geom) // 存储几何对象
f.setId('berth-' + berthInfo.berthId) // 设置唯一ID
shipMap.sourceList.berthSource.addFeature(f) // 添加到泊位数据源
}
// 清除所有泊位
/**
* 清除所有泊位
* @param {Map} map - 地图实例
* @returns {boolean} 清除结果
*/
shipFun.clearallBerth = (map) => {
shipMap.sourceList.berthSource.clear()
shipMap.sourceList.berthSource.clear() // 清空泊位数据源
return true
}
//清除所有港口
/**
* 清除所有港口
* @param {Map} map - 地图实例
* @returns {boolean} 清除结果
*/
shipFun.clearallPort = (map) => {
shipMap.sourceList.portSource.clear()
return true
}
//判定图层是否存在map
//判断当前图层是否存在地图中
/**
* 判断图层是否存在于地图中
* @param {Layer} layer - 要检查的图层
* @returns {boolean} 检查结果
*/
function hasLayerInMap(layer) {
var layers = shipMap.map.getLayers()
for (var i = 0; i < layers.getLength(); i++) {
@@ -1101,18 +1242,33 @@ function hasLayerInMap(layer) {
return false
}
//清除所有船舶
/**
* 清除所有船舶
* @param {Map} map - 地图实例
* @returns {boolean} 清除结果
*/
shipFun.clearallship = (map) => {
shipMap.sourceList.shipSource.clear()
shipMap.sourceList.shipSource.clear() // 清空船舶数据源
return true
}
/**
* 清除所有车辆
* @param {Map} map - 地图实例
* @returns {boolean} 清除结果
*/
shipFun.clearAllCar = (map) => {
shipMap.sourceList.carSource.clear()
shipMap.sourceList.carSource.clear() // 清空车辆数据源
return true
}
/**
* 添加车辆
* @param {Array} data - 车辆数据数组
* @param {Map} map - 地图实例
*/
shipFun.addCar = (data, map) => {
const source = shipMap.sourceList.carSource
const features = source.getFeatures()
// 移除已存在的车辆要素
if (features) {
for (let i = 0; i < data.length; i++) {
const item = data[i]
@@ -1123,10 +1279,12 @@ shipFun.addCar = (data, map) => {
})
}
}
// 创建新的车辆要素
const newFeatures = []
for (let i = 0; i < data.length; i++) {
const item = data[i]
if(item.carNum){
// 创建点几何对象
const f = new Feature({
geometry: new Point(transform([Number(item.longitude), Number(item.latitude)], 'EPSG:4326', 'EPSG:3857'))
})
@@ -1138,23 +1296,31 @@ shipFun.addCar = (data, map) => {
newFeatures.push(f)
}
}
// 添加新的车辆要素到数据源
source.addFeatures(newFeatures)
}
/**
* 判断点在线段的南北方向
* @param {Array} lines - 线段的坐标数组
* @param {Array} point - 要判断的点坐标
* @returns {boolean} true表示点在线段南边false表示点在线段北边
*/
function identifySouOrNor(lines, point) {
//将线段的点切割为数组,数组的每个元素为一条线段
// 将线段的点切割为数组,数组的每个元素为一条线段
const lineData = []
for(let i = 0; i<lines.length-1; i++){
lineData.push([lines[i], lines[i+1]])
}
//先根据经度判断这个点垂直投影在哪一条线段上
// 先根据经度判断这个点垂直投影在哪一条线段上
let projectionLine = []
lineData.forEach(line => {
if(point[0]>line[0][0] && point[0]<line[1][0]){
projectionLine = line
}
})
//判断这个点是在线段的上(北)还是下(南)
// 判断这个点是在线段的上(北)还是下(南)
if(projectionLine.length == 0){
// 如果没有找到投影线段,比较点的纬度与所有线段点的最大纬度
const latitudeArr = []
lines.map(item => {
latitudeArr.push(item[1])
@@ -1166,6 +1332,7 @@ function identifySouOrNor(lines, point) {
return true
}
}
// 处理水平线情况
if (projectionLine[0][1] == projectionLine[1][1]) {
if (point[1]<projectionLine[0][1]) {
return true
@@ -1173,6 +1340,7 @@ function identifySouOrNor(lines, point) {
return false
}
}
// 使用线性方程判断点在线段的哪一侧
const A = projectionLine[1][1] - projectionLine[0][1]
const B = projectionLine[0][0] - projectionLine[1][0]
const C = projectionLine[1][0]*projectionLine[1][1] - projectionLine[0][0]*projectionLine[1][1]
@@ -1183,11 +1351,18 @@ function identifySouOrNor(lines, point) {
return false
}
}
//添加 船舶
/**
* 添加船舶
* @param {Array} dataList - 船舶数据数组
* @param {number} zoom - 缩放级别
* @param {Map} map - 地图实例
* @param {Array} selectlist - 选中的船舶列表
*/
shipFun.addShip = (dataList, zoom, map, selectlist) => {
// shipMap.sourceList.shipSource.clear()
for (let i = 0; i < dataList.length; i++) {
const element = dataList[i]
// 坐标转换将经纬度单位转换为Web墨卡托
let coor = transform(
[dataList[i].n / 600000, dataList[i].a / 600000],
'EPSG:4326',
@@ -1201,25 +1376,25 @@ shipFun.addShip = (dataList, zoom, map, selectlist) => {
let f = new Feature({
geometry: geom, //添加点图形
type: 'ship',
shiptype: element.y,
mmsi: element.m,
heading: element.h,
callsign: element.g,
cog: element.c,
imo: element.o,
sog: element.s,
aisNavStatus: element.v,
length: element.l,
destPort: element.p,
breadth: element.b,
eta: element.r,
draught: element.d,
posTime: element.t,
shipId: element.i,
lon: dataList[i].n,
lat: dataList[i].a,
shipnameEn: dataList[i].e,
color: element.color
shiptype: element.y, // 船舶类型
mmsi: element.m, // MMSI号
heading: element.h, // 航向
callsign: element.g, // 呼号
cog: element.c, // 对地航向
imo: element.o, // IMO号
sog: element.s, // 对地航速
aisNavStatus: element.v, // AIS导航状态
length: element.l, // 长度
destPort: element.p, // 目的港
breadth: element.b, // 宽度
eta: element.r, // 预计到达时间
draught: element.d, // 吃水
posTime: element.t, // 位置时间
shipId: element.i, // 船舶ID
lon: dataList[i].n, // 经度
lat: dataList[i].a, // 纬度
shipnameEn: dataList[i].e, // 英文船名
color: element.color // 颜色
})
f.setId(dataList[i].i)
f.set('geom', geom)
@@ -1227,7 +1402,12 @@ shipFun.addShip = (dataList, zoom, map, selectlist) => {
}
}
shipFun.getShipFeaturesByIds = (shipIds) => { //根据shipId找到对应的feature
/**
* 根据船舶ID获取船舶要素
* @param {Array} shipIds - 船舶ID数组
* @returns {Array} 船舶要素数组
*/
shipFun.getShipFeaturesByIds = (shipIds) => {
const features = []
shipIds.forEach(id => {
let f = shipMap.sourceList.shipSource.getFeatureById(id)
@@ -1244,10 +1424,17 @@ shipFun.getCarFeaturesByIds = (carIds) => { //根据shipId找到对应的featur
return features
}
/**
* 绘图相关函数
* @param {number} number - 绘图参数
*/
shipFun.plotting = function (number) {
shipStyle.plotting(number)
}
//添加全屏控件
/**
* 添加全屏控件
* @param {Map} map - 地图实例
*/
shipFun.addFullScreen = (map) => {
let FullScreenControl = new FullScreen({
// units: 'metric',
@@ -1257,7 +1444,10 @@ shipFun.addFullScreen = (map) => {
map.addControl(FullScreenControl)
}
//添加比例尺控件
/**
* 添加比例尺控件
* @param {Map} map - 地图实例
*/
shipFun.addScaleLine = (map) => {
let scaleLineControl = new ScaleLine({
units: 'nautical',
@@ -1783,14 +1973,21 @@ shipFun.addMousePos = function (map) {
shipFun.hideShips = (idList) => {
let type = typeof idList
if (type == 'string') {
// 隐藏单个船舶
let f = shipMap.sourceList.shipSource.getFeatureById(idList)
if (f) {
shipMap.sourceList.shipSource.removeFeature(f)
} else if (type == 'object') {
idList.forEach((x, i) => {
let f = shipMap.sourceList.shipSource.getFeatureById(x)
}
} else if (Array.isArray(idList)) {
// 隐藏多个船舶遍历逐个移除兼容不支持removeFeatures的OpenLayers版本
if (idList.length === 0) return ''
idList.forEach(id => {
let f = shipMap.sourceList.shipSource.getFeatureById(id)
if (f) {
shipMap.sourceList.shipSource.removeFeature(f)
}
})
} else {
}
return ''
}

View File

@@ -1,57 +1,68 @@
<template>
<div class="my-ships-main" id="map">
<!--气象点击显示内容-->
<!-- 气象点击显示内容组件 -->
<OnClickWeather></OnClickWeather>
<!-- 港口鼠标悬浮显示内容 -->
<!-- 港口鼠标悬浮显示内容组件 -->
<PortHoverView></PortHoverView>
<!--船舶鼠标悬浮时显示信息-->
<!-- 船舶鼠标悬浮时显示信息组件 -->
<ShipHoverView></ShipHoverView>
<!-- 地图底部船舶类型备注框-->
<MapBottomShipType></MapBottomShipType>
<!-- 地图底部鼠标位置经纬度显示-->
<!-- 地图底部船舶类型备注框组件 -->
<MapBottomShipType :carData="carData" :shipData="shipData"></MapBottomShipType>
<!-- 地图底部鼠标位置经纬度显示组件 -->
<MapBottomMousePos></MapBottomMousePos>
<!-- 弹窗最小化-->
<!-- 弹窗最小化组件 -->
<MiniBox></MiniBox>
<!-- 船舶轨迹鼠标悬浮时显示信息-->
<!-- 船舶轨迹鼠标悬浮时显示信息组件 -->
<TrackHoverView></TrackHoverView>
<!-- 消息列表循环滚动展示-->
<!-- 消息列表循环滚动展示组件 -->
<realTimeInfo></realTimeInfo>
<!-- 显示全部港口 -->
<!-- 显示全部港口组件 -->
<ViewAllPort></ViewAllPort>
</div>
</template>
<script lang="ts">
// 地图工具函数集合
import ShipFun from '@/util/map/map.js'
// Vue装饰器
import {Component, Vue} from 'vue-property-decorator'
// 状态管理工具
import storeUtil from '@/util/store'
import OnClickWeather from "@/components/on-map-view/OnClickWeather.vue";
import PortHoverView from "@/components/on-map-view/PortHoverView.vue";
import ShipHoverView from "@/components/on-map-view/ShipHoverView.vue";
import MapBottomShipType from "@/components/on-map-view/MapBottomShipType.vue";
import MapBottomMousePos from "@/components/on-map-view/MapBottomMousePos.vue";
import MiniBox from "@/components/on-map-view/MiniBox.vue";
import TrackHoverView from "@/components/on-map-view/TrackHoverView.vue";
import realTimeInfo from "@/components/real-time-info/realTimeInfo.vue";
import HttpApi from "@/api";
import DialogUtil from "@/util/new-dialog";
import PortQueryTemp from '../new-dialog/PortQueryTemp.vue';
import {DialogType} from '@/util/temp-interface';
import {SearchLogUtil} from "@/util/tool";
import bus from '@/util/bus';
import ShipInfoTemp from '../new-dialog/ShipInfoTemp.vue';
import CarInfoTemp from '../new-dialog/CarInfoTemp.vue';
import ViewAllPort from "@/components/on-map-view/ViewAllPort.vue";
import {Style} from 'ol/style.js'
import Icon from 'ol/style/Icon'
import styleText from 'ol/style/Text' //添加文字标注
import styleFill from 'ol/style/Fill' //添加填充样式
import styleStroke from 'ol/style/Stroke' //添加填充样式
const warningImg = require('../../util/map/img/shipWarningIcon.png')
const config = require('../../../public/config/sys-config.json')
const elementResizeDetectorMaker = require("element-resize-detector")
// 组件导入
import OnClickWeather from "@/components/on-map-view/OnClickWeather.vue"; // 气象点击显示组件
import PortHoverView from "@/components/on-map-view/PortHoverView.vue"; // 港口悬浮显示组件
import ShipHoverView from "@/components/on-map-view/ShipHoverView.vue"; // 船舶悬浮显示组件
import MapBottomShipType from "@/components/on-map-view/MapBottomShipType.vue"; // 船舶类型备注组件
import MapBottomMousePos from "@/components/on-map-view/MapBottomMousePos.vue"; // 鼠标位置显示组件
import MiniBox from "@/components/on-map-view/MiniBox.vue"; // 弹窗最小化组件
import TrackHoverView from "@/components/on-map-view/TrackHoverView.vue"; // 轨迹悬浮显示组件
import realTimeInfo from "@/components/real-time-info/realTimeInfo.vue"; // 实时消息组件
import ViewAllPort from "@/components/on-map-view/ViewAllPort.vue"; // 显示全部港口组件
// API调用
import HttpApi from "@/api"; // HTTP请求API
// 弹窗工具
import DialogUtil from "@/util/new-dialog"; // 弹窗管理工具
// 弹窗模板
import PortQueryTemp from '../new-dialog/PortQueryTemp.vue'; // 港口查询弹窗模板
import ShipInfoTemp from '../new-dialog/ShipInfoTemp.vue'; // 船舶信息弹窗模板
import CarInfoTemp from '../new-dialog/CarInfoTemp.vue'; // 车辆信息弹窗模板
// 类型定义
import {DialogType,DialogType as DialogType2} from '@/util/temp-interface'; // 弹窗类型枚举'
// 工具类
import {SearchLogUtil,ShipUtils} from "@/util/tool"; // 搜索日志工具
import bus from '@/util/bus'; // 事件总线
// OpenLayers样式
import {Style} from 'ol/style.js' // 样式基础类
import Icon from 'ol/style/Icon' // 图标样式
import styleText from 'ol/style/Text' // 文字标注样式
import styleFill from 'ol/style/Fill' // 填充样式
import styleStroke from 'ol/style/Stroke' // 描边样式
// 常量定义
const warningImg = require('../../util/map/img/shipWarningIcon.png') // 警告图标
const config = require('../../../public/config/sys-config.json') // 系统配置
const elementResizeDetectorMaker = require("element-resize-detector") // 元素大小变化检测工具
// 组件装饰器,注册子组件
@Component({
components: {
OnClickWeather,
@@ -66,15 +77,24 @@ const elementResizeDetectorMaker = require("element-resize-detector")
}
})
export default class Main extends Vue {
private map: any
private map: any // 地图实例对象
private shipData = []
private carData = []
private shipData = [] // 船舶数据数组
private carData = [] // 车辆数据数组
/**
* 获取船舶数据并处理
* 1. 从API获取船舶分组数据
* 2. 提取船舶ID并获取详细信息
* 3. 处理船舶数据并添加到地图
*/
private async getShips() {
const ships: any = []
// 获取船舶分组数据
await HttpApi.getShipRanks({}).then((res: any) => {
res.forEach((item: any) => {
// 筛选有效的船舶分组
if (item.idw !== -1 && item.followShipList && item.followShipList.length) {
// 处理每个船舶的分组信息
item.followShipList.forEach(async (item2: any) => {
item2.groupName = item.name;
item2.color = item.color;
@@ -84,12 +104,16 @@ export default class Main extends Vue {
})
})
// 提取船舶ID数组
const shipIds: any[] = []
for (let i = 0; i < ships.length; i++) {
shipIds.push(ships[i].shipId)
}
// 处理船舶详细信息根据ID数量选择不同的请求方式
if (shipIds.length <= 100) {
// 单次请求获取船舶详细信息
await HttpApi.getShipMessage({shipId: shipIds.toString()}).then((res: any[]) => {
// 合并船舶详细信息到现有数据
for (let i = 0; i < ships.length; i++) {
res.forEach((item: any) => {
if (ships[i].shipId === item.shipId) {
@@ -101,10 +125,12 @@ export default class Main extends Vue {
}
})
} else {
// 批量请求获取船舶详细信息每100个ID一组
const p: any[] = []
const idGroup = this.spArray(shipIds, 100);
for (let i = 0; i < idGroup.length; i++) {
const ids = idGroup[i];
// 创建批量请求Promise
p.push(new Promise((resolve) => {
HttpApi.getShipMessage({shipId: ids.toString()}).then((res) => {
resolve(res)
@@ -113,7 +139,9 @@ export default class Main extends Vue {
}
const newGetShips: any[] = []
// 等待所有请求完成
await Promise.all(p).then((res) => {
// 合并所有返回的船舶数据
for (let i = 0; i < res.length; i++) {
const shipG: any[] = res[i];
for (let j = 0; j < shipG.length; j++) {
@@ -122,6 +150,7 @@ export default class Main extends Vue {
}
})
// 合并详细信息到现有船舶数据
for (let i = 0; i < ships.length; i++) {
newGetShips.forEach((item: any) => {
if (ships[i].shipId === item.shipId) {
@@ -132,93 +161,135 @@ export default class Main extends Vue {
})
}
}
// 更新船舶数据
this.shipData = ships
// 解析船舶数据并添加到地图
this.jiexiRanks(ships);
// 存储船舶数据到状态管理
storeUtil.setShipFleet(ships);
// 设置船舶警告(注释掉)
// this.setShipWarning(ships);
}
/**
* 获取车辆数据并添加到地图
* 1. 从API获取车辆数据
* 2. 处理车辆数据格式
* 3. 将有效的车辆数据添加到地图
* 4. 设置车辆警告
*/
private getCars() {
const carData: any = []
// 获取车辆数据
HttpApi.getCarInfo().then((res) => {
// 处理车辆数据,转换为统一格式
for (let i = 0; i < Object.keys(res).length; i++) {
const key = Object.keys(res)[i];
carData.push({
carNum: res[key].carNum,
companyName: res[key].companyName,
customsCreateDate: res[key].customsCreateDate,
gpsTime: res[key].data && res[key].data.GpsTime ? res[key].data.GpsTime : '',
latitude: res[key].data && res[key].data.Latitude ? res[key].data.Latitude : '',
longitude: res[key].data && res[key].data.Longitude ? res[key].data.Longitude : '',
entryId: res[key].entryId,
firstRegistrationDate: res[key].firstRegistrationDate,
vehicleModel: res[key].vehicleModel
carNum: res[key].carNum, // 车牌号
companyName: res[key].companyName, // 公司名称
customsCreateDate: res[key].customsCreateDate, // 海关创建日期
gpsTime: res[key].data && res[key].data.GpsTime ? res[key].data.GpsTime : '', // GPS时间
latitude: res[key].data && res[key].data.Latitude ? res[key].data.Latitude : '', // 纬度
longitude: res[key].data && res[key].data.Longitude ? res[key].data.Longitude : '', // 经度
entryId: res[key].entryId, // 入境ID
firstRegistrationDate: res[key].firstRegistrationDate, // 首次注册日期
vehicleModel: res[key].vehicleModel // 车辆型号
})
}
// 更新车辆数据
this.carData = carData
console.log(carData,'carData')
// 将有效的车辆数据(有经纬度)添加到地图
ShipFun.addCar(carData.filter((item: any) => {
return item.latitude && item.longitude
}), this.map)
// 设置车辆警告
this.setCarWarning(carData.filter((item: any) => {
return item.latitude && item.longitude
}))
})
}
/**
* 将数组按指定长度分组
* @param {Array} array - 要分组的数组
* @param {number} len - 每组的长度
* @returns {Array} 分组后的二维数组
*/
private spArray(array: any[], len: number) {
const newArray: any[] = [];
item(array, len)
// 递归分组函数
function item(array: any[], len: number) {
if (array.length && array.length > len) {
// 截取指定长度的数组并添加到结果
newArray.push(array.splice(0, len))
// 递归处理剩余数组
item(array, len);
} else if (array.length && array.length < len) {
// 将剩余数组作为一组添加到结果
newArray.push(array)
array = []
}
}
// 调用分组函数
item(array, len)
return newArray
}
/**
* 解析船舶数据并添加到地图
* @param {Array} data - 船舶数据数组
* 将船舶数据转换为地图工具需要的格式,并添加到地图
*/
private jiexiRanks(data: any) {
const newShipList: any = []
// 遍历船舶数据,转换为地图工具需要的格式
data.forEach((item: any) => {
newShipList.push({
y: item.shiptype,
m: item.mmsi,
h: item.heading,
g: item.callsign,
c: item.cog,
o: item.imo,
s: item.sog,
v: item.aisNavStatus,
l: item.length,
p: item.destPort,
b: item.breadth,
r: item.eta,
d: item.draught,
t: item.posTime,
i: item.shipId,
n: item.lon,
a: item.lat,
e: item.shipnameEn,
color: item.color,
isFleet: true
y: item.shiptype, // 船舶类型
m: item.mmsi, // MMSI号
h: item.heading, // 航向
g: item.callsign, // 呼号
c: item.cog, // 对地航向
o: item.imo, // IMO号
s: item.sog, // 对地航速
v: item.aisNavStatus, // AIS导航状态
l: item.length, // 长度
p: item.destPort, // 目的港
b: item.breadth, // 宽度
r: item.eta, // 预计到达时间
d: item.draught, // 吃水
t: item.posTime, // 位置时间
i: item.shipId, // 船舶ID
n: item.lon, // 经度
a: item.lat, // 纬度
e: item.shipnameEn, // 英文船名
color: item.color, // 颜色
isFleet: true // 是否为船队
})
})
// 深拷贝数组并添加船舶到地图
ShipFun.addShip(JSON.parse(JSON.stringify(newShipList)), 11, storeUtil.getMapInstance())
}
private setShipWarning(ships: any[]) { //将更新时间超出30天的游艇的图标设置为感叹号
const nowTime: number = (new Date()).getTime()
const temp: string[] = []
/**
* 设置船舶警告
* 将更新时间超出30天的船舶图标设置为感叹号
* @param {Array} ships - 船舶数据数组
*/
private setShipWarning(ships: any[]) {
const nowTime: number = (new Date()).getTime() // 当前时间戳
const temp: string[] = [] // 超过30天未更新的船舶ID数组
// 遍历船舶数据筛选超过30天未更新的船舶
ships.forEach((item: any) => {
// 计算船舶数据更新时间与当前时间的差值(天数)
let timeDiff: number = item.posTime ? (nowTime - (item.posTime * 1000)) / (24 * 60 * 60 * 1000) : 0
if (timeDiff > 30) {
temp.push(item.shipId)
temp.push(item.shipId) // 添加到警告列表
}
})
// 获取需要警告的船舶要素
const features = ShipFun.getShipFeaturesByIds(temp)
// 创建警告图标样式
const iconStyle = new Style({
image: new Icon({
color: '#ffffff',
@@ -227,21 +298,30 @@ export default class Main extends Vue {
scale: 0.18
})
})
// 为超过30天未更新的船舶设置警告图标
features.forEach((f: any) => {
f.setStyle(iconStyle)
})
}
/**
* 设置车辆警告
* 将更新时间超出3天的车辆图标设置为感叹号
* @param {Array} dataList - 车辆数据数组
*/
private setCarWarning(dataList: any[]) {
const nowTime: number = (new Date()).getTime()
const temp: string[] = []
const nowTime: number = (new Date()).getTime() // 当前时间戳
const temp: string[] = [] // 超过3天未更新的车辆ID数组
// 遍历车辆数据筛选超过3天未更新的车辆
dataList.forEach((item: any) => {
// 计算车辆数据更新时间与当前时间的差值(天数)
let timeDiff: number = item.gpsTime ? (nowTime - new Date(item.gpsTime).getTime()) / (24 * 60 * 60 * 1000) : 0
if (timeDiff > 3) {
temp.push(item.carNum)
temp.push(item.carNum) // 添加到警告列表
}
})
// 获取需要警告的车辆要素
const features = ShipFun.getCarFeaturesByIds(temp)
// 为每个警告车辆设置警告样式
features.forEach((f: any) => {
const iconStyle = new Style({
image: new Icon({
@@ -268,32 +348,58 @@ export default class Main extends Vue {
f.setStyle(iconStyle)
})
}
/**
* 重新显示所有数据
* 从已有的数据中重新添加船舶和车辆到地图,并设置警告
*/
private showAllData() {
//展示所有数据
//this.getShips()
//this.getCars()
// 重新解析船舶数据并添加到地图
this.jiexiRanks(this.shipData);
// 重新设置船舶警告(注释掉)
// this.setShipWarning(this.shipData);
// 重新添加车辆数据到地图
ShipFun.addCar(this.carData.filter((item: any) => {
return item.latitude && item.longitude
}), this.map)
// 重新设置车辆警告
this.setCarWarning(this.carData.filter((item: any) => {
return item.latitude && item.longitude
}))
}
/**
* 组件挂载完成后的初始化
* 1. 初始化地图
* 2. 配置地图参数
* 3. 添加地图控件
* 4. 添加地图事件监听
* 5. 获取初始数据
*/
public mounted() {
// 创建地图实例
this.map = ShipFun.creatMap()
// 更新地图大小
this.map.updateSize()
// 存储地图实例到状态管理
storeUtil.setMapInstance(this.map)
// 存储地图工具函数到状态管理
storeUtil.setMapFun(ShipFun);
// 设置地图实例到工具函数
ShipFun.setmap(this.map)
// 设置地图中心点
ShipFun.setCenter([110, 19]);
// 设置地图缩放级别
ShipFun.setZoom(9)
// 切换地图类型为天地图
ShipFun.changeMap(this.map, 'tdt')
ShipFun.createMeasureTooltip(this.map) //测距显示
// 创建测距显示工具
ShipFun.createMeasureTooltip(this.map) // 测距显示
// 添加全屏按钮(注释掉)
// ShipFun.addFullScreen(this.map) // 全屏按钮显示
// 添加比例尺
ShipFun.addScaleLine(this.map) // 添加比例尺
// 添加鼠标位置显示
ShipFun.addMousePos(this.map) // 添加显示鼠标位置的经纬度信息
// 添加地图缩放监听器
ShipFun.addZoomListener(this.map, () => {
this.map
.getControls()
@@ -309,15 +415,20 @@ export default class Main extends Vue {
}
})
})
// 添加地图点击事件监听
ShipFun.addClick(async (res: any) => {
// 处理港口点击
if (res.isType === 'port') {
let port: any = {}
// 获取港口详细信息
await HttpApi.getPortInfo(res.portId).then((res2: any) => {
port = res2
Object.assign(port, res)
})
// 计算港口坐标
port.cLon = port.portLon * 600000
port.cLat = port.portLat * 600000
// 显示港口查询弹窗
DialogUtil.showDialog(PortQueryTemp, DialogType.PORT_QUERY, {
dialogData: {
title: `${port.nameCn}-${port.nameEn}`,
@@ -331,9 +442,11 @@ export default class Main extends Vue {
} else if (res.isType === 'ship') {
let shipData: any = {}
let docData = {}
// 获取船舶详细信息
await HttpApi.getShipMessage({shipId: res.shipId}).then((res: any) => {
const SearchLog = (this as any).$SearchLog
let flag = true
// 检查是否已存在于搜索日志
if (SearchLog.length) {
for (let i = 0; i < SearchLog.length; i++) {
const item = SearchLog[i]
@@ -343,23 +456,27 @@ export default class Main extends Vue {
}
}
}
// 添加到搜索日志
if (flag) {
SearchLogUtil.addSearchLog(res[0])
}
// 存储船舶数据到状态管理
storeUtil.setShipData(res[0])
shipData = res[0]
})
// 获取船舶文档信息
await HttpApi.getShipDoc({shipId: res.shipId}).then((res: any) => {
if (res) {
docData = res
}
})
// 获取船舶中文名称
await HttpApi.getShipCnName({mmsi: shipData.mmsi}).then((res: any) => {
if (res) {
shipData.shipnameCn = res.nameCn
}
})
// 显示船舶信息弹窗
DialogUtil.showDialog(ShipInfoTemp, DialogType.SHIP_INFO, {
dialogData: {
title: shipData.shipnameCn,
@@ -384,15 +501,174 @@ export default class Main extends Vue {
})
}
})
// 当侧边栏修改的时候去地图去适应宽高的变化
// 监听地图容器大小变化,更新地图大小
elementResizeDetectorMaker().listenTo(document.getElementById('map'), () => {
this.map.updateSize()
})
// 获取初始船舶数据
this.getShips()
// 获取初始车辆数据
this.getCars()
// 监听全局事件,重新显示所有数据
bus.$on('showAllData', () => {
this.showAllData()
})
// 初始化筛选和搜索事件监听
this.initFilterEvent()
}
/**
* 初始化筛选和搜索事件
*/
private initFilterEvent() {
// 类型筛选事件
bus.$on('filterTypeChange', (data: any) => {
this.handleFilterTypeChange(data)
})
// 搜索事件
bus.$on('filterSearch', (data: any) => {
this.handleFilterSearch(data)
})
}
/**
* 处理类型筛选变化
* @param {Object} data - 筛选数据
* @param {string} data.type - 类型ship, car, yacht
* @param {boolean} data.isChecked - 是否勾选
*/
private handleFilterTypeChange(data: any) {
const { type, isChecked } = data
switch (type) {
case 'ship':
if (isChecked) {
// 显示船舶
const filteredShips = this.shipData.filter((ship: any) => {
return ship.shipTypeName !== '其他'
})
this.jiexiRanks(filteredShips)
} else {
// 只隐藏船舶,保留游艇
const shipIds = this.shipData
.filter((ship: any) => ship.shipTypeName !== '其他')
.map((ship: any) => ship.shipId)
console.log(shipIds,'shipIds')
ShipFun.hideShips(shipIds)
DialogUtil.closeCom(DialogType.SHIP_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
}
break
case 'car':
if (isChecked) {
// 显示车辆
ShipFun.addCar(this.carData.filter((item: any) => {
return item.latitude && item.longitude
}), this.map)
} else {
// 隐藏车辆
ShipFun.clearAllCar(this.map)
DialogUtil.closeCom(DialogType.CAR_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
ShipFun.delPath()
}
break
case 'yacht':
if (isChecked) {
// 显示游艇
const yachts = this.shipData.filter((ship: any) => {
return ship.shipTypeName === '其他'
})
this.jiexiRanks(yachts)
} else {
// 只隐藏游艇,保留船舶
const yachtIds = this.shipData
.filter((ship: any) => ship.shipTypeName === '其他')
.map((ship: any) => ship.shipId)
console.log(yachtIds,'yachtIds')
ShipFun.hideShips(yachtIds)
DialogUtil.closeCom(DialogType.SHIP_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
}
break
}
}
/**
* 处理搜索
* @param {Object} data - 搜索数据
* @param {string} data.type - 类型ship, car, yacht
* @param {string} data.value - 搜索值
*/
private handleFilterSearch(data: any) {
const { type, value } = data
if (!value) {
// 搜索值为空,显示所有数据
this.handleFilterTypeChange({ type, isChecked: true })
return
}
switch (type) {
case 'ship': {
// 船舶搜索
const allShips = this.shipData.filter((ship: any) => {
const isShip = ship.shipTypeName !== '其他'
return isShip
})
console.log(allShips,'所有的船舶数据')
console.log(value,'搜索值')
// 再根据mmsi包含value筛选
const filteredaShips = allShips.filter((ship: any) => {
const mmsi = ship.mmsi || ''
const isMatch = mmsi.includes(value)
return isMatch
})
// 清空现有船舶数据
console.log(filteredaShips,'筛选后的船舶数据')
ShipFun.clearallship(this.map)
if (filteredaShips.length > 0) {
this.jiexiRanks(filteredaShips)
} else {
console.log('没有匹配到船舶数据')
}
break
}
case 'car': {
// 车辆搜索
const filteredCars = this.carData.filter((car: any) => {
const carNum = car.carNum || ''
return carNum.includes(value)
})
ShipFun.clearAllCar(this.map)
ShipFun.addCar(filteredCars.filter((item: any) => {
return item.latitude && item.longitude
}), this.map)
break
}
case 'yacht': {
// 游艇搜索 - 先过滤游艇数据再根据mmsi包含value筛选
// 先过滤出游艇数据
const allYachts = this.shipData.filter((ship: any) => {
const isYacht = ship.shipTypeName === '其他'
return isYacht
})
// 再根据mmsi包含value筛选
const filteredYachts = allYachts.filter((ship: any) => {
const mmsi = ship.mmsi || ''
const isMatch = mmsi.includes(value)
return isMatch
})
// 清空现有船舶数据
ShipFun.clearallship(this.map)
if (filteredYachts.length > 0) {
// 直接调用jiexiRanks方法处理数据并添加到地图
this.jiexiRanks(filteredYachts)
} else {
console.log('没有匹配到游艇数据')
}
break
}
}
}
}
</script>