类名 util/ConvertRendererToStyleLayerUtil.js
import {
  RendererType,
  SimpleFillSymbol,
  SimpleLineSymbol,
  SimpleMarkerSymbol,
  SymbolType,
  defaultValue
} from '@mapgis/webclient-common'
import MapboxglPlugin from './MapboxglPlugin'
import UniqueValueSteps from './support/UniqueValueSteps'
import ClassBreakSteps from './support/ClassBreakSteps'

/**
 * @description: 运算符计算
 * @param {*} map 取值映射,取值表示的mapboxgl表达式
 * @param {*} calcArr 计算组
 * @param {*} calcFlag 运算符
 * @param {*}
 * @return {*}
 */
function setFeatureAttributeMap(map, calcArr, calcFlag, currentInfo) {
  while (calcArr.findIndex((v) => calcFlag.some((t) => t === v)) > -1) {
    currentInfo.index += 1
    const key = `calc_${currentInfo.index}`
    const flagIndex = calcArr.findIndex((v) => calcFlag.some((t) => t === v))
    const first =
      map[calcArr[flagIndex - 1]] ||
      (currentInfo.isNum
        ? parseFloat(calcArr[flagIndex - 1])
        : calcArr[flagIndex - 1])
    const end =
      map[calcArr[flagIndex + 1]] ||
      (currentInfo.isNum
        ? parseFloat(calcArr[flagIndex + 1])
        : calcArr[flagIndex + 1])
    map[key] = [calcArr[flagIndex], first, end]
    currentInfo.key = key
    currentInfo.expression = calcArr.splice(flagIndex - 1, 3, key)
  }
}

/**
 * @description: 解析表达式为mapboxgl表达式
 * @param {*} expression
 * @return {*}
 */
