Allow initializing MGLGeoJSONSource from a shape#7145
Conversation
|
@frederoni, thanks for your PR! By analyzing the history of the files in this pull request, we identified @boundsj, @ituaijagbone and @1ec5 to be potential reviewers. |
|
I share you hesitation about removing |
| id serialized = [NSJSONSerialization JSONObjectWithData:self.geoJSONData options:0 error:nil]; | ||
| BOOL isFeatureCollection = [serialized isKindOfClass:NSArray.class] || ([serialized isKindOfClass:NSDictionary.class] && [[(NSDictionary *)serialized objectForKey:@"type"] isEqualToString:@"FeatureCollection"]); | ||
|
|
||
| if (isFeatureCollection) { |
There was a problem hiding this comment.
Why does this conditional exist? mbgl::style::Source::setGeoJSON() takes a mapbox::geojson::geojson, which is the type returned by mapbox::geojson::parse(), so no get() is needed for either case. _feature is of type id <MGLFeature>, so MGLFeatureFromMBGLFeature() works for both cases too.
| for (id <MGLFeaturePrivate> feature in self.features) { | ||
| featureCollection.push_back([feature mbglFeature]); | ||
| // Feature collection | ||
| if ([self.feature isMemberOfClass:MGLShapeCollectionFeature.class]) { |
There was a problem hiding this comment.
MGLShapeCollectionFeature implements -mbglFeature, so this case is also unnecessary.
daa94bc to
7b32cd4
Compare
|
Removed debug code and cleaned up.
|
40df884 to
4eb7c7b
Compare
There was a problem hiding this comment.
We can't assign features here if we're gonna support plain geometries or a single feature. Would it make sense to rename features to geometries and write a GeoJSON evaluator which overloads featureCollection, feature and geometry?
There was a problem hiding this comment.
We need a shape property that can be any MGLShape (or any MGLFeature). We can have a features property as a convenience for when shape happens to contain an MGLShapeCollectionFeature.
|
Depending on what we want, I think this is good for another review and possibly also merge. |
4eb7c7b to
339c774
Compare
|
Maybe that means it needs to be a |
| @param feature A feature that conform to the `MGLFeature` protocol. | ||
| @param options An `NSDictionary` of options for this source. | ||
| @return An initialized GeoJSON source. | ||
| */ |
There was a problem hiding this comment.
Since mbgl::style::GeoJSONSource::setGeoJSON() takes an mbgl::GeoJSON (a variant of geometry, feature, or feature_collection), it would be better for this initializer to take any MGLShape. All MGLShape subclasses in turn have subclasses that conform to MGLFeature, so we wouldn’t be losing any expressiveness that way.
There was a problem hiding this comment.
We need a shape property that can be any MGLShape (or any MGLFeature). We can have a features property as a convenience for when shape happens to contain an MGLShapeCollectionFeature.
7a85f4b to
0172790
Compare
| `-[MGLStyle addSource]`. | ||
| */ | ||
| @property (nonatomic, nullable) NS_ARRAY_OF(id <MGLFeature>) *features; | ||
| @property (nonatomic, nullable) MGLShape *shape; |
There was a problem hiding this comment.
Update the documentation for this property.
| @property (nonatomic, nullable) NSURL *URL; | ||
|
|
||
| /** | ||
| Returns all objects from the shape that conforms to `MGLFeature`. |
There was a problem hiding this comment.
This makes it sound like it does a deep search through the shape collection feature for any shapes, whereas it's only set to the top-level features, and only if a shape collection feature or array of features was passed in on initialization.
| /** | ||
| Returns all objects from the shape that conforms to `MGLFeature`. | ||
| */ | ||
| - (NSArray<MGLFeature> *)features; |
There was a problem hiding this comment.
Use NS_ARRAY_OF() in public APIs for the benefit of developers compiling against the iOS 8 or OS X 10.10 SDKs.
Also, this method should be a property like before.
Finally, the return type should be an array of id <MGLFeature>, not an NSArray conforming to the MGLFeature protocol.
| } | ||
|
|
||
| mbgl::FillAnnotation annotation { geometry }; | ||
| auto polygon = mbgl::Polygon<double> { [self ring] }; |
| MGLShapeCollection *collection = (MGLShapeCollection *)_shape; | ||
| for (MGLShape *shape in collection.shapes) { | ||
| if ([shape conformsToProtocol:@protocol(MGLFeature)]) { | ||
| [features addObject:(id <MGLFeature>)shape]; |
There was a problem hiding this comment.
Would it be more efficient to compute the features property as soon as shape is set, or on demand when calling -features? Or perhaps a lazy-loading strategy is called for, similar to how I treated overlayBounds in #7251. Basically, if a features ivar is nil, -features would compute the features in the shape and store the results in that ivar. -initWithIdentifier:features:options: would go ahead and set that ivar to the features that are passed in, so a subsequent call to -features would require no computation.
| return self; | ||
| } | ||
|
|
||
| - (instancetype)initWithIdentifier:(NSString *)identifier feature:(id<MGLFeature>)feature options:(NSDictionary<MGLGeoJSONSourceOption,id> *)options |
There was a problem hiding this comment.
Any reason to require a feature instead of any ordinary shape? Looks like we’re only using this parameter for an MGLShape-typed property anyways.
There was a problem hiding this comment.
Mostly not to break the API. Should we rename it to -[MGLGeoJSONSource initWithIdentifier:shape:options:] or keep feature: and features: while adding shape: ?
There was a problem hiding this comment.
How about -initWithIdentifier:shape:options:, which would be expressive enough to take any MGLShape, including any MGLFeature. We’d want to treat the shape as a feature if possible and fall back to treating it as a non-feature, to avoid losing attribute information.
As for the other initializer, which is merely a convenience, how about -initWithIdentifier:features:options: initializer, as incongruous as it’d be? I don’t think we’d want to encourage developers to intermix non-features and features at the top level of the same source, because then we’d have to cater to the lowest common denominator and treat them all like ordinary shapes. (They can still do so by passing an MGLShapeCollection into -initWithIdentifier:shape:options:.)
I’m starting to think we should omit the features property just to keep things simple for us. The developer can still get the features out of shape as long as they cast shape to an MGLShapeCollectionFeature. Maybe @incanus or @ericrwolfe have opinions here, given that they’ve been working with this API at the application level recently.
2ad126b to
52695cf
Compare
1ec5
left a comment
There was a problem hiding this comment.
One question and one minor issue, but otherwise good to go.
| @return An initialized GeoJSON source. | ||
| */ | ||
| - (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<id<MGLFeature>> *)features options:(nullable NS_DICTIONARY_OF(MGLGeoJSONSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; | ||
| - (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NS_DICTIONARY_OF(MGLGeoJSONSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; |
There was a problem hiding this comment.
What does it mean to create a source from no shapes at all? It does work in the current release branch, using an empty array of features, but we should make sure it works when shape is nil.
There was a problem hiding this comment.
nil shape works but I can't think of any case where you'd want to do that.
| self.rawSource->setGeoJSON(geojson); | ||
|
|
||
| _features = MGLFeaturesFromMBGLFeatures(featureCollection); | ||
| [self willChangeValueForKey:@"shape"]; |
There was a problem hiding this comment.
These change notifications are unnecessary. Since setShape: is a standard KVC selector, these change notifications will be generated for you implicitly.
|
The Coveralls failure is caused by an unrelated change in test coverage in mbgl. You can get around it by pushing another change, so that coverage will be even. |
52695cf to
bec9b4c
Compare
There was a problem hiding this comment.
@1ec5 what was the purpose of this test?
There was a problem hiding this comment.
Looks like it's intended to trip an exception in -[MGLShapeCollectionFeature mbglFeature], which is unimplemented in the previous code.
bec9b4c to
3e2cf4e
Compare
|
The iOS release branch now has c8e34d5 so if this branch is rebased on top of that, the node tests will pass. |
b78e8aa to
47fd189
Compare
47fd189 to
fdd3c40
Compare
First stab at #6986
I'm slightly hesitant about replacing
-[MGLGeoJSONSource initWithIdentifier:features:options:]withfeaturebecause of the additional initialization of MGLShapeCollectionFeature devs has to do now compared to a concise array literal prior to this change.@1ec5 👀