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

Commit

Permalink
[ios, macos] Add convenience initializers to shape source (#7665)
Browse files Browse the repository at this point in the history
This adds two new convenience initializers to MGLShapeSource:

-initWithIdentifier:features:options: takes an array of shape objects
that conform to MGLFeature, inserts them in a shape collection feature
and creates a source with that shape. -initWithIdentifier:shapes:options
does the same but with concrete MGLShape objects that get added to
a shape collection.

Throw an exception if an shape source is created with the features
initializer but is sent an array of features that contains something
that is not actually an object that conforms to the feature protocol.

Updates to geojson data guide

Qualify APIs that take arrays of shapes that are features
  • Loading branch information
boundsj authored Jan 12, 2017
1 parent 4332b3b commit 6ad47cf
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 8 deletions.
10 changes: 10 additions & 0 deletions platform/darwin/docs/guides/Working with GeoJSON Data.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ resulting shape or feature object into the
the map, or you can use the object and its properties to power non-map-related
functionality in your application.

To include multiple shapes in the source, create and pass an `MGLShapeCollection` or
`MGLShapeCollectionFeature` object to
`-[MGLShapeSource initWithIdentifier:shape:options:]`. Alternatively, use the
`-[MGLShapeSource initWithIdentifier:features:options:]` or
`-[MGLShapeSource initWithIdentifier:shapes:options:]` method to create a shape source
with an array. `-[MGLShapeSource initWithIdentifier:features:options:]` accepts only `MGLFeature`
instances, such as `MGLPointFeature` objects, whose attributes you can use when
applying a predicate to `MGLVectorStyleLayer` or configuring a style layer’s
appearance.

## Extracting GeoJSON data from the map

Any `MGLShape`, `MGLFeature`, or `MGLShapeCollectionFeature` object has an
Expand Down
2 changes: 1 addition & 1 deletion platform/darwin/src/MGLFeature.mm
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ @implementation MGLShapeCollectionFeature

@dynamic shapes;

+ (instancetype)shapeCollectionWithShapes:(NSArray *)shapes {
+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)shapes {
return [super shapeCollectionWithShapes:shapes];
}

Expand Down
46 changes: 43 additions & 3 deletions platform/darwin/src/MGLShapeSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ extern const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
CLLocationCoordinate2D(latitude: 38.91, longitude: -77.04),
]
let polyline = MGLPolylineFeature(coordinates: &coordinates, count: UInt(coordinates.count))
let shape = MGLShapeCollectionFeature(shapes: [polyline])
let source = MGLShapeSource(identifier: "lines", shape: shape, options: nil)
let source = MGLShapeSource(identifier: "lines", features: [polyline], options: nil)
mapView.style?.addSource(source)
```
*/
Expand All @@ -131,7 +130,9 @@ extern const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
To specify attributes about the shape, use an instance of an `MGLShape`
subclass that conforms to the `MGLFeature` protocol, such as `MGLPointFeature`.
To include multiple shapes in the source, use an `MGLShapeCollection` or
`MGLShapeCollectionFeature` object.
`MGLShapeCollectionFeature` object, or use the
`-initWithIdentifier:features:options:` or
`-initWithIdentifier:shapes:options:` methods.
To create a shape from GeoJSON source code, use the
`+[MGLShape shapeWithData:encoding:error:]` method.
Expand All @@ -143,6 +144,45 @@ extern const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
*/
- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;

/**
Returns a shape source with an identifier, an array of features, and a dictionary
of options for the source.
Unlike `-initWithIdentifier:shapes:options:`, this method accepts `MGLFeature`
instances, such as `MGLPointFeature` objects, whose attributes you can use when
applying a predicate to `MGLVectorStyleLayer` or configuring a style layer’s
appearance.
To create a shape from GeoJSON source code, use the
`+[MGLShape shapeWithData:encoding:error:]` method.
@param identifier A string that uniquely identifies the source.
@param features An array of objects that conform to the MGLFeature protocol.
@param options An `NSDictionary` of options for this source.
@return An initialized shape source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;

/**
Returns a shape source with an identifier, an array of shapes, and a dictionary of
options for the source.
Any `MGLFeature` instance passed into this initializer is treated as an ordinary
shape, causing any attributes to be inaccessible to an `MGLVectorStyleLayer` when
evaluating a predicate or configuring certain layout or paint attributes. To
preserve the attributes associated with each feature, use the
`-initWithIdentifier:features:options:` method instead.
To create a shape from GeoJSON source code, use the
`+[MGLShape shapeWithData:encoding:error:]` method.
@param identifier A string that uniquely identifies the source.
@param shapes An array of shapes; each shape is a member of a concrete subclass of MGLShape.
@param options An `NSDictionary` of options for this source.
@return An initialized shape source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;

#pragma mark Accessing a Source’s Content

/**
Expand Down
15 changes: 15 additions & 0 deletions platform/darwin/src/MGLShapeSource.mm
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ - (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLSha
return self;
}

- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
for (id <MGLFeature> 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];
}

- (instancetype)initWithRawSource:(mbgl::style::GeoJSONSource *)rawSource {
return [super initWithRawSource:rawSource];
}
Expand Down
3 changes: 1 addition & 2 deletions platform/darwin/test/MGLDocumentationExampleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
CLLocationCoordinate2D(latitude: 38.91, longitude: -77.04),
]
let polyline = MGLPolylineFeature(coordinates: &coordinates, count: UInt(coordinates.count))
let shape = MGLShapeCollectionFeature(shapes: [polyline])
let source = MGLShapeSource(identifier: "lines", shape: shape, options: nil)
let source = MGLShapeSource(identifier: "lines", features: [polyline], options: nil)
mapView.style?.addSource(source)
//#-end-example-code

Expand Down
39 changes: 39 additions & 0 deletions platform/darwin/test/MGLShapeSourceTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,43 @@ - (void)testMGLShapeSourceWithShapeCollectionFeatures {
XCTAssert(shape.shapes.count == 6, @"Shape collection should contain 6 shapes");
}

- (void)testMGLShapeSourceWithFeaturesConvenienceInitializer {
CLLocationCoordinate2D coordinates[] = {
CLLocationCoordinate2DMake(100.0, 0.0),
CLLocationCoordinate2DMake(101.0, 0.0),
CLLocationCoordinate2DMake(101.0, 1.0),
CLLocationCoordinate2DMake(100.0, 1.0),
CLLocationCoordinate2DMake(100.0, 0.0)};

MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];

MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" features:@[polygonFeature] options:nil];
MGLShapeCollectionFeature *shape = (MGLShapeCollectionFeature *)source.shape;

XCTAssertTrue([shape isKindOfClass:[MGLShapeCollectionFeature class]]);
XCTAssertEqual(shape.shapes.count, 1, @"Shape collection should contain 1 shape");

// when a shape is included in the features array
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];

XCTAssertThrowsSpecificNamed([[MGLShapeSource alloc] initWithIdentifier:@"source-id-invalid" features:@[polygon] options:nil], NSException, NSInvalidArgumentException, @"Shape source should raise an exception if a shape is sent to the features initializer");
}

- (void)testMGLShapeSourceWithShapesConvenienceInitializer {
CLLocationCoordinate2D coordinates[] = {
CLLocationCoordinate2DMake(100.0, 0.0),
CLLocationCoordinate2DMake(101.0, 0.0),
CLLocationCoordinate2DMake(101.0, 1.0),
CLLocationCoordinate2DMake(100.0, 1.0),
CLLocationCoordinate2DMake(100.0, 0.0)};

MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];

MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shapes:@[polygon] options:nil];
MGLShapeCollectionFeature *shape = (MGLShapeCollectionFeature *)source.shape;

XCTAssertTrue([shape isKindOfClass:[MGLShapeCollection class]]);
XCTAssertEqual(shape.shapes.count, 1, @"Shape collection should contain 1 shape");
}

@end
3 changes: 1 addition & 2 deletions platform/ios/app/MBXViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -919,8 +919,7 @@ - (void)styleQuery
}

dispatch_async(dispatch_get_main_queue(), ^{
MGLShapeCollectionFeature *features = [MGLShapeCollectionFeature shapeCollectionWithShapes:visibleFeatures];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:querySourceID shape:features options:nil];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:querySourceID features:visibleFeatures options:nil];
[self.mapView.style addSource:source];

MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:queryLayerID source:source];
Expand Down

0 comments on commit 6ad47cf

Please sign in to comment.