#import "MGLShapeSource_Private.h" #import "MGLStyle_Private.h" #import "MGLMapView_Private.h" #import "MGLSource_Private.h" #import "MGLFeature_Private.h" #import "MGLShape_Private.h" #import "NSPredicate+MGLAdditions.h" #import "NSURL+MGLAdditions.h" #include #include #include const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered"; const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius"; const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering"; const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel"; const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer"; const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance"; @interface MGLShapeSource () @property (nonatomic, readwrite) NSDictionary *options; @property (nonatomic, readonly) mbgl::style::GeoJSONSource *rawSource; @end @implementation MGLShapeSource - (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NS_DICTIONARY_OF(NSString *, id) *)options { auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); auto source = std::make_unique(identifier.UTF8String, geoJSONOptions); if (self = [super initWithPendingSource:std::move(source)]) { self.URL = url; } return self; } - (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); auto source = std::make_unique(identifier.UTF8String, geoJSONOptions); if (self = [super initWithPendingSource:std::move(source)]) { self.shape = shape; } return self; } - (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { for (id feature in features) { if (![feature conformsToProtocol:@protocol(MGLFeature)]) { [NSException raise:NSInvalidArgumentException format:@"The object %@ included in the features argument does not conform to the MGLFeature protocol.", feature]; } } MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:features]; return [self initWithIdentifier:identifier shape:shapeCollectionFeature options:options]; } - (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { MGLShapeCollection *shapeCollection = [MGLShapeCollection shapeCollectionWithShapes:shapes]; return [self initWithIdentifier:identifier shape:shapeCollection options:options]; } - (mbgl::style::GeoJSONSource *)rawSource { return (mbgl::style::GeoJSONSource *)super.rawSource; } - (NSURL *)URL { auto url = self.rawSource->getURL(); return url ? [NSURL URLWithString:@(url->c_str())] : nil; } - (void)setURL:(NSURL *)url { if (url) { self.rawSource->setURL(url.mgl_URLByStandardizingScheme.absoluteString.UTF8String); _shape = nil; } else { self.shape = nil; } } - (void)setShape:(MGLShape *)shape { self.rawSource->setGeoJSON({ shape.geoJSONObject }); _shape = shape; } - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = %@; shape = %@>", NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape]; } - (NS_ARRAY_OF(id ) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate { mbgl::optional optionalFilter; if (predicate) { optionalFilter = predicate.mgl_filter; } std::vector features; if (self.style) { features = self.style.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter }); } return MGLFeaturesFromMBGLFeatures(features); } @end mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) { auto geoJSONOptions = mbgl::style::GeoJSONOptions(); if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."]; } geoJSONOptions.maxzoom = value.integerValue; } if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException format:@"MGLShapeSourceOptionBuffer must be an NSNumber."]; } geoJSONOptions.buffer = value.integerValue; } if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."]; } geoJSONOptions.tolerance = value.doubleValue; } if (NSNumber *value = options[MGLShapeSourceOptionClusterRadius]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException format:@"MGLShapeSourceOptionClusterRadius must be an NSNumber."]; } geoJSONOptions.clusterRadius = value.integerValue; } if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevelForClustering]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."]; } geoJSONOptions.clusterMaxZoom = value.integerValue; } if (NSNumber *value = options[MGLShapeSourceOptionClustered]) { if (![value isKindOfClass:[NSNumber class]]) { [NSException raise:NSInvalidArgumentException format:@"MGLShapeSourceOptionClustered must be an NSNumber."]; } geoJSONOptions.cluster = value.boolValue; } return geoJSONOptions; }