summaryrefslogtreecommitdiff
path: root/platform/darwin/src/MGLComputedShapeSource.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin/src/MGLComputedShapeSource.mm')
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.mm169
1 files changed, 169 insertions, 0 deletions
diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm
new file mode 100644
index 0000000000..0a3c92bb97
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource.mm
@@ -0,0 +1,169 @@
+#import "MGLComputedShapeSource.h"
+
+#import "MGLMapView_Private.h"
+#import "MGLSource_Private.h"
+#import "MGLShape_Private.h"
+#import "MGLAbstractShapeSource_Private.h"
+#import "MGLGeometry_Private.h"
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/geojson.hpp>
+
+@interface MGLComputedShapeSource () {
+ std::unique_ptr<mbgl::style::CustomGeometrySource> _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<MGLComputedShapeSourceDataSource> 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<mbgl::style::CustomGeometrySource *>(source.rawSource);
+ _rawSource = rawSource;
+ return self;
+}
+
+- (void)main {
+ if ([self isCancelled]) {
+ return;
+ }
+
+ NSArray<MGLShape <MGLFeature> *> *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 <MGLFeature> * feature in data) {
+ mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ 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 {
+ NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init];
+ requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier];
+ requestQueue.qualityOfService = NSQualityOfServiceUtility;
+ requestQueue.maxConcurrentOperationCount = 4;
+
+ auto sourceOptions = MBGLCustomGeometrySourceOptionsFromDictionary(options);
+ sourceOptions.fetchTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
+ NSOperation *operation = [[MGLComputedShapeSourceFetchOperation alloc] initForSource:self tile:tileID];
+ [requestQueue addOperation:operation];
+ };
+
+ sourceOptions.cancelTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
+ for (MGLComputedShapeSourceFetchOperation *operation in requestQueue.operations) {
+ if (operation.x == tileID.x && operation.y == tileID.y && operation.z == tileID.z) {
+ [operation cancel];
+ }
+ }
+ };
+
+ auto source = std::make_unique<mbgl::style::CustomGeometrySource>(identifier.UTF8String, sourceOptions);
+
+ if (self = [super initWithPendingSource:std::move(source)]) {
+ _requestQueue = requestQueue;
+ }
+ return self;
+}
+
+- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+ if (self = [self initWithIdentifier:identifier options:options]) {
+ [self setDataSource:dataSource];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self.requestQueue cancelAllOperations];
+}
+
+- (void)setFeatures:(NSArray<MGLShape <MGLFeature> *>*)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 <MGLFeature> * feature in features) {
+ mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ featureCollection.push_back(geoJsonObject);
+ }
+ const auto geojson = mbgl::GeoJSON{featureCollection};
+ static_cast<mbgl::style::CustomGeometrySource *>(self.rawSource)->setTileData(tileID, geojson);
+}
+
+- (void)setDataSource:(id<MGLComputedShapeSourceDataSource>)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