summaryrefslogtreecommitdiff
path: root/src/mbgl/geometry/dem_data.cpp
blob: 627f140cf7c109754f0bd998a29ed0e8e9c103fe (plain)
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
#include <mbgl/geometry/dem_data.hpp>
#include <mbgl/math/clamp.hpp>

namespace mbgl {

DEMData::DEMData(const PremultipliedImage& image):
    level(image.size.height, std::max<int32_t>(std::ceil(image.size.height / 2), 1)){
    if (image.size.height != image.size.width){
        throw std::runtime_error("raster-dem tiles must be square.");
    }

    for (int32_t y = 0; y < level.dim; y++) {
        for (int32_t x = 0; x < level.dim; x++) {
            const int32_t i = y * level.dim + x;
            const int32_t j = i * 4;
            level.set(x, y, (image.data[j] * 256 * 256 + image.data[j+1] * 256 + image.data[j+2])/10 - 10000);
        }
    }
    
    // in order to avoid flashing seams between tiles, here we are initially populating a 1px border of
    // pixels around the image with the data of the nearest pixel from the image. this data is eventually
    // replaced when the tile's neighboring tiles are loaded and the accurate data can be backfilled using
    // DEMData#backfillBorder

    for (int32_t x = 0; x < level.dim; x++) {
        // left vertical border
        level.set(-1, x, level.get(0, x));

        // right vertical border
        level.set(level.dim, x, level.get(level.dim - 1, x));

        //left horizontal border
        level.set(x, -1, level.get(x, 0));

        // right horizontal border
        level.set(x, level.dim, level.get(x, level.dim - 1));
    }

    // corners
    level.set(-1, -1, level.get(0, 0));
    level.set(level.dim, -1, level.get(level.dim - 1, 0));
    level.set( -1, level.dim, level.get(0, level.dim - 1));
    level.set(level.dim, level.dim, level.get(level.dim - 1, level.dim - 1));
    loaded = true;
}

// This function takes the DEMData from a neighboring tile and backfills the edge/corner
// data in order to create a one pixel "buffer" of image data around the tile. This is
// necessary because the hillshade formula calculates the dx/dz, dy/dz derivatives at each
// pixel of the tile by querying the 8 surrounding pixels, and if we don't have the pixel
// buffer we get seams at tile boundaries.
void DEMData::backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy) {
    auto& t = level;
    auto& o = borderTileData.level;

    // Tiles from the same source should always be of the same dimensions.
    assert(t.dim == o.dim);

    // We determine the pixel range to backfill based which corner/edge `borderTileData`
    // represents. For example, dx = -1, dy = -1 represents the upper left corner of the
    // base tile, so we only need to backfill one pixel at coordinates (-1, -1) of the tile
    // image.
    int32_t _xMin = dx * t.dim;
    int32_t _xMax = dx * t.dim + t.dim;
    int32_t _yMin = dy * t.dim;
    int32_t _yMax = dy * t.dim + t.dim;
    
    if (dx == -1) _xMin = _xMax - 1;
    else if (dx == 1) _xMax = _xMin + 1;
    
    if (dy == -1) _yMin = _yMax - 1;
    else if (dy == 1) _yMax = _yMin + 1;
    
    int32_t xMin = util::clamp(_xMin, -t.border, t.dim + t.border);
    int32_t xMax = util::clamp(_xMax, -t.border, t.dim + t.border);
    
    int32_t yMin = util::clamp(_yMin, -t.border, t.dim + t.border);
    int32_t yMax = util::clamp(_yMax, -t.border, t.dim + t.border);
    
    int32_t ox = -dx * t.dim;
    int32_t oy = -dy * t.dim;
    
    for (int32_t y = yMin; y < yMax; y++) {
        for (int32_t x = xMin; x < xMax; x++) {
            t.set(x, y, o.get(x + ox, y + oy));
        }
    }
}

DEMData::Level::Level(int32_t dim_, int32_t border_)
    : dim(dim_),
      border(border_),
      stride(dim + 2 * border),
      image({ static_cast<uint32_t>(stride),
              static_cast<uint32_t>(stride) }) {
    assert(dim > 0);
    std::memset(image.data.get(), 0, image.bytes());
}

} // namespace mbgl