[macos, ios] query source features

This commit is contained in:
Ivo van Dongen
2017-03-05 14:12:37 -08:00
committed by Ivo van Dongen
parent 0d10d2df1c
commit 12568f21a3
10 changed files with 266 additions and 0 deletions

View File

@@ -210,6 +210,22 @@ MGL_EXPORT
*/
@property (nonatomic, copy, nullable) NSURL *URL;
/**
Returns an array of map features for this source, filtered by the given predicate.
Each object in the returned array represents a feature for the current style
and provides access to attributes specified by the source
Features come from tiled GeoJSON data that is converted to tiles internally,
so feature geometries are clipped at tile boundaries and features
may appear duplicated across tiles.
@param predicate A predicate to filter the returned features.
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
- (NS_ARRAY_OF(id <MGLFeature>) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate;
@end
NS_ASSUME_NONNULL_END

View File

@@ -5,6 +5,7 @@
#import "MGLFeature_Private.h"
#import "MGLShape_Private.h"
#import "NSPredicate+MGLAdditions.h"
#import "NSURL+MGLAdditions.h"
#include <mbgl/map/map.hpp>
@@ -135,6 +136,17 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape];
}
- (NS_ARRAY_OF(id <MGLFeature>) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate {
mbgl::optional<mbgl::style::Filter> optionalFilter;
if (predicate) {
optionalFilter = predicate.mgl_filter;
}
std::vector<mbgl::Feature> features = self.rawSource->querySourceFeatures({ {}, optionalFilter });
return MGLFeaturesFromMBGLFeatures(features);
}
@end
mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {

View File

@@ -1,3 +1,4 @@
#import "MGLFeature.h"
#import "MGLFoundation.h"
#import "MGLTileSource.h"
@@ -42,10 +43,35 @@ NS_ASSUME_NONNULL_BEGIN
MGL_EXPORT
@interface MGLVectorSource : MGLTileSource
#pragma mark Initializing a Source
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
#pragma mark Accessing a Source’s Content
/**
Returns an array of map features loaded by this source, restricted to the
given source layers and filtered by the given predicate.
Each object in the returned array represents a feature for the
current style and provides access to attributes specified by the
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">source</a>.
Features come from tiled vector data that is converted to tiles internally,
so feature geometries are clipped at tile boundaries and features
may appear duplicated across tiles.
@param sourceLayerIdentifiers The source layers to include in the query. Only the
features contained in these source layers are included in the returned array. At
least one source layer is required.
@param predicate A predicate to filter the returned features.
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
- (NS_ARRAY_OF(id <MGLFeature>) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(features(sourceLayerIdentifiers:predicate:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,8 +1,10 @@
#import "MGLVectorSource_Private.h"
#import "MGLMapView_Private.h"
#import "MGLFeature_Private.h"
#import "MGLSource_Private.h"
#import "MGLTileSource_Private.h"
#import "NSPredicate+MGLAdditions.h"
#import "NSURL+MGLAdditions.h"
#include <mbgl/map/map.hpp>
@@ -91,4 +93,25 @@
return attribution ? @(attribution->c_str()) : nil;
}
- (NS_ARRAY_OF(id <MGLFeature>) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate {
mbgl::optional<std::vector<std::string>> optionalSourceLayerIDs;
if (sourceLayerIdentifiers) {
__block std::vector<std::string> layerIDs;
layerIDs.reserve(sourceLayerIdentifiers.count);
[sourceLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop) {
layerIDs.push_back(identifier.UTF8String);
}];
optionalSourceLayerIDs = layerIDs;
}
mbgl::optional<mbgl::style::Filter> optionalFilter;
if (predicate) {
optionalFilter = predicate.mgl_filter;
}
std::vector<mbgl::Feature> features = self.rawSource->querySourceFeatures({ optionalSourceLayerIDs, optionalFilter });
return MGLFeaturesFromMBGLFeatures(features);
}
@end

View File

@@ -0,0 +1,73 @@
#import <Mapbox/Mapbox.h>
#import "NSBundle+MGLAdditions.h"
#import <XCTest/XCTest.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#import <Cocoa/Cocoa.h>
#endif
@interface MGLSourceQueryTests : XCTestCase <MGLMapViewDelegate>
@property (nonatomic) MGLMapView *mapView;
@property (nonatomic) MGLStyle *style;
@end
@implementation MGLSourceQueryTests {
XCTestExpectation *_styleLoadingExpectation;
}
- (void)setUp {
[super setUp];
[MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"query-style" withExtension:@"json"];
self.mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) styleURL:styleURL];
self.mapView.delegate = self;
if (!self.mapView.style) {
_styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
[self waitForExpectationsWithTimeout:1 handler:nil];
}
}
- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style {
XCTAssertNotNil(mapView.style);
XCTAssertEqual(mapView.style, style);
[_styleLoadingExpectation fulfill];
}
- (void)tearDown {
_styleLoadingExpectation = nil;
self.mapView = nil;
[super tearDown];
}
- (MGLStyle *)style {
return self.mapView.style;
}
- (void) testQueryVectorSource {
MGLVectorSource *source = (MGLVectorSource *)[self.style sourceWithIdentifier:@"source5"];
NSSet *sourceLayers = [NSSet setWithObjects:@"buildings", @"water", nil];
NSArray* features = [source featuresInSourceLayersWithIdentifiers:sourceLayers predicate:nil];
// Source won't be loaded yet, so features is 0
XCTAssertEqual([features count], 0);
}
- (void) testQueryShapeSource {
MGLShapeSource *source = (MGLShapeSource *)[self.style sourceWithIdentifier:@"source4"];
NSPredicate *eqPredicate = [NSPredicate predicateWithFormat:@"key1 == 'value1'"];
NSArray* features = [source featuresMatchingPredicate:eqPredicate];
// Source won't be loaded yet, so features is 0
XCTAssertEqual([features count], 0);
}
@end

View File

@@ -0,0 +1,98 @@
{
"version": 8,
"sources": {
"source1": {
"type": "geojson",
"data": {
"type": "Point",
"coordinates": [
0,
0
]
}
},
"source2": {
"type": "geojson",
"data": {
"type": "Point",
"coordinates": [
0,
0
]
}
},
"source3": {
"type": "geojson",
"data": {
"type": "Point",
"coordinates": [
0,
0
]
}
},
"source4": {
"type": "geojson",
"data": {
"type": "Feature",
"id": "feature1",
"geometry": {
"type": "Point",
"coordinates": [
0.0,
0.0
]
},
"properties": {
"key1": "value1",
"key2": 1.5,
"key3": false,
"key4": 0.5
}
}
},
"source5": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-streets-v6"
},
"source6": {
"type": "raster",
"url": "mapbox://mapbox.satellite",
"tileSize": 256
}
},
"layers": [
{
"id": "layer1",
"type": "symbol",
"source": "source1",
"layout": {
"icon-image": "test-icon"
}
},
{
"id": "layer2",
"type": "symbol",
"source": "source2",
"layout": {
"icon-image": "test-icon"
}
},
{
"id": "layer3",
"type": "symbol",
"source": "source3",
"layout": {
"icon-image": "test-icon"
}
},
{
"id": "layer4",
"type": "symbol",
"source": "source4",
"layout": {
"icon-image": "test-icon"
}
}
]
}

View File

@@ -28,6 +28,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Fixed an issue causing vector style layer predicates to be evaluated as if each feature had a `$type` attribute of 1, 2, or 3. The `$type` key path can now be compared to `Point`, `LineString`, or `Polygon`, as described in the documentation. ([#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
* When setting an `MGLShapeSource`’s shape to an `MGLFeature` instance, any `UIColor` attribute value is now converted to the equivalent CSS string representation for use with `MGLInterpolationModeIdentity` in style functions. ([#8025](https://github.com/mapbox/mapbox-gl-native/pull/8025))
* An exception is no longer thrown if layers or sources are removed from a style before they are added. ([#7962](https://github.com/mapbox/mapbox-gl-native/pull/7962))
* Added feature querying on vector and GeoJSON sources [#8263](https://github.com/mapbox/mapbox-gl-native/pull/8263)
### User interaction

View File

@@ -186,6 +186,8 @@
7E016D851D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */; };
7E016D861D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; };
7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; };
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */; };
9221B2F11E6F9D1400A2385E /* query-style.json in Resources */ = {isa = PBXBuildFile; fileRef = 9221B2F01E6F9D1400A2385E /* query-style.json */; };
968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3557F7AE1E1D27D300CCA5E6 /* MGLDistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
96E027231E57C76E004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027251E57C76E004B8E66 /* Localizable.strings */; };
DA00FC8E1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -640,6 +642,8 @@
7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolyline+MGLAdditions.m"; sourceTree = "<group>"; };
7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = "<group>"; };
7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolygon+MGLAdditions.m"; sourceTree = "<group>"; };
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; };
9221B2F01E6F9D1400A2385E /* query-style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "query-style.json"; path = "../../darwin/test/query-style.json"; sourceTree = "<group>"; };
9660916B1E5BBFD700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916C1E5BBFD900A9A03B /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916D1E5BBFDB00A9A03B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -1057,6 +1061,7 @@
isa = PBXGroup;
children = (
40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */,
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */,
4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */,
);
name = Sources;
@@ -1185,6 +1190,7 @@
DA2E88551CC036F400F24E7B /* Info.plist */,
DA2784FB1DF02FF4001D5B8D /* Media.xcassets */,
DA35D0871E1A6309007DED41 /* one-liner.json */,
9221B2F01E6F9D1400A2385E /* query-style.json */,
);
name = "SDK Tests";
path = test;
@@ -1963,6 +1969,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9221B2F11E6F9D1400A2385E /* query-style.json in Resources */,
DA2784FC1DF02FF4001D5B8D /* Media.xcassets in Resources */,
353BAEF71D646370009A8DA9 /* amsterdam.geojson in Resources */,
DA35D0881E1A6309007DED41 /* one-liner.json in Resources */,
@@ -2074,6 +2081,7 @@
3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */,
DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */,
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */,
DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */,
353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */,

