| 
                        123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 | 
                        - /**
 -  * @Author: Caven
 -  * @Date: 2021-01-18 20:13:30
 -  */
 - 
 - import Vector from './Vector'
 - 
 - class Field {
 -   constructor(params) {
 -     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
 -    * https://en.wikipedia.org/wiki/Bilinear_interpolation
 -    * @param   {Number} x
 -    * @param   {Number} y
 -    * @param   {Number[]} g00
 -    * @param   {Number[]} g10
 -    * @param   {Number[]} g01
 -    * @param   {Number[]} g11
 -    * @returns {Vector}
 -    */
 -   bilinearInterpolateVector(x, y, g00, g10, g01, g11) {
 -     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) {
 -     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, lat) {
 -     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, n) {
 -     return a - n * Math.floor(a / n)
 -   }
 - 
 -   /**
 -    *
 -    * @param lon
 -    * @param lat
 -    */
 -   getDecimalIndexes(lon, lat) {
 -     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, lat) {
 -     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, lat) {
 -     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, lat) {
 -     let value = this.valueAt(lon, lat)
 -     return value !== null
 -   }
 - 
 -   /**
 -    *
 -    * @param i
 -    * @param j
 -    */
 -   interpolatePoint(i, j) {
 -     //         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
 -    * @private
 -    * @param   {Number} ii - index
 -    * @returns {Number} i - inside the allowed indexes
 -    */
 -   clampColumnIndex(ii) {
 -     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
 -    * @private
 -    * @param   {Number} jj index
 -    * @returns {Number} j - inside the allowed indexes
 -    */
 -   clampRowIndex(jj) {
 -     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
 -    * @private
 -    * @param   {Number} i - decimal index
 -    * @param   {Number} j - decimal index
 -    * @returns {Array} [fi, ci, fj, cj]
 -    */
 -   getFourSurroundingIndexes(i, j) {
 -     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
 -    * @private
 -    * @param   {Number} fi
 -    * @param   {Number} ci
 -    * @param   {Number} fj
 -    * @param   {Number} cj
 -    * @returns {Array}
 -    */
 -   getFourSurroundingValues(fi, ci, fj, cj) {
 -     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, j) {
 -     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, j) {
 -     let lon = this.longitudeAtX(i)
 -     let lat = this.latitudeAtY(j)
 -     return [lon, lat]
 -   }
 - 
 -   /**
 -    * Longitude for grid-index
 -    * @param   {Number} i - column index (integer)
 -    * @returns {Number} longitude at the center of the cell
 -    */
 -   longitudeAtX(i) {
 -     let halfXPixel = this.deltaX / 2.0
 -     let lon = this.xmin + halfXPixel + i * this.deltaX
 -     if (this.wrappedX) {
 -       lon = lon > 180 ? lon - 360 : lon
 -     }
 -     return lon
 -   }
 - 
 -   /**
 -    * Latitude for grid-index
 -    * @param j
 -    * @returns {number}
 -    */
 -   latitudeAtY(j) {
 -     let halfYPixel = this.deltaY / 2.0
 -     return this.ymax - halfYPixel - j * this.deltaY
 -   }
 - 
 -   /**
 -    *
 -    * @param o
 -    * @param width
 -    * @param height
 -    * @param unproject
 -    * @returns {{}}
 -    */
 -   randomize(o, width, height, unproject) {
 -     if (o === void 0) {
 -       o = {}
 -     }
 -     let i = (Math.random() * (width || this.cols)) | 0
 -     let j = (Math.random() * (height || this.rows)) | 0
 -     let coords = unproject([i, j])
 -     if (coords !== null) {
 -       o.x = coords[0]
 -       o.y = coords[1]
 -     } else {
 -       o.x = this.longitudeAtX(i)
 -       o.y = this.latitudeAtY(j)
 -     }
 -     return o
 -   }
 - 
 -   /**
 -    * check is custom field
 -    */
 -   checkFields() {
 -     return this.isFields
 -   }
 - }
 - 
 - export default Field
 
 
  |