Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement VectorSource#features #308

Merged
merged 3 commits into from
Aug 13, 2019
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
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package com.mapbox.rctmgl.components.styles.sources;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.annotation.Size;

import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import com.mapbox.rctmgl.events.AndroidCallbackEvent;
import com.mapbox.rctmgl.events.FeatureClickEvent;

import java.util.List;

/**
* Created by nickitaliano on 9/8/17.
*/
Expand Down Expand Up @@ -34,4 +43,15 @@ public VectorSource makeSource() {
}
return new VectorSource(mID, mURL);
}

public void querySourceFeatures(String callbackID,
@Size(min = 1) List<String> layerIDs,
@Nullable Expression filter) {
List<Feature> features = mSource.querySourceFeatures(layerIDs.toArray(new String[layerIDs.size()]), filter);
WritableMap payload = new WritableNativeMap();
payload.putString("data", FeatureCollection.fromFeatures(features).toJson());

AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload);
mManager.handleEvent(event);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.mapbox.rctmgl.components.styles.sources;

import android.support.annotation.Nullable;
import android.view.View;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.mapbox.rctmgl.components.AbstractEventEmitter;
import com.mapbox.rctmgl.components.mapview.RCTMGLMapView;
import com.mapbox.rctmgl.events.constants.EventKeys;
import com.mapbox.rctmgl.utils.ConvertUtils;
import com.mapbox.rctmgl.utils.ExpressionParser;

import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -79,6 +83,32 @@ public void setHitbox(RCTMGLVectorSource source, ReadableMap map) {
public Map<String, String> customEvents() {
return MapBuilder.<String, String>builder()
.put(EventKeys.VECTOR_SOURCE_LAYER_CLICK, "onMapboxVectorSourcePress")
.put(EventKeys.MAP_ANDROID_CALLBACK, "onAndroidCallback")
.build();
}

//region React Methods
public static final int METHOD_FEATURES = 102;

@Nullable
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.<String, Integer>builder()
.put("features", METHOD_FEATURES)
.build();
}

@Override
public void receiveCommand(RCTMGLVectorSource vectorSource, int commandID, @Nullable ReadableArray args) {

switch (commandID) {
case METHOD_FEATURES:
vectorSource.querySourceFeatures(
args.getString(0),
ConvertUtils.toStringList(args.getArray(1)),
ExpressionParser.from(args.getArray(2))
);
break;
}
}
}
21 changes: 21 additions & 0 deletions example/src/examples/CustomVectorSource.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import MapboxGL from '@react-native-mapbox-gl/maps';
import {Text} from 'react-native';

import sheet from '../styles/sheet';

import BaseExamplePropTypes from './common/BaseExamplePropTypes';
import Page from './common/Page';
import Bubble from './common/Bubble';

