1 |
|
2 | goog.require('ol.Map');
|
3 | goog.require('ol.View');
|
4 | goog.require('ol.layer.Image');
|
5 | goog.require('ol.layer.Tile');
|
6 | goog.require('ol.source.OSM');
|
7 | goog.require('ol.source.Raster');
|
8 | goog.require('ol.source.XYZ');
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | function shade(inputs, data) {
|
19 | var elevationImage = inputs[0];
|
20 | var width = elevationImage.width;
|
21 | var height = elevationImage.height;
|
22 | var elevationData = elevationImage.data;
|
23 | var shadeData = new Uint8ClampedArray(elevationData.length);
|
24 | var dp = data.resolution * 2;
|
25 | var maxX = width - 1;
|
26 | var maxY = height - 1;
|
27 | var pixel = [0, 0, 0, 0];
|
28 | var twoPi = 2 * Math.PI;
|
29 | var halfPi = Math.PI / 2;
|
30 | var sunEl = Math.PI * data.sunEl / 180;
|
31 | var sunAz = Math.PI * data.sunAz / 180;
|
32 | var cosSunEl = Math.cos(sunEl);
|
33 | var sinSunEl = Math.sin(sunEl);
|
34 | var pixelX, pixelY, x0, x1, y0, y1, offset,
|
35 | z0, z1, dzdx, dzdy, slope, aspect, cosIncidence, scaled;
|
36 | for (pixelY = 0; pixelY <= maxY; ++pixelY) {
|
37 | y0 = pixelY === 0 ? 0 : pixelY - 1;
|
38 | y1 = pixelY === maxY ? maxY : pixelY + 1;
|
39 | for (pixelX = 0; pixelX <= maxX; ++pixelX) {
|
40 | x0 = pixelX === 0 ? 0 : pixelX - 1;
|
41 | x1 = pixelX === maxX ? maxX : pixelX + 1;
|
42 |
|
43 |
|
44 | offset = (pixelY * width + x0) * 4;
|
45 | pixel[0] = elevationData[offset];
|
46 | pixel[1] = elevationData[offset + 1];
|
47 | pixel[2] = elevationData[offset + 2];
|
48 | pixel[3] = elevationData[offset + 3];
|
49 | z0 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
|
50 |
|
51 |
|
52 | offset = (pixelY * width + x1) * 4;
|
53 | pixel[0] = elevationData[offset];
|
54 | pixel[1] = elevationData[offset + 1];
|
55 | pixel[2] = elevationData[offset + 2];
|
56 | pixel[3] = elevationData[offset + 3];
|
57 | z1 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
|
58 |
|
59 | dzdx = (z1 - z0) / dp;
|
60 |
|
61 |
|
62 | offset = (y0 * width + pixelX) * 4;
|
63 | pixel[0] = elevationData[offset];
|
64 | pixel[1] = elevationData[offset + 1];
|
65 | pixel[2] = elevationData[offset + 2];
|
66 | pixel[3] = elevationData[offset + 3];
|
67 | z0 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
|
68 |
|
69 |
|
70 | offset = (y1 * width + pixelX) * 4;
|
71 | pixel[0] = elevationData[offset];
|
72 | pixel[1] = elevationData[offset + 1];
|
73 | pixel[2] = elevationData[offset + 2];
|
74 | pixel[3] = elevationData[offset + 3];
|
75 | z1 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
|
76 |
|
77 | dzdy = (z1 - z0) / dp;
|
78 |
|
79 | slope = Math.atan(Math.sqrt(dzdx * dzdx + dzdy * dzdy));
|
80 |
|
81 | aspect = Math.atan2(dzdy, -dzdx);
|
82 | if (aspect < 0) {
|
83 | aspect = halfPi - aspect;
|
84 | } else if (aspect > halfPi) {
|
85 | aspect = twoPi - aspect + halfPi;
|
86 | } else {
|
87 | aspect = halfPi - aspect;
|
88 | }
|
89 |
|
90 | cosIncidence = sinSunEl * Math.cos(slope) +
|
91 | cosSunEl * Math.sin(slope) * Math.cos(sunAz - aspect);
|
92 |
|
93 | offset = (pixelY * width + pixelX) * 4;
|
94 | scaled = 255 * cosIncidence;
|
95 | shadeData[offset] = scaled;
|
96 | shadeData[offset + 1] = scaled;
|
97 | shadeData[offset + 2] = scaled;
|
98 | shadeData[offset + 3] = elevationData[offset + 3];
|
99 | }
|
100 | }
|
101 |
|
102 | return {data: shadeData, width: width, height: height};
|
103 | }
|
104 |
|
105 | var elevation = new ol.source.XYZ({
|
106 | url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png',
|
107 | crossOrigin: 'anonymous'
|
108 | });
|
109 |
|
110 | var raster = new ol.source.Raster({
|
111 | sources: [elevation],
|
112 | operationType: 'image',
|
113 | operation: shade
|
114 | });
|
115 |
|
116 | var map = new ol.Map({
|
117 | target: 'map',
|
118 | layers: [
|
119 | new ol.layer.Tile({
|
120 | source: new ol.source.OSM()
|
121 | }),
|
122 | new ol.layer.Image({
|
123 | opacity: 0.3,
|
124 | source: raster
|
125 | })
|
126 | ],
|
127 | view: new ol.View({
|
128 | extent: [-13675026, 4439648, -13580856, 4580292],
|
129 | center: [-13615645, 4497969],
|
130 | minZoom: 10,
|
131 | maxZoom: 16,
|
132 | zoom: 13
|
133 | })
|
134 | });
|
135 |
|
136 | var controlIds = ['vert', 'sunEl', 'sunAz'];
|
137 | var controls = {};
|
138 | controlIds.forEach(function(id) {
|
139 | var control = document.getElementById(id);
|
140 | var output = document.getElementById(id + 'Out');
|
141 | control.addEventListener('input', function() {
|
142 | output.innerText = control.value;
|
143 | raster.changed();
|
144 | });
|
145 | output.innerText = control.value;
|
146 | controls[id] = control;
|
147 | });
|
148 |
|
149 | raster.on('beforeoperations', function(event) {
|
150 |
|
151 | var data = event.data;
|
152 | data.resolution = event.resolution;
|
153 | for (var id in controls) {
|
154 | data[id] = Number(controls[id].value);
|
155 | }
|
156 | });
|