forked from github/dataease
feat(图表): 符号地图支持自定义符号 #10408
This commit is contained in:
parent
543c1d2c96
commit
1e371bccdc
@ -318,6 +318,10 @@ declare interface ChartBasicStyle {
|
|||||||
* 缩放等级
|
* 缩放等级
|
||||||
*/
|
*/
|
||||||
zoomLevel: number
|
zoomLevel: number
|
||||||
|
/**
|
||||||
|
* 符号地图自定义符号形状
|
||||||
|
*/
|
||||||
|
customIcon: string
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 表头属性
|
* 表头属性
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, PropType, reactive, watch } from 'vue'
|
import { computed, onMounted, PropType, reactive, watch, ref } from 'vue'
|
||||||
import {
|
import {
|
||||||
COLOR_PANEL,
|
COLOR_PANEL,
|
||||||
DEFAULT_BASIC_STYLE,
|
DEFAULT_BASIC_STYLE,
|
||||||
DEFAULT_MISC
|
DEFAULT_MISC
|
||||||
} from '@/views/chart/components/editor/util/chart'
|
} from '@/views/chart/components/editor/util/chart'
|
||||||
|
import icon_info_outlined from '@/assets/svg/icon_info_outlined.svg'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import CustomColorStyleSelect from '@/views/chart/components/editor/editor-style/components/CustomColorStyleSelect.vue'
|
import CustomColorStyleSelect from '@/views/chart/components/editor/editor-style/components/CustomColorStyleSelect.vue'
|
||||||
import { cloneDeep, defaultsDeep } from 'lodash-es'
|
import { cloneDeep, debounce, defaultsDeep } from 'lodash-es'
|
||||||
import { SERIES_NUMBER_FIELD } from '@antv/s2'
|
import { SERIES_NUMBER_FIELD } from '@antv/s2'
|
||||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { isNumber } from 'mathjs'
|
import { isNumber } from 'mathjs'
|
||||||
import { ElMessage } from 'element-plus-secondary'
|
import { ElMessage, UploadProps } from 'element-plus-secondary'
|
||||||
|
import { svgStrToUrl } from '../../../js/util'
|
||||||
|
|
||||||
const dvMainStore = dvMainStoreWithOut()
|
const dvMainStore = dvMainStoreWithOut()
|
||||||
const { batchOptStatus } = storeToRefs(dvMainStore)
|
const { batchOptStatus } = storeToRefs(dvMainStore)
|
||||||
@ -40,21 +42,9 @@ const state = reactive({
|
|||||||
fieldColumnWidth: {
|
fieldColumnWidth: {
|
||||||
fieldId: '',
|
fieldId: '',
|
||||||
width: 0
|
width: 0
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(
|
|
||||||
[
|
|
||||||
() => props.chart.customAttr.basicStyle,
|
|
||||||
() => props.chart.customAttr.misc,
|
|
||||||
() => props.chart.customAttr.tableHeader,
|
|
||||||
() => props.chart.xAxis,
|
|
||||||
() => props.chart.yAxis
|
|
||||||
],
|
|
||||||
() => {
|
|
||||||
init()
|
|
||||||
},
|
},
|
||||||
{ deep: true }
|
fileList: []
|
||||||
)
|
})
|
||||||
const emit = defineEmits(['onBasicStyleChange', 'onMiscChange'])
|
const emit = defineEmits(['onBasicStyleChange', 'onMiscChange'])
|
||||||
const changeBasicStyle = (prop?: string, requestData = false) => {
|
const changeBasicStyle = (prop?: string, requestData = false) => {
|
||||||
emit('onBasicStyleChange', { data: state.basicStyleForm, requestData }, prop)
|
emit('onBasicStyleChange', { data: state.basicStyleForm, requestData }, prop)
|
||||||
@ -82,6 +72,13 @@ const init = () => {
|
|||||||
const basicStyle = cloneDeep(props.chart.customAttr.basicStyle)
|
const basicStyle = cloneDeep(props.chart.customAttr.basicStyle)
|
||||||
const miscStyle = cloneDeep(props.chart.customAttr.misc)
|
const miscStyle = cloneDeep(props.chart.customAttr.misc)
|
||||||
configCompat(basicStyle)
|
configCompat(basicStyle)
|
||||||
|
if (
|
||||||
|
basicStyle.mapSymbol === 'custom' &&
|
||||||
|
state.basicStyleForm.customIcon !== basicStyle.customIcon
|
||||||
|
) {
|
||||||
|
const file = svgStrToUrl(basicStyle.customIcon)
|
||||||
|
file && (state.fileList[0] = { url: file })
|
||||||
|
}
|
||||||
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
|
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
|
||||||
state.miscForm = defaultsDeep(miscStyle, cloneDeep(DEFAULT_MISC)) as ChartMiscAttr
|
state.miscForm = defaultsDeep(miscStyle, cloneDeep(DEFAULT_MISC)) as ChartMiscAttr
|
||||||
if (!state.customColor) {
|
if (!state.customColor) {
|
||||||
@ -90,6 +87,18 @@ const init = () => {
|
|||||||
}
|
}
|
||||||
initTableColumnWidth()
|
initTableColumnWidth()
|
||||||
}
|
}
|
||||||
|
const debouncedInit = debounce(init, 500)
|
||||||
|
watch(
|
||||||
|
[
|
||||||
|
() => props.chart.customAttr.basicStyle,
|
||||||
|
() => props.chart.customAttr.misc,
|
||||||
|
() => props.chart.customAttr.tableHeader,
|
||||||
|
() => props.chart.xAxis,
|
||||||
|
() => props.chart.yAxis
|
||||||
|
],
|
||||||
|
debouncedInit,
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
const configCompat = (basicStyle: ChartBasicStyle) => {
|
const configCompat = (basicStyle: ChartBasicStyle) => {
|
||||||
// 悬浮改为图例和缩放按钮
|
// 悬浮改为图例和缩放按钮
|
||||||
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
|
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
|
||||||
@ -230,8 +239,42 @@ const mapSymbolOptions = [
|
|||||||
{ name: t('chart.map_symbol_pentagon'), value: 'pentagon' },
|
{ name: t('chart.map_symbol_pentagon'), value: 'pentagon' },
|
||||||
{ name: t('chart.map_symbol_hexagon'), value: 'hexagon' },
|
{ name: t('chart.map_symbol_hexagon'), value: 'hexagon' },
|
||||||
{ name: t('chart.map_symbol_octagon'), value: 'octogon' },
|
{ name: t('chart.map_symbol_octagon'), value: 'octogon' },
|
||||||
{ name: t('chart.line_symbol_diamond'), value: 'rhombus' }
|
{ name: t('chart.line_symbol_diamond'), value: 'rhombus' },
|
||||||
|
{ name: t('commons.custom'), value: 'custom' }
|
||||||
]
|
]
|
||||||
|
const iconUpload = ref()
|
||||||
|
const onIconChange: UploadProps['onChange'] = async uploadFile => {
|
||||||
|
const rawFile = uploadFile.raw
|
||||||
|
let validIcon = true
|
||||||
|
if (rawFile.type !== 'image/svg+xml') {
|
||||||
|
ElMessage.error('请选择正确的 SVG 文件!')
|
||||||
|
validIcon = false
|
||||||
|
}
|
||||||
|
if (rawFile.size / 1024 / 1024 > 1) {
|
||||||
|
ElMessage.error('文件大小不能超过 1MB!')
|
||||||
|
validIcon = false
|
||||||
|
}
|
||||||
|
if (!validIcon) {
|
||||||
|
iconUpload.value?.clearFiles()
|
||||||
|
state.fileList.splice(0)
|
||||||
|
const svg = state.basicStyleForm.customIcon
|
||||||
|
if (svg) {
|
||||||
|
const file = svgStrToUrl(svg)
|
||||||
|
file && (state.fileList[0] = { url: file })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.basicStyleForm.customIcon = await rawFile.text()
|
||||||
|
changeBasicStyle('customIcon')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeMapSymbol = () => {
|
||||||
|
if (state.basicStyleForm.mapSymbol === 'custom' && state.basicStyleForm.customIcon) {
|
||||||
|
const file = svgStrToUrl(state.basicStyleForm.customIcon)
|
||||||
|
file && (state.fileList[0] = { url: file })
|
||||||
|
}
|
||||||
|
changeBasicStyle('mapSymbol')
|
||||||
|
}
|
||||||
|
|
||||||
const customSymbolicMapSizeRange = computed(() => {
|
const customSymbolicMapSizeRange = computed(() => {
|
||||||
let { extBubble } = JSON.parse(JSON.stringify(props.chart))
|
let { extBubble } = JSON.parse(JSON.stringify(props.chart))
|
||||||
@ -477,11 +520,24 @@ onMounted(() => {
|
|||||||
<div class="map-flow-style" v-if="showProperty('symbolicMapStyle')">
|
<div class="map-flow-style" v-if="showProperty('symbolicMapStyle')">
|
||||||
<el-row style="flex: 1">
|
<el-row style="flex: 1">
|
||||||
<el-col>
|
<el-col>
|
||||||
<el-form-item :label="'符号形状'" class="form-item" :class="'form-item-' + themes">
|
<el-form-item class="form-item" :class="'form-item-' + themes">
|
||||||
|
<template v-if="state.basicStyleForm.mapSymbol === 'custom'" #label>
|
||||||
|
<span class="data-area-label">
|
||||||
|
<span style="margin-right: 4px">符号形状</span>
|
||||||
|
<el-tooltip class="item" effect="dark" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
<div>支持 1MB 以内的 SVG 文件</div>
|
||||||
|
</template>
|
||||||
|
<el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }">
|
||||||
|
<Icon name="icon_info_outlined"><icon_info_outlined class="svg-icon" /></Icon>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<el-select
|
<el-select
|
||||||
:effect="themes"
|
:effect="themes"
|
||||||
v-model="state.basicStyleForm.mapSymbol"
|
v-model="state.basicStyleForm.mapSymbol"
|
||||||
@change="changeBasicStyle('mapSymbol')"
|
@change="changeMapSymbol()"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in mapSymbolOptions"
|
v-for="item in mapSymbolOptions"
|
||||||
@ -493,6 +549,28 @@ onMounted(() => {
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
<el-row style="flex: 1" v-if="state.basicStyleForm.mapSymbol === 'custom'">
|
||||||
|
<el-col>
|
||||||
|
<el-form-item class="form-item uploader" :class="'form-item-' + themes">
|
||||||
|
<div class="avatar-uploader-container" :class="`img-area_${themes}`">
|
||||||
|
<el-upload
|
||||||
|
action="#"
|
||||||
|
accept=".svg"
|
||||||
|
class="avatar-uploader"
|
||||||
|
list-type="picture-card"
|
||||||
|
ref="iconUpload"
|
||||||
|
:effect="themes"
|
||||||
|
:auto-upload="false"
|
||||||
|
:file-list="state.fileList"
|
||||||
|
:on-change="onIconChange"
|
||||||
|
:limit="1"
|
||||||
|
>
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
<div class="alpha-setting">
|
<div class="alpha-setting">
|
||||||
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
|
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
|
||||||
{{ t('chart.size') }}
|
{{ t('chart.size') }}
|
||||||
@ -550,7 +628,7 @@ onMounted(() => {
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="alpha-setting">
|
<div v-if="state.basicStyleForm.mapSymbol !== 'custom'" class="alpha-setting">
|
||||||
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
|
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
|
||||||
{{ t('chart.not_alpha') }}
|
{{ t('chart.not_alpha') }}
|
||||||
</label>
|
</label>
|
||||||
@ -568,7 +646,7 @@ onMounted(() => {
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="alpha-setting">
|
<div v-if="state.basicStyleForm.mapSymbol !== 'custom'" class="alpha-setting">
|
||||||
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
|
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
|
||||||
{{ t('visualization.borderWidth') }}
|
{{ t('visualization.borderWidth') }}
|
||||||
</label>
|
</label>
|
||||||
@ -1345,9 +1423,6 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.form-item {
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-picker-style {
|
.color-picker-style {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 1003;
|
z-index: 1003;
|
||||||
@ -1424,4 +1499,99 @@ onMounted(() => {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.avatar-uploader-container {
|
||||||
|
:deep(.ed-upload--picture-card) {
|
||||||
|
background: #eff0f1;
|
||||||
|
border: 1px dashed #dee0e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.ed-icon {
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.ed-icon {
|
||||||
|
color: var(--ed-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.img-area_dark {
|
||||||
|
:deep(.ed-upload-list__item).is-ready {
|
||||||
|
border-color: #434343;
|
||||||
|
}
|
||||||
|
:deep(.ed-upload--picture-card) {
|
||||||
|
background: #373737;
|
||||||
|
border-color: #434343;
|
||||||
|
.ed-icon {
|
||||||
|
color: #ebebeb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.img-area_light {
|
||||||
|
:deep(.ed-upload-list__item).is-ready {
|
||||||
|
border-color: #dee0e3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.ed-upload-list__item-preview) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
:deep(.ed-upload-list__item-delete) {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
:deep(.ed-upload-list__item-status-label) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
:deep(.ed-icon--close-tip) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.avatar-uploader {
|
||||||
|
width: 90px;
|
||||||
|
height: 80px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.avatar-uploader {
|
||||||
|
width: 90px;
|
||||||
|
:deep(.ed-upload) {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
line-height: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ed-upload-list li) {
|
||||||
|
width: 80px !important;
|
||||||
|
height: 80px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ed-upload--picture-card) {
|
||||||
|
background: #eff0f1;
|
||||||
|
border: 1px dashed #dee0e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.ed-icon {
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.ed-icon {
|
||||||
|
color: var(--ed-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.uploader {
|
||||||
|
:deep(.ed-form-item__content) {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.data-area-label {
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
L7Wrapper
|
L7Wrapper
|
||||||
} from '@/views/chart/components/js/panel/types/impl/l7'
|
} from '@/views/chart/components/js/panel/types/impl/l7'
|
||||||
import { MAP_EDITOR_PROPERTY_INNER } from '@/views/chart/components/js/panel/charts/map/common'
|
import { MAP_EDITOR_PROPERTY_INNER } from '@/views/chart/components/js/panel/charts/map/common'
|
||||||
import { hexColorToRGBA, parseJson } from '@/views/chart/components/js/util'
|
import { hexColorToRGBA, parseJson, svgStrToUrl } from '@/views/chart/components/js/util'
|
||||||
import { deepCopy } from '@/utils/utils'
|
import { deepCopy } from '@/utils/utils'
|
||||||
import { GaodeMap } from '@antv/l7-maps'
|
import { GaodeMap } from '@antv/l7-maps'
|
||||||
import { Scene } from '@antv/l7-scene'
|
import { Scene } from '@antv/l7-scene'
|
||||||
@ -101,8 +101,10 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
|||||||
if (basicStyle.autoFit === false) {
|
if (basicStyle.autoFit === false) {
|
||||||
center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
|
center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
|
||||||
}
|
}
|
||||||
// 底层
|
const chartObj = drawOption.chartObj as unknown as L7Wrapper<L7Config, Scene>
|
||||||
const scene = new Scene({
|
let scene = chartObj?.getScene()
|
||||||
|
if (!scene) {
|
||||||
|
scene = new Scene({
|
||||||
id: container,
|
id: container,
|
||||||
logoVisible: false,
|
logoVisible: false,
|
||||||
map: new GaodeMap({
|
map: new GaodeMap({
|
||||||
@ -114,6 +116,16 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
|||||||
showLabel: !(basicStyle.showLabel === false)
|
showLabel: !(basicStyle.showLabel === false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
if (scene.getLayers()?.length) {
|
||||||
|
await scene.removeAllLayer()
|
||||||
|
scene.setCenter(center)
|
||||||
|
scene.setPitch(miscStyle.mapPitch)
|
||||||
|
scene.setZoom(basicStyle.autoFit === false ? basicStyle.zoomLevel : 2.5)
|
||||||
|
scene.setMapStyle(mapStyle)
|
||||||
|
scene.map.showLabel = !(basicStyle.showLabel === false)
|
||||||
|
}
|
||||||
|
}
|
||||||
mapRendering(container)
|
mapRendering(container)
|
||||||
scene.once('loaded', () => {
|
scene.once('loaded', () => {
|
||||||
mapRendered(container)
|
mapRendered(container)
|
||||||
@ -122,7 +134,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
|||||||
return new L7Wrapper(scene, undefined)
|
return new L7Wrapper(scene, undefined)
|
||||||
}
|
}
|
||||||
const configList: L7Config[] = []
|
const configList: L7Config[] = []
|
||||||
const symbolicLayer = this.buildSymbolicLayer(chart, basicStyle)
|
const symbolicLayer = await this.buildSymbolicLayer(chart, scene)
|
||||||
configList.push(symbolicLayer)
|
configList.push(symbolicLayer)
|
||||||
const tooltipLayer = this.buildTooltip(chart, container, symbolicLayer)
|
const tooltipLayer = this.buildTooltip(chart, container, symbolicLayer)
|
||||||
if (tooltipLayer) {
|
if (tooltipLayer) {
|
||||||
@ -179,7 +191,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
|||||||
* 构建符号图层
|
* 构建符号图层
|
||||||
* @param chart
|
* @param chart
|
||||||
*/
|
*/
|
||||||
buildSymbolicLayer = (chart, basicStyle) => {
|
buildSymbolicLayer = async (chart, scene: Scene) => {
|
||||||
|
const { basicStyle } = parseJson(chart.customAttr) as ChartAttr
|
||||||
const xAxis = deepCopy(chart.xAxis)
|
const xAxis = deepCopy(chart.xAxis)
|
||||||
const xAxisExt = deepCopy(chart.xAxisExt)
|
const xAxisExt = deepCopy(chart.xAxisExt)
|
||||||
const extBubble = deepCopy(chart.extBubble)
|
const extBubble = deepCopy(chart.extBubble)
|
||||||
@ -225,10 +238,24 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
|||||||
y: xAxis[1].dataeaseName
|
y: xAxis[1].dataeaseName
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.shape(mapSymbol)
|
|
||||||
.active(true)
|
.active(true)
|
||||||
if (xAxisExt[0]?.dataeaseName) {
|
if (xAxisExt[0]?.dataeaseName) {
|
||||||
pointLayer.color(xAxisExt[0]?.dataeaseName, colorsWithAlpha)
|
if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) {
|
||||||
|
const parser = new DOMParser()
|
||||||
|
for (let index = 0; index < Math.min(colorsWithAlpha.length, colorIndex + 1); index++) {
|
||||||
|
const color = colorsWithAlpha[index]
|
||||||
|
const fillRegex = /(fill="[^"]*")/g
|
||||||
|
const svgStr = basicStyle.customIcon.replace(fillRegex, '')
|
||||||
|
const doc = parser.parseFromString(svgStr, 'image/svg+xml')
|
||||||
|
const svgEle = doc.documentElement
|
||||||
|
svgEle.setAttribute('fill', color)
|
||||||
|
await scene.addImage(`icon-${color}`, svgStrToUrl(svgEle.outerHTML))
|
||||||
|
}
|
||||||
|
pointLayer.shape('color', c => {
|
||||||
|
return `icon-${c}`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
pointLayer.shape(mapSymbol).color(xAxisExt[0]?.dataeaseName, colorsWithAlpha)
|
||||||
pointLayer.style({
|
pointLayer.style({
|
||||||
stroke: {
|
stroke: {
|
||||||
field: 'color'
|
field: 'color'
|
||||||
@ -236,6 +263,18 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
|||||||
strokeWidth: mapSymbolStrokeWidth,
|
strokeWidth: mapSymbolStrokeWidth,
|
||||||
opacity: mapSymbolOpacity / 10
|
opacity: mapSymbolOpacity / 10
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) {
|
||||||
|
const parser = new DOMParser()
|
||||||
|
const color = colorsWithAlpha[0]
|
||||||
|
const fillRegex = /(fill="[^"]*")/g
|
||||||
|
const svgStr = basicStyle.customIcon.replace(fillRegex, '')
|
||||||
|
const doc = parser.parseFromString(svgStr, 'image/svg+xml')
|
||||||
|
const svgEle = doc.documentElement
|
||||||
|
svgEle.setAttribute('fill', color)
|
||||||
|
await scene.addImage(`customIcon`, svgStrToUrl(svgEle.outerHTML))
|
||||||
|
pointLayer.shape('customIcon')
|
||||||
} else {
|
} else {
|
||||||
pointLayer.color(colorsWithAlpha[0])
|
pointLayer.color(colorsWithAlpha[0])
|
||||||
pointLayer.style({
|
pointLayer.style({
|
||||||
@ -244,6 +283,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
|||||||
opacity: mapSymbolOpacity / 10
|
opacity: mapSymbolOpacity / 10
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (sizeKey) {
|
if (sizeKey) {
|
||||||
pointLayer.size('size', [mapSymbolSizeMin, mapSymbolSizeMax])
|
pointLayer.size('size', [mapSymbolSizeMin, mapSymbolSizeMax])
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,6 +28,10 @@ export class L7Wrapper<
|
|||||||
> extends ChartWrapper<S> {
|
> extends ChartWrapper<S> {
|
||||||
private readonly config: O | Array<O>
|
private readonly config: O | Array<O>
|
||||||
private readonly scene: S | null = null
|
private readonly scene: S | null = null
|
||||||
|
|
||||||
|
public getScene() {
|
||||||
|
return this.scene
|
||||||
|
}
|
||||||
constructor(scene: S, l7config: O | Array<O> | undefined) {
|
constructor(scene: S, l7config: O | Array<O> | undefined) {
|
||||||
super()
|
super()
|
||||||
this.chartInstance = scene
|
this.chartInstance = scene
|
||||||
@ -42,6 +46,15 @@ export class L7Wrapper<
|
|||||||
}
|
}
|
||||||
render = () => {
|
render = () => {
|
||||||
if (this.scene && this.config) {
|
if (this.scene && this.config) {
|
||||||
|
if (this.scene.loaded) {
|
||||||
|
if (Array.isArray(this.config)) {
|
||||||
|
this.config?.forEach(p => {
|
||||||
|
this.handleConfig(p)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.handleConfig(this.config)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
this.scene.on('loaded', () => {
|
this.scene.on('loaded', () => {
|
||||||
if (Array.isArray(this.config)) {
|
if (Array.isArray(this.config)) {
|
||||||
this.config?.forEach(p => {
|
this.config?.forEach(p => {
|
||||||
@ -53,6 +66,7 @@ export class L7Wrapper<
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleConfig = (config: L7Config) => {
|
handleConfig = (config: L7Config) => {
|
||||||
if (config) {
|
if (config) {
|
||||||
|
@ -1038,3 +1038,14 @@ export function convertToAlphaColor(color: string, alpha: number): string {
|
|||||||
}
|
}
|
||||||
return 'rgba(255,255,255,1)'
|
return 'rgba(255,255,255,1)'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function svgStrToUrl(svgStr: string): string {
|
||||||
|
let file = ''
|
||||||
|
try {
|
||||||
|
if (svgStr) {
|
||||||
|
const blob = new Blob([svgStr], { type: 'image/svg+xml' })
|
||||||
|
file = URL.createObjectURL(blob)
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
@ -299,7 +299,6 @@ let mapL7Timer: number
|
|||||||
const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callback) => {
|
const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callback) => {
|
||||||
mapL7Timer && clearTimeout(mapL7Timer)
|
mapL7Timer && clearTimeout(mapL7Timer)
|
||||||
mapL7Timer = setTimeout(async () => {
|
mapL7Timer = setTimeout(async () => {
|
||||||
myChart?.destroy()
|
|
||||||
myChart = await chartView.drawChart({
|
myChart = await chartView.drawChart({
|
||||||
chartObj: myChart,
|
chartObj: myChart,
|
||||||
container: containerId,
|
container: containerId,
|
||||||
|
Loading…
Reference in New Issue
Block a user