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

Add features initializer to MGLGeoJSONSource #6524

Merged
merged 2 commits into from
Oct 14, 2016

Conversation

ituaijagbone
Copy link
Contributor

fixes #6302

@ituaijagbone ituaijagbone added iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS ⚠️ DO NOT MERGE Work in progress, proof of concept, or on hold runtime styling labels Sep 29, 2016
@mention-bot
Copy link

@ituaijagbone, thanks for your PR! By analyzing this pull request, we identified @incanus, @1ec5 and @boundsj to be potential reviewers.

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're on the right track. Here are a few preliminary comments.


NSMutableArray *coordinates = [NSMutableArray array];

for (int index = 0; index < self.pointCount; index++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int might not be large enough for the number of coordinates that may be in this polyline. Use the same type as pointCount.

@@ -131,6 +131,9 @@ extern NSString * const MGLGeoJSONToleranceOption;
*/
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url options:(NS_DICTIONARY_OF(NSString *, id) *)options NS_DESIGNATED_INITIALIZER;


- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier Features:(NS_ARRAY_OF(id<MGLFeature>) *)features options:(nullable NS_DICTIONARY_OF(NSString *, id) *)options NS_DESIGNATED_INITIALIZER;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: lowercase the Features parameter.

NSString *string = [[NSString alloc] initWithData:self.geoJSONData encoding:NSUTF8StringEncoding];
const auto geojson = mapbox::geojson::parse(string.UTF8String).get<mapbox::geojson::feature_collection>();
source->setGeoJSON(geojson);
_features = MGLFeaturesFromMBGLFeatures(geojson);
} else {

NSMutableArray<NS_DICTIONARY_OF(NSString *, id) *> *featuresArray = [NSMutableArray array];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the macros consistently: either change NSMutableArray to NS_MUTABLE_ARRAY_OF, or change NS_DICTIONARY_OF to NSDictionary.

@"features":featuresArray};

NSError *error;
NSData *featuresJSONData = [NSJSONSerialization dataWithJSONObject:featureCollection options:NSJSONWritingPrettyPrinted error:&error];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty printing is good for debugging purposes, but it'll need to be removed before merging because it increases memory consumption.

- (NS_DICTIONARY_OF(NSString *, id) *)featureDictionary {
// TODO: MGLPolygonFeature

NS_MUTABLE_ARRAY_OF(NS_MUTABLE_ARRAY_OF(NS_ARRAY_OF(NSNumber *) *) *) *coordinates = [NSMutableArray array];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These macros (or the lightweight generics syntax) are useful in headers and method signatures, but they’re totally optional inside method implementations. As you can see, they’re a bit unwieldy, so there’s no shame in leaving them out inside the method.

return geometries;
}

- (void)addTypeAndCoordinatesFrom:(id<MGLFeature>)feature to:(NS_MUTABLE_ARRAY_OF(NS_DICTIONARY_OF(NSString *, id) *)**)geometries {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a questionable refactoring. First of all, since the array is mutable, you can simply pass a pointer to it rather than a double-pointer. Secondly, if all the classes that implement MGLFeature have a -featureDictionary method, it would be cleaner to declare that method on MGLFeature. That way, -geometryCollection: can simply call feature.featureDictionary without caring which specific class feature happens to be.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes You are right. Calling -featureDictionary should work directly. Thank you.

}

- (NS_MUTABLE_ARRAY_OF(NS_DICTIONARY_OF(NSString *, id) *) *)geometryCollection:(NS_ARRAY_OF(MGLShape *) *)shapes {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MGLShapeCollectionFeature already knows about its shapes, so there’s no need to pass it in here. Just use self.shapes inside the method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 087f013

Copy link
Contributor

@boundsj boundsj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Seems like we will need to regenerate the layer code to get the tests passing again now that the GeoJSON source method signatures have been updated.

@@ -23,11 +24,23 @@ @implementation MGLPointFeature

@synthesize identifier;
@synthesize attributes;
// synthesize the feature dictionary
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd fine to remove this comment


- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}

