summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzmiao <miao.zhao@mapbox.com>2020-03-04 23:19:08 +0200
committerzmiao <miao.zhao@mapbox.com>2020-03-09 12:27:04 +0200
commitfe934717192863a47286efa364178842a44bc1bf (patch)
tree0253ed90e5a854c807494ae5d4ec5f284421f66d
parente6024a2e020ce28f87a785522b99212f91ed3ce4 (diff)
downloadqtlocation-mapboxgl-fe934717192863a47286efa364178842a44bc1bf.tar.gz
[core] Add a helper class to generate geometry buffer for lines
-rw-r--r--CMakeLists.txt1
-rw-r--r--include/mbgl/util/geometry_buffer.hpp283
-rw-r--r--platform/glfw/glfw_view.cpp64
-rw-r--r--platform/glfw/helsinki_route.hpp273
4 files changed, 620 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 588975e589..6caec8e2c3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -238,6 +238,7 @@ add_library(
${PROJECT_SOURCE_DIR}/include/mbgl/util/geo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/util/geojson.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/util/geometry.hpp
+ ${PROJECT_SOURCE_DIR}/include/mbgl/util/geometry_buffer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/util/ignore.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/util/image.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/util/immutable.hpp
diff --git a/include/mbgl/util/geometry_buffer.hpp b/include/mbgl/util/geometry_buffer.hpp
new file mode 100644
index 0000000000..d54fa4c117
--- /dev/null
+++ b/include/mbgl/util/geometry_buffer.hpp
@@ -0,0 +1,283 @@
+#pragma once
+
+#include <boost/geometry.hpp>
+#include <boost/geometry/geometries/geometries.hpp>
+#include <boost/geometry/geometries/point_xy.hpp>
+
+#include <rapidjson/prettywriter.h>
+#include <rapidjson/rapidjson.h>
+#include <rapidjson/stringbuffer.h>
+
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+
+#include <mapbox/geojson_impl.hpp>
+#include <mapbox/geometry/geometry.hpp>
+
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+namespace mbgl {
+namespace {
+
+typedef double coordinate_type;
+
+typedef boost::geometry::model::d2::point_xy<coordinate_type> BoostPoint;
+typedef boost::geometry::model::multi_point<BoostPoint> BoostMultiPoint;
+typedef boost::geometry::model::linestring<BoostPoint> BoostLineString;
+typedef boost::geometry::model::multi_linestring<BoostLineString> BoostMultiLineString;
+typedef boost::geometry::model::polygon<BoostPoint> BoostPolygon;
+typedef boost::geometry::model::multi_polygon<BoostPolygon> BoostMultiPolygon;
+
+typedef boost::geometry::strategy::buffer::distance_symmetric<coordinate_type> DistanceStrategy;
+typedef boost::geometry::strategy::buffer::join_round JoinStrategy;
+typedef boost::geometry::strategy::buffer::end_round EndStrategy;
+typedef boost::geometry::strategy::buffer::point_circle CircleStrategy;
+typedef boost::geometry::strategy::buffer::side_straight SideStrategy;
+
+static const double earthRadius = 6371008.8;
+
+double mercatorXfromLng(double lng) {
+ return (180 + lng) / 360;
+}
+
+double mercatorYfromLat(double lat) {
+ return (180 - (180 / M_PI * std::log(std::tan(M_PI / 4 + lat * M_PI / 360)))) / 360;
+}
+
+double lngFromMercatorX(double x) {
+ return x * 360 - 180;
+}
+
+double latFromMercatorY(double y) {
+ const auto y2 = 180 - y * 360;
+ return 360 / M_PI * std::atan(std::exp(y2 * M_PI / 180)) - 90;
+}
+
+BoostPoint mercatorPointFromLngLat(const mapbox::geometry::point<double> point) {
+ return {mercatorXfromLng(point.x), mercatorYfromLat(point.y)};
+}
+
+mapbox::geometry::point<double> lngLatPointFromMercator(const BoostPoint& point) {
+ return {lngFromMercatorX(point.x()), latFromMercatorY(point.y())};
+}
+
+// Determine the Mercator scale factor for a given latitude, see
+// https://en.wikipedia.org/wiki/Mercator_projection#Scale_factor
+//
+// At the equator the scale factor will be 1, which increases at higher latitudes.
+double mercatorScale(double latitude) {
+ return 1 / std::cos(latitude * M_PI / 180);
+}
+
+// Returns the distance of 1 meter in `MercatorCoordinate` units at this latitude.
+//
+// For coordinates in real world units using meters, this naturally provides the scale
+// to transform into `MercatorCoordinate`s.
+double meterInMercatorCoordinateUnits(double lattitude) {
+ // 1 meter / circumference at equator in meters * Mercator projection scale factor at this latitude
+ static const double earthCircumfrence = 2 * M_PI * earthRadius; // meters
+ return 1 / earthCircumfrence * mercatorScale(lattitude);
+}
+
+// Convert polygon geometry from mercator to lnglat
+mapbox::geometry::multi_polygon<double> lngLatPolygonFromMercator(const BoostMultiPolygon& polygons) {
+ mapbox::geometry::multi_polygon<double> mapboxPolygons;
+ mapboxPolygons.reserve(polygons.size());
+ for (const auto& polygon : polygons) {
+ mapbox::geometry::polygon<double> mapboxPolygon;
+ const auto& outerRing = polygon.outer();
+ const auto& innerRings = polygon.inners();
+ mapboxPolygon.reserve(1 + innerRings.size());
+
+ mapbox::geometry::linear_ring<double> ring;
+ ring.reserve(outerRing.size());
+ for (const auto& p : outerRing) {
+ ring.emplace_back(lngLatPointFromMercator(p));
+ }
+ mapboxPolygon.push_back(std::move(ring));
+
+ for (const auto& r : innerRings) {
+ ring.reserve(r.size());
+ for (const auto& p : r) {
+ ring.emplace_back(lngLatPointFromMercator(p));
+ }
+ mapboxPolygon.push_back(std::move(ring));
+ }
+ mapboxPolygons.push_back(std::move(mapboxPolygon));
+ }
+ return mapboxPolygons;
+}
+
+void generateFile(const std::string& filename, const std::string& data) {
+ FILE* fd = fopen(filename.c_str(), "wb");
+ if (fd) {
+ fwrite(data.data(), sizeof(std::string::value_type), data.size(), fd);
+ fclose(fd);
+ } else {
+ throw std::runtime_error(std::string("Failed to open file ") + filename);
+ }
+}
+
+// Convert geometry to geojson
+std::string getGeoStringBuffer(const mapbox::geometry::geometry<double> geometry, bool singleLine = false) {
+ using JSValue = rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
+ unsigned indent = 2;
+ rapidjson::CrtAllocator allocator;
+ rapidjson::StringBuffer buffer;
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
+ if (singleLine) {
+ writer.SetFormatOptions(rapidjson::kFormatSingleLineArray);
+ }
+ writer.SetIndent(' ', indent);
+ JSValue json(rapidjson::kObjectType);
+ json.AddMember("type", "Feature", allocator);
+ JSValue v;
+ v.SetObject();
+ json.AddMember("properties", v, allocator);
+ json.AddMember("geometry", mapbox::geojson::convert(geometry, allocator), allocator);
+ json.Accept(writer);
+
+ return buffer.GetString();
+}
+
+// Generate polygon buffer based on the type of geometry inputs
+mapbox::geometry::multi_polygon<double> getPolygonBuffer(const mapbox::geometry::geometry<double>& geometries,
+ const double distanceInMeter,
+ const int pointsPerCircle) {
+ double bufferDistance = 0;
+ return geometries.match(
+ [&distanceInMeter, &bufferDistance, &pointsPerCircle](const mapbox::geometry::point<double>& point) {
+ // Convert meter to boost geometry distance
+ bufferDistance = meterInMercatorCoordinateUnits(point.y) * distanceInMeter;
+ // Convert geometry to Mercator
+ const auto boostGeometry = mercatorPointFromLngLat(point);
+ BoostMultiPolygon polygons;
+ boost::geometry::buffer(boostGeometry,
+ polygons,
+ DistanceStrategy(bufferDistance),
+ SideStrategy(),
+ JoinStrategy(pointsPerCircle),
+ EndStrategy(pointsPerCircle),
+ CircleStrategy(pointsPerCircle));
+
+ assert(polygons.size() > 0);
+ return lngLatPolygonFromMercator(polygons);
+ },
+ [&distanceInMeter, &bufferDistance, &pointsPerCircle](const mapbox::geometry::multi_point<double>& points) {
+ assert(points.size());
+ // Convert meter to boost geometry distance
+ bufferDistance = meterInMercatorCoordinateUnits(points.front().y) * distanceInMeter;
+
+ BoostMultiPoint boostPoints;
+ for (const auto& p : points) {
+ boostPoints.push_back(mercatorPointFromLngLat(p));
+ }
+ BoostMultiPolygon polygons;
+ boost::geometry::buffer(boostPoints,
+ polygons,
+ DistanceStrategy(bufferDistance),
+ SideStrategy(),
+ JoinStrategy(pointsPerCircle),
+ EndStrategy(pointsPerCircle),
+ CircleStrategy(pointsPerCircle));
+
+ assert(polygons.size() > 0);
+ return lngLatPolygonFromMercator(polygons);
+ },
+ [&distanceInMeter, &bufferDistance, &pointsPerCircle](const mapbox::geometry::line_string<double>& line) {
+ // Convert meter to boost geometry distance
+ assert(line.size());
+ bufferDistance = meterInMercatorCoordinateUnits(line.front().y) * distanceInMeter;
+
+ BoostLineString boostLine;
+ for (const auto& p : line) {
+ boostLine.push_back(mercatorPointFromLngLat(p));
+ }
+ BoostMultiPolygon polygons;
+ boost::geometry::buffer(boostLine,
+ polygons,
+ DistanceStrategy(bufferDistance),
+ SideStrategy(),
+ JoinStrategy(pointsPerCircle),
+ EndStrategy(pointsPerCircle),
+ CircleStrategy(pointsPerCircle));
+
+ assert(polygons.size() > 0);
+ return lngLatPolygonFromMercator(polygons);
+ },
+ [&distanceInMeter, &bufferDistance, &pointsPerCircle](
+ const mapbox::geometry::multi_line_string<double>& lines) {
+ // Convert meter to boost geometry distance
+ assert(lines.size());
+ bufferDistance = meterInMercatorCoordinateUnits(lines.front().front().y) * distanceInMeter;
+
+ BoostMultiLineString boostLines;
+ for (const auto& line : lines) {
+ BoostLineString boostLine;
+ for (const auto& p : line) {
+ boostLine.push_back(mercatorPointFromLngLat(p));
+ }
+ boostLines.push_back(boostLine);
+ }
+
+ BoostMultiPolygon polygons;
+ boost::geometry::buffer(boostLines,
+ polygons,
+ DistanceStrategy(bufferDistance),
+ SideStrategy(),
+ JoinStrategy(pointsPerCircle),
+ EndStrategy(pointsPerCircle),
+ CircleStrategy(pointsPerCircle));
+
+ assert(polygons.size() > 0);
+ return lngLatPolygonFromMercator(polygons);
+ },
+ [](const auto&) {
+ mbgl::Log::Warning(mbgl::Event::General, "Geometry only supports Point/LineString geometry type.");
+ return mapbox::geometry::multi_polygon<double>();
+ });
+}
+} // namespace
+
+class GeometryBuffer {
+public:
+ GeometryBuffer(const mapbox::geometry::geometry<double>& geometries_,
+ const double bufferRadiusInMeter_,
+ const uint32_t pointsPerCircle_)
+ : geometries(geometries_), bufferRadiusInMeter(bufferRadiusInMeter_), pointsPerCircle(pointsPerCircle_){};
+
+ mbgl::optional<mapbox::geometry::multi_polygon<double>> getGeometryBuffer() {
+ auto ret = getPolygonBuffer(geometries, bufferRadiusInMeter, pointsPerCircle);
+ if (ret.empty()) {
+ return nullopt;
+ }
+ return ret;
+ }
+
+ std::string getGeoJSONBuffer(bool singleLine = false) {
+ const auto geoBuffer = getGeometryBuffer();
+ if (geoBuffer) {
+ return getGeoStringBuffer(*geoBuffer, singleLine);
+ }
+ return {};
+ }
+
+ static std::string geoJSONFromGeometryBuffer(const mapbox::geometry::multi_polygon<double>& geometryBuffer,
+ bool singleLine = false) {
+ return getGeoStringBuffer(geometryBuffer, singleLine);
+ }
+
+ static void writeGeoJSON(const std::string& path, const std::string& json) { generateFile(path, json); }
+
+private:
+ mapbox::geometry::geometry<double> geometries;
+ double bufferRadiusInMeter;
+ uint32_t pointsPerCircle;
+};
+
+} // namespace mbgl
diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp
index d6c85e3068..caf8f89cc4 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -2,6 +2,7 @@
#include "glfw_backend.hpp"
#include "glfw_renderer_frontend.hpp"
#include "ny_route.hpp"
+#include "helsinki_route.hpp"
#include "test_writer.hpp"
#include <mbgl/annotation/annotation.hpp>
@@ -14,6 +15,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>
@@ -23,11 +25,14 @@
#include <mbgl/util/logging.hpp>
#include <mbgl/util/platform.hpp>
#include <mbgl/util/string.hpp>
+#include <mbgl/util/geometry_buffer.hpp>
#include <mapbox/cheap_ruler.hpp>
#include <mapbox/geometry.hpp>
#include <mapbox/geojson.hpp>
+#include <sstream>
+
#if MBGL_USE_GLES2
#define GLFW_INCLUDE_ES2
#endif // MBGL_USE_GLES2
@@ -458,6 +463,63 @@ 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;
+ // Add new paint properties to the layer "country-label" that need to highlight country labels that are
+ // inside the polygon
+ static mapbox::geojson::geojson route{mapbox::geojson::parse(mbgl::platform::glfw::helsinkiRoute)};
+ const auto& geometry = route.match(
+ [](const mapbox::geometry::geometry<double>& geometrySet) {
+ return mbgl::Feature(geometrySet).geometry;
+ },
+ [](const mapbox::feature::feature<double>& feature) {
+ return mbgl::Feature(feature).geometry;
+ },
+ [](const mapbox::feature::feature_collection<double>& features) {
+ return mbgl::Feature(features.front()).geometry;
+ },
+ [](const auto&) {
+ return mapbox::geometry::empty();
+ });
+
+// const auto &geometry = route.get<mapbox::geometry::geometry<double>>();
+// const auto &lineString = geometry.get<mapbox::geometry::line_string<double>>();
+
+ const auto geojson = mbgl::GeometryBuffer(geometry, 100.0, 50).getGeoJSONBuffer();
+ const std::string polygonSource("/Users/miaozhao/Work/MacOs/polygon.geojson");
+ mbgl::GeometryBuffer::writeGeoJSON(polygonSource, geojson);
+
+ auto s1 = std::make_unique<GeoJSONSource>("polygon");
+ s1->setGeoJSON(mapbox::geojson::parse(geojson));
+ view->map->getStyle().addSource(std::move(s1));
+
+ 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"(["within", )") << geojson << std::string(R"( ])");
+ auto expr = createExpression(ss.str().c_str());
+ if (expr) {
+ symbolLayer->setFilter(Filter(std::move(expr)));
+ }
+ }
+ } break;
}
}
@@ -931,4 +993,4 @@ void GLFWView::toggleCustomSource() {
layer->setVisibility(layer->getVisibility() == mbgl::style::VisibilityType::Visible ?
mbgl::style::VisibilityType::None : mbgl::style::VisibilityType::Visible);
}
-}
+} \ No newline at end of file
diff --git a/platform/glfw/helsinki_route.hpp b/platform/glfw/helsinki_route.hpp
new file mode 100644
index 0000000000..d9627b5451
--- /dev/null
+++ b/platform/glfw/helsinki_route.hpp
@@ -0,0 +1,273 @@
+#include <string>
+
+namespace mbgl {
+namespace platform {
+namespace glfw {
+
+constexpr const char* helsinkiRoute = R"route(
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "MultiLineString",
+ "coordinates": [
+ [
+ [
+ 24.919674396514893,
+ 60.16455037398201
+ ],
+ [
+ 24.921332001686096,
+ 60.16460108220136
+ ],
+ [
+ 24.92250680923462,
+ 60.164705167248556
+ ],
+ [
+ 24.92311298847198,
+ 60.16476254732591
+ ],
+ [
+ 24.923794269561768,
+ 60.16481192080062
+ ],
+ [
+ 24.924674034118652,
+ 60.16487063510643
+ ],
+ [
+ 24.92523193359375,
+ 60.1648919857371
+ ],
+ [
+ 24.925435781478882,
+ 60.164910667527536
+ ],
+ [
+ 24.92561012506485,
+ 60.16495069989984
+ ],
+ [
+ 24.925816655158997,
+ 60.165010748366896
+ ],
+ [
+ 24.926661550998688,
+ 60.165288304299814
+ ],
+ [
+ 24.92731064558029,
+ 60.1655044760466
+ ],
+ [
+ 24.927439391613003,
+ 60.165561854728224
+ ],
+ [
+ 24.928563237190247,
+ 60.16595149335769
+ ],
+ [
+ 24.929917752742767,
+ 60.16642652598447
+ ],
+ [
+ 24.930156469345093,
+ 60.16644253820056
+ ],
+ [
+ 24.930403232574463,
+ 60.166394501528906
+ ],
+ [
+ 24.93046224117279,
+ 60.16634913349672
+ ],
+ [
+ 24.930633902549744,
+ 60.166365145750525
+ ],
+ [
+ 24.93071973323822,
+ 60.16642385728104
+ ],
+ [
+ 24.93074119091034,
+ 60.166511924380124
+ ],
+ [
+ 24.930797517299652,
+ 60.16665069629966
+ ],
+ [
+ 24.931159615516663,
+ 60.16733387258923
+ ],
+ [
+ 24.93122398853302,
+ 60.1673819078876
+ ]
+ ],
+ [
+ [
+ 24.93122935295105,
+ 60.167383242200444
+ ],
+ [
+ 24.931269586086273,
+ 60.16739658532588
+ ],
+ [
+ 24.93135541677475,
+ 60.167392582388814
+ ],
+ [
+ 24.93148148059845,
+ 60.16739525101357
+ ],
+ [
+ 24.931594133377075,
+ 60.167403256886566
+ ],
+ [
+ 24.931905269622803,
+ 60.167397919638134
+ ],
+ [
+ 24.932076930999752,
+ 60.167405925510465
+ ],
+ [
+ 24.932237863540646,
+ 60.16741660000386
+ ],
+ [
+ 24.93250608444214,
+ 60.1674673038002
+ ],
+ [
+ 24.933310747146603,
+ 60.167734164596524
+ ],
+ [
+ 24.934450685977936,
+ 60.168130448879936
+ ],
+ [
+ 24.934933483600616,
+ 60.16830123659222
+ ],
+ [
+ 24.934713542461395,
+ 60.16847068914809
+ ],
+ [
+ 24.934949576854706,
+ 60.16855474796568
+ ],
+ [
+ 24.935268759727478,
+ 60.16866682605459
+ ],
+ [
+ 24.936030507087704,
+ 60.16894701960423
+ ],
+ [
+ 24.93632823228836,
+ 60.16903107720314
+ ],
+ [
+ 24.93696391582489,
+ 60.16926456940512
+ ],
+ [
+ 24.938240647315975,
+ 60.16974755943638
+ ],
+ [
+ 24.938401579856873,
+ 60.16979692542083
+ ],
+ [
+ 24.938731491565704,
+ 60.16987297557607
+ ],
+ [
+ 24.939565658569332,
+ 60.17019985581767
+ ],
+ [
+ 24.939801692962643,
+ 60.17026389727989
+ ],
+ [
+ 24.939911663532257,
+ 60.17027590504016
+ ],
+ [
+ 24.941115975379944,
+ 60.170305257324564
+ ],
+ [
+ 24.94418442249298,
+ 60.170386643066905
+ ],
+ [
+ 24.94469404220581,
+ 60.17047469954465
+ ],
+ [
+ 24.945133924484253,
+ 60.17062412817889
+ ],
+ [
+ 24.94567573070526,
+ 60.17080023962529
+ ],
+ [
+ 24.946464300155636,
+ 60.17102971676008
+ ],
+ [
+ 24.94716167449951,
+ 60.17129921692988
+ ],
+ [
+ 24.94742453098297,
+ 60.17141929055101
+ ],
+ [
+ 24.947537183761593,
+ 60.17157405145976
+ ],
+ [
+ 24.9476820230484,
+ 60.171819532785136
+ ],
+ [
+ 24.948642253875732,
+ 60.17268670756399
+ ],
+ [
+ 24.949076771736145,
+ 60.17306558905074
+ ],
+ [
+ 24.949543476104736,
+ 60.1734391299005
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+})route";
+
+} // namespace glfw
+} // namespace platform
+} // namespace mbgl