OpenLayers 计算GeoTIFF影像NDVI

关注我,带你一起学GIS ^

注:当前使用的是 ol [9.2.4] 版本,天地图使用的key请到天地图官网申请,并替换为自己的key

前言

NDVI(Normalized Difference Vegetation Index)即归一化植被指数,是反应农作物长势和营养信息的重要参数之一,用于监测植物生长状态、植被覆盖度和消除部分辐射误差。其值在[-1,1]之间,-1表示可见光高反射;0表示有岩石或裸土等,NIR和R近似相等;正值,表示有植被覆盖,且值越大,表明植被覆盖度越高。计算公式:NDVI = (NIR – RED)/ (NIR + RED)

1. 加载数据源

Landsat卫星影像中,B4为红色通道,B5为近红外通道,所以加载B4B5通道数据,并设置无值数据为0,设置图层为不透明度。

const source = new ol.source.GeoTIFF({
    sources: [
        {
            // red band
            url: "http://localhost/GeoTIFF/LC08_L2SP_129043_20211120_20211130_02_T1/LC08_L2SP_129043_20211120_20211130_02_T1_SR_B4.TIF",
            max: 40000
        },
        {
            // near infraed
            url: "http://localhost/GeoTIFF/LC08_L2SP_129043_20211120_20211130_02_T1/LC08_L2SP_129043_20211120_20211130_02_T1_SR_B5.TIF",
            max: 45000
        }
    ],
    nodata: 0, // 设置无效数据为0
    opaque: true, // 设置不透明
    // convertToRGB: true // 将色彩系统转换为RGB
})

2. 加载GeoTIFF图层

根据公式计算NDVI值,并进行可视化表达。其中['/', value1, value2]表示用value2除以value1['-', ['band', 2], ['band', 1]]表示用band2band1,也就是近红外减去红外,['+', ['band', 2], ['band', 1]]表示band2+band1,也就是近红外加上红外。

const tiffLayer = new ol.layer.WebGLTile({
    sourcesource,
    style: {
        color: [
            'interpolate',
            ['linear'],
            // 计算NDVI值
            ['/', ['-', ['band', 2], ['band', 1]], ['+', ['band', 2], ['band', 1]]],
            // NDVI色带,其值为[-1,1]
            -0.2,
            [191, 191, 191],
            -0.1,
            [219, 219, 219],
            0,
            [255, 255, 224],
            0.025,
            [255, 250, 204],
            0.05,
            [237, 232, 181],
            0.075,
            [222, 217, 156],
            0.1,
            [204, 199, 130],
            0.125,
            [189, 184, 107],
            0.15,
            [176, 194, 97],
            0.175,
            [163, 204, 89],
            0.2,
            [145, 191, 82],
            0.25,
            [128, 179, 71],
            0.3,
            [112, 163, 64],
            0.35,
            [97, 150, 54],
            0.4,
            [79, 138, 46],
            0.45,
            [64, 125, 36],
            0.5,
            [48, 110, 28],
            0.55,
            [33, 97, 18],
            0.6,
            [15, 84, 10],
            0.65,
            [0, 69, 0],
        ]
    }
})

3. 显示NDVI值

鼠标在地图上移动或者点击地图时,显示NDVI值。

const displayPixelValue = (event) => {
  // 返回像素位置数据,根据数据类型而定
  const data = tiffLayer.getData(event.pixel)
  if (!data) {
    return
  }
  const red = data[0]
  const nir = data[1]
  const ndvi = (nir - red) / (nir + red)
  const ndviInput = document.querySelector(".layui-input")
  ndviInput.value = ndvi.toFixed(4)
}
// 监听鼠标移动和点击事件
map.on(["pointermove""click"], displayPixelValue)

4. 完整代码

其中libs文件夹下的包需要更换为自己下载的本地包或者引用在线资源。本示例引用了layui组件,请自行替换。

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>OpenLayers 根据Landsat计算NDVI</title>
    <meta charset="utf-8" />

    <script src="../../../libs/proj4.js"></script>
    <script src="../../../js/ol9.2.4.js"></script>
    <script src="../../../libs/layui/layui.js"></script>

    <link rel="stylesheet" href="../../../css/ol9.2.4.css">
    <link rel="stylesheet" href="../../../libs/layui/css/layui.css">

    <script src="https://cdn.jsdelivr.net/npm/geotiff@2.1.3/dist-browser/geotiff.min.js"></script>
    <style>
        * {
            padding: 0;
            margin: 0;
            font-size: 14px;
            font-family: '微软雅黑';
        }

        .clearfix::after {
            display: block;
            content: "";
            clear: both;
        }

        html,
        body {
            width: 100%;
            height: 100%;
        }

        #map {
            position: absolute;
            top: 50px;
            bottom: 0;
            width: 100%;
        }

        #top-content {
            position: absolute;
            width: 100%;
            height: 50px;
            line-height: 50px;
            background: linear-gradient(135deg, #ff00cc, #ffcc00, #00ffcc, #ff0066);
            color: #fff;
            text-align: center;
            font-size: 32px;
        }

        #top-content span {
            font-size: 32px;
        }

        #layer-container {
            position: absolute;
            top: 20%;
            left: 20px;
            width: 250px;
            padding: 10px;
            background: #fff;
            color: #fff;
            border: 1px solid #ddd;
            border-radius: 2.5px;
        }

        .layer-head {
            background: #16baaa;
            padding: 10px;
            margin-bottom: 15px;
        }

        .layer-form {
            width: 100%;
        }

        .layui-form-label {
            width: 44%;
            padding: 8px 15px;
            height: 38px;
            line-height: 20px;
            border-width: 1px;
            border-style: solid;
            border-radius: 2px 0 0 2px;
            text-align: center;
            background-color: #fafafa;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            box-sizing: border-box;
            border-color: #eee;
            font-weight: 400;
            color: #000;
        }

        .layui-form-item {
            margin-bottom: 0;
        }

        .layui-input[disabled] {
            background-color: #fff;
        }
    </style>
