Skip to content

Commit

Permalink
Added ShapeSource.features and features can throw if source is not ye…
Browse files Browse the repository at this point in the history
…t available
  • Loading branch information
mfazekas committed Jun 9, 2020
1 parent bb7b8d5 commit 6000aa3
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@
import android.graphics.drawable.BitmapDrawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;
import androidx.core.content.res.ResourcesCompat;

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.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
import com.mapbox.rctmgl.R;
import com.mapbox.rctmgl.components.mapview.RCTMGLMapView;
import com.mapbox.rctmgl.events.AndroidCallbackEvent;
import com.mapbox.rctmgl.events.FeatureClickEvent;
import com.mapbox.rctmgl.utils.DownloadMapImageTask;
import com.mapbox.rctmgl.utils.ImageEntry;
Expand Down Expand Up @@ -148,4 +154,21 @@ private GeoJsonOptions getOptions() {

return options;
}

public void querySourceFeatures(String callbackID,
@Nullable Expression filter) {
if (mSource == null) {
WritableMap payload = new WritableNativeMap();
payload.putString("error", "source is not yet loaded");
AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload);
mManager.handleEvent(event);
return;
}
List<Feature> features = mSource.querySourceFeatures(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
Expand Up @@ -7,6 +7,8 @@
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
Expand All @@ -21,6 +23,7 @@
import com.mapbox.rctmgl.components.mapview.RCTMGLMapView;
import com.mapbox.rctmgl.components.styles.layers.RCTLayer;
import com.mapbox.rctmgl.events.constants.EventKeys;
import com.mapbox.rctmgl.utils.ExpressionParser;
import com.mapbox.rctmgl.utils.ImageEntry;
import com.mapbox.rctmgl.utils.ResourceUtils;

Expand Down Expand Up @@ -139,6 +142,30 @@ public void setHitbox(RCTMGLShapeSource source, ReadableMap map) {
public Map<String, String> customEvents() {
return MapBuilder.<String, String>builder()
.put(EventKeys.SHAPE_SOURCE_LAYER_CLICK, "onMapboxShapeSourcePress")
.put(EventKeys.MAP_ANDROID_CALLBACK, "onAndroidCallback")
.build();
}

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

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

@Override
public void receiveCommand(RCTMGLShapeSource source, int commandID, @Nullable ReadableArray args) {
switch (commandID) {
case METHOD_FEATURES:
source.querySourceFeatures(
args.getString(0),
ExpressionParser.from(args.getArray(1))
);
break;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ public VectorSource makeSource() {
public void querySourceFeatures(String callbackID,
@Size(min = 1) List<String> layerIDs,
@Nullable Expression filter) {
if (mSource == null) {
WritableMap payload = new WritableNativeMap();
payload.putString("error", "source is not yet loaded");
AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload);
mManager.handleEvent(event);
return;
}
List<Feature> features = mSource.querySourceFeatures(layerIDs.toArray(new String[layerIDs.size()]), filter);
WritableMap payload = new WritableNativeMap();
payload.putString("data", FeatureCollection.fromFeatures(features).toJson());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@

import java.util.Locale;

import javax.annotation.Nullable;

public class ExpressionParser {
static final String TYPE_STRING = "string";
static final String TYPE_ARRAY = "array";
static final String TYPE_NUMBER = "number";
static final String TYPE_MAP = "hashmap";
static final String TYPE_BOOL = "boolean";

public static Expression from(ReadableArray rawExpressions) {
public static @Nullable Expression from(@Nullable ReadableArray rawExpressions) {
if (rawExpressions == null || rawExpressions.size() == 0) {
return null;
}
Expand Down
16 changes: 16 additions & 0 deletions docs/ShapeSource.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@
| &nbsp;&nbsp;height | `number` | `none` | `true` | FIX ME NO DESCRIPTION |

### methods
#### features([filter])

Returns all features from the source that match the query parameters regardless of whether or not the feature is<br/>currently rendered on the map.

##### arguments
| Name | Type | Required | Description |
| ---- | :--: | :------: | :----------: |
| `filter` | `Array` | `No` | an optional filter statement to filter the returned Features. |



```javascript
shapeSource.features()
```


#### onPress(event)


Expand Down
27 changes: 27 additions & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3530,6 +3530,33 @@
"description": "ShapeSource is a map content source that supplies vector shapes to be shown on the map.\nThe shape may be a url or a GeoJSON object",
"displayName": "ShapeSource",
"methods": [
{
"name": "features",
"docblock": "Returns all features from the source that match the query parameters regardless of whether or not the feature is\ncurrently rendered on the map.\n\n@example\nshapeSource.features()\n\n@param {Array=} filter - an optional filter statement to filter the returned Features.\n@return {FeatureCollection}",
"modifiers": [
"async"
],
"params": [
{
"name": "filter",
"description": "an optional filter statement to filter the returned Features.",
"type": {
"name": "Array"
},
"optional": true
}
],
"returns": {
"description": null,
"type": {
"name": "FeatureCollection"
}
},
"description": "Returns all features from the source that match the query parameters regardless of whether or not the feature is\ncurrently rendered on the map.",
"examples": [
"\nshapeSource.features()\n\n"
]
},
{
"name": "onPress",
"docblock": null,
Expand Down
15 changes: 10 additions & 5 deletions javascript/components/NativeBridgeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const NativeBridgeComponent = (B) =>
this._preRefMapMethodQueue = [];
}

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

_removeAndroidCallback(id) {
Expand All @@ -30,7 +30,12 @@ const NativeBridgeComponent = (B) =>
}

this._callbackMap.delete(callbackID);
callback.call(null, e.nativeEvent.payload);
let {payload} = e.nativeEvent;
if (payload.error) {
callback.reject.call(null, new Error(payload.error));
} else {
callback.resolve.call(null, payload);
}
}

async _runPendingNativeCommands(nativeRef) {
Expand Down Expand Up @@ -61,10 +66,10 @@ const NativeBridgeComponent = (B) =>
}

if (isAndroid()) {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
callbackIncrement += 1;
const callbackID = `${methodName}_${callbackIncrement}`;
this._addAddAndroidCallback(callbackID, resolve);
this._addAddAndroidCallback(callbackID, resolve, reject);
args.unshift(callbackID);
runNativeCommand(this._nativeModuleName, methodName, nativeRef, args);
});
Expand Down
38 changes: 37 additions & 1 deletion javascript/components/ShapeSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import React from 'react';
import PropTypes from 'prop-types';
import {NativeModules, requireNativeComponent} from 'react-native';

import {getFilter} from '../utils/filterUtils';
import {
toJSONString,
cloneReactChildrenWithProps,
viewPropTypes,
isFunction,
isAndroid,
} from '../utils';
import {copyPropertiesAsDeprecated} from '../utils/deprecation';

import AbstractSource from './AbstractSource';
import NativeBridgeComponent from './NativeBridgeComponent';

const MapboxGL = NativeModules.MGLModule;

Expand All @@ -20,7 +23,7 @@ export const NATIVE_MODULE_NAME = 'RCTMGLShapeSource';
* ShapeSource is a map content source that supplies vector shapes to be shown on the map.
* The shape may be a url or a GeoJSON object
*/
class ShapeSource extends AbstractSource {
class ShapeSource extends NativeBridgeComponent(AbstractSource) {
static NATIVE_ASSETS_KEY = 'assets';

static propTypes = {
Expand Down Expand Up @@ -107,6 +110,37 @@ class ShapeSource extends AbstractSource {
id: MapboxGL.StyleSource.DefaultSourceID,
};

constructor(props) {
super(props, NATIVE_MODULE_NAME);
}

_setNativeRef(nativeRef) {
this._nativeRef = nativeRef;
super._runPendingNativeCommands(nativeRef);
}

/**
* Returns all features from the source that match the query parameters regardless of whether or not the feature is
* currently rendered on the map.
*
* @example
* shapeSource.features()
*
* @param {Array=} filter - an optional filter statement to filter the returned Features.
* @return {FeatureCollection}
*/
async features(filter = []) {
const res = await this._runNativeCommand('features', this._nativeRef, [
getFilter(filter),
]);

if (isAndroid()) {
return JSON.parse(res.data);
}

return res.data;
}

setNativeProps(props) {
const shallowProps = Object.assign({}, props);

Expand Down Expand Up @@ -169,6 +203,8 @@ class ShapeSource extends AbstractSource {
buffer: this.props.buffer,
tolerance: this.props.tolerance,
onPress: undefined,
ref: (nativeRef) => this._setNativeRef(nativeRef),
onAndroidCallback: isAndroid() ? this._onAndroidCallback : undefined,
};

return (
Expand Down

0 comments on commit 6000aa3

Please sign in to comment.