riku
2025-03-14 5f44d21b3921abc88506a7ec46b3fe6f078664aa
src/utils/map/grid.js
@@ -2,51 +2,419 @@
 * 网格绘制
 */
import { map } from './index_old';
import calculate from './calculate';
import { Legend } from '@/model/Legend';
import { getHexColor, getColorBetweenTwoColors } from '../color';
/**
 * 角度增减,确保角度处于0 - 360度之间
 * @param {number} angle 原角度
 * @param {number} offset 偏移量
 */
function plusAngle(angle, offset) {
  const result = angle + offset;
  if (result > 360) {
    return result - 360;
  } else if (result < 0) {
    return result + 360;
  } else {
    return result;
  }
}
/**
 * 根据网格中心点,生成正方形网格4个顶点的坐标
 * @param {Array} points 网格中心点经纬度数组
 */
function parseGridPoint(points) {
  if (points.length < 2) throw new Error('坐标点数量小于2');
  const p1 = points[0];
  const p2 = points[1];
  // 两中心点间的角度
  const angle = calculate.getAngle(p1[0], p1[1], p2[0], p2[1]);
  // const angle = calculate.bearing(
  //   { latitude: p1[0], longitude: p1[1] },
  //   { latitude: p2[0], longitude: p2[1] }
  // );
  // 两中心点间的距离
  const dis = calculate.getDistance(p1[0], p1[1], p2[0], p2[1]);
  // 网格正方形对角线的一半长度
  const halfDiagonal = Math.sqrt((dis / 2) * (dis / 2) * 2);
  // 计算首个正方形各顶点相对于中心点的角度,得到正方形各顶点的坐标
  const angle1 = plusAngle(angle, 45);
  const gp1 = calculate.getLatLon(p1, halfDiagonal, angle1);
  const angle2 = plusAngle(angle1, 90);
  const gp2 = calculate.getLatLon(p1, halfDiagonal, angle2);
  const angle3 = plusAngle(angle2, 90);
  const gp3 = calculate.getLatLon(p1, halfDiagonal, angle3);
  const angle4 = plusAngle(angle3, 90);
  const gp4 = calculate.getLatLon(p1, halfDiagonal, angle4);
  // 计算4个顶点分别与中心点的经纬度差值
  const diff = {
    diff1: {
      dx: gp1[0] - p1[0],
      dy: gp1[1] - p1[1]
    },
    diff2: {
      dx: gp2[0] - p1[0],
      dy: gp2[1] - p1[1]
    },
    diff3: {
      dx: gp3[0] - p1[0],
      dy: gp3[1] - p1[1]
    },
    diff4: {
      dx: gp4[0] - p1[0],
      dy: gp4[1] - p1[1]
    }
  };
  // 得到所有正方形网格的4个顶点信息
  return points.map((p) => {
    return [
      [p[0] + diff.diff1.dx, p[1] + diff.diff1.dy],
      [p[0] + diff.diff2.dx, p[1] + diff.diff2.dy],
      [p[0] + diff.diff3.dx, p[1] + diff.diff3.dy],
      [p[0] + diff.diff4.dx, p[1] + diff.diff4.dy]
    ];
  });
}
/**
 * 文本标记
 * 可修改position
 */
function textMaker({ position, text, anchor, type, color }) {
  let style = {};
  if (type == 'data') {
    style = {
      'font-size': '12px',
      'text-align': 'center',
      'font-weight': 600,
      color: color ? color : 'white',
      background: '#122b54a9',
      // background: 'white',
      'text-shadow': 'black 1px 1px 1px',
      border: '0px',
      'margin-top': '4px'
    };
  } else if (type == 'rank') {
    style = {
      'font-size': '14px',
      'text-align': 'center',
      color: color ? color : 'white',
      background: 'transparent',
      'text-shadow': 'black 2px 2px 2px',
      'border-radius': '2px',
      border: '1px solid #122b54a9',
      // border: '1px solid rgba(255, 255, 255, 0.62)',
      'margin-bottom': '4px'
    };
  }
  // eslint-disable-next-line no-undef
  return new AMap.Text({
    text: text,
    anchor,
    position: position,
    style: style
  });
}
/**
 * 海量文本标注
 */
