cesium 图层构建的那些事 (十九)
我们之前构建过arcgis 的风场图层 这次我们切换构建cesium的风场图层
我们先设计一下风场的工具类
vector类定义
-
class Vector {
-
public u:number;
-
public v:number;
-
public m:number;
-
constructor(u:number, v:number) {
-
this.u = u
-
this.v = v
-
this.m = this.magnitude()
-
}
-
/**
-
* the vector value
-
* @returns {Number}
-
*/
-
magnitude() {
-
return Math.sqrt(this.u * this.u + this.v * this.v)
-
}
-
/**
-
* Angle in degrees (0 to 360º) --> Towards
-
* N is 0º and E is 90º
-
* @returns {Number}
-
*/
-
directionTo() {
-
let verticalAngle = Math.atan2(this.u, this.v)
-
let inDegrees = verticalAngle * (180.0 / Math.PI)
-
if (inDegrees < 0) {
-
inDegrees += 360.0
-
}
-
return inDegrees
-
}
-
-
/**
-
* Angle in degrees (0 to 360º) From x-->
-
* N is 0º and E is 90º
-
* @returns {Number}
-
*/
-
directionFrom() {
-
let a = this.directionTo()
-
return (a + 180.0) % 360.0
-
}
-
}
-
-
export default Vector
Filed类定义
```
import Vector from './Vector'
class Field {
public grid:any = null;
public xmin:any = null;
public xmax:any= null;
public ymin:any= null;
public ymax:any= null;
public rows:any= null;
public cols:any= null;
public us:any= null;
public vs:any= null;
public deltaX:any= null;
public deltaY:any= null;
public isFields:any= null;
public isContinuous:any= null;
public wrappedX:any= null;
public range:any= null;
constructor(params:any) {
this.grid = []
this.xmin = params.xmin
this.xmax = params.xmax
this.ymin = params.ymin
this.ymax = params.ymax
this.cols = params.cols // 列数
this.rows = params.rows // 行数
this.us = params.us //
this.vs = params.vs
this.deltaX = params.deltaX // x 方向增量
this.deltaY = params.deltaY // y方向增量
if (this.deltaY < 0 && this.ymin < this.ymax) { // eslint-disable-next-line no-console console.warn('[wind-core]: The data is flipY') } else { this.ymin = Math.min(params.ymax, params.ymin) this.ymax = Math.max(params.ymax, params.ymin) } this.isFields = true let cols = Math.ceil((this.xmax - this.xmin) / params.deltaX) // 列 let rows = Math.ceil((this.ymax - this.ymin) / params.deltaY) // 行 if (cols !== this.cols || rows !== this.rows) { // eslint-disable-next-line no-console console.warn('[wind-core]: The data grid not equal') } // Math.floor(ni * Δλ) >= 360;
this.isContinuous = Math.floor(this.cols * params.deltaX) >= 360
this.wrappedX = 'wrappedX' in params ? params.wrappedX : this.xmax > 180 // [0, 360] --> [-180, 180];
this.grid = this.buildGrid()
this.range = this.calculateRange()
}
// from https://github.com/sakitam-fdd/wind-layer/blob/95368f9433/src/windy/windy.js#L110
buildGrid() {
let grid = []
let p = 0
let _a = this,
rows = _a.rows,
cols = _a.cols,
us = _a.us,
vs = _a.vs
for (let j = 0; j < rows; j++) {
let row = []
for (let i = 0; i < cols; i++, p++) {
let u = us[p]
let v = vs[p]
let valid = this.isValid(u) && this.isValid(v)
row[i] = valid ? new Vector(u, v) : null
}
if (this.isContinuous) {
row.push(row[0])
}
grid[j] = row
}
return grid
}
/**
*
*/
release() {
this.grid = []
}
/***
*
-
@returns {(*)[]}
*/
extent() {
return [this.xmin, this.ymin, this.xmax, this.ymax]
}/**
-
Bilinear interpolation for Vector
-
@param {Number} x
-
@param {Number} y
-
@param {Number[]} g00
-
@param {Number[]} g10
-
@param {Number[]} g01
-
@param {Number[]} g11
-
@returns {Vector}
*/
bilinearInterpolateVector(x:any, y:any, g00:any, g10:any, g01:any, g11:any) {
let rx = 1 - x
let ry = 1 - y
let a = rx * ry
let b = x * ry
let c = rx * y
let d = x * y
let u = g00.u * a + g10.u * b + g01.u * c + g11.u * d
let v = g00.v * a + g10.v * b + g01.v * c + g11.v * d
return new Vector(u, v)
}/**
-
calculate vector value range
/ calculateRange() { if (!this.grid || !this.grid[0]) { return } let rows = this.grid.length let cols = this.grid[0].length // const vectors = []; let min let max // @from: https://stackoverflow.com/questions/13544476/how-to-find-max-and-min-in-array-using-minimum-comparisons for (let j = 0; j < rows; j++) { for (let i = 0; i < cols; i++) { let vec = this.grid[j][i] if (vec !== null) { let val = vec.m || vec.magnitude() // vectors.push(); if (min === undefined) { min = val } else if (max === undefined) { max = val // update min max // 1. Pick 2 elements(a, b), compare them. (say a > b) min = Math.min(min, max) max = Math.max(min, max) } else { // 2. Update min by comparing (min, b) // 3. Update max by comparing (max, a) min = Math.min(val, min) max = Math.max(val, max) } } } } return [min, max] } /*
* -
@param x
-
@private
*/
isValid(x:any) {
return x !== null && x !== undefined
}getWrappedLongitudes() {
let xmin = this.xmin
let xmax = this.xmax
if (this.wrappedX) {
if (this.isContinuous) {
xmin = -180
xmax = 180
} else {
xmax = this.xmax - 360
xmin = this.xmin - 360
}
}
return [xmin, xmax]
}contains(lon:any, lat:any) {
let _a = this.getWrappedLongitudes(),
xmin = _a[0],
xmax = _a[1]
let longitudeIn = lon >= xmin && lon <= xmax let latitudeIn if (this.deltaY >= 0) {
latitudeIn = lat >= this.ymin && lat <= this.ymax } else { latitudeIn = lat >= this.ymax && lat <= this.ymin
}
return longitudeIn && latitudeIn
}/**
* -
@param a
-
@param n
-
@returns {number}
*/
floorMod(a:any, n:any) {
return a - n * Math.floor(a / n)
}/**
* -
@param lon
-
@param lat
*/
getDecimalIndexes(lon:any, lat:any) {
let i = this.floorMod(lon - this.xmin, 360) / this.deltaX // calculate longitude index in wrapped range [0, 360)
let j = (this.ymax - lat) / this.deltaY // calculate latitude index in direction +90 to -90
return [i, j]
}/**
-
Nearest value at lon-lat coordinates
* -
@param lon
-
@param lat
/ valueAt(lon:any, lat:any) { if (!this.contains(lon, lat)) { return null } let indexes = this.getDecimalIndexes(lon, lat) let ii = Math.floor(indexes[0]) let jj = Math.floor(indexes[1]) let ci = this.clampColumnIndex(ii) let cj = this.clampRowIndex(jj) return this.valueAtIndexes(ci, cj) } /* -
Get interpolated grid value lon-lat coordinates
-
@param lon
-
@param lat
*/
interpolatedValueAt(lon:any, lat:any) {
if (!this.contains(lon, lat)) {
return null
}
let _a = this.getDecimalIndexes(lon, lat),
i = _a[0],
j = _a[1]
return this.interpolatePoint(i, j)
}hasValueAt(lon:any, lat:any) {
let value = this.valueAt(lon, lat)
return value !== null
}/**
* -
@param i
-
@param j
/ interpolatePoint(i:any, j:any) { // 1 2 After converting λ and φ to fractional grid indexes i and j, we find the // fi i ci four points 'G' that enclose point (i, j). These points are at the four // | =1.4 | corners specified by the floor and ceiling of i and j. For example, given // ---G--|---G--- fj 8 i = 1.4 and j = 8.3, the four surrounding grid points are (1, 8), (2, 8), // j ___|_ . | (1, 9) and (2, 9). // =8.3 | | // ---G------G--- cj 9 Note that for wrapped grids, the first column is duplicated as the last // | | column, so the index ci can be used without taking a modulo. let indexes = this.getFourSurroundingIndexes(i, j) let fi = indexes[0], ci = indexes[1], fj = indexes[2], cj = indexes[3] let values = this.getFourSurroundingValues(fi, ci, fj, cj) if (values) { let g00 = values[0], g10 = values[1], g01 = values[2], g11 = values[3] // @ts-ignore return this.bilinearInterpolateVector(i - fi, j - fj, g00, g10, g01, g11) } return null } /* -
Check the column index is inside the field,
-
adjusting to min or max when needed
-
@param {Number} ii - index
-
@returns {Number} i - inside the allowed indexes
*/
clampColumnIndex(ii:any) {
let i = ii
if (ii < 0) { i = 0 } let maxCol = this.cols - 1 if (ii > maxCol) {
i = maxCol
}
return i
}/**
-
Check the row index is inside the field,
-
adjusting to min or max when needed
-
@param {Number} jj index
-
@returns {Number} j - inside the allowed indexes
*/
clampRowIndex(jj:any) {
let j = jj
if (jj < 0) { j = 0 } let maxRow = this.rows - 1 if (jj > maxRow) {
j = maxRow
}
return j
}/**
-
from: https://github.com/IHCantabria/Leaflet.CanvasLayer.Field/blob/master/src/Field.js#L252
-
@param {Number} i - decimal index
-
@param {Number} j - decimal index
-
@returns {Array} [fi, ci, fj, cj]
*/
getFourSurroundingIndexes(i:any, j:any) {
let fi = Math.floor(i) // 左
let ci = fi + 1 // 右
// duplicate colum to simplify interpolation logic (wrapped value)
if (this.isContinuous && ci >= this.cols) {
ci = 0
}
ci = this.clampColumnIndex(ci)
let fj = this.clampRowIndex(Math.floor(j)) // 上 纬度方向索引(取整)
let cj = this.clampRowIndex(fj + 1) // 下
return [fi, ci, fj, cj]
}/**
-
from https://github.com/IHCantabria/Leaflet.CanvasLayer.Field/blob/master/src/Field.js#L277
-
Get four surrounding values or null if not available,
-
from 4 integer indexes
-
@param {Number} fi
-
@param {Number} ci
-
@param {Number} fj
-
@param {Number} cj
-
@returns {Array}
*/
getFourSurroundingValues(fi:any, ci:any, fj:any, cj:any) {
let row
if ((row = this.grid[fj])) {
let g00 = row[fi] // << left let g10 = row[ci] // right >>
if (this.isValid(g00) && this.isValid(g10) && (row = this.grid[cj])) {
// lower row vv
let g01 = row[fi] // << left let g11 = row[ci] // right >>
if (this.isValid(g01) && this.isValid(g11)) {
return [g00, g10, g01, g11] // 4 values found!
}
}
}
return null
}/**
-
Value for grid indexes
-
@param {Number} i - column index (integer)
-
@param {Number} j - row index (integer)
-
@returns {Vector|Number}
*/
valueAtIndexes(i:any, j:any) {
return this.grid[j][i] // <-- j,i !!
}/**
-
Lon-Lat for grid indexes
-
@param {Number} i - column index (integer)
-
@param {Number} j - row index (integer)
-
@returns {Number[]} [lon, lat]
*/
lonLatAtIndexes(i:any, j:any) {
let lon = this.longitudeAtX(i)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)