# 八.海面上升

需求背景

模拟陆地随着海平面上升呈现的地图面貌

# 1.WMTS 模拟海面上升

查看代码详情
<template>
  <div>
    <div ref="map" class="map"></div>
    <label>
      海平面上升高度:
      <input ref="slider" type="range" value="10" max="100" min="-1" />
    </label>
    <span ref="theinfo"></span>
  </div>
</template>

<script>
export default {
  mounted() {
    let {
      proj: { get: getProjection },
      Map,
      View,
      layer: { Tile: TileLayer },
      source: { OSM, WMTS },
      extent: { getTopLeft, getWidth },
      tilegrid: { WMTS: WMTSTileGrid },
    } = ol;
    const projection = getProjection("EPSG:3857");
    const tileSizePixels = 256;
    const tileSizeMtrs = getWidth(projection.getExtent()) / tileSizePixels;
    const matrixIds = [];
    const resolutions = [];
    for (let i = 0; i <= 14; i++) {
      matrixIds[i] = i;
      resolutions[i] = tileSizeMtrs / Math.pow(2, i);
    }
    const tileGrid = new WMTSTileGrid({
      origin: getTopLeft(projection.getExtent()),
      resolutions: resolutions,
      matrixIds: matrixIds,
    });
    const wmtsSource = new WMTS({
      url: "https://ts2.scalgo.com/olpatch/wmts?token=" + mapkeys.scalgo,
      layer: "SRTM_4_1:SRTM_4_1_flooded_sealevels",
      format: "image/png",
      matrixSet: "EPSG:3857",
      attributions: [
        '<a href="https://scalgo.com" target="_blank">SCALGO</a>',
        '<a href="https://cgiarcsi.community/data/' +
          'srtm-90m-digital-elevation-database-v4-1"' +
          ' target="_blank">CGIAR-CSI SRTM</a>',
      ],
      tileGrid: tileGrid,
      style: "default",
      dimensions: {
        threshold: 100,
      },
    });
    const map = new Map({
      target: this.$refs.map,
      view: new View({
        projection: projection,
        center: [12579156, 3274244],
        zoom: 6,
      }),
      layers: [
        new TileLayer({
          source: new OSM(),
        }),
        new TileLayer({
          opacity: 0.5,
          source: wmtsSource,
        }),
      ],
    });
    const slider = this.$refs.slider;
    const updateSourceDimension = function () {
      wmtsSource.updateDimensions({ threshold: slider.value });
      this.$refs.theinfo.innerHTML = slider.value + " 米";
    }.bind(this);
    slider.addEventListener("input", updateSourceDimension);
    slider.addEventListener("change", updateSourceDimension);
    updateSourceDimension();
  },
};
</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

# 2.XYZ 模拟海平面上升

查看代码详情
<template>
  <div>
    <div ref="map" class="map"></div>
    <label>
      海平面
      <input ref="level" type="range" min="0" max="100" value="1" />
      +<span ref="output"></span> m
    </label>
    <br />
    Go to
    <a class="location" data-center="-122.3267,37.8377" data-zoom="11">旧金山</a
    >,
    <a class="location" data-center="-73.9338,40.6861" data-zoom="11">纽约</a>,
    <a class="location" data-center="72.9481,18.9929" data-zoom="11">孟买</a>,
    or
    <a class="location" data-center="120.831,31.160" data-zoom="9">上海</a>
  </div>
</template>