</head>

<body>
    <div id="top-content">
        <span>OpenLayers 根据Landsat计算NDVI</span>
    </div>
    <div id="map" title="地图显示"></div>
    <div id="layer-container">
        <div class="layer-form">
            <div class="layui-form-item">
                <label class="layui-form-label">NDVI值</label>
                <div class="layui-input-block">
                    <input type="number" disabled lay-affix="number" min="0" max="1" placeholder="显示NDVI值"
                        class="layui-input rectangle-layer">
                </div>
            </div>
        </div>
    </div>
</body>

</html>

<script>
    const source = new ol.source.GeoTIFF({
        sources: [
            {
                // red band
                url: "http://localhost/GeoTIFF/LC08_L2SP_129043_20211120_20211130_02_T1/LC08_L2SP_129043_20211120_20211130_02_T1_SR_B4.TIF",
                max: 40000
            },
            {
                // near infraed
                url: "http://localhost/GeoTIFF/LC08_L2SP_129043_20211120_20211130_02_T1/LC08_L2SP_129043_20211120_20211130_02_T1_SR_B5.TIF",
                max: 45000
            }
        ],
        nodata: 0, // 设置无效数据为0
        opaque: false, // 开启透明
        // convertToRGB: true // 将色彩系统转换为RGB
    })
    // 加载GeoTIFF影像数据
    const tiffLayer = new ol.layer.WebGLTile({
        sourcesource,
        style: {
            color: [
                'interpolate',
                ['linear'],
                // 计算NDVI值
                ['/', ['-', ['band', 2], ['band', 1]], ['+', ['band', 2], ['band', 1]]],
                // NDVI色带,其值为[-1,1]
                -0.2,
                [191, 191, 191],
                -0.1,
                [219, 219, 219],
                0,
                [255, 255, 224],
                0.025,
                [255, 250, 204],
                0.05,
                [237, 232, 181],
                0.075,
                [222, 217, 156],
                0.1,
                [204, 199, 130],
                0.125,
                [189, 184, 107],
                0.15,
                [176, 194, 97],
                0.175,
                [163, 204, 89],
                0.2,
                [145, 191, 82],
                0.25,
                [128, 179, 71],
                0.3,
                [112, 163, 64],
                0.35,
                [97, 150, 54],
                0.4,
                [79, 138, 46],
                0.45,
                [64, 125, 36],
                0.5,
                [48, 110, 28],
                0.55,
                [33, 97, 18],
                0.6,
                [15, 84, 10],
                0.65,
                [0, 69, 0],
            ]
        }
    })

    source.getView().then(res => {
        console.log(res)
    })

    const map = new ol.Map({
        target: "map",
        loadTilesWhileInteracting: true,
        view: source.getView(),
        // 地图默认控件
        controls: ol.control.defaults.defaults({
            zoom: false,
            attribution: true,
            rotate: true
        })
    })

    map.addLayer(tiffLayer)

    const displayPixelValue = (event) => {
        // 返回像素位置数据,根据数据类型而定
        const data = tiffLayer.getData(event.pixel)
        if (!data) {
            return
        }
        const red = data[0]
        const nir = data[1]
        const ndvi = (nir - red) / (nir + red)
        const ndviInput = document.querySelector(".layui-input")
        ndviInput.value = ndvi.toFixed(4)
    }

    map.on(["pointermove""click"], displayPixelValue)
</script>

如何下载Landsat卫星影像请参考:https://mp.weixin.qq.com/s?__biz=MzkwOTEzNjI5NQ==&mid=2247485131&idx=1&sn=6bf2af3b75f98a573ccc96a347fe837a&chksm=c13e1e03f649971529861ab97834750ea1bc824f992a32f3325f74a6a3174309b64cffb8f759#rd

如何加载GeoTIFF影像请参考:https://mp.weixin.qq.com/s?__biz=MzkwOTEzNjI5NQ==&mid=2247485173&idx=1&sn=2e52d6df4ee71b67e1db8e32c9a22693&chksm=c13e1e3df649972b6f9edb7d7b2a452d9edcd81c535f88d1ffce7f480884f7a2fffdc017bfd9#rd

如何加载GeoTIFF投影坐标请参考:https://mp.weixin.qq.com/s?__biz=MzkwOTEzNjI5NQ==&mid=2247485224&idx=1&sn=6d4e81a1f6e98deece1f427c9737542f&chksm=c13e1fe0f64996f6ed98101539b5270e0076bc933f05769f956f33059025dff252dbe7666501#rd

OpenLayers示例数据下载,请在公众号后台回复:ol数据

全国信息化工程师-GIS 应用水平考试资料,请在公众号后台回复:GIS考试

GIS之路公众号已经接入了智能助手,欢迎大家前来提问。

欢迎访问我的博客网站-长谈GIShttp://shanhaitalk.com

都看到这了,不要忘记点赞、收藏+关注 

本号不定时更新有关 GIS开发  相关内容,欢迎关注 

 

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部