• 三十六、openlayers官网示例Earthquake Clusters解析——在聚合图层鼠标触摸显示五角星


    官网demo地址:

    Earthquake Clusters

    这篇展示了鼠标触摸聚合图层点位显示五角星的效果。

    首先是初始化地图,加载了一个KML格式的矢量数据源,extractStyles为false表示不从kml数据源中提取样式。使用Select添加了鼠标选中的交互事件

    1. vector = new VectorLayer({
    2. source: new Cluster({
    3. distance: 40,
    4. source: new VectorSource({
    5. url: "https://openlayers.org/en/latest/examples/data/kml/2012_Earthquakes_Mag5.kml",
    6. format: new KML({
    7. extractStyles: false,
    8. }),
    9. }),
    10. }),
    11. style: styleFunction,
    12. });
    13. const raster = new TileLayer({
    14. source: new StadiaMaps({
    15. layer: "stamen_toner",
    16. }),
    17. });
    18. const map = new Map({
    19. layers: [raster, vector],
    20. interactions: defaultInteractions().extend([
    21. new Select({
    22. condition: function (evt) {
    23. return evt.type == "pointermove" || evt.type == "singleclick";
    24. },
    25. style: selectStyleFunction,
    26. }),
    27. ]),
    28. target: "map",
    29. view: new View({
    30. center: [0, 0],
    31. zoom: 2,
    32. }),
    33. });

    其中有两个样式函数,先来看第一个styleFunction。

    如果有子feature就显示为黄色圆圈,如果没有子feature则绘制成五角星。

    1. let currentResolution;
    2. function styleFunction(feature, resolution) {
    3. if (resolution != currentResolution) {
    4. calculateClusterInfo(resolution);
    5. currentResolution = resolution;
    6. }
    7. let style;
    8. const size = feature.get("features").length;
    9. if (size > 1) {
    10. style = new Style({
    11. image: new CircleStyle({
    12. radius: feature.get("radius"),
    13. fill: new Fill({
    14. color: [255, 153, 0, Math.min(0.8, 0.4 + size / maxFeatureCount)],
    15. }),
    16. }),
    17. text: new Text({
    18. text: size.toString(),
    19. fill: textFill,
    20. stroke: textStroke,
    21. }),
    22. });
    23. } else {
    24. const originalFeature = feature.get("features")[0];
    25. style = createEarthquakeStyle(originalFeature);
    26. }
    27. return style;
    28. }

    使用calculateClusterInfo 函数计算圆圈的半径,将子feature的extent合并到了一起,结合分辨率算出半径。

    1. const calculateClusterInfo = function (resolution) {
    2. maxFeatureCount = 0;
    3. const features = vector.getSource().getFeatures();
    4. let feature, radius;
    5. for (let i = features.length - 1; i >= 0; --i) {
    6. feature = features[i];
    7. const originalFeatures = feature.get("features");
    8. const extent = createEmpty(); //创建一个空的范围对象,用来存储聚类的总范围。
    9. let j, jj;
    10. for (j = 0, jj = originalFeatures.length; j < jj; ++j) {
    11. //获取当前原始特征的几何范围。将这个几何范围合并到总范围 extent 中
    12. extend(extent, originalFeatures[j].getGeometry().getExtent());
    13. }
    14. maxFeatureCount = Math.max(maxFeatureCount, jj);
    15. radius = (0.25 * (getWidth(extent) + getHeight(extent))) / resolution;
    16. feature.set('radius',radius)
    17. }
    18. };

    extend方法示例

    假设你有一个聚类包含三个特征,其范围分别为:

    • 特征1: [0, 0, 1, 1]
    • 特征2: [2, 2, 3, 3]
    • 特征3: [1, 1, 4, 4]

    通过逐步扩展 extent:

    • 初始 extent 是空的。
    • 扩展第一个特征后,extent 变为 [0, 0, 1, 1]
    • 扩展第二个特征后,extent 变为 [0, 0, 3, 3]
    • 扩展第三个特征后,extent 变为 [0, 0, 4, 4]

    最终的 extent 包含了所有特征的范围,即 [0, 0, 4, 4]

     createEarthquakeStyle是绘制星星的方法,主要用了RegularShape这个类。

    1. function createEarthquakeStyle(feature) {
    2. const name = feature.get("name");
    3. const magnitude = parseFloat(name.substr(2));
    4. const radius = 5 + 20 * (magnitude - 5);
    5. return new Style({
    6. geometry: feature.getGeometry(),
    7. image: new RegularShape({
    8. radius: radius,
    9. radius2: 3,
    10. points: 5,
    11. angle: Math.PI,
    12. fill: earthquakeFill,
    13. stroke: earthquakeStroke,
    14. }),
    15. });
    16. }

    写一个小demo来理解RegularShape

    1. //小demo
    2. let piontArr = [-213399.46385070545, -7204129.9025042085];
    3. let pointFeature = new Feature({
    4. geometry: new MultiPoint([piontArr]),
    5. });
    6. let newLayer = new VectorLayer({
    7. source: new VectorSource({
    8. features: [pointFeature],
    9. }),
    10. style: [
    11. new Style({
    12. image: new RegularShape({
    13. radius: 50,
    14. radius2:20,
    15. points: 5,
    16. angle: Math.PI,
    17. fill: earthquakeFill,
    18. stroke: earthquakeStroke,
    19. }),
    20. }),
    21. ],
    22. });
    23. map.addLayer(newLayer)

     RegularShape参数解释:

    • radius:

      • 含义: 图形的外半径,即从图形中心到外顶点的距离。
    • radius2:

      • 含义: 图形的内半径,仅在绘制星形时有效。表示从图形中心到内顶点的距离。
    • points:

      • 含义: 图形的顶点数。如果 radius2 被定义,则 points 表示星形的顶点数(外顶点和内顶点的总数),否则表示多边形的边数。
      • 示例值: 6 表示绘制一个六边形或六角星形。
    • angle:

      • 含义: 图形的旋转角度,以弧度为单位。Math.PI 表示旋转 180 度。
      • 示例值: Math.PI 表示图形旋转 180 度。

     然后是第二个样式函数selectStyleFunction

    鼠标触摸的时候获取到feature自定义属性features取出来,把每一个子feature绘制成星星形状展示。

    1. function selectStyleFunction(feature) {
    2. const styles = [
    3. new Style({
    4. image: new CircleStyle({
    5. radius: feature.get("radius"),
    6. fill: invisibleFill,
    7. }),
    8. }),
    9. ];
    10. const originalFeatures = feature.get("features");
    11. let originalFeature;
    12. for (let i = originalFeatures.length - 1; i >= 0; --i) {
    13. originalFeature = originalFeatures[i];
    14. styles.push(createEarthquakeStyle(originalFeature));
    15. }
    16. return styles;
    17. }

    完整代码:

    1. <template>
    2. <div class="box">
    3. <h1>Earthquake Clusters</h1>
    4. <div id="map"></div>
    5. </div>
    6. </template>
    7. <script>
    8. import KML from "ol/format/KML.js";
    9. import Map from "ol/Map.js";
    10. import View from "ol/View.js";
    11. import {
    12. Circle as CircleStyle,
    13. Fill,
    14. RegularShape,
    15. Stroke,
    16. Style,
    17. Text,
    18. Circle,
    19. } from "ol/style.js";
    20. import { MultiPoint, Point } from "ol/geom.js";
    21. import { Cluster, StadiaMaps, Vector as VectorSource } from "ol/source.js";
    22. import { Select, defaults as defaultInteractions } from "ol/interaction.js";
    23. import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
    24. import { createEmpty, extend, getHeight, getWidth } from "ol/extent.js";
    25. import Feature from "ol/Feature.js";
    26. export default {
    27. name: "",
    28. components: {},
    29. data() {
    30. return {
    31. map: null,
    32. };
    33. },
    34. computed: {},
    35. created() {},
    36. mounted() {
    37. const earthquakeFill = new Fill({
    38. color: "rgba(255, 153, 0, 0.8)",
    39. });
    40. const earthquakeStroke = new Stroke({
    41. color: "rgba(255, 204, 0, 0.2)",
    42. width: 1,
    43. });
    44. const textFill = new Fill({
    45. color: "#fff",
    46. });
    47. const textStroke = new Stroke({
    48. color: "rgba(0, 0, 0, 0.6)",
    49. width: 3,
    50. });
    51. const invisibleFill = new Fill({
    52. color: "rgba(255, 255, 255, 0.01)",
    53. });
    54. function createEarthquakeStyle(feature) {
    55. const name = feature.get("name");
    56. const magnitude = parseFloat(name.substr(2));
    57. const radius = 5 + 20 * (magnitude - 5);
    58. return new Style({
    59. geometry: feature.getGeometry(),
    60. image: new RegularShape({
    61. radius: radius,
    62. radius2: 3,
    63. points: 5,
    64. angle: Math.PI,
    65. fill: earthquakeFill,
    66. stroke: earthquakeStroke,
    67. }),
    68. });
    69. }
    70. let maxFeatureCount;
    71. let vector = null;
    72. const calculateClusterInfo = function (resolution) {
    73. maxFeatureCount = 0;
    74. const features = vector.getSource().getFeatures();
    75. let feature, radius;
    76. for (let i = features.length - 1; i >= 0; --i) {
    77. feature = features[i];
    78. const originalFeatures = feature.get("features");
    79. const extent = createEmpty();
    80. let j, jj;
    81. for (j = 0, jj = originalFeatures.length; j < jj; ++j) {
    82. extend(extent, originalFeatures[j].getGeometry().getExtent());
    83. }
    84. maxFeatureCount = Math.max(maxFeatureCount, jj);
    85. radius = (0.25 * (getWidth(extent) + getHeight(extent))) / resolution;
    86. feature.set('radius',radius)
    87. }
    88. };
    89. let currentResolution;
    90. function styleFunction(feature, resolution) {
    91. if (resolution != currentResolution) {
    92. calculateClusterInfo(resolution);
    93. currentResolution = resolution;
    94. }
    95. let style;
    96. const size = feature.get("features").length;
    97. if (size > 1) {
    98. style = new Style({
    99. image: new CircleStyle({
    100. radius: feature.get("radius"),
    101. fill: new Fill({
    102. color: [255, 153, 0, Math.min(0.8, 0.4 + size / maxFeatureCount)],
    103. }),
    104. }),
    105. text: new Text({
    106. text: size.toString(),
    107. fill: textFill,
    108. stroke: textStroke,
    109. }),
    110. });
    111. } else {
    112. const originalFeature = feature.get("features")[0];
    113. style = createEarthquakeStyle(originalFeature);
    114. }
    115. return style;
    116. }
    117. function selectStyleFunction(feature) {
    118. const styles = [
    119. new Style({
    120. image: new CircleStyle({
    121. radius: feature.get("radius"),
    122. fill: invisibleFill,
    123. }),
    124. }),
    125. ];
    126. const originalFeatures = feature.get("features");
    127. let originalFeature;
    128. for (let i = originalFeatures.length - 1; i >= 0; --i) {
    129. originalFeature = originalFeatures[i];
    130. styles.push(createEarthquakeStyle(originalFeature));
    131. }
    132. return styles;
    133. }
    134. vector = new VectorLayer({
    135. source: new Cluster({
    136. distance: 40,
    137. source: new VectorSource({
    138. url: "https://openlayers.org/en/latest/examples/data/kml/2012_Earthquakes_Mag5.kml",
    139. format: new KML({
    140. extractStyles: false,
    141. }),
    142. }),
    143. }),
    144. style: styleFunction,
    145. });
    146. const raster = new TileLayer({
    147. source: new StadiaMaps({
    148. layer: "stamen_toner",
    149. }),
    150. });
    151. const map = new Map({
    152. layers: [raster,vector],
    153. interactions: defaultInteractions().extend([
    154. new Select({
    155. condition: function (evt) {
    156. return evt.type == "pointermove" || evt.type == "singleclick";
    157. },
    158. style: selectStyleFunction,
    159. }),
    160. ]),
    161. target: "map",
    162. view: new View({
    163. center: [0, 0],
    164. zoom: 2,
    165. }),
    166. });
    167. //小demo
    168. let piontArr = [-213399.46385070545, -7204129.9025042085];
    169. let pointFeature = new Feature({
    170. geometry: new MultiPoint([piontArr]),
    171. });
    172. let newLayer = new VectorLayer({
    173. source: new VectorSource({
    174. features: [pointFeature],
    175. }),
    176. style: [
    177. new Style({
    178. image: new RegularShape({
    179. radius: 50,
    180. radius2:20,
    181. points: 5,
    182. angle: Math.PI,
    183. fill: earthquakeFill,
    184. stroke: earthquakeStroke,
    185. }),
    186. }),
    187. ],
    188. });
    189. // map.addLayer(newLayer)
    190. },
    191. methods: {},
    192. };
    193. </script>
    194. <style lang="scss" scoped>
    195. #map {
    196. width: 100%;
    197. height: 500px;
    198. }
    199. .box {
    200. height: 100%;
    201. }
    202. </style>

  • 相关阅读:
    gdb调试
    英语单词(二)
    不会接口测试?用Postman轻松入门(七)——文件上传请求
    文件目录操作——Linux命令核心
    ubuntu 20.04 docker 安装 mysql
    深入浅出Java的多线程编程——第一篇
    51单片机STC89C52RC——2.2 独立按键控制LED亮灭Plus
    自创Web框架之过度Django框架
    枚举--用枚举封装一个工具类
    战神引擎传奇假设教程
  • 原文地址:https://blog.csdn.net/aaa_div/article/details/139447243