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

[ios, macos] fixes #5962 added geojson options to support clustering #6217

Merged
merged 2 commits into from
Sep 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions platform/darwin/src/MGLGeoJSONSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,50 @@ NS_ASSUME_NONNULL_BEGIN

@protocol MGLFeature;

/**
An `NSNumber` object containing a Boolean enabling or disabling clustering.
If the `features` property contains point features, setting this option to
`YES` clusters the points by radius into groups. The default value is `NO`.
*/
extern NSString * const MGLGeoJSONClusterOption;

/**
An `NSNumber` object containing an integer; specifies the radius of each
cluster when clustering points, measured in <sup>1</sup>/<sub>512</sub>ths of a
tile. The default value is 50.
*/
extern NSString * const MGLGeoJSONClusterRadiusOption;

/**
An `NSNumber` object containing an integer; specifies the maximum zoom level at
which to cluster points. Defaults to one zoom level less than the value of
`MGLGeoJSONMaximumZoomLevelOption`, so that at the last zoom level, the
features are not clustered.
*/
extern NSString * const MGLGeoJSONClusterMaximumZoomLevelOption;

/**
An `NSNumber` object containing an integer; specifies the maximum zoom level at
which to create vector tiles. A greater value produces greater detail at high
zoom levels. The default value is 18.
*/
extern NSString * const MGLGeoJSONMaximumZoomLevelOption;

/**
An `NSNumber` object containing an integer; specifies the tile buffer size on
each side. This option is measured in <sup>1</sup>/<sub>512</sub>ths of a tile.
A higher value reduces rendering artifacts near tile edges but may impact
performance. The default value is 128.
*/
extern NSString * const MGLGeoJSONBufferOption;

/**
An `NSNumber` object containing a double; specifies the Douglas-Peucker
simplification tolerance. A greater value produces simpler geometries and
improves performance. The default value is 0.375.
*/
extern NSString * const MGLGeoJSONToleranceOption;

@interface MGLGeoJSONSource : MGLSource

/**
Expand Down Expand Up @@ -46,6 +90,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier geoJSONData:(NSData *)data NS_DESIGNATED_INITIALIZER;

/**
Initializes a source with the given identifier, GeoJSON data, and a dictionary of options for the source.

@param sourceIdentifier A string that uniquely identifies the source.
@param geoJSONData An NSData object representing GeoJSON source code.
@param options An NSDictionary of attributes for this source specified by the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">the style specification</a>.
*/
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier geoJSONData:(NSData *)data options:(NS_DICTIONARY_OF(NSString *, id) *)options NS_DESIGNATED_INITIALIZER;

/**
Initializes a source with the given identifier and URL.

Expand All @@ -55,6 +108,16 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url NS_DESIGNATED_INITIALIZER;

/**
Initializes a source with the given identifier, a URL, and a dictionary of options for the source.

@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 options An NSDictionary of attributes for this source specified by the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">the style specification</a>.
*/
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url options:(NS_DICTIONARY_OF(NSString *, id) *)options NS_DESIGNATED_INITIALIZER;

@end

NS_ASSUME_NONNULL_END
102 changes: 96 additions & 6 deletions platform/darwin/src/MGLGeoJSONSource.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,114 @@

#include <mbgl/style/sources/geojson_source.hpp>

NSString * const MGLGeoJSONClusterOption = @"MGLGeoJSONCluster";
NSString * const MGLGeoJSONClusterRadiusOption = @"MGLGeoJSONClusterRadius";
NSString * const MGLGeoJSONClusterMaximumZoomLevelOption = @"MGLGeoJSONClusterMaximumZoomLevel";
NSString * const MGLGeoJSONMaximumZoomLevelOption = @"MGLGeoJSONMaximumZoomLevel";
NSString * const MGLGeoJSONBufferOption = @"MGLGeoJSONBuffer";
NSString * const MGLGeoJSONToleranceOption = @"MGLGeoJSONOptionsClusterTolerance";

@interface MGLGeoJSONSource ()

