On this page

# GeoDataSet.js

    import DataSet from "./DataSet.js"
    import { bboxMetricSize } from './gis.js'
    class GeoDataSet extends DataSet {

        /**
         * Mostly the same a DataSet except it has bounds.
         * A few methods, like slope, dzdx, dzdy, and aspect are different because they take into account the size of the bbox in meters
         *
         * @param {Number} width width of the DataSet in pixels
         * @param {Number} height height of the DataSet in pixels
         * @param {Array} bbox [west, south, east, north]
         * @param {TypedArray} data
         */
        constructor(width, height, bbox, data) {
            super(width, height, data)
            this.bbox = bbox
        }

        /**
         * Create a view of a Dataset including the bounds.
         *
         * @static
         * @param {DataSet} dataSet
         * @param {Array} bbox [west, south, east, north]
         * @returns {GeoDataSet} GeoDataSet view of the dataset data. It is a view not a copy.
         */
        static viewFromDataSet(dataSet, bbox) {
            return new GeoDataSet(dataSet.width, dataSet.height, bbox, dataSet.data)
        }

        lat2y(lat) {
            const [west, south, east, north] = this.bbox
            const y = Math.round(this.height * (lat - south) / (north - south))
            return y
        }

        lon2x(lng) {
            const [west, south, east, north] = this.bbox
            const x = Math.round(this.width * (lng - west) / (east - west))
            return x
        }

        // Convert from geoon/lat coords to pixel coords
        toPixel(geoX, geoY) {
            return [this.lon2x(geoX), this.lat2y(geoY)]
        }

        // Get pixel from geo lon/lat coords to pixel coords
        getGeo(geoX, geoY) {
            const [x, y] = this.toPixel(geoX, geoY)
            return this.getXY(x, y)
        }

        // Set pixel from geo lon/lat coords to pixel coords
        // I think this could be a slow way to set data for large amounts
        setGeo(geoX, geoY, value) {
            const [x, y] = this.toPixel(geoX, geoY)
            return this.setXY(x, y, value)
        }

        /**
         * Samples a pixel at a given latitude and longitude
         *
         * @param {Number} geoX longitude
         * @param {Number} geoY latitude
         * @param {Boolean} useNearest
         * @throws Out Of Range Error - when it is outside of the bbox
         * @returns {Number}
         */
        sampleGeo(geoX, geoY, useNearest = true) {
            const [x, y] = this.toPixel(geoX, geoY)
            return this.sample(x, y, useNearest)
        }

        /**
         * Change in z value in terms of x. Finite difference.
         *
         * @returns {GeoDataset}
         */
        dzdx() {
            const [widthMeters, heightMeters] = bboxMetricSize(this.bbox)
            const pixelScale = widthMeters / this.width
            const dzdx = super.dzdx(2, (1 / 8) * (1 / pixelScale)) // (1/8) for the kernel and 1/pixelscale to get units right
            const dzdx2 = GeoDataSet.viewFromDataSet(dzdx, this.bbox)
            return dzdx2
        }

        /**
         * Change in z value in terms of y. Finite difference.
         *
         * @returns {GeoDataSet}
         */
        dzdy() {
            const [widthMeters, heightMeters] = bboxMetricSize(this.bbox)
            const pixelScale = heightMeters / this.height
            const dzdy = super.dzdy(2, (1 / 8) * (1 / pixelScale))
            const dzdy2 = GeoDataSet.viewFromDataSet(dzdy, this.bbox)
            return dzdy2
        }

        /**
         * Create dzdx, dzdy, slope, and aspect in one function.
         *
         * @returns {SlopeAndAspect} {dzdx, dzdy, slope, aspect}
         */
        slopeAndAspect() {
            const dzdx = this.dzdx()
            const dzdy = this.dzdy()
            const slope = this.slope(dzdx, dzdy)
            const aspect = this.aspect(dzdx, dzdy)
            return { dzdx, dzdy, slope, aspect }
        }

        /**
         * The aspect of each pixel in radians.
         *
         * @param {GeoDataset} dzdx
         * @param {GeoDataSet} dzdy
         * @returns {GeoDataSet} Aspect in radians.
         */
        aspect(dzdx = this.dzdx(), dzdy = this.dzdy()) {
            const asp = dzdx.map((x, i) => {
                const y = dzdy.data[i]
                const a = Math.atan2(-y, -x)
                return a
            })
            return asp
        }

        /**
         * Returns the slope in radians
         *
         * @param {GeoDataset} dzdx
         * @param {GeoDataset} dzdy
         * @returns {GeoDataset}
         */
        slope(dzdx = this.dzdx(), dzdy = this.dzdy()) {
            const slop = dzdx.map((x, i) => {
                const y = dzdy.data[i]
                const a = Math.hypot(-x, -y)
                const sl = (Math.PI / 2) - Math.atan2(1, a)
                return sl
            })
            return slop
        }

        //
        // The functions below are the same as DataSet's version except they return a GeoDataset instead of a dataset.
        // Is there a better way to do this?
        //
        //
        clone() {
            return new GeoDataSet(this.width, this.height, this.bbox, this.data)
        }

        resample(width, height, useNearest = true, Type = Array) {
            const a = super.resample(width, height, useNearest, Type)
            const b = GeoDataSet.viewFromDataSet(a, this.bbox)
            return b
        }

        convolve(kernel, factor = 1, crop = false) {
            const a = super.convolve(kernel, factor, crop)
            const b = GeoDataSet.viewFromDataSet(a, this.bbox)
            return b
        }

        normalize(lo = 0, hi = 1, round = false) {
            const a = super.normalize(lo, hi, round)
            const b = GeoDataSet.viewFromDataSet(a, this.bbox)
            return b
        }

        map(f) {
            const a = super.map(f)
            const b = GeoDataSet.viewFromDataSet(a, this.bbox)
            return b
        }
    }

    export default GeoDataSet

Type anything to view search result