From 246417ef2435934261c8d2ab080a78572c64cbec Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 20 Nov 2017 14:04:08 -0800 Subject: [ios, macos] Implement MGLComputedShapeSource binding for CustomGeometrySource --- platform/darwin/src/MGLComputedShapeSource.mm | 164 ++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 platform/darwin/src/MGLComputedShapeSource.mm (limited to 'platform/darwin/src/MGLComputedShapeSource.mm') diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm new file mode 100644 index 0000000000..3176e61a72 --- /dev/null +++ b/platform/darwin/src/MGLComputedShapeSource.mm @@ -0,0 +1,164 @@ +#import "MGLComputedShapeSource.h" + +#import "MGLMapView_Private.h" +#import "MGLSource_Private.h" +#import "MGLShape_Private.h" +#import "MGLAbstractShapeSource_Private.h" +#import "MGLGeometry_Private.h" + +#include +#include +#include +#include + +@interface MGLComputedShapeSource () { + std::unique_ptr _pendingSource; +} + +@property (nonatomic, readwrite) NSDictionary *options; +@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForTile; +@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds; + +@end + +@interface MGLComputedShapeSourceFetchOperation : NSOperation + +@property (nonatomic, readonly) uint8_t z; +@property (nonatomic, readonly) uint32_t x; +@property (nonatomic, readonly) uint32_t y; +@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForTile; +@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds; +@property (nonatomic, weak, nullable) id dataSource; +@property (nonatomic, nullable) mbgl::style::CustomGeometrySource *rawSource; + +- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileId; + +@end + +@implementation MGLComputedShapeSourceFetchOperation + +- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileID { + self = [super init]; + _z = tileID.z; + _x = tileID.x; + _y = tileID.y; + _dataSourceImplementsFeaturesForTile = source.dataSourceImplementsFeaturesForTile; + _dataSourceImplementsFeaturesForBounds = source.dataSourceImplementsFeaturesForBounds; + _dataSource = source.dataSource; + mbgl::style::CustomGeometrySource *rawSource = static_cast(source.rawSource); + _rawSource = rawSource; + return self; +} + +- (void)main { + if ([self isCancelled]) { + return; + } + + NSArray *> *data; + if(!self.dataSource) { + data = nil; + } else if(self.dataSourceImplementsFeaturesForTile) { + data = [self.dataSource featuresInTileAtX:self.x + y:self.y + zoomLevel:self.z]; + } else { + mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID(self.z, self.x, self.y); + mbgl::LatLngBounds tileBounds = mbgl::LatLngBounds(tileID); + data = [self.dataSource featuresInCoordinateBounds:MGLCoordinateBoundsFromLatLngBounds(tileBounds) + zoomLevel:self.z]; + } + + if(![self isCancelled]) { + mbgl::FeatureCollection featureCollection; + featureCollection.reserve(data.count); + for (MGLShape * feature in data) { + mbgl::Feature geoJsonObject = [feature geoJSONObject].get(); + featureCollection.push_back(geoJsonObject); + } + const auto geojson = mbgl::GeoJSON{featureCollection}; + if(![self isCancelled] && self.rawSource) { + self.rawSource->setTileData(mbgl::CanonicalTileID(self.z, self.x, self.y), geojson); + } + } +} + +- (void)cancel { + [super cancel]; + self.rawSource = NULL; +} + +@end + +@implementation MGLComputedShapeSource + +- (instancetype)initWithIdentifier:(NSString *)identifier options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { + _requestQueue = [[NSOperationQueue alloc] init]; + self.requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier]; + self.requestQueue.qualityOfService = NSQualityOfServiceUtility; + self.requestQueue.maxConcurrentOperationCount = 4; + + auto sourceOptions = MBGLCustomGeometrySourceOptionsFromDictionary(options); + sourceOptions.fetchTileFunction = ^void(const mbgl::CanonicalTileID& tileID) { + NSOperation *operation = [[MGLComputedShapeSourceFetchOperation alloc] initForSource:self tile:tileID]; + [self.requestQueue addOperation:operation]; + }; + + sourceOptions.cancelTileFunction = ^void(const mbgl::CanonicalTileID& tileID) { + for(MGLComputedShapeSourceFetchOperation *operation in [self.requestQueue operations]) { + if(operation.x == tileID.x && operation.y == tileID.y && operation.z == tileID.z) { + [operation cancel]; + } + } + }; + + auto source = std::make_unique(identifier.UTF8String, sourceOptions); + return self = [super initWithPendingSource:std::move(source)]; +} + +- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id)dataSource options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { + self = [self initWithIdentifier:identifier options:options]; + [self setDataSource:dataSource]; + return self; +} + +- (void)dealloc { + [self.requestQueue cancelAllOperations]; +} + +- (void)setFeatures:(NSArray *>*)features inTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel { + mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID((uint8_t)zoomLevel, (uint32_t)x, (uint32_t)y); + mbgl::FeatureCollection featureCollection; + featureCollection.reserve(features.count); + for (MGLShape * feature in features) { + mbgl::Feature geoJsonObject = [feature geoJSONObject].get(); + featureCollection.push_back(geoJsonObject); + } + const auto geojson = mbgl::GeoJSON{featureCollection}; + static_cast(self.rawSource)->setTileData(tileID, geojson); +} + +- (void)setDataSource:(id)dataSource { + [self.requestQueue cancelAllOperations]; + // Check which method the datasource implements, to avoid having to check for each tile + self.dataSourceImplementsFeaturesForTile = [dataSource respondsToSelector:@selector(featuresInTileAtX:y:zoomLevel:)]; + self.dataSourceImplementsFeaturesForBounds = [dataSource respondsToSelector:@selector(featuresInCoordinateBounds:zoomLevel:)]; + + if(!self.dataSourceImplementsFeaturesForBounds && !self.dataSourceImplementsFeaturesForTile) { + [NSException raise:@"Invalid Datasource" format:@"Datasource does not implement any MGLComputedShapeSourceDataSource methods"]; + } else if(self.dataSourceImplementsFeaturesForBounds && self.dataSourceImplementsFeaturesForTile) { + [NSException raise:@"Invalid Datasource" format:@"Datasource implements multiple MGLComputedShapeSourceDataSource methods"]; + } + + _dataSource = dataSource; +} + +- (void) invalidateBounds:(MGLCoordinateBounds)bounds { + ((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateRegion(MGLLatLngBoundsFromCoordinateBounds(bounds)); +} + +- (void) invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)z { + ((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateTile(mbgl::CanonicalTileID(z, (unsigned int)x, (unsigned int)y)); +} + +@end -- cgit v1.2.1