- (NS_DICTIONARY_OF(NSString *, id) *)featureDictionary {
return @{@"type":@"Feature",
@"properties":(self.attributes) ? self.attributes : [NSDictionary dictionary],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit this could be an empty NSDictionary literal

@@ -70,6 +104,24 @@ - (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}

- (NS_DICTIONARY_OF(NSString *, id) *)featureDictionary {
// TODO: MGLMultiPointFeature
NS_MUTABLE_ARRAY_OF(NS_ARRAY_OF(NSNumber *) *) *coordinates = [NSMutableArray array];
Copy link
Contributor

@boundsj boundsj Sep 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I definitely prefer, inside method implementations, to just write NSMutableArray *coordinates (+1 to #6524 (comment))

options for the source as specified by the
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">the
style specification</a>.

@param sourceIdentifier A string that uniquely identifies the source.
@param URL An HTTP(S) URL, absolute file URL, or local file URL relative to the
current application’s resource bundle.
@param features An array of features that conform to the MGLFeature protocol.
Copy link
Contributor

@boundsj boundsj Sep 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It'd be nice to surround MGLFeature in back ticks so that the generated documentation highlights the word like MGLFeature



- (void)testMGLGeoJSONSourceWithPolygonFeatures {
// TODO: Create feature
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit remove the scaffolding comments

@"features":featuresArray};

NSError *error;
NSData *featuresJSONData = [NSJSONSerialization dataWithJSONObject:featureCollection options:0 error:&error];
Copy link
Contributor

@boundsj boundsj Sep 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#6524 (comment) suggested removing the pretty printing. Noting also that this will be refactored away in a future commit where we eventually just set mbgl::GeoJSON{ featureCollection } and avoid the trip to create JSON data.

@boundsj boundsj changed the title [WIP] added GeoJSON Serialization [WIP] Add features initializer to MGLGeoJSONSource Oct 6, 2016
@boundsj
Copy link
Contributor

boundsj commented Oct 6, 2016

We should probably wait for #6588 to land before continuing with this because of the the duplicate work and conflicts in MGLGeoJSONSource and related tests

cc @1ec5 @ituaijagbone

@1ec5
Copy link
Contributor

1ec5 commented Oct 7, 2016

#6588 has landed. As expected, there are a number of conflicts, but they should be straightforward to resolve.

@boundsj boundsj force-pushed the ituaijagbone-geojson-with-features branch from 48ff580 to 44f88ab Compare October 11, 2016 23:25
@boundsj boundsj added ✓ ready for review and removed ⚠️ DO NOT MERGE Work in progress, proof of concept, or on hold labels Oct 11, 2016
@boundsj
Copy link
Contributor

boundsj commented Oct 11, 2016

@incanus @1ec5 @frederoni please feel free to have another look at this when you have a chance. I'm not sure why the CI builds started failing today but it does not seem to be related to any changes in this PR.

Note the comment in 70962eb. I'm up for suggestions about where / how to share the mbgl::Value conversion logic.

@boundsj
Copy link
Contributor

boundsj commented Oct 11, 2016

Example of a query for features in rect that are then used to initialize a GeoJSON source and styled with a fill layer.

screen shot 2016-10-11 at 4 36 55 pm


@implementation MGLFeatureUtility

// TODO: This is copied from NSExpression+MGLAdditions. Where should a shared implementation go?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about stuffing the value in an NSExpression and calling the existing category method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3770a036d68a4bcca7ffa91a4ff11036262d491a

- (NS_DICTIONARY_OF(NSString *, id) *)featureDictionary {
return @{@"type": @"Feature",
@"properties": (self.attributes) ? self.attributes : @{},
@"geometry" :@{@"type":@"Point",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: funky spacing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3770a036d68a4bcca7ffa91a4ff11036262d491a

return { };
}

+ (mbgl::PropertyMap)propertyMapForDictionary:(NSDictionary *)dictionary propertyMap:(mbgl::PropertyMap *)propertyMap {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass C++ objects around by reference rather than as pointers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3770a036d68a4bcca7ffa91a4ff11036262d491a

if ([dictionary[key] isKindOfClass:[NSDictionary class]]) {
std::unordered_map<std::string, mbgl::Value> unorderedMap;
[self propertyMapForDictionary:dictionary[key] propertyMap:&unorderedMap];
(*propertyMap)[[key cStringUsingEncoding:NSUTF8StringEncoding]] = unorderedMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use .UTF8String.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3770a036d68a4bcca7ffa91a4ff11036262d491a


+ (mbgl::Feature)mbglFeatureWithPropertyMapForFeature:(mbgl::Feature)feature mglAttributes:(NSDictionary *)attributes {
mbgl::PropertyMap propertyMap;
[MGLFeatureUtility propertyMapForDictionary:attributes propertyMap:&propertyMap];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more convenient for NSDictionary to know how to turn itself into a PropertyMap?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3770a036d68a4bcca7ffa91a4ff11036262d491a


- (mbgl::Feature)mbglFeature {
[NSException raise:@"Method unavailable" format:@"%s is not available on %@. Please use a concrete subclass of %@", __PRETTY_FUNCTION__, [self class], [self class]];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MGLShapeCollectionFeature is a concrete class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3a6be23e3a922e739b6e7b1687d5630621bb7ba6

or polygon.
The `MGLMultiPoint` class is used to define shapes composed of multiple points.
You can use the method and properties of this class to access information about
the specific points associated with a line or polygon.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention that this class also doubles as a superclass of MGLPolyline and MGLPolygon, despite being a distinct geometry.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3a6be23e3a922e739b6e7b1687d5630621bb7ba6

@param coords The array of coordinates defining the shape. The data in this
array is copied to the new object.
@param count The number of items in the `coords` array.
@return A new MultiPoint object.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: lowercase.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3a6be23e3a922e739b6e7b1687d5630621bb7ba6

@implementation MGLPolygon (MGLAdditions)

- (NS_ARRAY_OF(id) *)mgl_coordinates {
NS_MUTABLE_ARRAY_OF(NS_MUTABLE_ARRAY_OF(NS_ARRAY_OF(NSNumber *) *) *) *coordinates = [NSMutableArray array];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really unwieldy. Just use an unqualified mutable array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also initialize it with a capacity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3770a036d68a4bcca7ffa91a4ff11036262d491a

@@ -166,6 +166,7 @@
404C26E71D89C55D000AA13D /* MGLTileSet_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSet_Private.h */; };
404C26E81D89C55D000AA13D /* MGLTileSet_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSet_Private.h */; };
4085AF091D933DEA00F11B22 /* MGLTileSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */; };
40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */; };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bunch of these files are missing from the macOS project.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4268576d1e5dc05514ebf412a6b9723ab4623b93

@boundsj boundsj changed the title [WIP] Add features initializer to MGLGeoJSONSource Add features initializer to MGLGeoJSONSource Oct 12, 2016
@boundsj
Copy link
Contributor

boundsj commented Oct 12, 2016

Since yesterday afternoon, I'm still getting npm ERR! Permission denied (publickey). for all CI builds and I have no idea why.


#import <mbgl/util/geometry.hpp>
#import <mapbox/geometry/feature.hpp>

@interface MGLFeatureUtility: NSObject
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class doesn't seem to be adding much. The lone function can be a standalone C++ function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 206af31

- (NS_DICTIONARY_OF(NSString *, id) *)featureDictionary {
return @{@"type": @"Feature",
@"properties": (self.attributes) ? self.attributes : @{},
@"geometry": @{@"type": @"Point",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The non-feature direct subclasses of MGLShape should have geoJSONDictionary methods that encapsulate these geometry structures.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once that happens, the bodies of most of these featureDictionary methods can be factored out.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took this I idea and ran with it a bit in ad4e54d

}
}

return [geometries copy];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+[NSArray arrayWithArray:] creates a non-mutable array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. The method says that it returns a non-mutable array even though it uses a mutable array internally to create the collection

(NSArray *)geometryCollection

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, I figured -[NSMutableArray copy] returns an NSMutableArray, but it returns an NSArray. Carry on.

return [self propertyMapWithPropertyMap:propertyMap dictionary:self];
}

- (mbgl::PropertyMap)propertyMapWithPropertyMap:(mbgl::PropertyMap &)propertyMap dictionary:(NSDictionary *)dictionary {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing in the property map by non-const reference is similar to an inout C parameter: you're actually mutating the map that's being passed in. So there's no need to return anything. In fact, this method could be renamed to "populatePropertyMap" or somesuch. But if an empty property map is always passed in, why not create it locally instead of accepting it as a parameter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the intent is to convert a dictionary into a property map, this could be a category method on NSDictionary that takes no parameters and returns a property map.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was fixed in bc1dda3. Note thatpropertyMapWithDictionary: and vectorForArray: (name change in 95d548b) still take NSDictionarys and NSArrays so that the entire dictionary can be traversed (recursively) including if any node is a dictionary or array. The (mbgl::PropertyMap)mbgl_propertyMap method already exists and is the only public method of the category. It is intended to take no parameters and return a property map.

return propertyMap;
}

- (std::vector<mbgl::Value>)vectorForNSArray:(NSArray *)array vector:(std::vector<mbgl::Value> &)vector {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, this would be a category method on NSArray that returns a vector of values, and the method name would be something like mgl_valueVector.

The dictionary includes the required geometry and properties dictionaries along
with an "id" key and value if the underlying `MGLFeature` has an identifier.
*/
- (NS_DICTIONARY_OF(NSString *, id) *)geoJSONFeatureDictionary;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word “feature” in this method name is redundant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0c317fa

@@ -91,6 +91,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable id)attributeForKey:(NSString *)key;

/**
Returns a dictionary that can be serialized as a GeoJSON Feature representation
of the `MGLFeature` subclass.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pedantry: it’s a representation of an instance of the class, not of the class itself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0c317fa

Returns a dictionary that can be serialized as a GeoJSON Feature representation
of the `MGLFeature` subclass.

The dictionary includes the required geometry and properties dictionaries along
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dictionary includes a geometry key corresponding to the receiver’s underlying geometry data, a properties key corresponding to the receiver’s attributes property, and an id key corresponding to the receiver’s identifier property.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0c317fa

@@ -42,6 +50,14 @@ - (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}

- (NSDictionary *)geoJSONFeatureDictionary {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename -geoJSONGeometryDictionary to -geoJSONDictionary and -geoJSONFeatureDictionary to geoJSONDictionary. Then this method can call super’s implementation and wrap it in NSDictionaryFeatureForGeometry().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0c317fa

NSMutableDictionary *feature = [@{@"type": @"Feature",
@"properties": (attributes) ?: [NSNull null],
@"geometry": geometry} mutableCopy];
if (identifier) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the if statement is unnecessary; it’s perfectly fine to set to nil using subscript notation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remove that conditional, I get a runtime exception when [NSExpression expressionForConstantValue:identifier]; is called. I'm using NSExpression for the conversion of the id to the mbgl variant .

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, I wouldn't've expected foo[@"bar"] = nil to do anything other than remove the "bar" key from foo.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was in mbglFeature() not NSDictionaryFeatureForGeometry(). Fixed in acf792b

return [self propertyMapWithDictionary:self];
}

- (mbgl::PropertyMap)propertyMapWithDictionary:(NSDictionary *)dictionary {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the dictionary parameter and use self instead. Below, call the method recursively on dictionary[key] rather than on self. Then rename this method to -mgl_propertyMap (not -mbgl_propertyMap – MGL is the class prefix for this target).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0c317fa

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And 3909852

} else if ([number compare:@(0)] == NSOrderedAscending) {
return mbglValue.get<int64_t>();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we throw an exception if we encounter an unknown type?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this and decided to not throw an exception when I added it yesterday.

The reason is that the first thing mgl_featureIdentifier does is call mgl_convertedValueWithValue which will throw an exception if there is an unknown type. mgl_featureIdentifier exists only to be able to get the correct raw value from mbgl::Value returned by mgl_convertedValueWithValue and the only reason we are doing any of this here is to share the conversion logic that happens to currently live in the NSExpression category.

I think you could make the case that we should catch an exception thrown by mgl_convertedValueWithValue and re-raise it with a different message (instead of the Can’t convert %s:%@ to mbgl::Value) that a developer will see if they send an invalid value for the id / identifier. However, I was thinking that it might make sense to wait and refactor this if we introduce a category method on NSObject as you've suggested in conversation. It is a bit weird at the moment though.

return propertyMap;
}

- (std::vector<mbgl::Value>)vectorForArray:(NSArray *)array {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this parameter too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 61f4d69

std::vector<mbgl::Value> vector;
vector.reserve(array.count);
for (id value in array) {
if ([value isKindOfClass:[NSArray class]]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we refactor this so that any object knows how to "valueize" itself, we'll be able to remove these conditionals too.

XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
}

- (void)testShapeCollectionFeatureFeatureGeoJSONDictionary {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: feature feature! 🍕

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍕 - 1 in 184c475

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there!

@@ -157,6 +157,9 @@
35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; };
35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; };
36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F1153B1D46080700878E1A /* libmbgl-core.a */; };
400533011DB0862B0069F638 /* NSArray+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */; };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These files are missing from macos.xcodeproj. Also, NSArray+MGLAdditions.mm appears to be in Sources twice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix macos. I used the standard mechanism in xcode for adding files. I guess I'll manually edit the file but seems strange that I need to do this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this is common. I think every impl file is in there twice and often headers are too. Look just below:
404C26E21D89B877000AA13D /* MGLTileSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E01D89B877000AA13D /* MGLTileSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; 404C26E31D89B877000AA13D /* MGLTileSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E01D89B877000AA13D /* MGLTileSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; 404C26E41D89B877000AA13D /* MGLTileSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = 404C26E11D89B877000AA13D /* MGLTileSet.mm */; }; 404C26E51D89B877000AA13D /* MGLTileSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = 404C26E11D89B877000AA13D /* MGLTileSet.mm */; };

Maybe this is a bug related to adding to both static and dynamic targets?

I'm not going to touch this in this PR. Let's open a new issue to clean this up if we need to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to macos in c4ea541

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you’re right, I thought I was looking at the project structure but I was looking at target membership instead.

Regarding the macOS side, adding a file to ios.xcodeproj won’t automatically add it to macos.xcodeproj. I realize it’s a bit inconvenient. #5942 was an attempt at merging the two projects so that you always get presented with all three targets when adding a file. Unfortunately it slowed down the index too much for comfort.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's fine. I just forgot

This adds a features initializer to MGLGeoJSONSource. The initializer
takes shapes and converts them to JSON representation before passing
to core. This also adds methods to the MGLShape concrete subclasses
so that they can represent themselves in NSDictionary form suitable
for transforming to JSON (GeoJSON).
@boundsj boundsj force-pushed the ituaijagbone-geojson-with-features branch from c4ea541 to ac22db1 Compare October 14, 2016 14:29
@boundsj
Copy link
Contributor

boundsj commented Oct 14, 2016

Noting that although the Qt and Node builds show that they were unsuccessful they actually passed if you click through to bitrise.

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whew!

MGLShape subclasses can now return NSDictionaries that represent the shapes' GeoJSON geometries. MGLFeature classes can now return NSDictionaries that represent the features as GeoJSON features. This makes it trivial to serialize iOS and macOS shapes and features into GeoJSON.

MGLFeature instances can also return a representation of themselves as an mbgl::Feature. This capability is used in a refactoring of the implementations of MGLGeoJSONSource to more efficiently transform MGLFeatures into mbgl::Features so they can be fed directly into mbgl::GeoJSONSource without having to round trip through NSJSONSerialization. The MGLFeature identifier is converted into the mbgl::identifier type based on the type of the identifier. The MGLFeature attributes are recursively converted into an mbgl::PropertyMap and each value is converted into one of the types that the PropertyMap's Value variant supports.
@boundsj boundsj force-pushed the ituaijagbone-geojson-with-features branch from ac22db1 to 6ffe51b Compare October 14, 2016 21:10
@boundsj boundsj merged commit 0a8858f into master Oct 14, 2016
@boundsj boundsj deleted the ituaijagbone-geojson-with-features branch October 14, 2016 21:59
@boundsj
Copy link
Contributor

boundsj commented Oct 14, 2016

image

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS runtime styling
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MGLGeoJSONSource should have a features initializer
4 participants