interp3.mjs

import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import size from 'lodash-es/size.js'
import isestr from 'wsemi/src/isestr.mjs'
import iseobj from 'wsemi/src/iseobj.mjs'
import isearr from 'wsemi/src/isearr.mjs'
import isnum from 'wsemi/src/isnum.mjs'
import cdbl from 'wsemi/src/cdbl.mjs'
import execPyodide from 'wsemi/src/execPyodide.mjs'
import ptsXYZtoArr from './ptsXYZtoArr.mjs'
import ptsXYZVtoArr from './ptsXYZVtoArr.mjs'


/**
 * 針對不規則三維數據進行指定內插點數值
 *
 * Unit Test: {@link https://github.com/yuda-lyu/w-gis/blob/master/test/interp3.test.mjs Github}
 * @memberOf w-gis
 * @param {Array} psSrc 輸入點陣列,為[{x:x1,y:y1},{x:x2,y:y2},...]點物件之陣列
 * @param {Array|Object} psTar 輸入點陣列或點物件,為[{x:x1,y:y1},{x:x2,y:y2},...]點物件之陣列,或{x:x1,y:y1}點物件
 * @param {String} [opt.keyX='x'] 輸入點物件之x座標欄位字串,預設'x'
 * @param {String} [opt.keyY='y'] 輸入點物件之y座標欄位字串,預設'y'
 * @param {String} [opt.keyZ='z'] 輸入點物件之z座標或值欄位字串,預設'z'
 * @returns {Promise|Array|Object} 回傳Promise或點物件陣列或點物件,若useSync=false則回傳Promise,resolve為回傳點物件陣列或點物件,reject為失敗訊息,若useSync=true則回傳點物件陣列或點物件,若發生錯誤則回傳錯誤訊息物件
 * @example
 *
 * async function test() {
 *
 *     let ps
 *     let p
 *     let r
 *
 *     ps = [{ x: 243, y: 206, z: 95, v: 2.2 }, { x: 233, y: 225, z: 146, v: 15.1 }, { x: 21, y: 325, z: 22, v: 7.9 }, { x: 953, y: 28, z: 223, v: 5.1 }, { x: 1092, y: 290, z: 39, v: 17.5 }, { x: 744, y: 200, z: 191, v: 6.6 }, { x: 174, y: 3, z: 22, v: 1.4 }, { x: 537, y: 368, z: 249, v: 2.9 }, { x: 1151, y: 371, z: 86, v: 7.3 }, { x: 814, y: 252, z: 125, v: 8.2 }]
 *     p = {
 *         x: 243,
 *         y: 207,
 *         z: 100,
 *     }
 *     r = await interp3(ps, p)
 *     console.log(r)
 *     // => { x: 243, y: 207, z: 100, v: 3.4894028270041137 }
 *
 *     ps = [{ x: 243, y: 206, z: 95, v: 2.2 }, { x: 233, y: 225, z: 146, v: 15.1 }, { x: 21, y: 325, z: 22, v: 7.9 }, { x: 953, y: 28, z: 223, v: 5.1 }, { x: 1092, y: 290, z: 39, v: 17.5 }, { x: 744, y: 200, z: 191, v: 6.6 }, { x: 174, y: 3, z: 22, v: 1.4 }, { x: 537, y: 368, z: 249, v: 2.9 }, { x: 1151, y: 371, z: 86, v: 7.3 }, { x: 814, y: 252, z: 125, v: 8.2 }]
 *     p = {
 *         x: 283,
 *         y: 207,
 *         z: 100,
 *     }
 *     r = await interp3(ps, p)
 *     console.log(r)
 *     // => { x: 283, y: 207, z: 100, v: 2.5832273787671967 }
 *
 *     ps = [{ x: 243, y: 206, z: 95, v: 2.2 }, { x: 233, y: 225, z: 146, v: 15.1 }, { x: 21, y: 325, z: 22, v: 7.9 }, { x: 953, y: 28, z: 223, v: 5.1 }, { x: 1092, y: 290, z: 39, v: 17.5 }, { x: 744, y: 200, z: 191, v: 6.6 }, { x: 174, y: 3, z: 22, v: 1.4 }, { x: 537, y: 368, z: 249, v: 2.9 }, { x: 1151, y: 371, z: 86, v: 7.3 }, { x: 814, y: 252, z: 125, v: 8.2 }]
 *     p = {
 *         x: 1160,
 *         y: 380,
 *         z: 100,
 *     }
 *     r = await interp3(ps, p)
 *     console.log(r)
 *     // => { x: 1160, y: 380, z: null }
 *
 *     ps = [{ a: 243, b: 206, c: 95, d: 2.2 }, { a: 233, b: 225, c: 146, d: 15.1 }, { a: 21, b: 325, c: 22, d: 7.9 }, { a: 953, b: 28, c: 223, d: 5.1 }, { a: 1092, b: 290, c: 39, d: 17.5 }, { a: 744, b: 200, c: 191, d: 6.6 }, { a: 174, b: 3, c: 22, d: 1.4 }, { a: 537, b: 368, c: 249, d: 2.9 }, { a: 1151, b: 371, c: 86, d: 7.3 }, { a: 814, b: 252, c: 125, d: 8.2 }]
 *     p = {
 *         a: 243,
 *         b: 207,
 *         c: 100,
 *     }
 *     r = await interp3(ps, p, { keyX: 'a', keyY: 'b', keyZ: 'c', keyV: 'd' })
 *     console.log(r)
 *     // => { a: 243, b: 207, c: 100, d: 3.4894028270041137 }
 *
 *     ps = [{ x: 243, y: 206, z: 95, v: 2.2 }, { x: 233, y: 225, z: 146, v: 15.1 }, { x: 21, y: 325, z: 22, v: 7.9 }, { x: 953, y: 28, z: 223, v: 5.1 }, { x: 1092, y: 290, z: 39, v: 17.5 }, { x: 744, y: 200, z: 191, v: 6.6 }, { x: 174, y: 3, z: 22, v: 1.4 }, { x: 537, y: 368, z: 249, v: 2.9 }, { x: 1151, y: 371, z: 86, v: 7.3 }, { x: 814, y: 252, z: 125, v: 8.2 }]
 *     p = [
 *         {
 *             x: 243,
 *             y: 207,
 *             z: 100,
 *         },
 *         {
 *             x: 283,
 *             y: 207,
 *             z: 100,
 *         },
 *     ]
 *     r = await interp3(ps, p)
 *     console.log(r)
 *     // => [
 *     //   { x: 243, y: 207, z: 100, v: 3.4894028270041137 },
 *     //   { x: 283, y: 207, z: 100, v: 2.5832273787671967 }
 *     // ]
 *
 *     ps = [{ x: 0.000243, y: 0.000206, z: 0.000095, v: 2.2 }, { x: 0.000233, y: 0.000225, z: 0.000146, v: 15.1 }, { x: 0.000021, y: 0.000325, z: 0.000022, v: 7.9 }, { x: 0.000953, y: 0.000028, z: 0.000223, v: 5.1 }, { x: 0.001092, y: 0.00029, z: 0.000039, v: 17.5 }, { x: 0.000744, y: 0.0002, z: 0.000191, v: 6.6 }, { x: 0.000174, y: 0.000003, z: 0.000022, v: 1.4 }, { x: 0.000537, y: 0.000368, z: 0.000249, v: 2.9 }, { x: 0.001151, y: 0.000371, z: 0.000086, v: 7.3 }, { x: 0.000814, y: 0.000252, z: 0.000125, v: 8.2 }]
 *     p = {
 *         x: 0.000243,
 *         y: 0.000207,
 *         z: 0.0001,
 *     }
 *     r = await interp3(ps, p)
 *     console.log(r)
 *     // => { x: 0.000243, y: 0.000207, z: 100, v: 3.489402827004115 }
 *
 * }
 * test()
 *     .catch((err) => {
 *         console.log(err)
 *     })
 *
 */