@property (nonatomic, readwrite) NSDictionary *options;

@end

@implementation MGLGeoJSONSource

- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier geoJSONData:(NSData *)data {
if (self = [super initWithSourceIdentifier:sourceIdentifier]) {
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier geoJSONData:(NSData *)data
{
if (self = [super initWithSourceIdentifier:sourceIdentifier])
{
_geoJSONData = data;
}
return self;
}

- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier geoJSONData:(NSData *)data options:(NS_DICTIONARY_OF(NSString *, id) *)options
{
if (self = [super initWithSourceIdentifier:sourceIdentifier])
{
_geoJSONData = data;
_options = options;
}
return self;
}

- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url
{
if (self = [super initWithSourceIdentifier:sourceIdentifier])
{
_URL = url;
}
return self;
}

- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url {
if (self = [super initWithSourceIdentifier:sourceIdentifier]) {
- (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL *)url options:(NS_DICTIONARY_OF(NSString *, id) *)options
{
if (self = [super initWithSourceIdentifier:sourceIdentifier])
{
_URL = url;
_options = options;
}
return self;
}

- (std::unique_ptr<mbgl::style::Source>)mbgl_source
- (mbgl::style::GeoJSONOptions)geoJSONOptions
{
auto mbglOptions = mbgl::style::GeoJSONOptions();

if (self.options[MGLGeoJSONMaximumZoomLevelOption]) {
id value = self.options[MGLGeoJSONMaximumZoomLevelOption];
[self validateValue:value];
mbglOptions.maxzoom = [value integerValue];
}

if (self.options[MGLGeoJSONBufferOption]) {
id value = self.options[MGLGeoJSONBufferOption];
[self validateValue:value];
mbglOptions.buffer = [value integerValue];
}

if (self.options[MGLGeoJSONToleranceOption]) {
id value = self.options[MGLGeoJSONToleranceOption];
[self validateValue:value];
mbglOptions.tolerance = [value doubleValue];
}

if (self.options[MGLGeoJSONClusterRadiusOption]) {
id value = self.options[MGLGeoJSONClusterRadiusOption];
[self validateValue:value];
mbglOptions.clusterRadius = [value integerValue];
}

if (self.options[MGLGeoJSONClusterMaximumZoomLevelOption]) {
id value = self.options[MGLGeoJSONClusterMaximumZoomLevelOption];
[self validateValue:value];
mbglOptions.clusterMaxZoom = [value integerValue];
}

if (self.options[MGLGeoJSONClusterOption]) {
id value = self.options[MGLGeoJSONClusterOption];
[self validateValue:value];
mbglOptions.cluster = [value boolValue];
}

return mbglOptions;
}

- (void)validateValue:(id)value
{
if (! [value isKindOfClass:[NSNumber class]])
{
[NSException raise:@"Value not handled" format:@"%@ is not an NSNumber", value];
}
}

- (std::unique_ptr<mbgl::style::Source>)mbglSource
{
auto source = std::make_unique<mbgl::style::GeoJSONSource>(self.sourceIdentifier.UTF8String);
auto source = std::make_unique<mbgl::style::GeoJSONSource>(self.sourceIdentifier.UTF8String, [self geoJSONOptions]);

if (self.URL) {
NSURL *url = self.URL.mgl_URLByStandardizingScheme;
source->setURL(url.absoluteString.UTF8String);
Expand All @@ -35,6 +124,7 @@ - (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL
source->setGeoJSON(geojson);
_features = MGLFeaturesFromMBGLFeatures(geojson);
}

return std::move(source);
}

Expand Down
9 changes: 9 additions & 0 deletions platform/darwin/src/MGLGeoJSONSource_Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import "MGLGeoJSONSource_Private.h"

#include <mbgl/style/sources/geojson_source.hpp>

@interface MGLGeoJSONSource (Private)

- (mbgl::style::GeoJSONOptions)geoJSONOptions;

@end
2 changes: 1 addition & 1 deletion platform/darwin/src/MGLRasterSource.mm
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ - (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL
return self;
}

- (std::unique_ptr<mbgl::style::Source>)mbgl_source {
- (std::unique_ptr<mbgl::style::Source>)mbglSource {
auto source = std::make_unique<mbgl::style::RasterSource>(self.sourceIdentifier.UTF8String,
self.URL.absoluteString.UTF8String,
uint16_t(self.tileSize));
Expand Down
2 changes: 1 addition & 1 deletion platform/darwin/src/MGLSource.mm
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ - (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier {
return self;
}

- (std::unique_ptr<mbgl::style::Source>)mbgl_source {
- (std::unique_ptr<mbgl::style::Source>)mbglSource {
[NSException raise:@"Subclasses must override this method" format:@""];
return nil;
}
Expand Down
2 changes: 1 addition & 1 deletion platform/darwin/src/MGLSource_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@interface MGLSource (Private)

- (std::unique_ptr<mbgl::style::Source>)mbgl_source;
- (std::unique_ptr<mbgl::style::Source>)mbglSource;

@property (nonatomic) mbgl::style::Source *source;

Expand Down
2 changes: 1 addition & 1 deletion platform/darwin/src/MGLStyle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ - (void)insertLayer:(id <MGLStyleLayer, MGLStyleLayer_Private>)styleLayer

- (void)addSource:(MGLSource *)source
{
self.mapView.mbglMap->addSource([source mbgl_source]);
self.mapView.mbglMap->addSource([source mbglSource]);
}

- (void)removeSource:(MGLSource *)source
Expand Down
2 changes: 1 addition & 1 deletion platform/darwin/src/MGLVectorSource.mm
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ - (instancetype)initWithSourceIdentifier:(NSString *)sourceIdentifier URL:(NSURL
return self;
}

- (std::unique_ptr<mbgl::style::Source>)mbgl_source
- (std::unique_ptr<mbgl::style::Source>)mbglSource
{
auto source = std::make_unique<mbgl::style::VectorSource>(self.sourceIdentifier.UTF8String, self.URL.absoluteString.UTF8String);
return std::move(source);
Expand Down
18 changes: 18 additions & 0 deletions platform/ios/ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,12 @@
4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */; };
40CFA6511D7875BB008103BD /* MGLGeoJSONSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 40CFA6501D787579008103BD /* MGLGeoJSONSourceTests.mm */; };
40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */; };
40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
40F887701D7A1E58008ECB67 /* MGLGeoJSONSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLGeoJSONSource_Private.h */; };
40F887711D7A1E59008ECB67 /* MGLGeoJSONSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLGeoJSONSource_Private.h */; };
40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; };
554180421D2E97DE00012372 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; };
55D8C9961D0F18CE00F42F10 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D8C9951D0F18CE00F42F10 /* libsqlite3.tbd */; };
Expand Down Expand Up @@ -566,8 +569,10 @@
4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = "<group>"; };
402E9DE01CD2C76200FD4519 /* Mapbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Mapbox.playground; sourceTree = "<group>"; };
404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView_Private.h; sourceTree = "<group>"; };
40CFA6501D787579008103BD /* MGLGeoJSONSourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLGeoJSONSourceTests.mm; sourceTree = "<group>"; };
40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView.h; sourceTree = "<group>"; };
40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationContainerView.m; sourceTree = "<group>"; };
40F8876F1D7A1DB8008ECB67 /* MGLGeoJSONSource_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLGeoJSONSource_Private.h; sourceTree = "<group>"; };
40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; };
40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; };
554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -804,6 +809,7 @@
350098B91D480108004B2AF0 /* MGLVectorSource.h */,
350098BA1D480108004B2AF0 /* MGLVectorSource.mm */,
3566C7641D4A77BA008152BC /* MGLGeoJSONSource.h */,
40F8876F1D7A1DB8008ECB67 /* MGLGeoJSONSource_Private.h */,
3566C7651D4A77BA008152BC /* MGLGeoJSONSource.mm */,
3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */,
3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */,
Expand Down Expand Up @@ -880,6 +886,7 @@
children = (
3575798F1D513EF1000B822E /* Layers */,
35B8E08B1D6C8B5100E768D2 /* MGLFilterTests.mm */,
40CFA64E1D78754A008103BD /* Sources */,
);
name = Styling;
sourceTree = "<group>";
Expand Down Expand Up @@ -918,6 +925,14 @@
name = Playground;
sourceTree = "<group>";
};
40CFA64E1D78754A008103BD /* Sources */ = {
isa = PBXGroup;
children = (
40CFA6501D787579008103BD /* MGLGeoJSONSourceTests.mm */,
);
name = Sources;
sourceTree = "<group>";
};
DA1DC9411CB6C1C2006E619F = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1368,6 +1383,7 @@
35D13AB71D3D15E300AFB4E0 /* MGLStyleLayer.h in Headers */,
354D42DC1D4919F900F400A1 /* NSValue+MGLStyleAttributeAdditions_Private.h in Headers */,
DA88488E1CBB047F00AB86E3 /* reachability.h in Headers */,
40F887701D7A1E58008ECB67 /* MGLGeoJSONSource_Private.h in Headers */,
350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */,
404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */,
Expand Down Expand Up @@ -1513,6 +1529,7 @@
353933F31D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */,
3593E5271D529EDC006D9365 /* UIColor+MGLStyleAttributeAdditions_Private.h in Headers */,
3538AA1E1D542239008EC33D /* MGLBaseStyleLayer.h in Headers */,
40F887711D7A1E59008ECB67 /* MGLGeoJSONSource_Private.h in Headers */,
DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */,
DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */,
Expand Down Expand Up @@ -1846,6 +1863,7 @@
357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.m in Sources */,
357579871D502AFE000B822E /* MGLLineStyleLayerTests.m in Sources */,
357579891D502B06000B822E /* MGLCircleStyleLayerTests.m in Sources */,
40CFA6511D7875BB008103BD /* MGLGeoJSONSourceTests.mm in Sources */,
DA35A2C51CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m in Sources */,
35B8E08C1D6C8B5100E768D2 /* MGLFilterTests.mm in Sources */,
3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.m in Sources */,
Expand Down
39 changes: 39 additions & 0 deletions platform/ios/test/MGLGeoJSONSourceTests.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#import <XCTest/XCTest.h>

