riku
2025-03-14 5f44d21b3921abc88506a7ec46b3fe6f078664aa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/**
 * 网格绘制
 */
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(points) {
    const gridViews = [];
    points.forEach((p) => {
      const { lb, rt, c } = p;
 
      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: 1,
        strokeOpacity: 1,
        // strokeStyle还支持 solid
        strokeStyle: 'solid',
        fillColor: '#990D0D',
        fillOpacity: 0.8,
        cursor: 'pointer',
        zIndex: 50
      });
      gridViews.push(rectangle);
    });
    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;
  }
};