Skip to content

风箭,这个案例一般,主要看一下自定义箭头和角度计算,正常绘制风场还需要有动画效果,我在插件案例中介绍了一个好用的工具

风箭

核心代码

绘制风箭头

js
const shaft = new RegularShape({
  points: 2,
  radius: 5,
  stroke: new Stroke({
    width: 2,
    color: "black",
  }),
  rotateWithView: true,
});

const head = new RegularShape({
  points: 3,
  radius: 5,
  fill: new Fill({
    color: "black",
  }),
  rotateWithView: true,
});

const styles = [new Style({ image: shaft }), new Style({ image: head })];

计算角度

js
 style: function (feature) {
        const wind = feature.get('wind');
        // rotate arrow away from wind origin
        const angle = ((wind.deg - 180) * Math.PI) / 180;
        const scale = wind.speed / 10;
        shaft.setScale([1, scale]);
        shaft.setRotation(angle);
        head.setDisplacement([
          0,
          head.getRadius() / 2 + shaft.getRadius() * scale,
        ]);
        head.setRotation(angle);
        return styles;
      },

完整源码

我直接把官方代码做了转化,这里注意我使用的是OSM底图

展开代码
vue
<template>
  <div class="map-container">
    <div ref="mapRef" class="map"></div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import Feature from "ol/Feature.js";
import Map from "ol/Map.js";
import View from "ol/View.js";
import Point from "ol/geom/Point.js";
import TileLayer from "ol/layer/Tile.js";
import VectorLayer from "ol/layer/Vector.js";
import { fromLonLat } from "ol/proj.js";
import OSM from "ol/source/OSM.js";
import VectorSource from "ol/source/Vector.js";
import Fill from "ol/style/Fill.js";
import RegularShape from "ol/style/RegularShape.js";
import Stroke from "ol/style/Stroke.js";
import Style from "ol/style/Style.js";
import "ol/ol.css";

// 地图实例引用
let map = null;
// 地图容器引用
const mapRef = ref(null);

onMounted(async () => {
  // 创建风向箭头样式(箭头杆和箭头头)
  const shaft = new RegularShape({
    points: 2,
    radius: 5,
    stroke: new Stroke({
      width: 2,
      color: "black",
    }),
    rotateWithView: true,
  });

  const head = new RegularShape({
    points: 3,
    radius: 5,
    fill: new Fill({
      color: "black",
    }),
    rotateWithView: true,
  });

  const styles = [new Style({ image: shaft }), new Style({ image: head })];

  // 创建矢量数据源
  const source = new VectorSource({
    attributions: "Weather data by OpenWeather",
  });

  // 创建地图
  map = new Map({
    layers: [
      new TileLayer({
        source: new OSM(), // OpenStreetMap底图
      }),
      new VectorLayer({
        source: source,
        style: function (feature) {
          const wind = feature.get("wind");
          // 计算风向箭头角度(从风向旋转180度,指向风吹去的方向)
          const angle = ((wind.deg - 180) * Math.PI) / 180;
          // 根据风速计算箭头长度比例
          const scale = wind.speed / 10;

          // 设置箭头杆样式
          shaft.setScale([1, scale]);
          shaft.setRotation(angle);

          // 设置箭头头位置和旋转
          head.setDisplacement([
            0,
            head.getRadius() / 2 + shaft.getRadius() * scale,
          ]);
          head.setRotation(angle);

          return styles;
        },
      }),
    ],
    view: new View({
      center: [0, 0],
      zoom: 2,
    }),
  });

  // 设置地图容器并加载数据
  if (mapRef.value) {
    map.setTarget(mapRef.value);

    try {
      // 加载气象数据(注意:需要确保此路径下有对应的JSON文件)
      const response = await fetch("/src/openlayers/weather.json");
      const data = await response.json();

      // 创建气象数据要素
      const features = [];
      data.list.forEach(function (report) {
        // 创建点要素
        const feature = new Feature(
          new Point(fromLonLat([report.coord.lon, report.coord.lat]))
        );
        // 设置要素属性
        feature.setProperties(report);
        features.push(feature);
      });

      // 添加要素到数据源
      source.addFeatures(features);
      // 调整视图以适应数据范围
      map.getView().fit(source.getExtent());
    } catch (error) {
      console.error("加载气象数据时出错:", error);
      alert("无法加载气象数据,请检查文件路径是否正确");
    }
  }
});

onUnmounted(() => {
  // 组件卸载时清理地图实例
  if (map) {
    map.setTarget(null);
    map = null;
  }
});
</script>

<style scoped>
.map-container {
  width: 100vw;
  height: 100vh;
}

.map {
  width: 100%;
  height: 100%;
}
</style>