Files
MapLibreParticleLab/platform/darwin/test/MGLShapeSourceTests.mm
Minh Nguyễn ef71597932 [ios, macos] Simplify MGLSource and subclasses (#7377)
* [ios, macos] Audited source headers for nullability

* [macos] Made MGLTileSet public

* [ios, macos] Replaced MGLTileSet with MGLTileSource

MGLRasterSource and MGLVectorSource now share a common abstract superclass, MGLTileSource. MGLTileSet has been removed. MGLTileSource is modeled after mbgl::style::RasterSource and mbgl::style::VectorSource. It has initializers that incorporate the parameters of MGLTileSet’s initializers, but it lacks getters for everything but the attribution string. MGLTileSet’s properties have been converted into options that can be passed into MGLTileSource’s initializers in a dictionary.

Properly implement rawSource as a covariant property so that it doesn’t end up getting autosynthesized as a shadow ivar. This prevents concrete subclasses of MGLSource from setting _rawSource directly but getting a different value out of self.rawSource.

Marked -[MGLSource init] as unavailable and ensured that concrete subclasses of MGLSource have the right set of initializers marked as designated initializers.

Documentation comments for each concrete source class identify the corresponding source type in the style specification. Clarified the purpose of MGLTileSetScheme, now known as MGLTileCoordinateSystem.

* [ios, macos] Clarified tile size interpretation

Sticking to a default value of 256 for mapbox: URLs, but other URLs get the standard 512 value.

* [ios, macos] rawSource is always set

* [ios, macos] Cleaned up MGLShapeSource initialization

rawSource is never nil, so there’s no need for a -commonInit method. Extracted -geoJSONOptions from MGLShapeSource into a standalone function for easier testing.

* [ios, macos] Synchronized headers in project

Realphabetized headers in groups. Added headers missing from one project or the other.

* [ios, macos] Added MGLShape methods to (de)serialize GeoJSON data

Added a class initializer and instance method to MGLShape that deserialize and serialize the shape as GeoJSON data, respectively. The new initializer handles parsing errors gracefully.

Removed methods specific to GeoJSON data from MGLShapeSource, in an effort to reduce parallel state. Developers are now expected to go through the new MGLShape initializer to get an MGLShape representation. Alternatively, a local file URL can be passed into the other MGLShapeSource initializer.

* [ios, macos] Typo in assertion message

* [ios, macos] Simplified GeoJSON serialization

Every MGLShape now knows its most specific mbgl::GeoJSON representation.

* [ios, macos] Reremoved MGLFeaturePrivate

mbgl::GeoJSON, which is a variant, allows a single GeoJSON representation method to traffic in whatever type is needed for a particular shape class. This change removes some hidden private protocols, which are a bug waiting to happen.

* [ios, macos] Fixed covariant rawLayer property

Properly implement rawLayer as a covariant property so that it doesn’t end up getting autosynthesized as a shadow ivar. This prevents concrete subclasses of MGLStyleLayer from setting _rawLayer directly but getting a different value out of self.rawLayer.

* [ios, macos] Use MGLAttributionInfo for source attribution

Made MGLAttributionInfo public. Replaced MGLTileSource’s attribution property with an attributionInfos property set to an array of MGLAttributionInfo objects. Added an MGLTileSourceOption for specifying an array of MGLAttributionInfo objects instead of an HTML string (either is acceptable when creating an MGLTileSource).

* [ios, macos] Corrected method references in documentation
2016-12-13 17:36:15 -08:00

269 lines
14 KiB
Plaintext

#import <XCTest/XCTest.h>
#import <Mapbox/Mapbox.h>
#import "MGLFeature_Private.h"
#import "MGLShapeSource_Private.h"
#import "MGLSource_Private.h"
#include <mbgl/style/sources/geojson_source.hpp>
@interface MGLShapeSourceTests : XCTestCase
@end
@implementation MGLShapeSourceTests
- (void)testGeoJSONOptionsFromDictionary {
NSDictionary *options = @{MGLShapeSourceOptionClustered: @YES,
MGLShapeSourceOptionClusterRadius: @42,
MGLShapeSourceOptionMaximumZoomLevelForClustering: @98,
MGLShapeSourceOptionMaximumZoomLevel: @99,
MGLShapeSourceOptionBuffer: @1976,
MGLShapeSourceOptionSimplificationTolerance: @0.42};
auto mbglOptions = MGLGeoJSONOptionsFromDictionary(options);
XCTAssertTrue(mbglOptions.cluster);
XCTAssertEqual(mbglOptions.clusterRadius, 42);
XCTAssertEqual(mbglOptions.clusterMaxZoom, 98);
XCTAssertEqual(mbglOptions.maxzoom, 99);
XCTAssertEqual(mbglOptions.buffer, 1976);
XCTAssertEqual(mbglOptions.tolerance, 0.42);
options = @{MGLShapeSourceOptionClustered: @"number 1"};
XCTAssertThrows(MGLGeoJSONOptionsFromDictionary(options));
}
- (void)testNilShape {
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"id" shape:nil options:nil];
XCTAssertNil(source.shape);
}
- (void)testMGLShapeSourceWithDataMultipleFeatures {
NSString *geoJSON = @"{\"type\": \"FeatureCollection\",\"features\": [{\"type\": \"Feature\",\"properties\": {},\"geometry\": {\"type\": \"LineString\",\"coordinates\": [[-107.75390625,40.329795743702064],[-104.34814453125,37.64903402157866]]}}]}";
NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
MGLShape *shape = [MGLShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
XCTAssertNil(error);
XCTAssertNotNil(shape);
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shape:shape options:nil];
MGLShapeCollection *collection = (MGLShapeCollection *)source.shape;
XCTAssertNotNil(collection);
XCTAssertEqual(collection.shapes.count, 1);
XCTAssertTrue([collection.shapes.firstObject isMemberOfClass:[MGLPolylineFeature class]]);
}
- (void)testMGLShapeSourceWithSingleGeometry {
NSData *data = [@"{\"type\": \"Point\", \"coordinates\": [0, 0]}" dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
MGLShape *shape = [MGLShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
XCTAssertNil(error);
XCTAssertNotNil(shape);
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"geojson" shape:shape options:nil];
XCTAssertNotNil(source.shape);
XCTAssert([source.shape isKindOfClass:[MGLPointAnnotation class]]);
}
- (void)testMGLGeoJSONSourceWithSingleFeature {
NSString *geoJSON = @"{\"type\": \"Feature\", \"properties\": {\"color\": \"green\"}, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ -114.06847000122069, 51.050459433092655 ] }}";
NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
MGLShape *shape = [MGLShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error];
XCTAssertNil(error);
XCTAssertNotNil(shape);
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"geojson" shape:shape options:nil];
XCTAssertNotNil(source.shape);
XCTAssert([source.shape isKindOfClass:[MGLPointFeature class]]);
MGLPointFeature *feature = (MGLPointFeature *)source.shape;
XCTAssert([feature.attributes.allKeys containsObject:@"color"]);
}
- (void)testMGLShapeSourceWithPolylineFeatures {
CLLocationCoordinate2D coordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates count:2];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shape:polylineFeature options:nil];
XCTAssertNotNil(source.shape);
XCTAssertTrue([source.shape isMemberOfClass:[MGLPolylineFeature class]]);
}
- (void)testMGLShapeSourceWithPolygonFeatures {
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:5];
polygonFeature.identifier = @"feature-id";
NSString *stringAttribute = @"string";
NSNumber *boolAttribute = [NSNumber numberWithBool:YES];
NSNumber *doubleAttribute = [NSNumber numberWithDouble:1.23];
NSDictionary *nestedDictionaryValue = @{@"nested-key-1": @"nested-string-value"};
NSArray *arrayValue = @[@"string-value", @2];
NSDictionary *dictionaryValue = @{@"key-1": @"string-value",
@"key-2": @1,
@"key-3": nestedDictionaryValue,
@"key-4": arrayValue};
NSArray *arrayOfArrays = @[@[@1, @"string-value", @[@"jagged"]]];
NSArray *arrayOfDictionaries = @[@{@"key": @"value"}];
polygonFeature.attributes = @{@"name": stringAttribute,
@"bool": boolAttribute,
@"double": doubleAttribute,
@"dictionary-attribute": dictionaryValue,
@"array-attribute": arrayValue,
@"array-of-array-attribute": arrayOfArrays,
@"array-of-dictionary-attribute": arrayOfDictionaries};
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shape:polygonFeature options:nil];
XCTAssertNotNil(source.shape);
MGLPolygonFeature *expectedPolygonFeature = (MGLPolygonFeature *)source.shape;
XCTAssertEqualObjects(expectedPolygonFeature.identifier, polygonFeature.identifier);
XCTAssertTrue([expectedPolygonFeature isMemberOfClass:[MGLPolygonFeature class]]);
XCTAssertEqualObjects(expectedPolygonFeature.identifier, polygonFeature.identifier);
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"name"], stringAttribute);
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"bool"], boolAttribute);
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"double"], doubleAttribute);
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"dictionary-attribute"], dictionaryValue);
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-attribute"], arrayValue);
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-array-attribute"], arrayOfArrays);
XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-dictionary-attribute"], arrayOfDictionaries);
}
- (void)testMGLShapeSourceWithPolygonFeaturesInculdingInteriorPolygons {
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)};
CLLocationCoordinate2D interiorCoordinates[] = {
CLLocationCoordinate2DMake(100.2, 0.2),
CLLocationCoordinate2DMake(100.8, 0.2),
CLLocationCoordinate2DMake(100.8, 0.8),
CLLocationCoordinate2DMake(100.2, 0.8),
CLLocationCoordinate2DMake(100.2, 0.2)};
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:5];
MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shape:polygonFeature options:nil];
XCTAssertNotNil(source.shape);
XCTAssertTrue([source.shape isMemberOfClass:[MGLPolygonFeature class]]);
}
- (void)testMGLShapeSourceWithMultiPolylineFeatures {
CLLocationCoordinate2D firstCoordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
MGLPolylineFeature *firstPolylineFeature = [MGLPolylineFeature polylineWithCoordinates:firstCoordinates count:2];
CLLocationCoordinate2D secondCoordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
MGLPolylineFeature *secondPolylineFeature = [MGLPolylineFeature polylineWithCoordinates:secondCoordinates count:2];
MGLMultiPolylineFeature *multiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:@[firstPolylineFeature, secondPolylineFeature]];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shape:multiPolylineFeature options:nil];
XCTAssertNotNil(source.shape);
XCTAssertTrue([source.shape isMemberOfClass:[MGLMultiPolylineFeature class]]);
}
- (void)testMGLShapeSourceWithMultiPolygonFeatures {
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)};
CLLocationCoordinate2D interiorCoordinates[] = {
CLLocationCoordinate2DMake(100.2, 0.2),
CLLocationCoordinate2DMake(100.8, 0.2),
CLLocationCoordinate2DMake(100.8, 0.8),
CLLocationCoordinate2DMake(100.2, 0.8),
CLLocationCoordinate2DMake(100.2, 0.2)};
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:5];
MGLPolygonFeature *firstPolygon = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
MGLPolygonFeature *secondPolygon = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
MGLMultiPolygonFeature *multiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:@[firstPolygon, secondPolygon]];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shape:multiPolygonFeature options:nil];
XCTAssertNotNil(source.shape);
XCTAssertTrue([source.shape isMemberOfClass:[MGLMultiPolygonFeature class]]);
}
- (void)testMGLShapeSourceWithPointFeature {
MGLPointFeature *pointFeature = [MGLPointFeature new];
pointFeature.coordinate = CLLocationCoordinate2DMake(100.2, 0.2);
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"souce-id" shape:pointFeature options:nil];
XCTAssertNotNil(source.shape);
XCTAssertTrue([source.shape isMemberOfClass:[MGLPointFeature class]]);
}
- (void)testMGLShapeSourceWithPointCollectionFeature {
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)};
MGLPointCollectionFeature *pointCollectionFeature = [MGLPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"souce-id" shape:pointCollectionFeature options:nil];
XCTAssertNotNil(source.shape);
XCTAssertTrue([source.shape isMemberOfClass:[MGLPointCollectionFeature class]]);
}
- (void)testMGLShapeSourceWithShapeCollectionFeatures {
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)};
CLLocationCoordinate2D interiorCoordinates[] = {
CLLocationCoordinate2DMake(100.2, 0.2),
CLLocationCoordinate2DMake(100.8, 0.2),
CLLocationCoordinate2DMake(100.8, 0.8),
CLLocationCoordinate2DMake(100.2, 0.8),
CLLocationCoordinate2DMake(100.2, 0.2)};
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:5];
MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
CLLocationCoordinate2D coordinates_2[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates_2 count:2];
MGLMultiPolygonFeature *multiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:@[polygonFeature, polygonFeature]];
MGLMultiPolylineFeature *multiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:@[polylineFeature, polylineFeature]];
MGLPointCollectionFeature *pointCollectionFeature = [MGLPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
MGLPointFeature *pointFeature = [MGLPointFeature new];
pointFeature.coordinate = CLLocationCoordinate2DMake(100.2, 0.2);
MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature, pointCollectionFeature, pointFeature]];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shape:shapeCollectionFeature options:nil];
MGLShapeCollectionFeature *shape = (MGLShapeCollectionFeature *)source.shape;
XCTAssertNotNil(shape);
XCTAssert(shape.shapes.count == 6, @"Shape collection should contain 6 shapes");
}
@end