import * as L from '@mapgis/leaflet'
import { inputCss, isNumber } from '@mapgis/webclient-common/util/Utils'
import { SymbolType, defined } from '@mapgis/webclient-common'
import LeafletPlugin from '../../util/LeafletPlugin'
import ConvertSymbolUtil from '../../util/ConvertSymbolUtil'
function convertClusterSymbol(symbol, textSymbol, text) {
const dom = document.createElement('div')
const parentDom = document.createElement('div')
let bgSize = [10, 10]
let bgBorderWidth = 0
let bgColor = 'none'
// 解析符号为PictureMarker 和 SimpleMarker
if (SymbolType.pictureMarker === symbol.type) {
if (defined(symbol.width)) {
dom.style.width = `${parseFloat(symbol.width)}px`
bgSize[0] = parseFloat(symbol.width)
}
if (defined(symbol.height)) {
dom.style.height = `${parseFloat(symbol.height)}px`
bgSize[1] = parseFloat(symbol.height)
}
// 图片和几何点中心统一
if (defined(symbol.xoffset)) {
dom.style.marginLeft = symbol.xoffset
}
if (defined(symbol.yoffset)) {
dom.style.marginTop = symbol.yoffset
}
if (defined(symbol.angle)) {
dom.style.transform = `rotate(${symbol.angle}deg)`
}
if (defined(symbol.url)) {
dom.style.backgroundImage = `url(${symbol.url})`
dom.style.backgroundSize = `cover`
}
} else {
const {
size = 20,
outline = null,
style = 'circle',
path = '',
angle = 0,
xoffset = 0,
yoffset = 0,
color
} = symbol
// 不支持path 和 style
if (defined(outline)) {
const { width, color } = outline
if (style === 'circle') {
dom.style.borderRadius = `${size}px`
dom.style.backgroundClip = 'padding-box'
}
bgBorderWidth = width
bgColor = LeafletPlugin.convertColorToRgb(color)
}
if (defined(color)) {
dom.style.backgroundColor = LeafletPlugin.convertColorToRgb(color)
dom.style.backgroundSize = `cover`
}
if (defined(size)) {
bgSize = [size, size]
dom.style.width = `${size}px`
dom.style.height = `${size}px`
}
if (defined(xoffset)) {
dom.style.marginLeft = `${xoffset}px`
}
if (defined(yoffset)) {
dom.style.marginTop = `${yoffset}px`
}
if (defined(angle)) {
dom.style.transform = `rotate(${angle}deg)`
}
// 不支持path
}
const span = document.createElement('span')
// 设置文字居中
dom.style.textAlign = 'center'
span.style.lineHeight = `${bgSize[1]}px`
if (textSymbol) {
const {
color = '#000000',
angle = 0,
backgroundColor,
borderLineColor,
borderLineSize,
font,
haloColor,
haloSize,
xoffset,
yoffset
} = textSymbol
span.style.color = LeafletPlugin.convertColorToRgb(color)
span.style.transform = `rotate(${angle}deg)`
if (defined(backgroundColor)) {
span.style.backgroundColor =
LeafletPlugin.convertColorToRgb(backgroundColor)
}
if (defined(borderLineColor) && defined(borderLineSize)) {
span.style.border = `${borderLineSize}px solid ${LeafletPlugin.convertColorToRgb(
borderLineColor
)}`
}
if (defined(font)) {
const { family = 'Helvetica Neue', size, style, weight } = font
span.style.fontFamily = family
span.style.fontSize = `${size}px`
span.style.fontStyle = style
span.style.fontWeight = weight
}
if (defined(haloColor) && defined(haloSize)) {
span.style[
'-webkit-text-stroke'
] = `${haloSize}px ${LeafletPlugin.convertColorToRgb(borderLineColor)}`
}
// 不支持xoffset yoffset
}
const parentDomWidth = bgSize[0] + bgBorderWidth * 2
const parentDomHeight = bgSize[1] + bgBorderWidth * 2
parentDom.style = `border:none;width:${parentDomWidth}px;height:${parentDomHeight}px;background-color:${bgColor};border-radius:${
symbol.style === 'circle' ? `${parentDomWidth}px` : 'none'
};padding:${bgBorderWidth}px;`
span.innerText = text
dom.appendChild(span)
parentDom.appendChild(dom)
// divIcon
const divIcon = new L.DivIcon({
html: parentDom,
iconSize: new L.Point(parentDomWidth, parentDomHeight),
className: ``
})
return divIcon
}
// 部分专题图强依赖plugin层,因此需要对这些专题图特殊处理
class ThemeRenderUtil {
/**
* @description: 渲染聚类图
* @param {*} innerLayer
* @param {*} layer
* @param {*} innerView
* @param {*} features
* @return {*}
*/
static renderCluster(innerLayer, layer, innerView, features) {
// 添加聚类样式
inputCss(`
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
-webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
-moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
-o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
}
.leaflet-cluster-spider-leg {
/* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
-webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
-moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
-o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
}
`)
// 初始化配置
ThemeRenderUtil.updateCluster(innerLayer, layer, innerView)
features = features || []
features.forEach((feature) => {
const marker = L.marker(LeafletPlugin.getAreaCenter(feature.geometry))
innerLayer.addLayer(marker)
})
}
static updateCluster(innerLayer, layer, innerView) {
const options = innerLayer.options
const renderer = layer.renderer
const {
defaultSymbol,
radius,
clusterBoundSymbol,
maxScale,
isExpandOnClick,
isHoverShowBound
} = renderer
let clusterInfos = renderer.clusterInfos || []
// 设置聚类最大zoom
let zoom = 20
if (innerView) {
const resolution = (maxScale / 96) * 0.0254
const latlng1 = innerView.unproject(L.point(0, 0), 0)
const latlng2 = innerView.unproject(L.point(0, 1), 0)
let dis = innerView.distance(latlng1, latlng2)
let calcZoom = 0
while (calcZoom <= 20 && dis > resolution) {
calcZoom += 1
dis /= 2
}
zoom = Math.max(calcZoom - 1, 0)
}
options.disableClusteringAtZoom = zoom
options.singleMarkerMode = true
options.maxClusterRadius = radius
options.polygonOptions =
ConvertSymbolUtil.convertSymboltoLeafletStyle(clusterBoundSymbol)
options.zoomToBoundsOnClick = isExpandOnClick
options.showCoverageOnHover = isHoverShowBound
// 过滤不合法的聚类
clusterInfos = clusterInfos.filter(
(v) =>
v &&
isNumber(v.minValue) &&
isNumber(v.maxValue) &&
v.maxValue > v.minValue
)
options.iconCreateFunction = (cluster) => {
const childCount = cluster.getChildCount()
let infoSymbol = defaultSymbol
let infoTextSymbol
for (let i = 0; i < clusterInfos.length; i++) {
const { symbol, labelSymbol, minValue, maxValue } = clusterInfos[i]
if (childCount >= minValue && childCount < maxValue) {
infoSymbol = symbol
infoTextSymbol = labelSymbol
break
}
}
return convertClusterSymbol(
infoSymbol,
infoTextSymbol,
String(childCount)
)
}
}
}
export default ThemeRenderUtil