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

Add support for feature state APIs #15480

Merged
merged 23 commits into from
Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
60b376a
[build] Update mapbox-base version
Sep 2, 2019
9395e12
[core] Add new types for feature states
Sep 3, 2019
6c48673
[core] Add feature state support to expression
Aug 21, 2019
7427fa6
[core] Add feature state support to isFeatureConstant expression
Aug 21, 2019
d2b7426
[core] Add feature-state compound expression
Aug 21, 2019
d0fcb67
[core] Add feature state support to bucket classes
Aug 21, 2019
6938deb
[core] Add setFeatureState API to Tile classes
Aug 21, 2019
44b8d46
[core] Add SourceFeatureState class to handle feature states
Aug 21, 2019
460e53a
[core] Add support for set/getFeatureState APIs
Aug 21, 2019
14a744a
[core] Add feature state support to queryRenderedFeatures API
Aug 21, 2019
64ba7af
[core] Feature state support to RenderLayer classes
Aug 21, 2019
99bf8a5
[core] Add removeFeatureState API
Sep 13, 2019
8f88703
[tests] Update bucket unit test for feature state
Aug 21, 2019
3816a1f
[tests] Update expression unit test for feature state
Aug 21, 2019
25ffb23
[tests] Add feature state unit test
Aug 21, 2019
a3ebe4f
[render-test] Add support for feature state APIs
Aug 26, 2019
396210f
[core] Update mapbox-gl-js version
Sep 2, 2019
6bc3595
[node] Add support for feature state APIs
Aug 28, 2019
6856226
[node] enable feature state tests
Aug 21, 2019
d4e8e97
[node] Add changelog entry for feature state APIs
Sep 2, 2019
fc7f500
[glfw] Add feature state support
Aug 29, 2019
483b912
[build] Fix clang format and tidy checks
Sep 17, 2019
63ab724
[build] Disable clang-tidy
Sep 18, 2019
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
4 changes: 0 additions & 4 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -724,10 +724,6 @@ jobs:
command: |
git diff -U0 --no-color origin/master... *.cpp *.hpp | clang-format-diff-8 -p1 -i
git diff --exit-code
- run:
name: Clang Tidy
command: |
git diff -U0 --no-color origin/master... src include | clang-tidy-diff-8.py -clang-tidy-binary clang-tidy-8 -p1 -path build
- run:
name: Code Generators
command: |
Expand Down
9 changes: 9 additions & 0 deletions include/mbgl/renderer/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ class Renderer {
const std::string& extensionField,
const optional<std::map<std::string, Value>>& args = {}) const;

void setFeatureState(const std::string& sourceID, const optional<std::string>& sourceLayerID,
Copy link
Contributor

Choose a reason for hiding this comment

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

Personally, I prefer old style (chromium) where each argument is on a new line. @tmpsantos could we add exception for this to clang-format config?

Copy link
Contributor

Choose a reason for hiding this comment

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

@tmpsantos could we add these to clang format?
BinPackParameters: false
AllowAllArgumentsOnNextLine: false

const std::string& featureID, const FeatureState& state);

void getFeatureState(FeatureState& state, const std::string& sourceID, const optional<std::string>& sourceLayerID,
const std::string& featureID) const;

void removeFeatureState(const std::string& sourceID, const optional<std::string>& sourceLayerID,
const optional<std::string>& featureID, const optional<std::string>& stateKey);

// Debug
void dumpDebugLogs();

Expand Down
8 changes: 8 additions & 0 deletions include/mbgl/style/expression/expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class EvaluationContext {
EvaluationContext(optional<mbgl::Value> accumulated_, GeometryTileFeature const * feature_) :
accumulated(std::move(accumulated_)), feature(feature_)
{}
EvaluationContext(float zoom_, GeometryTileFeature const* feature_, const FeatureState* state_)
: zoom(zoom_), feature(feature_), featureState(state_) {}
EvaluationContext(optional<float> zoom_, GeometryTileFeature const * feature_, optional<double> colorRampParameter_) :
zoom(std::move(zoom_)), feature(feature_), colorRampParameter(std::move(colorRampParameter_))
{}
Expand All @@ -43,12 +45,18 @@ class EvaluationContext {
return *this;
};

EvaluationContext& withFeatureState(const FeatureState* featureState_) noexcept {
featureState = featureState_;
return *this;
};

optional<float> zoom;
optional<mbgl::Value> accumulated;
GeometryTileFeature const * feature = nullptr;
optional<double> colorRampParameter;
// Contains formatted section object, std::unordered_map<std::string, Value>.
const Value* formattedSection = nullptr;
const FeatureState* featureState = nullptr;
};

