# 九.GeoTIFF

GeoTIFF,是一种基于 TIFF 的地理空间栅格数据存储格式,也是目前被支持最广泛、最通用的空间栅格数据格式之一

通俗来讲,GeoTIFF 是 TIFF 格式的一种扩展,是支持地理空间参考的 TIFF. GeoTIFF 已经是 TIFF 规范的一部分

# 1.瓦片图

查看代码详情
<template>
    <div ref="map" class="map"></div>
  </template>
  
  <script>
  export default {
    mounted() {
      let {
        Map,
        layer: { WebGLTile: TileLayer },
        source: { GeoTIFF },
      } = ol;
      const source = new GeoTIFF({
        sources: [
          {
            url: "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/TCI.tif",
          },
        ],
      });
      const map = new Map({
        target: this.$refs.map,
        layers: [
          new TileLayer({
            source: source,
          }),
        ],
        view: source.getView(),
      });
    },
  };
  </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 2.更改瓦片层样式

查看代码详情
<template>
  <div>
    <div ref="map" class="map"></div>
    设置图层样式
    <select id="style">
      <option value="trueColor">彩色</option>
      <option value="falseColor">黑白</option>
      <option value="ndvi">植被覆盖指数</option>
    </select>
  </div>
</template>

<script>
export default {
  mounted() {
    let {
      Map,
      View,
      layer: { WebGLTile: TileLayer },
      source: { GeoTIFF },
    } = ol;
    const max = 3000;
    function normalize(value) {
      return ["/", value, max];
    }
    const red = normalize(["band", 1]);
    const green = normalize(["band", 2]);
    const blue = normalize(["band", 3]);
    const nir = normalize(["band", 4]);
    const trueColor = {
      color: ["array", red, green, blue, 1],
      gamma: 1.1,
    };
    const falseColor = {
      color: ["array", nir, red, green, 1],
      gamma: 1.1,
    };
    const ndvi = {
      color: [
        "interpolate",
        ["linear"],
        ["/", ["-", nir, red], ["+", nir, red]],
        -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],
      ],
    };
    const layer = new TileLayer({
      style: trueColor,
      source: new GeoTIFF({
        normalize: false,
        sources: [
          {
            url: "https://s2downloads.eox.at/demo/EOxCloudless/2020/rgbnir/s2cloudless2020-16bits_sinlge-file_z0-4.tif",
          },
        ],
      }),
    });
    const map = new Map({
      target: this.$refs.map,
      layers: [layer],
      view: new View({
        projection: "EPSG:4326",
        center: [12579156, 3274244],
        zoom: 2,
        maxZoom: 6,
      }),
    });
    const styles = { trueColor, falseColor, ndvi };
    const styleSelector = document.getElementById("style");
    function update() {
      const style = styles[styleSelector.value];
      console.log(style, "style");
      layer.setStyle(style);
    }
    styleSelector.addEventListener("change", update);
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

# 3.植被覆盖指数

查看代码详情
<template>
  <div>
    <div ref="map" class="map"></div>
    <div>植被覆盖指数: <span id="output"></span></div>
  </div>
</template>

<script>
export default {
  mounted() {
    let {
      Map,
      layer: { WebGLTile: TileLayer },
      source: { GeoTIFF },
    } = ol;

    const source = new GeoTIFF({
      sources: [
        {
          url: "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B04.tif",
          max: 10000,
        },
        {
          url: "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B08.tif",
          max: 10000,
        },
      ],
    });

    const layer = new TileLayer({
      style: {
        color: [
          "interpolate",
          ["linear"],
          [
            "/",
            ["-", ["band", 2], ["band", 1]],
            ["+", ["band", 2], ["band", 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: source,
    });
    const map = new Map({
      target: this.$refs.map,
      layers: [layer],
      view: source.getView(),
    });
    const output = document.getElementById("output");
    function displayPixelValue(event) {
      const data = layer.getData(event.pixel);
      if (!data) {
        return;
      }
      const red = data[0];
      const nir = data[1];
      const ndvi = (nir - red) / (nir + red);
      output.textContent = ndvi.toFixed(2);
    }
    map.on(["pointermove", "click"], displayPixelValue);
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

# 4.带有动态彩色坡道的 NDVI

查看代码详情
<template>
  <div>
    <div ref="map" class="map"></div>
    <table>
      <tbody>
        <tr>
          <td>最小植被覆盖指数</td>
          <td>
            <input
              type="range"
              id="min-value-input"
              min="-1.0"
              max="-0.1"
              step="0.01"
            />
          </td>
          <td class="data" id="min-value-output"></td>
          <td><input type="color" id="min-color" /></td>
        </tr>
        <tr>
          <td>最大植被覆盖指数</td>
          <td>
            <input
              type="range"
              id="max-value-input"
              min="0.1"
              max="1.0"
              step="0.01"
            />
          </td>
          <td class="data" id="max-value-output"></td>
          <td><input type="color" id="max-color" /></td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  mounted() {
    let {
      Map,
      layer: { WebGLTile: TileLayer },
      source: { GeoTIFF },
    } = ol;
    const segments = 10;
    const defaultMinColor = "#0300AD";
    const defaultMaxColor = "#00ff00";
    const defaultMinValue = -0.5;
    const defaultMaxValue = 0.7;
    const minColorInput = document.getElementById("min-color");
    minColorInput.value = defaultMinColor;
    const maxColorInput = document.getElementById("max-color");
    maxColorInput.value = defaultMaxColor;
    const minValueOutput = document.getElementById("min-value-output");
    const minValueInput = document.getElementById("min-value-input");
    minValueInput.value = defaultMinValue.toString();
    const maxValueOutput = document.getElementById("max-value-output");
    const maxValueInput = document.getElementById("max-value-input");
    maxValueInput.value = defaultMaxValue.toString();
    function getVariables() {
      const variables = {};
      const minColor = minColorInput.value;
      const maxColor = maxColorInput.value;
      const scale = chroma.scale([minColor, maxColor]).mode("lab");
      const minValue = parseFloat(minValueInput.value);
      const maxValue = parseFloat(maxValueInput.value);
      const delta = (maxValue - minValue) / segments;
      for (let i = 0; i <= segments; ++i) {
        const color = scale(i / segments).rgb();
        const value = minValue + i * delta;
        variables[`value${i}`] = value;
        variables[`red${i}`] = color[0];
        variables[`green${i}`] = color[1];
        variables[`blue${i}`] = color[2];
      }
      return variables;
    }
    function colors() {
      const stops = [];
      for (let i = 0; i <= segments; ++i) {
        stops[i * 2] = ["var", `value${i}`];
        const red = ["var", `red${i}`];
        const green = ["var", `green${i}`];
        const blue = ["var", `blue${i}`];
        stops[i * 2 + 1] = ["color", red, green, blue];
      }
      return stops;
    }
    const ndvi = [
      "/",
      ["-", ["band", 2], ["band", 1]],
      ["+", ["band", 2], ["band", 1]],
    ];
    const source = new GeoTIFF({
      sources: [
        {
          url: "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B04.tif",
          max: 10000,
        },
        {
          url: "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B08.tif",
          max: 10000,
        },
      ],
    });
    const layer = new TileLayer({
      style: {
        variables: getVariables(),
        color: ["interpolate", ["linear"], ndvi, ...colors()],
      },
      source: source,
    });
    function update() {
      layer.updateStyleVariables(getVariables());
      minValueOutput.innerText = parseFloat(minValueInput.value).toFixed(1);
      maxValueOutput.innerText = parseFloat(maxValueInput.value).toFixed(1);
    }
    minColorInput.addEventListener("input", update);
    maxColorInput.addEventListener("input", update);
    minValueInput.addEventListener("input", update);
    maxValueInput.addEventListener("input", update);
    update();
    const map = new Map({
      target: this.$refs.map,
      layers: [layer],
      view: source.getView(),
    });
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

# 5.来自两个 16 位 COG 的 NDVI+NDWI

GeoTIFF 图层从两个云优化的 Sentinel 2 GeoTIFF 计算归一化差异植被指数 (NDVI) 和归一化差异水指数 (NDWI):一个具有 10 m 分辨率和红色和近红外波段,一个具有 60 m 分辨率和短波红外通道。NDVI 显示为绿色,NDWI 显示为蓝色。第 4 个波段是 alpha 波段,当源 nodata 配置了值时会添加该波段

查看代码详情
<template>
  <div ref="map" class="map"></div>
</template>

<script>
export default {
  mounted() {
    let {
      Map,
      layer: { WebGLTile: TileLayer },
      source: { GeoTIFF },
    } = ol
    const source = new GeoTIFF({
      sources: [
        {
          url: "https://s2downloads.eox.at/demo/Sentinel-2/3857/R10m.tif",
          bands: [3, 4],
          min: 0,
          nodata: 0,
          max: 65535,
        },
        {
          url: "https://s2downloads.eox.at/demo/Sentinel-2/3857/R60m.tif",
          bands: [9],
          min: 0,
          nodata: 0,
          max: 65535,
        },
      ],
    })
    source.setAttributions(
      "<a href='https://s2maps.eu'>Sentinel-2 cloudless</a> by <a href='https://eox.at/'>EOX IT Services GmbH</a> (Contains modified Copernicus Sentinel data 2019)"
    )
    const ndvi = [
      "/",
      ["-", ["band", 2], ["band", 1]],
      ["+", ["band", 2], ["band", 1]],
    ]
    const ndwi = [
      "/",
      ["-", ["band", 3], ["band", 1]],
      ["+", ["band", 3], ["band", 1]],
    ]
    const map = new Map({
      target: this.$refs.map,
      layers: [
        new TileLayer({
          style: {
            color: [
              "color",
              ["*", 255, ["abs", ["-", ndvi, ndwi]]],
              ["*", 255, ndvi],
              ["*", 255, ndwi],
              ["band", 4],
            ],
          },
          source,
        }),
      ],
      view: source.getView(),
    })
  },
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# 6.带对比度拉伸

图层 layer source 备注
layer1 Tile GeoTIFF https://s2downloads.eox.at/demo/EOxCloudless/2020/rgbnir/s2cloudless2020-16bits_sinlge-file_z0-4.tif
查看代码详情
<template>
  <div>
    <div ref="map" class="map"></div>
    <div class="controls">
      <label for="red">Red channel</label>
      <select id="red">
        <option value="1" selected>visible red</option>
        <option value="2">visible green</option>
        <option value="3">visible blue</option>
        <option value="4">near infrared</option>
      </select>
      <label
        >max
        <input type="range" id="redMax" value="3000" min="2000" max="5000" />
      </label>
      <label for="green">Green channel</label>
      <select id="green">
        <option value="1">visible red</option>
        <option value="2" selected>visible green</option>
        <option value="3">visible blue</option>
        <option value="4">near infrared</option>
      </select>
      <label
        >max
        <input type="range" id="greenMax" value="3000" min="2000" max="5000" />
      </label>
      <label for="blue">Blue channel</label>
      <select id="blue">
        <option value="1">visible red</option>
        <option value="2">visible green</option>
        <option value="3" selected>visible blue</option>
        <option value="4">near infrared</option>
      </select>
      <label
        >max
        <input type="range" id="blueMax" value="3000" min="2000" max="5000" />
      </label>
    </div>
  </div>
</template>

<script>
export default {
  mounted() {
    let {
      Map,
      View,
      layer: { Tile: TileLayer },
      source: { GeoTIFF },
    } = ol;
    const channels = ["red", "green", "blue"];
    for (const channel of channels) {
      const selector = document.getElementById(channel);
      selector.addEventListener("change", update);

      const input = document.getElementById(`${channel}Max`);
      input.addEventListener("input", update);
    }
    function getVariables() {
      const variables = {};
      for (const channel of channels) {
        const selector = document.getElementById(channel);
        variables[channel] = parseInt(selector.value, 10);

        const inputId = `${channel}Max`;
        const input = document.getElementById(inputId);
        variables[inputId] = parseInt(input.value, 10);
      }
      return variables;
    }
    const layer = new TileLayer({
      style: {
        variables: getVariables(),
        color: [
          "array",
          ["/", ["band", ["var", "red"]], ["var", "redMax"]],
          ["/", ["band", ["var", "green"]], ["var", "greenMax"]],
          ["/", ["band", ["var", "blue"]], ["var", "blueMax"]],
          1,
        ],
      },
      source: new GeoTIFF({
        normalize: false,
        sources: [
          {
            url: "https://s2downloads.eox.at/demo/EOxCloudless/2020/rgbnir/s2cloudless2020-16bits_sinlge-file_z0-4.tif",
          },
        ],
      }),
    });
    function update() {
      layer.updateStyleVariables(getVariables());
    }
    const map = new Map({
      target: this.$refs.map,
      layers: [layer],
      view: new View({
        projection: "EPSG:4326",
        center: [12579156, 3274244],
        zoom: 2,
        maxZoom: 6,
      }),
    });
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105