CN-1563 feat: 轨迹地图部分代码
This commit is contained in:
50
nginx_server.conf
Normal file
50
nginx_server.conf
Normal file
@@ -0,0 +1,50 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
#access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
client_max_body_size 3072m;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://127.0.0.1:8090/;
|
||||
}
|
||||
|
||||
#error_page 404 /404.html;
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
||||
#
|
||||
#location ~ \.php$ {
|
||||
# proxy_pass http://127.0.0.1;
|
||||
#}
|
||||
|
||||
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
||||
#
|
||||
#location ~ \.php$ {
|
||||
# root html;
|
||||
# fastcgi_pass 127.0.0.1:9000;
|
||||
# fastcgi_index index.php;
|
||||
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
|
||||
# include fastcgi_params;
|
||||
#}
|
||||
|
||||
# deny access to .htaccess files, if Apache's document root
|
||||
# concurs with nginx's one
|
||||
#
|
||||
#location ~ /\.ht {
|
||||
# deny all;
|
||||
#}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
"dexie": "~3.2.0",
|
||||
"echarts": "^5.1.1",
|
||||
"element-plus": "~1.0.2-beta.71",
|
||||
"h3-js": "~3.7.2",
|
||||
"lib-flexible": "^0.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"maplibre-gl": "3.6.2",
|
||||
|
||||
@@ -300,4 +300,38 @@ if (openMock) {
|
||||
}
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(BASE_CONFIG.baseUrl + 'v1/locationIntelligence/baseStation.*'), 'get', function (requestObj) {
|
||||
return {
|
||||
msg: 'success',
|
||||
code: 200,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
longitude: 116.38,
|
||||
latitude: 39.9
|
||||
},
|
||||
{
|
||||
longitude: 116.39,
|
||||
latitude: 39.9
|
||||
},
|
||||
{
|
||||
longitude: 116.383,
|
||||
latitude: 39.886
|
||||
},
|
||||
{
|
||||
longitude: 116.378,
|
||||
latitude: 39.902
|
||||
},
|
||||
{
|
||||
longitude: 116.369,
|
||||
latitude: 39.91
|
||||
},
|
||||
{
|
||||
longitude: 116.38,
|
||||
latitude: 39.91
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -356,7 +356,8 @@ export const api = {
|
||||
map: apiVersion + '/locationIntelligence/map',
|
||||
density: apiVersion + '/locationIntelligence/population/density',
|
||||
trend: apiVersion + '/locationIntelligence/active/trend',
|
||||
count: apiVersion + '/locationIntelligence/active/count'
|
||||
count: apiVersion + '/locationIntelligence/active/count',
|
||||
baseStation: apiVersion + '/locationIntelligence/baseStation'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,19 +106,19 @@
|
||||
<template v-if="tooltip.type === tooltipType.hexagon">
|
||||
<div class="body__item">
|
||||
<div class="item__label">Number</div>
|
||||
<div class="item__value">{{currentPolygon.count}}</div>
|
||||
<div class="item__value">{{currentPolygon.number}}</div>
|
||||
</div>
|
||||
<div class="body__item">
|
||||
<div class="item__label">Locals</div>
|
||||
<div class="item__value">{{currentPolygon.count}}</div>
|
||||
<div class="item__value">{{currentPolygon.number}}</div>
|
||||
</div>
|
||||
<div class="body__item">
|
||||
<div class="item__label">Visitors</div>
|
||||
<div class="item__value">{{currentPolygon.count}}</div>
|
||||
<div class="item__value">{{currentPolygon.number}}</div>
|
||||
</div>
|
||||
<div class="body__item">
|
||||
<div class="item__label">Roamers</div>
|
||||
<div class="item__value">{{currentPolygon.count}}</div>
|
||||
<div class="item__value">{{currentPolygon.number}}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="tooltip.type === tooltipType.human">
|
||||
@@ -171,8 +171,6 @@
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { getNowTime, getSecond } from '@/utils/date-util'
|
||||
import maplibregl from 'maplibre-gl'
|
||||
import { MapboxOverlay } from '@deck.gl/mapbox'
|
||||
import { H3HexagonLayer } from '@deck.gl/geo-layers'
|
||||
import mapStyle from '@/views/charts2/charts/entityDetail/mapStyle'
|
||||
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
@@ -185,9 +183,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { urlParamsHandler, overwriteUrl } from '@/utils/tools'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import {
|
||||
geoToH3
|
||||
} from 'h3-js'
|
||||
import { h3ToGeoBoundary } from 'h3-js'
|
||||
|
||||
export default {
|
||||
name: 'Location',
|
||||
@@ -235,41 +231,39 @@ export default {
|
||||
// 最先渲染右上角饼图
|
||||
await this.renderDensityPie()
|
||||
// 然后渲染地图的色块、基站、人(包括右侧关注列表),最后渲染右上角折线图
|
||||
map.on('load', function () {
|
||||
map.on('load', async function () {
|
||||
console.info('map loaded')
|
||||
// 地图色块
|
||||
const hexagonData = _this.queryHexagon()
|
||||
const hexagon = new MapboxOverlay({
|
||||
layers: [
|
||||
new H3HexagonLayer({
|
||||
id: 'hex',
|
||||
data: hexagonData,
|
||||
elevationScale: 20,
|
||||
extruded: false,
|
||||
filled: true,
|
||||
getFillColor: d => _this.getHexagonFillColor(d.number),
|
||||
getLineColor: d => _this.getHexagonLineColor(d.number),
|
||||
getLineWidth: 10,
|
||||
getHexagon: d => d.hexId,
|
||||
wireframe: false,
|
||||
pickable: true
|
||||
})
|
||||
]
|
||||
/* 地图色块 */
|
||||
const hexagonData = await _this.queryHexagon()
|
||||
// 将查到的h3hexagon数据转为geojson
|
||||
const polygonSourceData = _this.hexagonDataConverter(hexagonData)
|
||||
map.addSource('hexGrid', {
|
||||
type: 'geojson',
|
||||
data: polygonSourceData
|
||||
})
|
||||
map.addControl(hexagon)
|
||||
// TODO 六边形边框,考虑加一层line layer
|
||||
map.addLayer({
|
||||
id: 'hexagon',
|
||||
type: 'fill',
|
||||
source: 'hexGrid',
|
||||
layout: {},
|
||||
paint: {
|
||||
'fill-color': ['get', 'color'],
|
||||
'fill-opacity': ['case', ['boolean', ['feature-state', 'hover'], false], 1, 0.6]
|
||||
}
|
||||
})
|
||||
// 六边形的鼠标事件
|
||||
_this.bindHexagonEvents()
|
||||
|
||||
// 地图上的基站
|
||||
/* 地图上的基站 */
|
||||
const baseStationData = await _this.queryBaseStation()
|
||||
/* 地图上的人 */
|
||||
|
||||
// 地图上的人
|
||||
/* 右侧关注列表 */
|
||||
|
||||
// 右侧关注列表
|
||||
|
||||
// 右上角折线图
|
||||
/* 右上角折线图 */
|
||||
_this.renderActiveSubscribersLine()
|
||||
})
|
||||
function hoverTrigger (source, id, hover) {
|
||||
map.setFeatureState({ source, id }, { hover })
|
||||
}
|
||||
},
|
||||
async renderDensityPie () {
|
||||
const params = {
|
||||
@@ -349,6 +343,19 @@ export default {
|
||||
}
|
||||
return []
|
||||
},
|
||||
async queryBaseStation () {
|
||||
this.loading.baseStationLoading = true
|
||||
try {
|
||||
const response = await axios.get(api.location.baseStation)
|
||||
return response.data.data.list
|
||||
} catch (e) {
|
||||
this.errorMsgHandler(e)
|
||||
console.error(e)
|
||||
} finally {
|
||||
this.loading.hexagonLoading = false
|
||||
}
|
||||
return []
|
||||
},
|
||||
// 先使用min=0的等宽分组法,若后续出现特大或特小的异常值导致等宽分组效果不理想,考虑用分位数分组法
|
||||
calculateValueRamp (data) {
|
||||
const max = _.maxBy(data, d => d.number)
|
||||
@@ -366,6 +373,27 @@ export default {
|
||||
}
|
||||
return result
|
||||
},
|
||||
hexagonDataConverter (data) {
|
||||
const features = data.map((d, i) => ({
|
||||
id: i + 1,
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [
|
||||
h3ToGeoBoundary(d.hexId, true)
|
||||
]
|
||||
},
|
||||
properties: {
|
||||
hexId: d.hexId,
|
||||
number: d.number,
|
||||
color: this.getHexagonFillColor(d.number)
|
||||
}
|
||||
}))
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: features
|
||||
}
|
||||
},
|
||||
getHexagonFillColor (number) {
|
||||
const ramp = this.pieValueRamp.filter(r => number >= r.start && number < r.end)
|
||||
if (ramp.length > 0) {
|
||||
@@ -373,12 +401,37 @@ export default {
|
||||
}
|
||||
return [255, 255, 255]
|
||||
},
|
||||
getHexagonLineColor (number) {
|
||||
const ramp = this.pieValueRamp.filter(r => number >= r.start && number < r.end)
|
||||
if (ramp.length > 0) {
|
||||
return ramp[0].color.split(',').map(n => n / 1.5)
|
||||
bindHexagonEvents () {
|
||||
const _this = this
|
||||
this.mapChart.on('mouseenter', 'hexagon', () => {
|
||||
_this.tooltip.mouseIsInPolygon = true
|
||||
})
|
||||
this.mapChart.on('mouseleave', 'hexagon', () => {
|
||||
_this.tooltip.showPolygonTooltip = false
|
||||
_this.tooltip.mouseIsInPolygon = false
|
||||
// 去掉上一块的高亮
|
||||
hoverTrigger('hexGrid', _this.currentPolygon.id, false)
|
||||
})
|
||||
this.mapChart.on('mousemove', 'hexagon', ({ point, originalEvent, features }) => {
|
||||
if (!_this.tooltip.mouseIsInMarker) {
|
||||
_this.tooltip.showPolygonTooltip = true
|
||||
_this.tooltip.type = _this.tooltipType.hexagon
|
||||
if (_this.tooltip.type === _this.tooltipType.hexagon && _this.currentPolygon.id && _this.currentPolygon.id !== features[0].id) {
|
||||
// 去掉上一块的高亮
|
||||
hoverTrigger('hexGrid', _this.currentPolygon.id, false)
|
||||
}
|
||||
_this.currentPolygon = features[0].properties
|
||||
_this.currentPolygon.id = features[0].id
|
||||
_this.tooltip.x = originalEvent.clientX + 15
|
||||
_this.tooltip.y = originalEvent.clientY + 5
|
||||
|
||||
// 鼠标滑过高亮
|
||||
hoverTrigger('hexGrid', _this.currentPolygon.id, true)
|
||||
}
|
||||
})
|
||||
function hoverTrigger (source, id, hover) {
|
||||
_this.mapChart.setFeatureState({ source, id }, { hover })
|
||||
}
|
||||
return [255, 255, 255]
|
||||
},
|
||||
tooltipMouseEnter () {
|
||||
},
|
||||
@@ -418,7 +471,10 @@ export default {
|
||||
computed: {
|
||||
tooltipHeaderColor () {
|
||||
if (this.tooltip.type === this.tooltipType.hexagon) {
|
||||
return `rgba(${this.pieColorRamp[this.currentPolygon.level]},.8)`
|
||||
const color = this.currentPolygon.color.split(',')
|
||||
color[0] = color[0].split('[')[1]
|
||||
color[2] = color[2].split(']')[0]
|
||||
return `rgba(${color.join(',')},.8)`
|
||||
} else if (this.tooltip.type === this.tooltipType.human) {
|
||||
return '#38ACD2'
|
||||
} else if (this.tooltip.type === this.tooltipType.baseStation) {
|
||||
@@ -469,7 +525,8 @@ export default {
|
||||
type: ''
|
||||
})
|
||||
// const pieColorRamp = ['186,224,255', '105,177,255', '22,119,255', '0,62,179', '0,29,102']
|
||||
const pieColorRamp = ['156,174,29', '241,198,0', '89,202,242', '63,133,186', '37,55,128']
|
||||
// const pieColorRamp = ['156,174,29', '241,198,0', '89,202,242', '63,133,186', '37,55,128']
|
||||
const pieColorRamp = ['196,214,59', '190,230,255', '135,206,250', '63,133,186', '37,55,128']
|
||||
const pieValueRamp = ref([])
|
||||
const boundaryBox = ref({}) // minLongitude、maxLongitude、minLatitude、maxLatitude
|
||||
const mapChart = shallowRef({})
|
||||
@@ -534,6 +591,9 @@ export default {
|
||||
padding: 0 20px 20px;
|
||||
position: relative;
|
||||
|
||||
.maplibregl-canvas:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
.geo-tools {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user