diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index 818cfe2ba5c..6888e24bcfd 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ class TileID; class OfflineTilePyramidRegionDefinition { public: OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float); + OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float, std::vector); /* Private */ std::vector tileCover(SourceType, uint16_t tileSize, const Range& zoomRange) const; @@ -37,6 +39,7 @@ class OfflineTilePyramidRegionDefinition { const double minZoom; const double maxZoom; const float pixelRatio; + const std::vector tileList; }; /* diff --git a/src/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp similarity index 100% rename from src/mbgl/tile/tile_id.hpp rename to include/mbgl/tile/tile_id.hpp diff --git a/platform/darwin/src/MGLTileID.h b/platform/darwin/src/MGLTileID.h new file mode 100644 index 00000000000..aabba00e85a --- /dev/null +++ b/platform/darwin/src/MGLTileID.h @@ -0,0 +1,28 @@ +#import + +#import "MGLFoundation.h" + +/** + Has integer z/x/y coordinates + All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid) + A companion object for CanonicalTileID + */ +typedef struct +{ + uint8_t z; + uint32_t x, y; +} MGLTileID; + +/** + Returns an MGTileID tile from tile key + */ +FOUNDATION_EXTERN MGL_EXPORT MGLTileID MGLTileIDFromKey(uint64_t tilekey); + +FOUNDATION_EXTERN MGL_EXPORT bool MGLTileIDsEqual(MGLTileID one, MGLTileID two); + +/** + Returns an uint64_t key that uniquely identifies the MGLTileID + */ +FOUNDATION_EXTERN MGL_EXPORT uint64_t MGLTileKey(MGLTileID tile); + +FOUNDATION_EXTERN MGL_EXPORT MGLTileID MGLTileIDMake(uint32_t x, uint32_t y, uint8_t z); diff --git a/platform/darwin/src/MGLTileID.m b/platform/darwin/src/MGLTileID.m new file mode 100644 index 00000000000..c2011bba21a --- /dev/null +++ b/platform/darwin/src/MGLTileID.m @@ -0,0 +1,42 @@ +// +// MGLTileID.m +// ios +// +// Created by Anna Johnson on 2/7/17. +// Copyright © 2017 Mapbox. All rights reserved. +// + +#import "MGLTileID.h" + +bool MGLTileIDsEqual(MGLTileID one, MGLTileID two) { + return (one.x == two.x) && (one.y == two.y) && (one.z == two.z); +} + + +MGLTileID MGLTileIDFromKey(uint64_t tilekey) { + MGLTileID t; + t.z = tilekey >> 56; + t.x = tilekey >> 28 & 0xFFFFFFFLL; + t.y = tilekey & 0xFFFFFFFLL; + return t; +} + +/// Returns a unique key of the tile for use in the SQLite cache +uint64_t MGLTileKey(MGLTileID tile) { + uint64_t zoom = (uint64_t) tile.z & 0xFFLL; // 8bits, 256 levels + uint64_t x = (uint64_t) tile.x & 0xFFFFFFFLL; // 28 bits + uint64_t y = (uint64_t) tile.y & 0xFFFFFFFLL; // 28 bits + + uint64_t key = (zoom << 56) | (x << 28) | (y << 0); + + return key; +} + + +MGLTileID MGLTileIDMake(uint32_t x, uint32_t y, uint8_t z) { + MGLTileID t; + t.x = x; + t.y = y; + t.z = z; + return t; +} diff --git a/platform/darwin/src/MGLTilePyramidOfflineRegion.h b/platform/darwin/src/MGLTilePyramidOfflineRegion.h index 31e5a419204..622d7ebe351 100644 --- a/platform/darwin/src/MGLTilePyramidOfflineRegion.h +++ b/platform/darwin/src/MGLTilePyramidOfflineRegion.h @@ -44,6 +44,12 @@ MGL_EXPORT */ @property (nonatomic, readonly) double maximumZoomLevel; +/** + A specific list of tiles to download for this region. + Contains NSNumbers representing uint64_t tilekeys created by MGLTileKey +*/ +@property (nonatomic, readonly, nullable) NSArray* tileList; + - (instancetype)init NS_UNAVAILABLE; /** @@ -75,6 +81,13 @@ MGL_EXPORT */ - (instancetype)initWithStyleURL:(nullable NSURL *)styleURL bounds:(MGLCoordinateBounds)bounds fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel NS_DESIGNATED_INITIALIZER; +/** + @param tileList An array of NSNumbers created by MGLTileKey representing the specific + set of tiles to download for this region. If this is nil or empty a rectangular area + will be downloaded. + */ +- (instancetype)initWithStyleURL:(NSURL *)styleURL bounds:(MGLCoordinateBounds)bounds fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel tileList:(nullable NSArray*)tileList; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLTilePyramidOfflineRegion.mm b/platform/darwin/src/MGLTilePyramidOfflineRegion.mm index e0d56484bfe..6b40b5998c4 100644 --- a/platform/darwin/src/MGLTilePyramidOfflineRegion.mm +++ b/platform/darwin/src/MGLTilePyramidOfflineRegion.mm @@ -7,6 +7,8 @@ #import "MGLOfflineRegion_Private.h" #import "MGLGeometry_Private.h" #import "MGLStyle.h" +#import "MGLTileID.h" +#include @interface MGLTilePyramidOfflineRegion () @@ -30,6 +32,13 @@ - (instancetype)init { return nil; } +- (instancetype)initWithStyleURL:(NSURL *)styleURL bounds:(MGLCoordinateBounds)bounds fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel tileList:(nullable NSArray*)tileList { + if (self = [self initWithStyleURL:styleURL bounds:bounds fromZoomLevel:minimumZoomLevel toZoomLevel:maximumZoomLevel]) { + _tileList = tileList; + } + return self; +} + - (instancetype)initWithStyleURL:(NSURL *)styleURL bounds:(MGLCoordinateBounds)bounds fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel { if (self = [super init]) { if (!styleURL) { @@ -49,6 +58,7 @@ - (instancetype)initWithStyleURL:(NSURL *)styleURL bounds:(MGLCoordinateBounds)b _bounds = bounds; _minimumZoomLevel = minimumZoomLevel; _maximumZoomLevel = maximumZoomLevel; + _tileList = nil; } return self; } @@ -56,7 +66,7 @@ - (instancetype)initWithStyleURL:(NSURL *)styleURL bounds:(MGLCoordinateBounds)b - (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineRegionDefinition &)definition { NSURL *styleURL = [NSURL URLWithString:@(definition.styleURL.c_str())]; MGLCoordinateBounds bounds = MGLCoordinateBoundsFromLatLngBounds(definition.bounds); - return [self initWithStyleURL:styleURL bounds:bounds fromZoomLevel:definition.minZoom toZoomLevel:definition.maxZoom]; + return [self initWithStyleURL:styleURL bounds:bounds fromZoomLevel:definition.minZoom toZoomLevel:definition.maxZoom tileList:nil]; } - (const mbgl::OfflineRegionDefinition)offlineRegionDefinition { @@ -65,10 +75,18 @@ - (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineRegionDefini #elif TARGET_OS_MAC const float scaleFactor = [NSScreen mainScreen].backingScaleFactor; #endif + + std::vector tileVector; + tileVector.reserve(_tileList.count); + for (NSNumber* value in _tileList) { + MGLTileID tileId = MGLTileIDFromKey([value unsignedLongLongValue]); + mbgl::CanonicalTileID canonicalTileID = mbgl::CanonicalTileID(tileId.z, tileId.x,tileId.y); + tileVector.push_back(canonicalTileID); + } return mbgl::OfflineTilePyramidRegionDefinition(_styleURL.absoluteString.UTF8String, MGLLatLngBoundsFromCoordinateBounds(_bounds), _minimumZoomLevel, _maximumZoomLevel, - scaleFactor); + scaleFactor, tileVector); } - (nullable instancetype)initWithCoder:(NSCoder *)coder { diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index fd2d47819b4..69d185a154d 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -12,18 +12,31 @@ namespace mbgl { OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, float pixelRatio_) + : OfflineTilePyramidRegionDefinition(styleURL_, bounds_, minZoom_, maxZoom_, pixelRatio_, {}) { + +} + +OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( + std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, + float pixelRatio_, std::vector tileList_) : styleURL(std::move(styleURL_)), bounds(std::move(bounds_)), minZoom(minZoom_), maxZoom(maxZoom_), - pixelRatio(pixelRatio_) { + pixelRatio(pixelRatio_), + tileList(std::move(tileList_)) { 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 OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Range& zoomRange) const { + + if (!tileList.empty()) { + return tileList; + } double minZ = std::max(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min); double maxZ = std::min(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max); @@ -65,8 +78,20 @@ OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) double minZoom = doc["min_zoom"].GetDouble(); double maxZoom = doc.HasMember("max_zoom") ? doc["max_zoom"].GetDouble() : INFINITY; float pixelRatio = doc["pixel_ratio"].GetDouble(); + + std::vector tileList; + + if (doc.HasMember("tile_list")) { + for (auto& v : doc["tile_list"].GetArray()) { + uint64_t key = v.GetUint64(); + CanonicalTileID tileID = mbgl::CanonicalTileID(key >> 56, + key >> 28 & 0xFFFFFFFLL, + key & 0xFFFFFFFLL); + tileList.push_back(tileID); + } + } - return { styleURL, bounds, minZoom, maxZoom, pixelRatio }; + return { styleURL, bounds, minZoom, maxZoom, pixelRatio, tileList}; } std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) { @@ -88,7 +113,21 @@ std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) } doc.AddMember("pixel_ratio", region.pixelRatio, doc.GetAllocator()); - + + if (!region.tileList.empty()) { + rapidjson::GenericValue, rapidjson::CrtAllocator> tileList(rapidjson::kArrayType); + + for ( auto &i : region.tileList ) { + uint64_t zoom = (uint64_t) i.z & 0xFFLL; // 8bits, 256 levels + uint64_t x = (uint64_t) i.x & 0xFFFFFFFLL; // 28 bits + uint64_t y = (uint64_t) i.y & 0xFFFFFFFLL; // 28 bits + uint64_t key = (zoom << 56) | (x << 28) | (y << 0); + tileList.PushBack(key, doc.GetAllocator()); + + } + doc.AddMember("tile_list", tileList, doc.GetAllocator()); + } + rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); doc.Accept(writer); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 785b0ee78ba..3cb98282d06 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -152,6 +152,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue that was causing the system location indicator to stay on in background after telemetry was disabled. ([#7833](https://github.com/mapbox/mapbox-gl-native/pull/7833)) * Added support for predicates in rendered feature querying [8256](https://github.com/mapbox/mapbox-gl-native/pull/8246) * Added a nightly build of the dynamic framework. ([#8337](https://github.com/mapbox/mapbox-gl-native/pull/8337)) +* Added optional tileList parameter to MGLTilePyramidOfflineRegion to download non-rectangular offline packs, such as along a track. ## 3.4.2 - February 21, 2017 diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 9af8387233e..761195067ab 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -193,6 +193,11 @@ 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; }; 5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; 5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; + 50D915441E4AA058007277BE /* MGLTileID.m in Sources */ = {isa = PBXBuildFile; fileRef = 50D915421E4A9902007277BE /* MGLTileID.m */; }; + 50D915451E4AA059007277BE /* MGLTileID.m in Sources */ = {isa = PBXBuildFile; fileRef = 50D915421E4A9902007277BE /* MGLTileID.m */; }; + 50D915461E4AA130007277BE /* MGLTileID.h in Headers */ = {isa = PBXBuildFile; fileRef = 50D915411E4A9902007277BE /* MGLTileID.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50D915471E4AA131007277BE /* MGLTileID.h in Headers */ = {isa = PBXBuildFile; fileRef = 50D915411E4A9902007277BE /* MGLTileID.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 554180421D2E97DE00012372 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; 556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 556660D81E1D085500E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D71E1D085500E2C41B /* MGLVersionNumber.m */; }; 556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -665,6 +670,8 @@ 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource_Private.h; sourceTree = ""; }; 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = ""; }; 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = ""; }; + 50D915411E4A9902007277BE /* MGLTileID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileID.h; sourceTree = ""; }; + 50D915421E4A9902007277BE /* MGLTileID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTileID.m; sourceTree = ""; }; 554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; 556660C91E1BF3A900E2C41B /* MGLFoundation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLFoundation.h; sourceTree = ""; }; 556660D71E1D085500E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = ""; }; @@ -822,7 +829,7 @@ DA88480D1CBAFA6200AB86E3 /* MGLPolyline.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPolyline.mm; sourceTree = ""; }; DA88480E1CBAFA6200AB86E3 /* MGLShape.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShape.mm; sourceTree = ""; }; DA88480F1CBAFA6200AB86E3 /* MGLStyle.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyle.mm; sourceTree = ""; }; - DA8848101CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTilePyramidOfflineRegion.mm; sourceTree = ""; }; + DA8848101CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTilePyramidOfflineRegion.mm; sourceTree = ""; }; DA8848111CBAFA6200AB86E3 /* MGLTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTypes.m; sourceTree = ""; }; DA8848121CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+MGLAdditions.h"; sourceTree = ""; }; DA8848131CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+MGLAdditions.m"; sourceTree = ""; }; @@ -1338,6 +1345,8 @@ DA8848111CBAFA6200AB86E3 /* MGLTypes.m */, DA8848911CBB049300AB86E3 /* reachability */, 35E1A4D71D74336F007AA97F /* MGLValueEvaluator.h */, + 50D915411E4A9902007277BE /* MGLTileID.h */, + 50D915421E4A9902007277BE /* MGLTileID.m */, ); name = Foundation; path = ../darwin/src; @@ -1687,6 +1696,7 @@ 353933F81D3FB79F003F57D7 /* MGLLineStyleLayer.h in Headers */, DAAF722D1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */, 071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */, + 50D915471E4AA131007277BE /* MGLTileID.h in Headers */, DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */, DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */, DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */, @@ -1760,6 +1770,7 @@ DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */, 350098BC1D480108004B2AF0 /* MGLVectorSource.h in Headers */, FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */, + 50D915461E4AA130007277BE /* MGLTileID.h in Headers */, 353933FC1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */, 3566C76D1D4A8DFA008152BC /* MGLRasterSource.h in Headers */, DAED38641D62D0FC00D7640F /* NSURL+MGLAdditions.h in Headers */, @@ -2271,6 +2282,7 @@ DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */, 4049C29F1DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */, 35136D3F1D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */, + 50D915451E4AA059007277BE /* MGLTileID.m in Sources */, DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */, DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */, 3510FFFB1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */, @@ -2354,6 +2366,7 @@ DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */, 4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */, 35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */, + 50D915441E4AA058007277BE /* MGLTileID.m in Sources */, DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */, DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */, 3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */, diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index abe16cc3eee..fc6f88a0002 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -60,3 +60,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "NSValue+MGLAdditions.h" #import "MGLStyleValue.h" #import "MGLAttributionInfo.h" +#import "MGLTileID.h"