async function interp3(psSrc, psTar, opt = {}) {

    //check psSrc
    if (!isearr(psSrc)) {
        return {
            err: 'psSrc is not an array'
        }
    }

    //check psTar
    if (!iseobj(psTar) && !isearr(psTar)) {
        return {
            err: 'psTar is not an object or array'
        }
    }

    //isOne
    let isOne = iseobj(psTar)
    if (isOne) {
        psTar = [psTar]
    }

    //keyX
    let keyX = get(opt, 'keyX')
    if (!isestr(keyX)) {
        keyX = 'x'
    }

    //keyY
    let keyY = get(opt, 'keyY')
    if (!isestr(keyY)) {
        keyY = 'y'
    }

    //keyZ
    let keyZ = get(opt, 'keyZ')
    if (!isestr(keyZ)) {
        keyZ = 'z'
    }

    //keyV
    let keyV = get(opt, 'keyV')
    if (!isestr(keyV)) {
        keyV = 'v'
    }

    //keyInd
    let keyInd = 'ind'

    //ptsXYZVtoArr
    psSrc = ptsXYZVtoArr(psSrc, { keyX, keyY, keyZ, keyV, keyInd })
    // console.log('ptsXYZVtoArr psSrc', psSrc)

    //check psSrc
    if (size(psSrc) === 0) {
        return {
            err: 'psSrc has no effective data'
        }
    }

    //ptsXYZtoArr
    psTar = ptsXYZtoArr(psTar, { keyX, keyY, keyZ, keyInd })
    // console.log('ptsXYZtoArr psTar', psTar)

    //check psTar
    if (size(psTar) === 0) {
        return {
            err: 'psTar has no effective data'
        }
    }

    //execPyodide
    let pkgs = [
        'scipy',
    ]
    let imps = [
        'from scipy.interpolate import griddata', //griddata是呼叫LinearNDInterpolator
    ]

    let _psLocs = []
    let _psValus = []
    for (let k = 0; k < psSrc.length; k++) {
        let o = psSrc[k]
        _psLocs.push([o.x, o.y, o.z])
        _psValus.push(o.v)
    }
    // console.log('_psLocs', _psLocs)
    // console.log('_psValus', _psValus)

    let _psTar = []
    for (let k = 0; k < psTar.length; k++) {
        let o = psTar[k]
        _psTar.push([o.x, o.y, o.z])
    }
    // console.log('_psTar', _psTar)

    //execPyodide
    let inps = [
        _psLocs,
        _psValus,
        _psTar,
    ]
    let content = `
ret = griddata(rIn1, rIn2, rIn3, method='linear')
    `
    let ts = await execPyodide({
        pkgs,
        imps,
        inps,
        content,
    })
    // console.log('ts', ts)

    //rs
    let rs = []
    each(psTar, (o, k) => {
        let v = ts[k]
        if (isnum(v)) {
            v = cdbl(v)
        }
        else {
            v = null
        }
        let r = {
            [keyX]: o.x,
            [keyY]: o.y,
            [keyZ]: o.z,
            [keyV]: v,
        }
        rs.push(r)
    })
    // console.log('rs', rs)

    //r
    let r
    if (isOne) {
        r = rs[0]
    }
    else {
        r = rs
    }

    return r
}


export default interp3