View File

@@ -26,6 +26,7 @@
* Fixed an issue causing vector style layer predicates to be evaluated as if each feature had a `$type` attribute of 1, 2, or 3. The `$type` key path can now be compared to `Point`, `LineString`, or `Polygon`, as described in the documentation. ([#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
* When setting an `MGLShapeSource`’s shape to an `MGLFeature` instance, any `NSColor` attribute value is now converted to the equivalent CSS string representation for use with `MGLInterpolationModeIdentity` in style functions. ([#8025](https://github.com/mapbox/mapbox-gl-native/pull/8025))
* An exception is no longer thrown if layers or sources are removed from a style before they are added. ([#7962](https://github.com/mapbox/mapbox-gl-native/pull/7962))
* Added feature querying on vector and GeoJSON sources [#8263](https://github.com/mapbox/mapbox-gl-native/pull/8263)
### User interaction

View File

@@ -70,6 +70,8 @@
558F18221D0B13B100123F46 /* libmbgl-loop.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 558F18211D0B13B000123F46 /* libmbgl-loop.a */; };
55D9B4B11D005D3900C1CCE2 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D9B4B01D005D3900C1CCE2 /* libz.tbd */; };
55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */; };
920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */; };
920A3E5B1E6F8E0700C16EFC /* query-style.json in Resources */ = {isa = PBXBuildFile; fileRef = 920A3E5A1E6F8E0700C16EFC /* query-style.json */; };
96E027311E57C9A7004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027331E57C9A7004B8E66 /* Localizable.strings */; };
DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC881D5EEAC3009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC8B1D5EEAC3009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC891D5EEAC3009AABC8 /* MGLAttributionInfo.mm */; };
@@ -330,6 +332,8 @@
55D9B4B01D005D3900C1CCE2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = "<group>"; };
55FE0E8D1D100A0900FD240B /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/macos/config.xcconfig; sourceTree = "<group>"; };
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLSourceQueryTests.m; sourceTree = "<group>"; };
920A3E5A1E6F8E0700C16EFC /* query-style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "query-style.json"; path = "../../darwin/test/query-style.json"; sourceTree = "<group>"; };
966091701E5BBFF700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
966091711E5BBFF900A9A03B /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
966091721E5BBFFA00A9A03B /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -723,6 +727,7 @@
children = (
DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */,
DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */,
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */,
);
name = Sources;
sourceTree = "<group>";
@@ -957,6 +962,7 @@
DAE6C33A1CC30DB200DB3429 /* Info.plist */,
DA2784FD1DF03060001D5B8D /* Media.xcassets */,
DA35D0891E1A631B007DED41 /* one-liner.json */,
920A3E5A1E6F8E0700C16EFC /* query-style.json */,
);
name = "SDK Tests";
path = test;
@@ -1293,6 +1299,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
920A3E5B1E6F8E0700C16EFC /* query-style.json in Resources */,
35724FC41D630502002A4AB4 /* amsterdam.geojson in Resources */,
DA2784FE1DF03060001D5B8D /* Media.xcassets in Resources */,
DA35D08A1E1A631B007DED41 /* one-liner.json in Resources */,
@@ -1398,6 +1405,7 @@
DAE6C3D41CC34C9900DB3429 /* MGLOfflineRegionTests.m in Sources */,
DAE6C3D61CC34C9900DB3429 /* MGLStyleTests.mm in Sources */,
DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */,
920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */,
DA35A2B61CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */,
35C6DF871E214C1800ACA483 /* MGLDistanceFormatterTests.m in Sources */,
DAE6C3D21CC34C9900DB3429 /* MGLGeometryTests.mm in Sources */,