function textLabelMarker(position, text, direction, style) {
  // eslint-disable-next-line no-undef
  return new AMap.LabelMarker({
    position: position,
    zooms: [10, 20],
    opacity: 1,
    zIndex: 2,
    // icon: {
    //   image: 'https://a.amap.com/jsapi_demos/static/images/poi-marker.png',
    //   anchor: 'bottom-center',
    //   size: [25, 34],
    //   clipOrigin: [459, 92],
    //   clipSize: [50, 68]
    // },
    text: {
      // 注意内容格式必须是string
      content: text ? text + '' : '',
      direction: direction ? direction : 'center',
      style: {
        'border-radius': '.25rem',
        fontSize: 12,
        fillColor: '#fff',
        strokeColor: 'rgba(0, 0, 0, 1)',
        strokeWidth: 4,
        // backgroundColor: '#122b54a9',
        padding: [3, 10],
        // backgroundColor: 'yellow',
        borderColor: '#ccc',
        borderWidth: 30
      }
    }
  });
}
/**
 * 计算每个网格颜色
 */
function calGridColor({ factorName, data, isCustomColor }) {
  let _colorList = [];
  if (isCustomColor) {
    var max, min;
    data.forEach((t) => {
      if (!t) return;
      if (!max || t > max) {
        max = t;
      }
      if (!min || t < min) {
        min = t;
      }
    });
    data.forEach((d) => {
      if (d) {
        // 根据遥测数据计算网格颜色
        const { color, nextColor, range, nextRange } =
          Legend.getCustomColorAndNext(d, min, max);
        const ratio = (d - range) / (nextRange - range);
        const _color = getColorBetweenTwoColors(
          color.map((v) => v * 255),
          nextColor.map((v) => v * 255),
          ratio
        );
        _colorList.push(_color);
      } else {
        _colorList.push(undefined);
      }
    });
  } else {
    data.forEach((d) => {
      if (d) {
        // 根据遥测数据计算网格颜色
        const { color, nextColor, range, nextRange } =
          Legend.getStandardColorAndNext(factorName, d);
        const ratio = (d - range) / (nextRange - range);
        const _color = getColorBetweenTwoColors(
          color.map((v) => v * 255),
          nextColor.map((v) => v * 255),
          ratio
        );
        _colorList.push(_color);
      } else {
        _colorList.push(undefined);
      }
    });
  }
  return _colorList;
}
export default {
  parseGridPoint,
  /**
   * 绘制网格风险图
   * @param {*} points
   */
  drawRectangle: function (points) {
  drawRectangle(points) {
    const gridViews = [];
    points.forEach((p) => {
      const { lb, rt, c } = p;
      // eslint-disable-next-line no-undef
      let pList = [lb, rt].map((v) => new AMap.LngLat(v[0], v[1]));
      let pList = [lb, rt].map((v) => {
        // eslint-disable-next-line no-undef
        return new AMap.LngLat(v[0], v[1]);
      });
      // eslint-disable-next-line no-undef
      var bounds = new AMap.Bounds(...pList);
      // eslint-disable-next-line no-undef
      var rectangle = new AMap.Rectangle({
        bounds: bounds,
        // strokeColor: '#ffffffff',
        strokeWeight: 0,
        strokeOpacity: 0,
        strokeColor: '#ffffffff',
        strokeWeight: 1,
        strokeOpacity: 1,
        // strokeStyle还支持 solid
        strokeStyle: 'solid',
        fillColor: '990D0D',
        fillColor: '#990D0D',
        fillOpacity: 0.8,
        cursor: 'pointer',
        zIndex: 50
      });
      // var text = new AMap.Text({
      //   text: p.value,
      //   anchor: 'center', // 设置文本标记锚点
      //   draggable: false,
      //   style: {
      //     'background-color': 'transparent',
      //     'border-width': 0,
      //     'text-align': 'center',
      //     'font-size': '12px',
      //     color: 'white'
      //   },
      //   position: c
      // });
      gridViews.push(rectangle);
      // that.textView.push(text);
    });
    map.add(gridViews);
    map.setFitView(gridViews);
  },
  /**
   * 绘制一组多边形
   * @param {Array} points 网格坐标点数组
   * @param {Boolean} draw 是否创建完成后同时绘制
   */
  drawPolylines({ points, draw, event }) {
    const gridViews = [];
    points.forEach((p) => {
      //创建多边形 Polygon 实例
      // eslint-disable-next-line no-undef
      var polygon = new AMap.Polygon({
        path: p.path, //路径
        fillColor: '#fff', //多边形填充颜色
        strokeWeight: 1, //线条宽度,默认为 2
        strokeColor: 'white', //线条颜色
        fillOpacity: 0,
        extData: p.extData
      });
      if (typeof event === 'function') {
        event(polygon);
      }
      gridViews.push(polygon);
    });
    if (draw) {
      map.add(gridViews);
      map.setFitView(gridViews);
    }
    return gridViews;
  },
  drawGridText({ points, textViews, anchor, type, isCustomColor, useColor }) {
    let colorList = [];
    if (useColor) {
      colorList = calGridColor({
        factorName: 'PM25',
        data: points.map((p) => p.data),
        isCustomColor: isCustomColor
      });
    }
    if (textViews) {
      map.remove(textViews);
    }
    const _textViews = [];
    points.forEach((p, i) => {
      const m = textMaker({
        position: p.lnglat_GD,
        text: p.data,
        anchor,
        type,
        color: useColor ? colorList[i] : 'white'
      });
      _textViews.push(m);
    });
    // map.add(_textViews);
    return { textViews: _textViews };
  },
  drawGridTextLabel(points, textViews, labelsLayer, direction) {
    if (textViews) {
      points.forEach((p, i) => {
        textViews[i].setPosition(p.lnglat_GD);
        textViews[i].setText({
          content: p.data ? p.data + '' : ''
        });
      });
      return { textViews, labelsLayer };
    } else {
      // 创建一个 LabelsLayer 实例来承载 LabelMarker,[LabelsLayer 文档](https://lbs.amap.com/api/jsapi-v2/documentation#labelslayer)
      // eslint-disable-next-line no-undef
      const labelsLayer = new AMap.LabelsLayer({
        zooms: [12, 20],
        zIndex: 1000,
        // 开启标注避让,默认为开启,v1.4.15 新增属性
        collision: false,
        // 开启标注淡入动画,默认为开启,v1.4.15 新增属性
        animation: false
      });
      const _textViews = [];
      points.forEach((p) => {
        const m = textLabelMarker(p.lnglat_GD, p.data, direction);
        _textViews.push(m);
        // 将 LabelMarker 实例添加到 LabelsLayer 上
        labelsLayer.add(m);
      });
      map.add(labelsLayer);
      // map.on('zoomend', () => {
      //   console.log(map.getZoom());
      // });
      return { textViews: _textViews, labelsLayer };
    }
  },
  /**
   * 根据遥测数据,设置对应网格的标准色,返回有数据的网格
   * @param {Array} gridViews 网格多边形对象数组
   * @param {Array} gridDataDetail 卫星遥测数据数组
   * @param {string} factorName 监测因子名称
   * @param {number} opacity 透明度
   */
  drawGridColor(gridViews, gridDataDetail, factorName, opacity, zIndex) {
    const res = [];
    // 遍历卫星遥测数据数组
    gridDataDetail.forEach((d, i) => {
      if (d.pm25) {
        const grid = gridViews[i];
        // 根据遥测数据计算网格颜色
        const data = d.pm25;
        const { color, nextColor, range, nextRange } =
          Legend.getStandardColorAndNext(factorName, data);
        const ratio = (data - range) / (nextRange - range);
        const _color = getColorBetweenTwoColors(
          color.map((v) => v * 255),
          nextColor.map((v) => v * 255),
          ratio
        );
        grid.setOptions({
          zIndex: zIndex ? zIndex : 10,
          fillColor: _color,
          fillOpacity: opacity ? opacity : color[3] == 0 ? 0 : 0.7
        });
        res.push(grid);
      }
    });
    return res;
  },
  drawGridColorCustom(gridViews, gridDataDetail, opacity) {
    var max, min;
    gridDataDetail.forEach((t) => {
      if (!t.pm25) return;
      if (!max || t.pm25 > max) {
        max = t.pm25;
      }
      if (!min || t.pm25 < min) {
        min = t.pm25;
      }
    });
    const res = [];
    // 遍历卫星遥测数据数组
    gridDataDetail.forEach((d, i) => {
      if (d.pm25) {
        const grid = gridViews[i];
        // 根据遥测数据计算网格颜色
        const data = d.pm25;
        const { color, nextColor, range, nextRange } =
          Legend.getCustomColorAndNext(data, min, max);
        const ratio = (data - range) / (nextRange - range);
        const _color = getColorBetweenTwoColors(
          color.map((v) => v * 255),
          nextColor.map((v) => v * 255),
          ratio
        );
        grid.setOptions({
          fillColor: _color,
          fillOpacity: opacity ? opacity : color[3] == 0 ? 0 : 0.7
        });
        res.push(grid);
      }
    });
    return res;
  }
};