1 #include <mbgl/geometry/dem_data.hpp>
2 #include <mbgl/math/clamp.hpp>
3 
4 namespace mbgl {
5 
DEMData(const PremultipliedImage & _image,Tileset::DEMEncoding encoding)6 DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding encoding):
7     dim(_image.size.height),
8     border(std::max<int32_t>(std::ceil(_image.size.height / 2), 1)),
9     stride(dim + 2 * border),
10     image({ static_cast<uint32_t>(stride), static_cast<uint32_t>(stride) }) {
11 
12     if (_image.size.height != _image.size.width){
13         throw std::runtime_error("raster-dem tiles must be square.");
14     }
15 
__anondc5c74880102(const uint8_t r, const uint8_t g, const uint8_t b)16     auto decodeMapbox = [] (const uint8_t r, const uint8_t g, const uint8_t b){
17         // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
18         return (r * 256 * 256 + g * 256 + b)/10 - 10000;
19     };
20 
__anondc5c74880202(const uint8_t r, const uint8_t g, const uint8_t b)21     auto decodeTerrarium = [] (const uint8_t r, const uint8_t g, const uint8_t b){
22         // https://aws.amazon.com/public-datasets/terrain/
23         return ((r * 256 + g + b / 256) - 32768);
24     };
25 
26     auto decodeRGB = encoding == Tileset::DEMEncoding::Terrarium ? decodeTerrarium : decodeMapbox;
27 
28     std::memset(image.data.get(), 0, image.bytes());
29 
30     for (int32_t y = 0; y < dim; y++) {
31         for (int32_t x = 0; x < dim; x++) {
32             const int32_t i = y * dim + x;
33             const int32_t j = i * 4;
34             set(x, y, decodeRGB(_image.data[j], _image.data[j+1], _image.data[j+2]));
35         }
36     }
37 
38     // in order to avoid flashing seams between tiles, here we are initially populating a 1px border of
39     // pixels around the image with the data of the nearest pixel from the image. this data is eventually
40     // replaced when the tile's neighboring tiles are loaded and the accurate data can be backfilled using
41     // DEMData#backfillBorder
42 
43     for (int32_t x = 0; x < dim; x++) {
44         // left vertical border
45         set(-1, x, get(0, x));
46 
47         // right vertical border
48         set(dim, x, get(dim - 1, x));
49 
50         //left horizontal border
51         set(x, -1, get(x, 0));
52 
53         // right horizontal border
54         set(x, dim, get(x, dim - 1));
55     }
56 
57     // corners
58     set(-1, -1, get(0, 0));
59     set(dim, -1, get(dim - 1, 0));
60     set( -1, dim, get(0, dim - 1));
61     set(dim, dim, get(dim - 1, dim - 1));
62 }
63 
64 // This function takes the DEMData from a neighboring tile and backfills the edge/corner
65 // data in order to create a one pixel "buffer" of image data around the tile. This is
66 // necessary because the hillshade formula calculates the dx/dz, dy/dz derivatives at each
67 // pixel of the tile by querying the 8 surrounding pixels, and if we don't have the pixel
68 // buffer we get seams at tile boundaries.
backfillBorder(const DEMData & borderTileData,int8_t dx,int8_t dy)69 void DEMData::backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy) {
70     auto& o = borderTileData;
71 
72     // Tiles from the same source should always be of the same dimensions.
73     assert(dim == o.dim);
74 
75     // We determine the pixel range to backfill based which corner/edge `borderTileData`
76     // represents. For example, dx = -1, dy = -1 represents the upper left corner of the
77     // base tile, so we only need to backfill one pixel at coordinates (-1, -1) of the tile
78     // image.
79     int32_t _xMin = dx * dim;
80     int32_t _xMax = dx * dim + dim;
81     int32_t _yMin = dy * dim;
82     int32_t _yMax = dy * dim + dim;
83 
84     if (dx == -1) _xMin = _xMax - 1;
85     else if (dx == 1) _xMax = _xMin + 1;
86 
87     if (dy == -1) _yMin = _yMax - 1;
88     else if (dy == 1) _yMax = _yMin + 1;
89 
90     int32_t xMin = util::clamp(_xMin, -border, dim + border);
91     int32_t xMax = util::clamp(_xMax, -border, dim + border);
92 
93     int32_t yMin = util::clamp(_yMin, -border, dim + border);
94     int32_t yMax = util::clamp(_yMax, -border, dim + border);
95 
96     int32_t ox = -dx * dim;
97     int32_t oy = -dy * dim;
98 
99     for (int32_t y = yMin; y < yMax; y++) {
100         for (int32_t x = xMin; x < xMax; x++) {
101             set(x, y, o.get(x + ox, y + oy));
102         }
103     }
104 }
105 
106 } // namespace mbgl
107