Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Arbitrary offline region shapes #11447

Merged
merged 4 commits into from
Aug 20, 2018

Conversation

ivovandongen
Copy link
Contributor

@ivovandongen ivovandongen commented Mar 14, 2018

Adds the ability to specify offline regions shapes as any GeoJson geometry.

Depends on:

TODO:

  • more unit tests
  • use streaming tile cover
  • android
  • ios/macos
  • offline cmd line

@ivovandongen ivovandongen added iOS Mapbox Maps SDK for iOS Android Mapbox Maps SDK for Android ⚠️ DO NOT MERGE Work in progress, proof of concept, or on hold Core The cross-platform C++ core, aka mbgl labels Mar 14, 2018
@ivovandongen ivovandongen self-assigned this Mar 14, 2018
@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch from d36f496 to 479b788 Compare March 14, 2018 16:29
@zugaldia
Copy link
Member

Replaces OfflineTilePyramidRegionDefinition with OfflineRegionDefinition as it covers all shapes.

Would it be possible to keep both around in order not to break the public API (for now)?

@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch from 479b788 to e6eaabe Compare March 22, 2018 14:31
@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch from e6eaabe to 4495625 Compare March 23, 2018 14:41
@asheemmamoowala asheemmamoowala force-pushed the tile-cover-geometry branch 2 times, most recently from fb53043 to 4bda046 Compare March 23, 2018 16:48
@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch 2 times, most recently from 1bdf9a1 to 89d094b Compare March 26, 2018 18:44
@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch 6 times, most recently from a2af3c3 to 2d44c1b Compare April 11, 2018 08:33
@ivovandongen
Copy link
Contributor Author

@zugaldia I've included the changes to the offline tool in this pr:

  ./build/macos/Debug/mbgl-offline {OPTIONS}

    Mapbox GL offline tool

  OPTIONS:

      -h, --help                        Display this help menu
      -t[key], --token=[key]            Mapbox access token
      -s[URL], --style=[URL]            Map stylesheet
      -o[file], --output=[file]         Output database file name
      LatLng bounds:
        --north=[degrees]                 North latitude
        --west=[degrees]                  West longitude
        --south=[degrees]                 South latitude
        --east=[degrees]                  East longitude
      GeoJson geometry:
        --geojson=[file]                  GeoJSON file containing the region
                                          geometry
      --minZoom=[number]                Min zoom level
      --maxZoom=[number]                Max zoom level
      --pixelRatio=[number]             Pixel ratio

