地图可视化,根据绘制的图形生成缩略图,经纬度转换二维canvas坐标系

一、h5在做可视化地图时,用高德地图绘制空域(圆形,线,多边形),碰到一个需求,根据绘制出来的图形给对应的空域列表项添加一个缩略图。

二、确定实现方法

  1. 要根据绘制的图形生成对应图形的缩略图,有两种方式,一是使用高德地图给每个列表项绘制对应的图形,二是自己实现。
  2. 第一种方法性能差,要是自己实现,我这边选择的是canvas画布。
  3. 难点:后台返回的是经纬度数据不是canvas坐标系数据,我们需要根据经纬度转化为我们所设计的画布大小的坐标系数据。

三、代码实现

import React, { useRef, useEffect, useState } from "react";

export default function GeneratePolygon({ type, coordinates }) {
  const canvasRef = useRef(null);
  const [context, setContext] = useState(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    // console.log('ctx',ctx)
    // 设置Canvas大小
    canvas.width = 100;
    canvas.height = 100;

    // 将经纬度转换为Canvas坐标
    const toCanvasCoords = (lng, lat) => {
      // debugger;
      if (coordinates.length && coordinates.length > 1) { // length为1为圆否则就是多边形和线
        const lons = coordinates.map((item) => item.lng);
        const lats = coordinates.map((item) => item.lat);
        const lonMax = Math.max(...lons);
        const lonMin = Math.min(...lons);
        const latMax = Math.max(...lats);
        const latMin = Math.min(...lats);
        const lonScale = lonMax === lonMin ? 1 : canvas.width / (lonMax - lonMin);
        const latScale = latMax === latMin ? 1 : canvas.height / (latMax - latMin);

        // 不让图形超出和贴边,x=0 x=100,y=0 y=100两种情况时自己挪一点
        const x = ((lng - lonMin) * lonScale) == 0 ? 2 : ((lng - lonMin) * lonScale)>= canvas.width ? canvas.width-2 : (lng - lonMin) * lonScale;
        const y = (canvas.height - (lat - latMin) * latScale) == 0 ? 2 : (canvas.height - (lat - latMin) * latScale) >= canvas.height ? canvas.height-2 : canvas.height - (lat - latMin) * latScale;
        if(lonMax === lonMin){
          return {x: canvas.width/2, y}
        }else if(latMax === latMin){
          return {x, y: canvas.height/2}
        }else{
          return { x, y };
        }
      } else {
        return { x: canvas.width / 2, y: canvas.height / 2 };
      }
    };
    // 根据type绘制不同的图形
    if (type === "2" && coordinates && coordinates.length > 1) {
      ctx.beginPath();
      coordinates.forEach((coord) => {
        const { x, y } = toCanvasCoords(coord.lng, coord.lat);
        if (ctx._firstPoint === undefined) {
          ctx._firstPoint = { x, y };
          ctx.moveTo(x, y);
        } else {
          ctx.lineTo(x, y);
        }
      });
      // 闭合多边形路径
      ctx.lineTo(
        toCanvasCoords(coordinates[0].lng, coordinates[0].lat).x,
        toCanvasCoords(coordinates[0].lng, coordinates[0].lat).y
      );
      ctx.fillStyle = "rgba(5,95,231,0.20)"; // 填充色
      ctx.strokeStyle = "#055FE7"; // 边框色
      ctx.lineWidth = 2;
      ctx.scale(1, 1);
      ctx.stroke();
      ctx.fill();
    } else if (type === "1" && coordinates && coordinates.length === 1) {
      const { lng, lat } = coordinates[0]; // 获取圆心的经纬度
      const cx = toCanvasCoords(lng, lat).x;
      const cy = toCanvasCoords(lng, lat).y;
      ctx.beginPath();
      ctx.arc(cx, cy, 45, 0, Math.PI * 2); // 绘制圆形
      ctx.fillStyle = "rgba(5,95,231,0.20)"; // 填充色
      ctx.strokeStyle = "#055FE7"; // 边框色
      ctx.lineWidth = 2;
      ctx.stroke();
      ctx.fill();
    } else if (type === "3" && coordinates && coordinates.length) {
      ctx.beginPath();
      coordinates.forEach(coord=>{
        ctx.lineTo(toCanvasCoords(coord.lng, coord.lat).x, toCanvasCoords(coord.lng, coord.lat).y);
      })
      ctx.strokeStyle = "#055FE7";
      ctx.lineWidth = 2;
      ctx.stroke();
    }

    setContext(ctx);
  }, [type, coordinates]); // 依赖type和coordinates的变化来重绘

  return (
    <canvas
      ref={canvasRef}
      style={{ height: "100px", width: "100px" }}
    />
  );
}

四、使用

<GeneratePolygon
  type={item?.type}
  coordinates={
    item?.polygonPoint ? JSON.parse(item?.polygonPoint) : []
  }
/>

五、效果


这样就生成了相对应的缩略图了,需要注意的是要处理坐标的边界值问题,不然会有贴边效果不太好看,over。