summaryrefslogtreecommitdiff
path: root/platform/default/mbgl/storage/offline.cpp
blob: 644684c8a68592076f0739f7797833951c36e59d (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
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
132
133
134
135
136
137
138
139
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/tileset.hpp>
#include <mbgl/util/projection.hpp>

#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>

#include <cmath>

namespace mbgl {

OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
    std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, float pixelRatio_)
    : styleURL(std::move(styleURL_)),
      bounds(std::move(bounds_)),
      minZoom(minZoom_),
      maxZoom(maxZoom_),
      pixelRatio(pixelRatio_) {
    if (minZoom < 0 || maxZoom < 0 || maxZoom < minZoom || pixelRatio < 0 ||
        !std::isfinite(minZoom) || std::isnan(maxZoom) || !std::isfinite(pixelRatio)) {
        throw std::invalid_argument("Invalid offline region definition");
    }
}

std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
    const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);

    std::vector<CanonicalTileID> result;

    for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
        for (const auto& tile : util::tileCover(bounds, z)) {
            result.emplace_back(tile.canonical);
        }
    }

    return result;
}

unsigned long OfflineTilePyramidRegionDefinition::tileCount(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
    
    const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
    unsigned long result = 0;;
    for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
        result +=  util::tileCount(bounds, z, tileSize);
    }

    return result;
}

Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
    double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min);
    double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max);

    assert(minZ >= 0);
    assert(maxZ >= 0);
    assert(minZ < std::numeric_limits<uint8_t>::max());
    assert(maxZ < std::numeric_limits<uint8_t>::max());
    return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) };
}

OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) {
    rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
    doc.Parse<0>(region.c_str());

    if (doc.HasParseError() ||
        !doc.HasMember("style_url") || !doc["style_url"].IsString() ||
        !doc.HasMember("bounds") || !doc["bounds"].IsArray() || doc["bounds"].Size() != 4 ||
          !doc["bounds"][0].IsDouble() || !doc["bounds"][1].IsDouble() ||
          !doc["bounds"][2].IsDouble() || !doc["bounds"][3].IsDouble() ||
        !doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble() ||
        (doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble()) ||
        !doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) {
        throw std::runtime_error("Malformed offline region definition");
    }

    std::string styleURL { doc["style_url"].GetString(), doc["style_url"].GetStringLength() };
    LatLngBounds bounds = LatLngBounds::hull(
        LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()),
        LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble()));
    double minZoom = doc["min_zoom"].GetDouble();
    double maxZoom = doc.HasMember("max_zoom") ? doc["max_zoom"].GetDouble() : INFINITY;
    float pixelRatio = doc["pixel_ratio"].GetDouble();

    return { styleURL, bounds, minZoom, maxZoom, pixelRatio };
}

std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) {
    rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
    doc.SetObject();

    doc.AddMember("style_url", rapidjson::StringRef(region.styleURL.data(), region.styleURL.length()), doc.GetAllocator());

    rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType);
    bounds.PushBack(region.bounds.south(), doc.GetAllocator());
    bounds.PushBack(region.bounds.west(), doc.GetAllocator());
    bounds.PushBack(region.bounds.north(), doc.GetAllocator());
    bounds.PushBack(region.bounds.east(), doc.GetAllocator());
    doc.AddMember("bounds", bounds, doc.GetAllocator());

    doc.AddMember("min_zoom", region.minZoom, doc.GetAllocator());
    if (std::isfinite(region.maxZoom)) {
        doc.AddMember("max_zoom", region.maxZoom, doc.GetAllocator());
    }

    doc.AddMember("pixel_ratio", region.pixelRatio, doc.GetAllocator());

    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    return buffer.GetString();
}

OfflineRegion::OfflineRegion(int64_t id_,
                             OfflineRegionDefinition definition_,
                             OfflineRegionMetadata metadata_)
    : id(id_),
      definition(std::move(definition_)),
      metadata(std::move(metadata_)) {
}

OfflineRegion::OfflineRegion(OfflineRegion&&) = default;
OfflineRegion::~OfflineRegion() = default;

const OfflineRegionDefinition& OfflineRegion::getDefinition() const {
    return definition;
}

const OfflineRegionMetadata& OfflineRegion::getMetadata() const {
    return metadata;
}

int64_t OfflineRegion::getID() const {
    return id;
}

} // namespace mbgl