function parseExpressionToMapboxglExpress(expression, isNum) {
  // 由于表达式规则的复杂性,将符号定义的字段表达式转换为mapboxgl表达式过程会出现解析错误,提前进行错误判断。
  try {
    let mapboxglExpression = expression
    // $feature.name
    // $feature.val / 10
    // 将$feature.xxx 替换为get表达式
    const features = mapboxglExpression
      .replace(/\s/g, '')
      .match(/\$feature\.[^+-/*%>=<]+/g)
    if (!features || features.length === 0) return null
    const featureAttributeMap = {}
    const currentInfo = {
      index: 0,
      key: null,
      isNum
    }
    features.forEach((v) => {
      currentInfo.index += 1
      const key = `calc_${currentInfo.index}`
      currentInfo.key = key
      featureAttributeMap[key] = isNum
        ? ['to-number', ['get', v.split('.')[1]]]
        : ['get', v.split('.')[1]]
      mapboxglExpression = mapboxglExpression.replace(v, `${key}`)
    })
    // 按照算法运算符分段 + - * / %
    // 按照逻辑运算符分段 < <= == > >=
    // 添加空格处理
    const flagMatchs = mapboxglExpression.match(/>=|<=|==|\+|-|\*|\/|>|<|=/)
    if (flagMatchs && flagMatchs.length > 0) {
      flagMatchs.forEach((v) => {
        mapboxglExpression = mapboxglExpression.replace(v, ` ${v} `)
      })
    }
    const calcArr = mapboxglExpression.split(' ').filter((v) => v !== '')

    setFeatureAttributeMap(
      featureAttributeMap,
      calcArr,
      ['*', '/', '%'],
      currentInfo
    )
    setFeatureAttributeMap(
      featureAttributeMap,
      calcArr,
      ['+', '-'],
      currentInfo
    )
    setFeatureAttributeMap(
      featureAttributeMap,
      calcArr,
      ['<', '<=', '>', '>='],
      currentInfo
    )
    setFeatureAttributeMap(featureAttributeMap, calcArr, ['=='], currentInfo)
    if (currentInfo.key) {
      return featureAttributeMap[currentInfo.key]
    }
  } catch (err) {}
}

/**
 * @description: 解析字段表达式
 * @param {*} field
 * @param {*} expression
 * @return {*}
 */
function parseFieldAndExpression(field, expression, isNum) {
  if (field) return isNum ? ['to-number', ['get', field]] : ['get', field]
  const express = parseExpressionToMapboxglExpress(expression, isNum)
  if (express) return express
  return ['get', 'undefined']
}

// 转换定义的symbol
function covertSimpleLineSymbol(
  type,
  paintStyle,
  layoutStyle,
  symbol,
  visible,
  opacity
) {
  const color = MapboxglPlugin.applyVisibleAndOpacityToColor(
    symbol.color,
    visible,
    opacity
  )
  paintStyle['line-color'] = color.toCssRGBString()
  paintStyle['line-opacity'] = symbol.opacity
  paintStyle['line-width'] = symbol.width
  layoutStyle['line-cap'] = symbol.cap
  layoutStyle['line-miter-limit'] = symbol.miterLimit
  layoutStyle['line-join'] = symbol.join
  const lineDasharray = MapboxglPlugin.convertLineStyle(
    symbol.style,
    symbol.cap === 'butt'
  )
  if (lineDasharray) {
    paintStyle['line-dasharray'] = lineDasharray
  }
  // 无法设置marker
}

function covertSimpleFillSymbol(
  type,
  paintStyle,
  layoutStyle,
  symbol,
  visible,
  opacity
) {
  switch (type) {
    case 'fill': {
      const color = MapboxglPlugin.applyVisibleAndOpacityToColor(
        symbol.color,
        visible,
        opacity
      )
      const outline = MapboxglPlugin.applyVisibleAndOpacityToColor(
        symbol.outline.color,
        visible,
        opacity
      )
      paintStyle['fill-color'] = color.toCssRGBString()
      paintStyle['fill-opacity'] = color.alpha
      paintStyle['fill-outline-color'] = outline.toCssRGBAString()
      break
    }
    default: {
      break
    }
  }
}

// 转换定义的symbol
function convertPictureMarkerSymbol(
  type,
  paintStyle,
  layoutStyle,
  symbol,
  visible,
  opacity
) {
  // ----------------- paint -----------------------------
  const color = MapboxglPlugin.applyVisibleAndOpacityToColor(
    symbol.color,
    visible,
    opacity
  )
  paintStyle['icon-color'] = color.toCssRGBString()
  paintStyle['icon-opacity'] = color.alpha
  // ----------------- layout -----------------------------
  layoutStyle['icon-allow-overlap'] = false
  layoutStyle['icon-size'] = Math.max(symbol.width, symbol.height) / 20
  layoutStyle['icon-rotate'] = symbol.angle
  layoutStyle['icon-image'] = 'marker-15'
  layoutStyle['icon-offset'] = [symbol.xoffset, symbol.yoffset]
}

// 转换定义的symbol
function convertTextSymbol(
  type,
  paintStyle,
  layoutStyle,
  symbol,
  visible,
  opacity
) {
  // ----------------- paint -----------------------------
  const color = MapboxglPlugin.applyVisibleAndOpacityToColor(
    symbol.color,
    visible,
    opacity
  )
  const haloColor = MapboxglPlugin.applyVisibleAndOpacityToColor(
    symbol.haloColor,
    visible,
    opacity
  )
  paintStyle['text-color'] = color.toCssRGBString()
  paintStyle['text-opacity'] = color.alpha
  paintStyle['text-halo-color'] = haloColor.toCssRGBAString()
  paintStyle['text-halo-width'] = symbol.haloSize
  // ----------------- layout -----------------------------
  // 设置默认文字field
  layoutStyle['text-field'] = symbol.text || ''
  // 开启注记避让
  layoutStyle['text-allow-overlap'] = false
  layoutStyle['text-font'] = ['Open Sans Regular', 'Arial Unicode MS Regular']
  layoutStyle['text-size'] = symbol.font.size
  layoutStyle['text-justify'] = symbol.horizontalAlignment
  layoutStyle['text-letter-spacing'] = symbol.kerning ? 0.1 : 0
  layoutStyle['text-rotate'] =
    symbol.rotated && symbol.angle !== 0 ? symbol.angle : 0
  layoutStyle['text-offset'] = [symbol.xoffset, symbol.yoffset]
  layoutStyle['text-line-height'] = symbol.lineHeight ? 1.2 : 0
  layoutStyle['text-max-width'] = defaultValue(symbol.lineWidth, 192)
  //   不支持文字基线
  layoutStyle['text-anchor'] = 'center'
  switch (symbol.verticalAlignment) {
    case 'top': {
      switch (symbol.horizontalAlignment) {
        case 'left': {
          layoutStyle['text-anchor'] = 'top-left'
          break
        }
        case 'right': {
          layoutStyle['text-anchor'] = 'top-right'
          break
        }
        default: {
          layoutStyle['text-anchor'] = 'top'
          break
        }
      }
      break
    }
    case 'bottom': {
      switch (symbol.horizontalAlignment) {
        case 'left': {
          layoutStyle['text-anchor'] = 'bottom-left'
          break
        }
        case 'right': {
          layoutStyle['text-anchor'] = 'bottom-right'
          break
        }
        default: {
          layoutStyle['text-anchor'] = 'bottom'
          break
        }
      }
      break
    }
    default: {
      switch (symbol.horizontalAlignment) {
        case 'left': {
          layoutStyle['text-anchor'] = 'left'
          break
        }
        case 'right': {
          layoutStyle['text-anchor'] = 'right'
          break
        }
        default: {
          layoutStyle['text-anchor'] = 'center'
          break
        }
      }
    }
  }
}

function covertSimpleMarkerSymbol(
  type,
  paintStyle,
  layoutStyle,
  symbol,
  visible,
  opacity
) {
  switch (type) {
    case 'circle': {
      const x = symbol.xoffset
      const y = symbol.yoffset
      paintStyle['circle-translate'] = [parseFloat(x), parseFloat(y)]
      const color = MapboxglPlugin.applyVisibleAndOpacityToColor(
        symbol.color,
        visible,
        opacity
      )
      paintStyle['circle-opacity'] = color.alpha
      paintStyle['circle-color'] = color.toCssRGBString()
      // stroke color
      const strokeColor = MapboxglPlugin.applyVisibleAndOpacityToColor(
        symbol.outline.color,
        visible,
        opacity
      )
      paintStyle['circle-stroke-color'] = strokeColor.toCssRGBString()
      paintStyle['circle-stroke-opacity'] = strokeColor.alpha
      paintStyle['circle-stroke-width'] = symbol.outline.width
      // size
      paintStyle['circle-radius'] = symbol.size / 2

      // 无法支持angle
      // 无法支持path
      // 无法支持style
      break
    }
    default: {
      break
    }
  }
}

function convertSymbol(
  symbol,
  renderType,
  paintStyle,
  layoutStyle,
  visible,
  opacity
) {
  switch (renderType) {
    case 'line': {
      if (!(symbol instanceof SimpleLineSymbol)) {
        symbol = new SimpleLineSymbol()
      }
      covertSimpleLineSymbol(
        renderType,
        paintStyle,
        layoutStyle,
        symbol,
        visible,
        opacity
      )
      break
    }
    case 'fill': {
      if (!(symbol instanceof SimpleFillSymbol)) {
        symbol = new SimpleFillSymbol()
      }
      covertSimpleFillSymbol(
        renderType,
        paintStyle,
        layoutStyle,
        symbol,
        visible,
        opacity
      )
      break
    }
    case 'circle': {
      if (!(symbol instanceof SimpleMarkerSymbol)) {
        symbol = new SimpleMarkerSymbol()
      }
      covertSimpleMarkerSymbol(
        renderType,
        paintStyle,
        layoutStyle,
        symbol,
        visible,
        opacity
      )
      break
    }
    case 'symbol': {
      if (symbol.type === SymbolType.text) {
        convertTextSymbol(
          renderType,
          paintStyle,
          layoutStyle,
          symbol,
          visible,
          opacity
        )
      } else if (symbol.type === SymbolType.pictureMarker) {
        convertPictureMarkerSymbol(
          renderType,
          paintStyle,
          layoutStyle,
          symbol,
          visible,
          opacity
        )
      }
      break
    }
    default: {
      break
    }
  }
}

/**
 * @description: 更新统一专题图
 * @param {*} renderer
 * @param {*} renderType
 * @param {*} paint
 * @param {*} layout
 * @param {*} visible
 * @param {*} opacity
 * @return {*}
 */
function updateSimpleRenderer(
  renderer,
  renderType,
  paint,
  layout,
  visible,
  opacity
) {
  const symbol = renderer.symbol
  // 根据不同图层类型
  convertSymbol(symbol, renderType, paint, layout, visible, opacity)
}

/**
 * @description: 更新单值专题图
 * @param {*} renderer
 * @param {*} renderType
 * @param {*} paint
 * @param {*} layout
 * @param {*} visible
 * @param {*} opacity
 * @return {*}
 */
function updateUniqueValueRenderer(
  renderer,
  renderType,
  paint,
  layout,
  visible,
  opacity
) {
  const symbol = renderer.defaultSymbol
  convertSymbol(symbol, renderType, paint, layout, visible, opacity)
  const expression = parseFieldAndExpression(
    renderer.field,
    renderer.valueExpression
  )
  const paintStepObject = {}
  const layoutStepObject = {}
  Object.keys(paint).forEach((key) => {
    paintStepObject[key] = new UniqueValueSteps(expression, paint[key])
  })
  Object.keys(layout).forEach((key) => {
    layoutStepObject[key] = new UniqueValueSteps(expression, layout[key])
  })

  const uniqueValueInfos = renderer.uniqueValueInfos || []
  uniqueValueInfos.forEach((info) => {
    // 不能走默认样式,否者会一致对属性分段
    const uniquePaint = {}
    const uniqueLayout = {}
    convertSymbol(
      info.symbol,
      renderType,
      uniquePaint,
      uniqueLayout,
      info.visible,
      opacity
    )
    const name = info.value

    // 添加分段样式
    Object.keys(paint).forEach((key) => {
      if (!paintStepObject[key]) {
        paintStepObject[key] = new UniqueValueSteps(
          expression,
          uniquePaint[key]
        )
      }
      paintStepObject[key].addStep(name, uniquePaint[key])
    })
    Object.keys(layout).forEach((key) => {
      if (!layoutStepObject[key]) {
        layoutStepObject[key] = new UniqueValueSteps(
          expression,
          uniqueLayout[key]
        )
      }
      layoutStepObject[key].addStep(name, uniqueLayout[key])
    })
  })
  // 重新设置style
  Object.keys(paintStepObject).forEach((key) => {
    paint[key] = paintStepObject[key].toExpress()
  })
  Object.keys(layoutStepObject).forEach((key) => {
    layout[key] = layoutStepObject[key].toExpress()
  })
}

/**
 * @description: 分段专题图渲染
 * @param {*} renderer
 * @param {*} renderType
 * @param {*} paint
 * @param {*} layout
 * @param {*} visible
 * @param {*} opacity
 * @return {*}
 */
function updateClassBreakRenderer(
  renderer,
  renderType,
  paint,
  layout,
  visible,
  opacity
) {
  const symbol = renderer.defaultSymbol
  convertSymbol(symbol, renderType, paint, layout, visible, opacity)
  const expression = parseFieldAndExpression(
    renderer.field,
    renderer.valueExpression,
    true
  )
  const paintStepObject = {}
  const layoutStepObject = {}

  Object.keys(paint).forEach((key) => {
    paintStepObject[key] = new ClassBreakSteps(expression, paint[key])
  })
  Object.keys(layout).forEach((key) => {
    layoutStepObject[key] = new ClassBreakSteps(expression, layout[key])
  })

  const classBreakInfos = renderer.classBreakInfos || []
  classBreakInfos.forEach((info) => {
    const classBreakPaint = {}
    const classBreakLayout = {}
    convertSymbol(
      info.symbol,
      renderType,
      classBreakPaint,
      classBreakLayout,
      info.visible,
      opacity
    )
    // 添加分段样式
    Object.keys(paint).forEach((key) => {
      if (!paintStepObject[key]) {
        paintStepObject[key] = new ClassBreakSteps(
          expression,
          classBreakPaint[key]
        )
      }
      paintStepObject[key].addStep(
        info.minValue,
        info.maxValue,
        classBreakPaint[key]
      )
    })

    Object.keys(layout).forEach((key) => {
      if (!layoutStepObject[key]) {
        layoutStepObject[key] = new ClassBreakSteps(
          expression,
          classBreakLayout[key]
        )
      }
      layoutStepObject[key].addStep(
        info.minValue,
        info.maxValue,
        classBreakLayout[key]
      )
    })
  })
  // 重新设置style
  Object.keys(paintStepObject).forEach((key) => {
    paint[key] = paintStepObject[key].toExpress()
  })
  Object.keys(layoutStepObject).forEach((key) => {
    layout[key] = layoutStepObject[key].toExpress()
  })
}

/**
 * @description: 将renderer转换为styleLayer样式
 * @param {*} renderer 渲染器
 * @param {*} renderType 渲染类型
 * @param {*} visible 显示或隐藏
 * @param {*} opacity 透明度
 * @param {*} paint 绘制属性
 * @param {*} layout 布局属性
 * @return {*}
 */
function updateStyleLayer(
  renderer,
  renderType,
  visible,
  opacity,
  paint,
  layout
) {
  // 针对renderer渲染,可参考common层renderer的解析
  switch (renderer.type) {
    case RendererType.simple: {
      updateSimpleRenderer(
        renderer,
        renderType,
        paint,
        layout,
        visible,
        opacity
      )
      break
    }
    case RendererType.uniqueValue: {
      updateUniqueValueRenderer(
        renderer,
        renderType,
        paint,
        layout,
        visible,
        opacity
      )
      break
    }
    case RendererType.classBreak: {
      updateClassBreakRenderer(
        renderer,
        renderType,
        paint,
        layout,
        visible,
        opacity
      )
      break
    }
    case RendererType.random: {
      break
    }
    default: {
      break
    }
  }
}

export { updateStyleLayer, convertSymbol }
构造函数
成员变量
方法
事件