const styles = {
boxFill: {
Expand All @@ -30,7 +32,19 @@ class CustomVectorSource extends React.PureComponent {
...BaseExamplePropTypes,
};

state = {
featuresCount: null,
};

queryFeatures = async () => {
const features = await this._vectorSource.features([
'react-native-example',
]);
this.setState({featuresCount: features.features.length});
};

render() {
const {featuresCount} = this.state;
return (
<Page {...this.props}>
<MapboxGL.MapView style={sheet.matchParent}>
Expand All @@ -42,6 +56,9 @@ class CustomVectorSource extends React.PureComponent {
<MapboxGL.VectorSource
id="customSourceExample"
url={VECTOR_SOURCE_URL}
ref={source => {
this._vectorSource = source;
}}
>
<MapboxGL.FillLayer
id="customSourceFill"
Expand All @@ -50,6 +67,10 @@ class CustomVectorSource extends React.PureComponent {
/>
</MapboxGL.VectorSource>
</MapboxGL.MapView>
<Bubble onPress={this.queryFeatures}>
<Text>Query features:</Text>
{featuresCount && <Text>Count: {featuresCount}</Text>}
</Bubble>
</Page>
);
}
Expand Down
2 changes: 2 additions & 0 deletions ios/RCTMGL/RCTMGLVectorSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@

@property (nonatomic, copy) NSString *url;

- (NSArray<id <MGLFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate;

@end
7 changes: 7 additions & 0 deletions ios/RCTMGL/RCTMGLVectorSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,11 @@ - (MGLSource*)makeSource
return [[MGLVectorTileSource alloc] initWithIdentifier:self.id configurationURL:[NSURL URLWithString:_url]];
}

- (NSArray<id <MGLFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate
{
MGLVectorTileSource* vectorSource = (MGLVectorTileSource*)self.source;

return [vectorSource featuresInSourceLayersWithIdentifiers:sourceLayerIdentifiers predicate: predicate];
}

@end
3 changes: 2 additions & 1 deletion ios/RCTMGL/RCTMGLVectorSourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
//

#import "ViewManager.h"
#import <React/RCTBridgeModule.h>

@interface RCTMGLVectorSourceManager : ViewManager
@interface RCTMGLVectorSourceManager : ViewManager<RCTBridgeModule>

@end
46 changes: 45 additions & 1 deletion ios/RCTMGL/RCTMGLVectorSourceManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
// Copyright © 2017 Mapbox Inc. All rights reserved.
//

#import <React/RCTUIManager.h>

#import "RCTMGLVectorSourceManager.h"
#import "RCTMGLVectorSource.h"

#import "FilterParser.h"

@implementation RCTMGLVectorSourceManager

RCT_EXPORT_MODULE();
RCT_EXPORT_MODULE(RCTMGLVectorSource);

RCT_EXPORT_VIEW_PROPERTY(id, NSString);

Expand All @@ -25,4 +29,44 @@ - (UIView*)view
RCT_REMAP_VIEW_PROPERTY(onMapboxVectorSourcePress, onPress, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(hitbox, NSDictionary)


RCT_EXPORT_METHOD(features:(nonnull NSNumber*)reactTag
withLayerIDs:(NSArray<NSString*>*)layerIDs
withFilter:(NSArray<NSDictionary<NSString *, id> *> *)filter
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary<NSNumber*, UIView*> *viewRegistry) {
RCTMGLVectorSource* vectorSource = viewRegistry[reactTag];

if (![vectorSource isKindOfClass:[RCTMGLVectorSource class]]) {
RCTLogError(@"Invalid react tag, could not find RCTMGLMapView");
return;
}

NSSet* layerIDSet = nil;
if (layerIDs != nil && layerIDs.count > 0) {
layerIDSet = [NSSet setWithArray:layerIDs];
}
NSPredicate* predicate = [FilterParser parse:filter];
NSArray<id<MGLFeature>> *shapes = [vectorSource
featuresInSourceLayersWithIdentifiers: layerIDSet
predicate: predicate];

NSMutableArray<NSDictionary*> *features = [[NSMutableArray alloc] initWithCapacity:shapes.count];
for (int i = 0; i < shapes.count; i++) {
[features addObject:shapes[i].geoJSONDictionary];
}

resolve(@{
@"data": @{ @"type": @"FeatureCollection", @"features": features }
});
}];
}

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}

@end
2 changes: 1 addition & 1 deletion javascript/components/MapView.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const styles = StyleSheet.create({
/**
* MapView backed by Mapbox Native GL
*/
class MapView extends NativeBridgeComponent {
class MapView extends NativeBridgeComponent(React.Component) {
static propTypes = {
...viewPropTypes,

Expand Down
123 changes: 62 additions & 61 deletions javascript/components/NativeBridgeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,81 @@ import React from 'react';

import {runNativeCommand, isAndroid} from '../utils';

class NativeBridgeComponent extends React.Component {
static callbackIncrement = 0;
let callbackIncrement = 0;

constructor(props, nativeModuleName) {
super(props);
const NativeBridgeComponent = B =>
class extends B {
constructor(props, nativeModuleName) {
super(props);

this._nativeModuleName = nativeModuleName;
this._onAndroidCallback = this._onAndroidCallback.bind(this);
this._callbackMap = new Map();
this._preRefMapMethodQueue = [];
}
this._nativeModuleName = nativeModuleName;
this._onAndroidCallback = this._onAndroidCallback.bind(this);
this._callbackMap = new Map();
this._preRefMapMethodQueue = [];
}

_addAddAndroidCallback(id, callback) {
this._callbackMap.set(id, callback);
}

_addAddAndroidCallback(id, callback) {
this._callbackMap.set(id, callback);
}
_removeAndroidCallback(id) {
this._callbackMap.remove(id);
}

_removeAndroidCallback(id) {
this._callbackMap.remove(id);
}
_onAndroidCallback(e) {
const callbackID = e.nativeEvent.type;
const callback = this._callbackMap.get(callbackID);

_onAndroidCallback(e) {
const callbackID = e.nativeEvent.type;
const callback = this._callbackMap.get(callbackID);
if (!callback) {
return;
}

if (!callback) {
return;
this._callbackMap.delete(callbackID);
callback.call(null, e.nativeEvent.payload);
}

this._callbackMap.delete(callbackID);
callback.call(null, e.nativeEvent.payload);
}

async _runPendingNativeCommands(nativeRef) {
if (nativeRef)
while (this._preRefMapMethodQueue.length > 0) {
const item = this._preRefMapMethodQueue.pop();
async _runPendingNativeCommands(nativeRef) {
if (nativeRef)
while (this._preRefMapMethodQueue.length > 0) {
const item = this._preRefMapMethodQueue.pop();

if (item && item.method && item.resolver) {
const res = await this._runNativeCommand(
item.method.name,
nativeRef,
item.method.args,
);
item.resolver(res);
if (item && item.method && item.resolver) {
const res = await this._runNativeCommand(
item.method.name,
nativeRef,
item.method.args,
);
item.resolver(res);
}
}
}
}
}

_runNativeCommand(methodName, nativeRef, args = []) {
if (!nativeRef) {
return new Promise(resolve => {
this._preRefMapMethodQueue.push({
method: {name: methodName, args},
resolver: resolve,
_runNativeCommand(methodName, nativeRef, args = []) {
if (!nativeRef) {
return new Promise(resolve => {
this._preRefMapMethodQueue.push({
method: {name: methodName, args},
resolver: resolve,
});
});
});
}
}

if (isAndroid()) {
return new Promise(resolve => {
NativeBridgeComponent.callbackIncrement++;
const callbackID = `${methodName}_${NativeBridgeComponent.callbackIncrement}`;
this._addAddAndroidCallback(callbackID, resolve);
args.unshift(callbackID);
runNativeCommand(this._nativeModuleName, methodName, nativeRef, args);
});
if (isAndroid()) {
return new Promise(resolve => {
callbackIncrement += 1;
const callbackID = `${methodName}_${callbackIncrement}`;
this._addAddAndroidCallback(callbackID, resolve);
args.unshift(callbackID);
runNativeCommand(this._nativeModuleName, methodName, nativeRef, args);
});
}
return runNativeCommand(
this._nativeModuleName,
methodName,
nativeRef,
args,
);
}
return runNativeCommand(
this._nativeModuleName,
methodName,
nativeRef,
args,
);
}
}
};

export default NativeBridgeComponent;
Loading