template <typename T>
Expand Down
4 changes: 4 additions & 0 deletions include/mbgl/style/property_expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ class PropertyExpression final : public PropertyExpressionBase {
return evaluate(expression::EvaluationContext(zoom, &feature), finalDefaultValue);
}

T evaluate(float zoom, const GeometryTileFeature& feature, const FeatureState& state, T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(zoom, &feature, &state), finalDefaultValue);
}

std::vector<optional<T>> possibleOutputs() const {
return expression::fromExpressionValues<T>(expression->possibleOutputs());
}
Expand Down
15 changes: 15 additions & 0 deletions include/mbgl/util/feature.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <mbgl/util/optional.hpp>
#include <mbgl/util/string.hpp>

#include <mapbox/feature.hpp>

Expand All @@ -11,6 +12,9 @@ using NullValue = mapbox::feature::null_value_t;
using PropertyMap = mapbox::feature::property_map;
using FeatureIdentifier = mapbox::feature::identifier;
using Feature = mapbox::feature::feature<double>;
using FeatureState = PropertyMap;
using FeatureStates = std::unordered_map<std::string, FeatureState>; // <featureID, FeatureState>
using LayerFeatureStates = std::unordered_map<std::string, FeatureStates>; // <sourceLayer, FeatureStates>

template <class T>
optional<T> numericValue(const Value& value) {
Expand All @@ -29,4 +33,15 @@ optional<T> numericValue(const Value& value) {
});
}

inline optional<std::string> featureIDtoString(const FeatureIdentifier& id) {
if (id.is<NullValue>()) {
return nullopt;
}

return id.match(
[](const std::string& value_) { return value_; }, [](uint64_t value_) { return util::toString(value_); },
[](int64_t value_) { return util::toString(value_); }, [](double value_) { return util::toString(value_); },
[](const auto&) -> optional<std::string> { return nullopt; });
}

} // namespace mbgl
2 changes: 2 additions & 0 deletions next/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ add_library(
${MBGL_ROOT}/src/mbgl/renderer/sources/render_tile_source.hpp
${MBGL_ROOT}/src/mbgl/renderer/sources/render_vector_source.cpp
${MBGL_ROOT}/src/mbgl/renderer/sources/render_vector_source.hpp
${MBGL_ROOT}/src/mbgl/renderer/source_state.cpp
${MBGL_ROOT}/src/mbgl/renderer/source_state.hpp
${MBGL_ROOT}/src/mbgl/renderer/style_diff.cpp
${MBGL_ROOT}/src/mbgl/renderer/style_diff.hpp
${MBGL_ROOT}/src/mbgl/renderer/tile_mask.hpp
Expand Down
103 changes: 93 additions & 10 deletions platform/glfw/glfw_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
#include "ny_route.hpp"

#include <mbgl/annotation/annotation.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/sources/custom_geometry_source.hpp>
#include <mbgl/gfx/backend.hpp>
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/style/expression/dsl.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/expression/dsl.hpp>
#include <mbgl/style/sources/custom_geometry_source.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/platform.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/gfx/backend.hpp>
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/map/camera.hpp>

#include <mapbox/cheap_ruler.hpp>
#include <mapbox/geometry.hpp>
Expand Down Expand Up @@ -323,6 +325,52 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
case GLFW_KEY_T:
view->toggleCustomSource();
break;
case GLFW_KEY_F: {
using namespace mbgl;
using namespace mbgl::style;
using namespace mbgl::style::expression::dsl;

auto &style = view->map->getStyle();
if (!style.getSource("states")) {
std::string url = "https://docs.mapbox.com/mapbox-gl-js/assets/us_states.geojson";
auto source = std::make_unique<GeoJSONSource>("states");
source->setURL(url);
style.addSource(std::move(source));

mbgl::CameraOptions cameraOptions;
cameraOptions.center = mbgl::LatLng{42.619626, -103.523181};
cameraOptions.zoom = 3;
cameraOptions.pitch = 0;
cameraOptions.bearing = 0;
view->map->jumpTo(cameraOptions);
}

auto layer = style.getLayer("state-fills");
if (!layer) {
auto fillLayer = std::make_unique<FillLayer>("state-fills", "states");
fillLayer->setFillColor(mbgl::Color{0.0, 0.0, 1.0, 0.5});
fillLayer->setFillOpacity(PropertyExpression<float>(
createExpression(R"(["case", ["boolean", ["feature-state", "hover"], false], 1, 0.5])")));
style.addLayer(std::move(fillLayer));
} else {
layer->setVisibility(layer->getVisibility() == mbgl::style::VisibilityType::Visible
? mbgl::style::VisibilityType::None
: mbgl::style::VisibilityType::Visible);
}

layer = style.getLayer("state-borders");
if (!layer) {
auto borderLayer = std::make_unique<LineLayer>("state-borders", "states");
borderLayer->setLineColor(mbgl::Color{0.0, 0.0, 1.0, 1.0});
borderLayer->setLineWidth(PropertyExpression<float>(
createExpression(R"(["case", ["boolean", ["feature-state", "hover"], false], 2, 1])")));
style.addLayer(std::move(borderLayer));
} else {
layer->setVisibility(layer->getVisibility() == mbgl::style::VisibilityType::Visible
? mbgl::style::VisibilityType::None
: mbgl::style::VisibilityType::Visible);
}
} break;
}
}