@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch from 2d44c1b to 35d6140 Compare April 25, 2018 14:24
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
result += definition.match(
[&](const OfflineTilePyramidRegionDefinition& reg){ return util::tileCount(reg.bounds, z); },
[&](const OfflineGeometryRegionDefinition& reg){ return util::tileCount(reg.geometry, z); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mbgl::util::tileCount(Geometry<>...) method will block, especially when called in a tight loop like this. #9675 was filed in the past for this same problem with large LatLngBounds

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@asheemmamoowala Correct. However, this is identical to the previous implementation.

I'm all for speeding this up. But maybe in a separate issue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems fine. I profiled these changes using mbgl-offline and don't see the tile count or tile cover operations showing up as significant blips on the main thread.

@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch from 35d6140 to ac556fe Compare April 27, 2018 07:56
@ivovandongen ivovandongen changed the base branch from tile-cover-geometry to master April 27, 2018 07:57
@ivovandongen ivovandongen added the macOS Mapbox Maps SDK for macOS label Apr 27, 2018
Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to the comments below, please:

  • Remove the note about MGLTilePyramidOfflineRegion from MGLOfflineRegion’s documentation comment, since it’s no longer an only child.
  • Add a blurb to the iOS and macOS changelogs noting the new class as a more granular alternative to MGLTilePyramidOfflineRegion.

Thanks!

range of zoom levels.
*/
MGL_EXPORT
@interface MGLGeometryOfflineRegion : NSObject <MGLOfflineRegion, NSSecureCoding, NSCopying>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On iOS and macOS, we say “shape” instead of “geometry”.

The geometry bounds for the geographic region covered by the downloaded
tiles.
*/
@property (nonatomic, readonly) MGLShape *geometry;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property would be named shape rather than geometry.

MGLPointAnnotation is a subclass of MGLShape that corresponds to a Point in GeoJSON. If the region needs to have dimension, we should require that the shape conform to the MGLOverlay protocol (MGLShape <MGLOverlay> *), though that would still allow for MGLPolyline. If the region needs to be two-dimensional, then we’d need to introduce either a runtime class check or a new protocol like “MGLSpaceFilling”.

The URL may be a full HTTP or HTTPS URL or a Mapbox URL indicating the style’s
map ID (`mapbox://styles/{user}/{style}`).
*/
@property (nonatomic, readonly) NSURL *styleURL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s promote styleURL to the MGLOfflineRegion protocol, since it’s shared in common between the two conforming classes and we don’t expect to introduce a new region class that doesn’t depend on a style URL. I could go either way about promoting minimumZoomLevel and maximumZoomLevel.

[NSException raise:@"Method unavailable"
format:
@"-[MGLGeometryOfflineRegion init] is unavailable. "
@"Use -initWithStyleURL:bounds:fromZoomLevel:toZoomLevel: instead."];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace “bounds” with “shape”.


/**
An offline region defined by a style URL, geographic coordinate bounds, and
range of zoom levels.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace “coordinate bounds” with “shape”.


/**
An offline region defined by a style URL, geographic coordinate bounds, and
range of zoom levels.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any benefits to using an MGLTilePyramidOfflineRegion instead of an MGLShapeOfflineRegion going forward, such as performance? If so, we can mention those benefits here and in MGLTilePyramidOfflineRegion’s documentation comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MGLTilePyramidOfflineRegion would perform better at computing the number of tile resources needed, and computing their actual IDs. But that performance advantage over MGLShapeOfflineRegion should be marginal when including the download as well.

More importantly, switching from using the bounding box to an actual shape would reduce the number of resources required, the storage and download cost. It would also help with the tile download ceiling for many customers interested in many small offline regions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, let’s mention the new class’s benefits in the documentation comments for both classes. The old class is being left in place for backwards compatibility reasons, but we don’t have to say that just yet since we aren’t deprecating it.

@1ec5
Copy link
Contributor

1ec5 commented Jul 3, 2018

#12285 tracks adding a UI to macosapp for selecting a non-pyramidal offline region for download.

@zugaldia
Copy link
Member

Tagging @julianrex to help review the last round of iOS changes. Otherwise it seems we're in good shape to get this merged.

@1ec5
Copy link
Contributor

1ec5 commented Aug 14, 2018

By the way, most of the changes requested in #11447 (review) are renames, so that review should be straightforward to address.

Copy link
Contributor

@julianrex julianrex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good from the iOS/macOS side - we should address @1ec5's rename and protocol recommendations .

NSURL *_styleURL;
}

@synthesize styleURL = _styleURL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to replace the @synthesize and the instance var declaration with a single @property

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't accept just the property, so left this for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

This ought to work, so I'd be interested in why it might not be for you:

@interface MGLShapeOfflineRegion () <MGLOfflineRegion_Private, MGLShapeOfflineRegion_Private>
@property (nonatomic, copy) NSURL *styleURL;
@end

Copy link
Contributor

@1ec5 1ec5 Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably has to do with the fact that the property’s getter is now declared in a protocol, MGLOfflineRegion, rather than a concrete class, which would prevent autosynthesis. If that’s the case, then explicit synthesis is fine.

}];
[pack requestProgress];
[self waitForExpectationsWithTimeout:1 handler:nil];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor: we should probably have pack = nil; at the end here, since it's a __block variable.

}];
[self waitForExpectationsWithTimeout:2 handler:nil];

XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1, @"Added pack should have been added to the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may have to revisit this for Xcode 10 - IIRC tests can be run in a random order (though I believe this needs to be enabled)

@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch 3 times, most recently from 084b3a2 to 27ce802 Compare August 20, 2018 16:58
@@ -78,7 +87,17 @@ - (void)dealloc {

const mbgl::OfflineRegionDefinition &regionDefinition = _mbglOfflineRegion->getDefinition();
NSAssert([MGLTilePyramidOfflineRegion conformsToProtocol:@protocol(MGLOfflineRegion_Private)], @"MGLTilePyramidOfflineRegion should conform to MGLOfflineRegion_Private.");
return [(id <MGLOfflineRegion_Private>)[MGLTilePyramidOfflineRegion alloc] initWithOfflineRegionDefinition:regionDefinition];
NSAssert([MGLShapeOfflineRegion conformsToProtocol:@protocol(MGLOfflineRegion_Private)], @"MGLGeometryOfflineRegion should conform to MGLOfflineRegion_Private.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace all instances of MGLGeometryOfflineRegion in this PR with MGLShapeOfflineRegion.

[NSException raise:@"Method unavailable"
format:
@"-[MGLShapeOfflineRegion init] is unavailable. "
@"Use -initWithStyleURL:bounds:fromZoomLevel:toZoomLevel: instead."];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace bounds: with shape:.

@@ -13,17 +13,6 @@ NS_ASSUME_NONNULL_BEGIN
MGL_EXPORT
@interface MGLTilePyramidOfflineRegion : NSObject <MGLOfflineRegion, NSSecureCoding, NSCopying>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To minimize the resources required by an irregularly shaped offline region, use the MGLShapeOfflineRegion class instead.

/ref #11447 (comment)

/**
An offline region defined by a style URL, geographic shape, and
range of zoom levels.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class requires fewer resources than MGLTilePyramidOfflineRegion for irregularly shaped regions.

/ref #11447 (comment)

@ivovandongen ivovandongen force-pushed the ivd-arbitrary-offline-region-shapes branch from 27ce802 to f0a4dcb Compare August 20, 2018 18:28
Copy link
Contributor

@julianrex julianrex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we just need to address @1ec5's comments, and we're good from the iOS/macOS side.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Android Mapbox Maps SDK for Android Core The cross-platform C++ core, aka mbgl iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants