summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzmiao <miao.zhao@mapbox.com>2020-03-10 00:01:34 +0200
committerzmiao <miao.zhao@mapbox.com>2020-03-10 00:01:34 +0200
commit37cea119e90c2f6450f1d5a87a6a9d2b191736ea (patch)
treeffd5dec93639812fd6f69096e0b34408d56ee327
parente6024a2e020ce28f87a785522b99212f91ed3ce4 (diff)
downloadqtlocation-mapboxgl-37cea119e90c2f6450f1d5a87a6a9d2b191736ea.tar.gz
[core] Introduce distance expression
-rw-r--r--CMakeLists.txt6
-rw-r--r--include/mbgl/style/expression/distance.hpp37
-rw-r--r--include/mbgl/style/expression/expression.hpp3
-rw-r--r--platform/glfw/glfw_view.cpp64
-rw-r--r--src/mbgl/style/expression/distance.cpp251
-rw-r--r--src/mbgl/style/expression/is_constant.cpp4
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp2
m---------vendor/cheap-ruler-cpp0
8 files changed, 365 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 588975e589..c070e88ca8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -166,6 +166,7 @@ add_library(
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/comparison.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/compound_expression.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/dsl.hpp
+ ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/distance.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/error.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/expression.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/find_zoom_curve.hpp
@@ -555,6 +556,7 @@ add_library(
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/collator_expression.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/comparison.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/compound_expression.cpp
+ ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/distance.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/dsl.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/dsl_impl.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/expression.cpp
@@ -940,6 +942,7 @@ include(${PROJECT_SOURCE_DIR}/vendor/protozero.cmake)
include(${PROJECT_SOURCE_DIR}/vendor/unique_resource.cmake)
include(${PROJECT_SOURCE_DIR}/vendor/vector-tile.cmake)
include(${PROJECT_SOURCE_DIR}/vendor/wagyu.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/cheap-ruler-cpp.cmake)
target_link_libraries(
mbgl-core
@@ -959,6 +962,7 @@ target_link_libraries(
mbgl-vendor-unique_resource
mbgl-vendor-vector-tile
mbgl-vendor-wagyu
+ mbgl-vendor-cheap-ruler-cpp
PUBLIC
Mapbox::Base::Extras::expected-lite
Mapbox::Base::Extras::rapidjson
@@ -994,7 +998,7 @@ if(MBGL_WITH_CORE_ONLY)
return()
endif()
-include(${PROJECT_SOURCE_DIR}/scripts/license.cmake)
+#include(${PROJECT_SOURCE_DIR}/scripts/license.cmake)
if(MBGL_WITH_QT)
include(${PROJECT_SOURCE_DIR}/platform/qt/qt.cmake)
diff --git a/include/mbgl/style/expression/distance.hpp b/include/mbgl/style/expression/distance.hpp
new file mode 100644
index 0000000000..b5bfe51494
--- /dev/null
+++ b/include/mbgl/style/expression/distance.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Distance final : public Expression {
+public:
+ explicit Distance(GeoJSON geoJSONSource_, Feature::geometry_type geometries_);
+
+ ~Distance() override;
+
+ EvaluationResult evaluate(const EvaluationContext&) const override;
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ void eachChild(const std::function<void(const Expression&)>&) const override {}
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ mbgl::Value serialize() const override;
+ std::string getOperator() const override;
+
+private:
+ GeoJSON geoJSONSource;
+ Feature::geometry_type geometries;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
index e522821185..51cc3b0f15 100644
--- a/include/mbgl/style/expression/expression.hpp
+++ b/include/mbgl/style/expression/expression.hpp
@@ -169,7 +169,8 @@ enum class Kind : int32_t {
NumberFormat,
ImageExpression,
In,
- Within
+ Within,
+ Distance
};
class Expression {
diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp
index d6c85e3068..5c086bb242 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -14,6 +14,7 @@
#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/layers/symbol_layer.hpp>
#include <mbgl/style/sources/custom_geometry_source.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/style.hpp>
@@ -28,6 +29,8 @@
#include <mapbox/geometry.hpp>
#include <mapbox/geojson.hpp>
+#include <sstream>
+
#if MBGL_USE_GLES2
#define GLFW_INCLUDE_ES2
#endif // MBGL_USE_GLES2
@@ -458,6 +461,67 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
// Snapshot with overlay
view->makeSnapshot(true);
} break;
+ case GLFW_KEY_G: {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+ using namespace mbgl::style::expression::dsl;
+
+ static const std::string points = R"({
+ "type": "MultiPoint",
+ "coordinates": [
+ [
+ 24.919674396514893,
+ 60.16455037398201
+ ],
+ [
+ 24.921332001686096,
+ 60.16460108220136
+ ],
+ [
+ 24.924674034118652,
+ 60.16487063510643
+ ],
+ [
+ 24.931159615516663,
+ 60.16733387258923
+ ]
+ ]
+ })";
+
+ static mapbox::geojson::geojson route{mapbox::geojson::parse(mbgl::platform::glfw::route)};
+
+ auto s2 = std::make_unique<GeoJSONSource>("line");
+ s2->setGeoJSON(route);
+ view->map->getStyle().addSource(std::move(s2));
+
+// auto fillLayer = std::make_unique<mbgl::style::FillLayer>("fill", "polygon");
+// fillLayer->setFillColor(mbgl::Color::black());
+// fillLayer->setFillOpacity(0.1);
+// view->map->getStyle().addLayer(std::move(fillLayer));
+
+ auto lineLayer = std::make_unique<mbgl::style::LineLayer>("line", "line");
+ lineLayer->setLineColor(mbgl::Color::red());
+ view->map->getStyle().addLayer(std::move(lineLayer));
+
+ auto &style = view->map->getStyle();
+ auto labelLayer = style.getLayer("poi-label");
+ if (labelLayer) {
+ auto symbolLayer = static_cast<mbgl::style::SymbolLayer *>(labelLayer);
+// std::stringstream ss;
+// ss << std::string(R"(["case", ["<", ["distance", )") << mbgl::platform::glfw::route << std::string(R"( ], 100.0], 1, 0.2] )");
+// auto expr = createExpression(ss.str().c_str());
+// if (expr) {
+//// symbolLayer->setIconOpacity(PropertyExpression<float>(std::move(expr)));
+// symbolLayer->setTextOpacity(PropertyExpression<float>(std::move(expr)));
+// }
+ std::stringstream ss;
+ ss << std::string(R"([ "<" , ["distance", )") << mbgl::platform::glfw::route << std::string(R"( ], 100])");
+ auto expr = createExpression(ss.str().c_str());
+ if (expr) {
+ symbolLayer->setFilter(Filter(std::move(expr)));
+ }
+ }
+ } break;
}
}
diff --git a/src/mbgl/style/expression/distance.cpp b/src/mbgl/style/expression/distance.cpp
new file mode 100644
index 0000000000..21dc57a02e
--- /dev/null
+++ b/src/mbgl/style/expression/distance.cpp
@@ -0,0 +1,251 @@
+#include <mbgl/style/expression/distance.hpp>
+
+#include <mapbox/geojson.hpp>
+#include <mapbox/geometry.hpp>
+#include <mapbox/cheap_ruler.hpp>
+
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/geometry_distance.hpp>
+
+#include <rapidjson/document.h>
+
+namespace mbgl {
+namespace {
+//
+////Returns the distance between a point P on a segment AB.
+//double distanceToLineSegment(const mapbox::geometry::point<double>& p, const mapbox::geometry::point<double>& a,const mapbox::geometry::point<double>& b) {
+// mapbox::geometry::point<double> vectorAB{(b.x - a.x), (b.y - a.y)};
+// mapbox::geometry::point<double> vectorAP{(p.x - a.x), (p.y - a.y)};
+// const auto dotProduct = [](const mapbox::geometry::point<double>& v,const mapbox::geometry::point<double>& w) {
+// return v.x * w.x + v.y * w.y;
+// };
+//
+// mapbox::cheap_ruler::CheapRuler ruler(p.y, mapbox::cheap_ruler::CheapRuler::Unit::Meters);
+// // dotProduct equals to |vectorAB| * |vectorAP| * cos();
+// const double dot1 = dotProduct(vectorAB, vectorAP);
+// if (dot1 < 0) return ruler.distance(p, a);
+//
+// const double dot2 = dotProduct(vectorAB, vectorAB);
+// if (dot1 >= dot2) return ruler.distance(p, b);
+//
+// const double ratio = dot1 / dot2;
+// const mapbox::geometry::point<double> pointOnAB = {(a.x + vectorAB.x * ratio), (a.y + vectorAB.y * ratio)};
+// return ruler.distance(p, pointOnAB);
+//}
+//
+//double distanceToLineInMeters(const mapbox::geometry::point<double>& point, const mapbox::geometry::line_string<double>& line){
+// double ret = std::numeric_limits<double>::infinity();
+// for (size_t i = 0; i < line.size() - 1; ++i){
+// ret = std::min(ret, distanceToLineSegment(point, line[i], line[i+1]));
+// }
+// return ret;
+//}
+//
+//double distanceToLinesInMeters(const mapbox::geometry::point<double>& point, const mapbox::geometry::multi_line_string<double>& lines){
+// double ret = std::numeric_limits<double>::infinity();
+// for (const auto& line: lines) {
+// ret = std::min(ret, distanceToLineInMeters(point, line));
+// }
+// return ret;
+//}
+
+double calculateDistance(const mbgl::GeometryTileFeature& feature,
+ const mbgl::CanonicalTileID& canonical,
+ const Feature::geometry_type& geoSet) {
+ return convertGeometry(feature, canonical).match(
+ [&geoSet](const mapbox::geometry::point<double>& point) -> double {
+ mapbox::cheap_ruler::CheapRuler ruler(point.y, mapbox::cheap_ruler::CheapRuler::Unit::Meters);
+ return geoSet.match(
+ [&point, &ruler](const mapbox::geometry::point<double>& point2) {
+ return ruler.distance(point, point2);
+ },
+ [&point, &ruler](const mapbox::geometry::multi_point<double>& points) {
+ double ret = std::numeric_limits<double>::infinity();
+ for (size_t i = 0; i < points.size() - 1; ++i){
+ ret = std::min(ret, ruler.distance(point, points[i]));
+ }
+ return ret;
+ },
+ [&point, &ruler](const mapbox::geometry::line_string<double>& line) {
+ double ret = std::numeric_limits<double>::infinity();
+ for (size_t i = 0; i < line.size() - 1; ++i){
+ ret = std::min(ret, ruler.distanceToLineSegment(point, line[i], line[i+1]));
+ }
+ return ret;
+ },
+ [&point,&ruler](const mapbox::geometry::multi_line_string<double>& lines) {
+ double ret = std::numeric_limits<double>::infinity();
+ for (const auto& line: lines) {
+ for (size_t i = 0; i < line.size() - 1; ++i){
+ ret = std::min(ret, ruler.distanceToLineSegment(point, line[i], line[i+1]));
+ }
+ }
+ return ret;
+ },
+ [](const auto&) -> double { return -1.0; });
+ },
+ [](const auto&) -> double { return -1.0; });
+}
+
+optional<mbgl::GeoJSON> parseValue(const mbgl::style::conversion::Convertible& value_,
+ mbgl::style::expression::ParsingContext& ctx) {
+ if (isObject(value_)) {
+ mbgl::style::conversion::Error error;
+ auto geojson = toGeoJSON(value_, error);
+ if (geojson && error.message.empty()) {
+ return geojson;
+ }
+ ctx.error(error.message);
+ }
+
+ ctx.error("'distance' expression requires valid geojson source that contains polygon geometry type.");
+ return nullopt;
+}
+
+optional<Feature::geometry_type> getGeometry(const Feature& feature, mbgl::style::expression::ParsingContext& ctx) {
+ const auto type = apply_visitor(ToFeatureType(), feature.geometry);
+ if (type == FeatureType::Point || type == FeatureType::LineString) {
+ return feature.geometry;
+ }
+ ctx.error("'distance' expression requires valid geojson source that contains Point/LineString geometry type.");
+ return nullopt;
+
+}
+} // namespace
+
+namespace style {
+namespace expression {
+
+Distance::Distance(GeoJSON geojson, Feature::geometry_type geometries_)
+ : Expression(Kind::Distance, type::Number),
+ geoJSONSource(std::move(geojson)),
+ geometries(std::move(geometries_)){}
+
+Distance::~Distance() = default;
+
+using namespace mbgl::style::conversion;
+
+EvaluationResult Distance::evaluate(const EvaluationContext& params) const {
+ if (!params.feature || !params.canonical) {
+ return -1.0;
+ }
+ auto geometryType = params.feature->getType();
+ // Currently only support Point/Points distance to LineString
+ if (geometryType == FeatureType::Point) {
+ auto distance = calculateDistance(*params.feature, *params.canonical, geometries);
+ return distance;
+ }
+ mbgl::Log::Warning(mbgl::Event::General,
+ "distance expression currently only support feature with Point geometry.");
+
+ return -1.0;
+}
+
+ParseResult Distance::parse(const Convertible& value, ParsingContext& ctx) {
+ if (isArray(value)) {
+ // object value, quoted with ["Distance", value]
+ if (arrayLength(value) != 2) {
+ ctx.error("'distance' expression requires exactly one argument, but found " +
+ util::toString(arrayLength(value) - 1) + " instead.");
+ return ParseResult();
+ }
+
+ auto parsedValue = parseValue(arrayMember(value, 1), ctx);
+ if (!parsedValue) {
+ return ParseResult();
+ }
+
+ return parsedValue->match(
+ [&parsedValue, &ctx](const mapbox::geometry::geometry<double>& geometrySet) {
+ if (auto ret = getGeometry(mbgl::Feature(geometrySet), ctx)) {
+ return ParseResult(std::make_unique<Distance>(*parsedValue, std::move(*ret)));
+ }
+ return ParseResult();
+ },
+ [&parsedValue, &ctx](const mapbox::feature::feature<double>& feature) {
+ if (auto ret = getGeometry(mbgl::Feature(feature), ctx)) {
+ return ParseResult(std::make_unique<Distance>(*parsedValue, std::move(*ret)));
+ }
+ return ParseResult();
+ },
+ [&parsedValue, &ctx](const mapbox::feature::feature_collection<double>& features) {
+ for (const auto& feature : features) {
+ if (auto ret = getGeometry(mbgl::Feature(feature), ctx)) {
+ return ParseResult(std::make_unique<Distance>(*parsedValue, std::move(*ret)));
+ }
+ }
+ return ParseResult();
+ },
+ [&ctx](const auto&) {
+ ctx.error("'distance' expression requires valid geojson that contains LineString/Point geometries.");
+ return ParseResult();
+ });
+ }
+ ctx.error("'distance' expression needs to be an array with exactly one argument.");
+ return ParseResult();
+}
+
+Value convertValue(const mapbox::geojson::rapidjson_value& v) {
+ if (v.IsDouble()) {
+ return v.GetDouble();
+ }
+ if (v.IsString()) {
+ return std::string(v.GetString());
+ }
+ if (v.IsArray()) {
+ std::vector<Value> result;
+ result.reserve(v.Size());
+ for (const auto& m : v.GetArray()) {
+ result.push_back(convertValue(m));
+ }
+ return result;
+ }
+ if (v.IsObject()) {
+ std::unordered_map<std::string, Value> result;
+ for (const auto& m : v.GetObject()) {
+ result.emplace(m.name.GetString(), convertValue(m.value));
+ }
+ return result;
+ }
+ // Ignore other types as valid geojson only contains above types.
+ return Null;
+}
+
+mbgl::Value Distance::serialize() const {
+ std::unordered_map<std::string, Value> serialized;
+ rapidjson::CrtAllocator allocator;
+ const mapbox::geojson::rapidjson_value value = mapbox::geojson::convert(geoJSONSource, allocator);
+ if (value.IsObject()) {
+ for (const auto& m : value.GetObject()) {
+ serialized.emplace(m.name.GetString(), convertValue(m.value));
+ }
+ } else {
+ mbgl::Log::Error(mbgl::Event::General,
+ "Failed to serialize 'distance' expression, converted rapidJSON is not an object");
+ }
+ return std::vector<mbgl::Value>{{getOperator(), *fromExpressionValue<mbgl::Value>(serialized)}};
+}
+
+bool Distance::operator==(const Expression& e) const {
+ if (e.getKind() == Kind::Distance) {
+ auto rhs = static_cast<const Distance*>(&e);
+ return geoJSONSource == rhs->geoJSONSource && geometries == rhs->geometries;
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Distance::possibleOutputs() const {
+ return { nullopt };
+}
+
+std::string Distance::getOperator() const {
+ return "distance";
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/is_constant.cpp b/src/mbgl/style/expression/is_constant.cpp
index e6bd05418f..7b14e79eda 100644
--- a/src/mbgl/style/expression/is_constant.cpp
+++ b/src/mbgl/style/expression/is_constant.cpp
@@ -33,6 +33,10 @@ bool isFeatureConstant(const Expression& expression) {
return false;
}
+ if (expression.getKind() == Kind::Distance) {
+ return false;
+ }
+
if (expression.getKind() == Kind::CollatorExpression) {
// Although the results of a Collator expression with fixed arguments
// generally shouldn't change between executions, we can't serialize them
diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp
index 2f1e1c1820..a6dc05f883 100644
--- a/src/mbgl/style/expression/parsing_context.cpp
+++ b/src/mbgl/style/expression/parsing_context.cpp
@@ -24,6 +24,7 @@
#include <mbgl/style/expression/number_format.hpp>
#include <mbgl/style/expression/step.hpp>
#include <mbgl/style/expression/within.hpp>
+#include <mbgl/style/expression/distance.hpp>
#include <mbgl/style/expression/find_zoom_curve.hpp>
#include <mbgl/style/expression/dsl.hpp>
@@ -120,6 +121,7 @@ MAPBOX_ETERNAL_CONSTEXPR const auto expressionRegistry =
{"case", Case::parse},
{"coalesce", Coalesce::parse},
{"collator", CollatorExpression::parse},
+ {"distance", Distance::parse},
{"format", FormatExpression::parse},
{"image", ImageExpression::parse},
{"interpolate", parseInterpolate},
diff --git a/vendor/cheap-ruler-cpp b/vendor/cheap-ruler-cpp
-Subproject 98cbe70dab74c5b77f139013ea5b3623d3ba3dc
+Subproject a65a1438c70bc2433abda383e3bd64d98cc50d0