#import <Mapbox/Mapbox.h>
#import "MGLGeoJSONSource_Private.h"

#include <mbgl/style/sources/geojson_source.hpp>

@interface MGLGeoJSONSourceTests : XCTestCase

@end

@implementation MGLGeoJSONSourceTests

- (void)testMGLGeoJSONSourceWithOptions {
NSURL *url = [NSURL URLWithString:@"http://www.mapbox.com/source"];

NSDictionary *options = @{MGLGeoJSONClusterOption: @(YES),
MGLGeoJSONClusterRadiusOption: @42,
MGLGeoJSONClusterMaximumZoomLevelOption: @98,
MGLGeoJSONMaximumZoomLevelOption: @99,
MGLGeoJSONBufferOption: @1976,
MGLGeoJSONToleranceOption: @0.42};
MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithSourceIdentifier:@"source-id" URL:url options:options];

auto mbglOptions = [source geoJSONOptions];
XCTAssertTrue(mbglOptions.cluster);
XCTAssertEqual(mbglOptions.clusterRadius, 42);
XCTAssertEqual(mbglOptions.clusterMaxZoom, 98);
XCTAssertEqual(mbglOptions.maxzoom, 99);
XCTAssertEqual(mbglOptions.buffer, 1976);
XCTAssertEqual(mbglOptions.tolerance, 0.42);

// when the supplied option cluster value is not of the correct type
options = @{MGLGeoJSONClusterOption: @"number 1"};
source = [[MGLGeoJSONSource alloc] initWithSourceIdentifier:@"source-id" URL:url options:options];
XCTAssertThrows([source geoJSONOptions]);
}

@end