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