<script>
export default {
  mounted() {
    let {
      Map,
      View,
      layer: { Tile: TileLayer, Image: ImageLayer },
      source: { XYZ, Raster: RasterSource },
      proj: { fromLonLat },
    } = ol;
    function flood(pixels, data) {
      const pixel = pixels[0];
      if (pixel[3]) {
        const height =
          -10000 + (pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1;
        if (height <= data.level) {
          pixel[0] = 134;
          pixel[1] = 203;
          pixel[2] = 249;
          pixel[3] = 255;
        } else {
          pixel[3] = 0;
        }
      }
      return pixel;
    }

    const attributions =
      '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
      '<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
    const elevation = new XYZ({
      url:
        "https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=" +
        mapkeys.maptiler,
      tileSize: 512,
      maxZoom: 12,
      crossOrigin: "",
      interpolate: false,
    });
    const raster = new RasterSource({
      sources: [elevation],
      operation: flood,
    });
    const map = new Map({
      target: this.$refs.map,
      layers: [
        new TileLayer({
          source: new XYZ({
            attributions: attributions,
            url:
              "https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=" +
              mapkeys.maptiler,
            tileSize: 512,
            maxZoom: 22,
          }),
        }),
        new ImageLayer({
          opacity: 0.6,
          source: raster,
        }),
      ],
      view: new View({
        center: fromLonLat([-122.3267, 37.8377]),
        zoom: 11,
      }),
    });
    const control = this.$refs.level;
    const output = this.$refs.output;
    const listener = function () {
      output.innerText = control.value;
      raster.changed();
    };
    control.addEventListener("input", listener);
    control.addEventListener("change", listener);
    output.innerText = control.value;
    raster.on("beforeoperations", function (event) {
      event.data.level = control.value;
    });
    const locations = document.getElementsByClassName("location");
    for (let i = 0, ii = locations.length; i < ii; ++i) {
      locations[i].addEventListener("click", relocate);
    }
    function relocate(event) {
      const data = event.target.dataset;
      const view = map.getView();
      view.setCenter(fromLonLat(data.center.split(",").map(Number)));
      view.setZoom(Number(data.zoom));
    }
  },
};
</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

# 3.WebGL+XYZ 模拟海平面上升

查看代码详情
<template>
  <div>
    <div ref="map" class="map"></div>
    <label>
      Sea level
      <input ref="level" type="range" min="0" max="100" value="1" />
      +<span ref="output"></span> m
    </label>
    <br />
    Go to
    <a class="location" data-center="-122.3267,37.8377" data-zoom="11">旧金山</a
    >,
    <a class="location" data-center="-73.9338,40.6861" data-zoom="11">纽约</a>,
    <a class="location" data-center="72.9481,18.9929" data-zoom="11">孟买</a>,
    or
    <a class="location" data-center="120.831,31.160" data-zoom="9">上海</a>
  </div>
</template>

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

    const attributions =
      '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
      '<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
    const elevation = [
      "+",
      -10000,
      [
        "*",
        0.1 * 255,
        [
          "+",
          ["*", 256 * 256, ["band", 1]],
          ["+", ["*", 256, ["band", 2]], ["band", 3]],
        ],
      ],
    ];
    const layer = new TileLayer({
      opacity: 0.6,
      source: new XYZ({
        url:
          "https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=" +
          mapkeys.maptiler,
        tileSize: 512,
        maxZoom: 12,
      }),
      style: {
        variables: {
          level: 0,
        },
        color: [
          "case",
          ["<=", elevation, ["var", "level"]],
          [139, 212, 255, 1],
          [139, 212, 255, 0],
        ],
      },
    });
    const map = new Map({
      target: this.$refs.map,
      layers: [
        new TileLayer({
          source: new XYZ({
            url:
              "https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=" +
              mapkeys.maptiler,
            attributions: attributions,
            tileSize: 512,
            maxZoom: 22,
          }),
        }),
        layer,
      ],
      view: new View({
        center: fromLonLat([-122.3267, 37.8377]),
        zoom: 11,
      }),
    });
    const control = this.$refs.level;
    const output = this.$refs.output;
    const listener = function () {
      output.innerText = control.value;
      layer.updateStyleVariables({ level: parseFloat(control.value) });
    };
    control.addEventListener("input", listener);
    control.addEventListener("change", listener);
    output.innerText = control.value;
    const locations = document.getElementsByClassName("location");
    for (let i = 0, ii = locations.length; i < ii; ++i) {
      locations[i].addEventListener("click", relocate);
    }
    function relocate(event) {
      const data = event.target.dataset;
      const view = map.getView();
      view.setCenter(fromLonLat(data.center.split(",").map(Number)));
      view.setZoom(Number(data.zoom));
    }
  },
};
</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