Expand Down Expand Up @@ -558,6 +606,41 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) {
}
view->lastX = x;
view->lastY = y;

auto &style = view->map->getStyle();
if (style.getLayer("state-fills")) {
auto screenCoordinate = mbgl::ScreenCoordinate{view->lastX, view->lastY};
const mbgl::RenderedQueryOptions queryOptions({{{"state-fills"}}, {}});
auto result = view->rendererFrontend->getRenderer()->queryRenderedFeatures(screenCoordinate, queryOptions);
using namespace mbgl;
FeatureState newState;

if (result.size() > 0) {
FeatureIdentifier id = result[0].id;
optional<std::string> idStr = featureIDtoString(id);

if (idStr) {
if (view->featureID && (*view->featureID != *idStr)) {
newState["hover"] = false;
view->rendererFrontend->getRenderer()->setFeatureState("states", {}, *view->featureID, newState);
view->featureID = nullopt;
}

if (!view->featureID) {
newState["hover"] = true;
view->featureID = featureIDtoString(id);
view->rendererFrontend->getRenderer()->setFeatureState("states", {}, *view->featureID, newState);
}
}
} else {
if (view->featureID) {
newState["hover"] = false;
view->rendererFrontend->getRenderer()->setFeatureState("states", {}, *view->featureID, newState);
view->featureID = nullopt;
}
}
view->invalidate();
}
}

void GLFWView::onWindowFocus(GLFWwindow *window, int focused) {
Expand Down
4 changes: 3 additions & 1 deletion platform/glfw/glfw_view.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#pragma once

#include <mbgl/map/map.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
#include <mbgl/util/geometry.hpp>

struct GLFWwindow;
class GLFWBackend;
Expand Down Expand Up @@ -134,4 +135,5 @@ class GLFWView : public mbgl::MapObserver {

GLFWwindow *window = nullptr;
bool dirty = false;
mbgl::optional<std::string> featureID;
};
3 changes: 3 additions & 0 deletions platform/node/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# master
* Add support for feature state APIs. ([#15480](https://github.com/mapbox/mapbox-gl-native/pull/15480))

# 4.3.0
* Introduce `text-writing-mode` layout property for symbol layer ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)). The `text-writing-mode` layout property allows control over symbol's preferred writing mode. The new property value is an array, whose values are enumeration values from a ( `horizontal` | `vertical` ) set.
* Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)).
Expand Down
6 changes: 6 additions & 0 deletions platform/node/src/node_feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ v8::Local<v8::Object> toJS(const Feature& feature) {
Nan::Set(result, Nan::New("id").ToLocalChecked(), FeatureIdentifier::visit(feature.id, ToValue()));
}

Nan::Set(result, Nan::New("source").ToLocalChecked(), toJS(feature.source));
if (!feature.sourceLayer.empty()) {
Nan::Set(result, Nan::New("sourceLayer").ToLocalChecked(), toJS(feature.sourceLayer));
}
Nan::Set(result, Nan::New("state").ToLocalChecked(), toJS(feature.state));

return scope.Escape(result);
}

Expand Down
Loading