1 #include <mbgl/storage/offline.hpp>
2 #include <mbgl/util/tile_cover.hpp>
3 #include <mbgl/util/tileset.hpp>
4 #include <mbgl/util/projection.hpp>
5 
6 #include <rapidjson/document.h>
7 #include <rapidjson/stringbuffer.h>
8 #include <rapidjson/writer.h>
9 
10 #include <cmath>
11 
12 namespace mbgl {
13 
OfflineTilePyramidRegionDefinition(std::string styleURL_,LatLngBounds bounds_,double minZoom_,double maxZoom_,float pixelRatio_)14 OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
15     std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, float pixelRatio_)
16     : styleURL(std::move(styleURL_)),
17       bounds(std::move(bounds_)),
18       minZoom(minZoom_),
19       maxZoom(maxZoom_),
20       pixelRatio(pixelRatio_) {
21     if (minZoom < 0 || maxZoom < 0 || maxZoom < minZoom || pixelRatio < 0 ||
22         !std::isfinite(minZoom) || std::isnan(maxZoom) || !std::isfinite(pixelRatio)) {
23         throw std::invalid_argument("Invalid offline region definition");
24     }
25 }
26 
tileCover(style::SourceType type,uint16_t tileSize,const Range<uint8_t> & zoomRange) const27 std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
28     const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
29 
30     std::vector<CanonicalTileID> result;
31 
32     for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
33         for (const auto& tile : util::tileCover(bounds, z)) {
34             result.emplace_back(tile.canonical);
35         }
36     }
37 
38     return result;
39 }
40 
tileCount(style::SourceType type,uint16_t tileSize,const Range<uint8_t> & zoomRange) const41 uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
42 
43     const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
44     unsigned long result = 0;;
45     for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
46         result +=  util::tileCount(bounds, z);
47     }
48 
49     return result;
50 }
51 
coveringZoomRange(style::SourceType type,uint16_t tileSize,const Range<uint8_t> & zoomRange) const52 Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
53     double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min);
54     double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max);
55 
56     assert(minZ >= 0);
57     assert(maxZ >= 0);
58     assert(minZ < std::numeric_limits<uint8_t>::max());
59     assert(maxZ < std::numeric_limits<uint8_t>::max());
60     return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) };
61 }
62 
decodeOfflineRegionDefinition(const std::string & region)63 OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) {
64     rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
65     doc.Parse<0>(region.c_str());
66 
67     if (doc.HasParseError() ||
68         !doc.HasMember("style_url") || !doc["style_url"].IsString() ||
69         !doc.HasMember("bounds") || !doc["bounds"].IsArray() || doc["bounds"].Size() != 4 ||
70           !doc["bounds"][0].IsDouble() || !doc["bounds"][1].IsDouble() ||
71           !doc["bounds"][2].IsDouble() || !doc["bounds"][3].IsDouble() ||
72         !doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble() ||
73         (doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble()) ||
74         !doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) {
75         throw std::runtime_error("Malformed offline region definition");
76     }
77 
78     std::string styleURL { doc["style_url"].GetString(), doc["style_url"].GetStringLength() };
79     LatLngBounds bounds = LatLngBounds::hull(
80         LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()),
81         LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble()));
82     double minZoom = doc["min_zoom"].GetDouble();
83     double maxZoom = doc.HasMember("max_zoom") ? doc["max_zoom"].GetDouble() : INFINITY;
84     float pixelRatio = doc["pixel_ratio"].GetDouble();
85 
86     return { styleURL, bounds, minZoom, maxZoom, pixelRatio };
87 }
88 
encodeOfflineRegionDefinition(const OfflineRegionDefinition & region)89 std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) {
90     rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
91     doc.SetObject();
92 
93     doc.AddMember("style_url", rapidjson::StringRef(region.styleURL.data(), region.styleURL.length()), doc.GetAllocator());
94 
95     rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType);
96     bounds.PushBack(region.bounds.south(), doc.GetAllocator());
97     bounds.PushBack(region.bounds.west(), doc.GetAllocator());
98     bounds.PushBack(region.bounds.north(), doc.GetAllocator());
99     bounds.PushBack(region.bounds.east(), doc.GetAllocator());
100     doc.AddMember("bounds", bounds, doc.GetAllocator());
101 
102     doc.AddMember("min_zoom", region.minZoom, doc.GetAllocator());
103     if (std::isfinite(region.maxZoom)) {
104         doc.AddMember("max_zoom", region.maxZoom, doc.GetAllocator());
105     }
106 
107     doc.AddMember("pixel_ratio", region.pixelRatio, doc.GetAllocator());
108 
109     rapidjson::StringBuffer buffer;
110     rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
111     doc.Accept(writer);
112 
113     return buffer.GetString();
114 }
115 
OfflineRegion(int64_t id_,OfflineRegionDefinition definition_,OfflineRegionMetadata metadata_)116 OfflineRegion::OfflineRegion(int64_t id_,
117                              OfflineRegionDefinition definition_,
118                              OfflineRegionMetadata metadata_)
119     : id(id_),
120       definition(std::move(definition_)),
121       metadata(std::move(metadata_)) {
122 }
123 
124 OfflineRegion::OfflineRegion(OfflineRegion&&) = default;
125 OfflineRegion::~OfflineRegion() = default;
126 
getDefinition() const127 const OfflineRegionDefinition& OfflineRegion::getDefinition() const {
128     return definition;
129 }
130 
getMetadata() const131 const OfflineRegionMetadata& OfflineRegion::getMetadata() const {
132     return metadata;
133 }
134 
getID() const135 int64_t OfflineRegion::getID() const {
136     return id;
137 }
138 
139 } // namespace mbgl
140