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

[android] bindings for aggregated cluster properties #15425

Merged
merged 5 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Expand Up @@ -1435,6 +1435,27 @@ public static Expression id() {
return new Expression("id");
}

/**
* Gets the value of a cluster property accumulated so far. Can only be used in the clusterProperties
* option of a clustered GeoJSON source.
* <p>
* Example usage:
* </p>
* <pre>
* {@code
* GeoJsonOptions options = new GeoJsonOptions()
* .withCluster(true)
* .withClusterProperty("max", max(accumulated(), get("max")).toArray(), get("mag").toArray());
* }
* </pre>
*
* @return expression
* @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-accumulated">Style specification</a>
*/
public static Expression accumulated() {
return new Expression("accumulated");
}

/**
* Gets the kernel density estimation of a pixel in a heatmap layer,
* which is a relative measure of how many data points are crowded around a particular pixel.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,33 @@ public GeoJsonOptions withClusterRadius(int clusterRadius) {
this.put("clusterRadius", clusterRadius);
return this;
}

/**
* An object defining custom properties on the generated clusters if clustering is enabled,
* aggregating values from clustered points. Has the form {"property_name": [operator, [map_expression]]} or
* {"property_name": [[operator, accumulated, expression], [map_expression]]}
*
* @param propertyName name of the property
* @param operator operator is any expression function that accepts at least 2 operands (e.g. "+" or "max") - it
* accumulates the property value from clusters/points the cluster contains. It can either be a
* string with single operator, or be an object array converted from valid expression
* @param mapExpr map expression produces the value of a single point, it shall be an object array, which is
* converted from a valid expression
* @return the current instance for chaining
*/
@NonNull
public GeoJsonOptions withClusterProperty(String propertyName, Object operator, Object mapExpr) {
tobrun marked this conversation as resolved.
Show resolved Hide resolved
if (this.containsKey("clusterProperties")) {
HashMap<String, Object[]> properties = (HashMap<String, Object[]>)(this.get("clusterProperties"));
properties.put(propertyName, new Object[]{operator, mapExpr});
properties.put(propertyName, new Object[]{operator, mapExpr});
tobrun marked this conversation as resolved.
Show resolved Hide resolved
this.put("clusterProperties", properties);
} else {
HashMap<String, Object[]> properties = new HashMap<String, Object[]>();
properties.put(propertyName, new Object[]{operator, mapExpr});
properties.put(propertyName, new Object[]{operator, mapExpr});
this.put("clusterProperties", properties);
}
tobrun marked this conversation as resolved.
Show resolved Hide resolved
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.util.Objects;

import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
import static com.mapbox.mapboxsdk.style.expressions.Expression.accumulated;
import static com.mapbox.mapboxsdk.style.expressions.Expression.concat;
import static com.mapbox.mapboxsdk.style.expressions.Expression.division;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
Expand All @@ -39,6 +41,8 @@
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
import static com.mapbox.mapboxsdk.style.expressions.Expression.max;
import static com.mapbox.mapboxsdk.style.expressions.Expression.neq;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
Expand Down Expand Up @@ -138,6 +142,9 @@ private GeoJsonSource createClusterSource() throws URISyntaxException {
.withCluster(true)
.withClusterMaxZoom(14)
.withClusterRadius(50)
.withClusterProperty("max", max(accumulated(), get("max")).toArray(), get("mag").toArray())
.withClusterProperty("sum", "+", get("mag").toArray())
.withClusterProperty("felt", "any", neq(get("felt"), literal("null")).toArray())
);
}

Expand Down Expand Up @@ -182,9 +189,9 @@ private CircleLayer createClusterLevelLayer(int level, int[][] layerColors) {
}

private SymbolLayer createClusterTextLayer() {
return new SymbolLayer("count", "earthquakes")
return new SymbolLayer("property", "earthquakes")
.withProperties(
textField(Expression.toString(get("point_count"))),
textField(concat(get("point_count"), literal(", "), get("max"))),
textSize(12f),
textColor(Color.WHITE),
textIgnorePlacement(true),
Expand Down
8 changes: 8 additions & 0 deletions platform/android/src/java_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ namespace java {

jni::jclass* Map::jclass;
jni::jmethodID* Map::getMethodId;
jni::jmethodID* Map::keySetMethodId;

jni::jclass* Set::jclass;
jni::jmethodID* Set::toArrayMethodId;

void registerNatives(JNIEnv& env) {
ObjectArray::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "[Ljava/lang/Object;")).release();
Expand All @@ -34,6 +38,10 @@ namespace java {

Map::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Map")).release();
Map::getMethodId = &jni::GetMethodID(env, *Map::jclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
Map::keySetMethodId = &jni::GetMethodID(env, *Map::jclass, "keySet", "()Ljava/util/Set;");

Set::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Set")).release();
Set::toArrayMethodId = &jni::GetMethodID(env, *Set::jclass, "toArray", "()[Ljava/lang/Object;");
}

}
Expand Down
6 changes: 6 additions & 0 deletions platform/android/src/java_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ namespace java {
struct Map {
static jni::jclass* jclass;
static jni::jmethodID* getMethodId;
static jni::jmethodID* keySetMethodId;
};

struct Set {
static jni::jclass* jclass;
static jni::jmethodID* toArrayMethodId;
};

void registerNatives(JNIEnv&);
Expand Down
15 changes: 12 additions & 3 deletions platform/android/src/style/android_conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,18 @@ class ConversionTraits<mbgl::android::Value> {
}

template <class Fn>
static optional<Error> eachMember(const mbgl::android::Value&, Fn&&) {
// TODO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome! we finally have found an use-case for this.

mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
static optional<Error> eachMember(const mbgl::android::Value& value, Fn&& fn) {
assert(value.isObject());
mbgl::android::Value keys = value.keyArray();
std::size_t length = arrayLength(keys);
for(std::size_t i = 0; i < length; ++i){
const auto k = keys.get(i).toString();
auto v = value.get(k.c_str());
optional<Error> result = fn(k, std::move(v));
if (result) {
return result;
}
}
return {};
}

Expand Down
6 changes: 6 additions & 0 deletions platform/android/src/style/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ namespace android {
return Value(env, jni::Local<jni::Object<>>(env, member));
}

Value Value::keyArray() const{
jni::jobject* set = jni::CallMethod<jni::jobject*>(env, value.get(), *java::Map::keySetMethodId);
jni::jobject* array = jni::CallMethod<jni::jobject*>(env, set, *java::Set::toArrayMethodId);
return Value(env, jni::Local<jni::Object<>>(env, array));
}

int Value::getLength() const {
auto array = (jni::jarray<jni::jobject>*) value.get();
return jni::GetArrayLength(env, *array);
Expand Down
1 change: 1 addition & 0 deletions platform/android/src/style/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Value {
long toLong() const;
bool toBool() const;
Value get(const char* key) const;
Value keyArray() const;
int getLength() const;
Value get(const int index ) const;

Expand Down