diff options
author | zmiao <miao.zhao@mapbox.com> | 2020-03-04 23:19:08 +0200 |
---|---|---|
committer | zmiao <miao.zhao@mapbox.com> | 2020-03-09 12:27:04 +0200 |
commit | fe934717192863a47286efa364178842a44bc1bf (patch) | |
tree | 0253ed90e5a854c807494ae5d4ec5f284421f66d | |
parent | e6024a2e020ce28f87a785522b99212f91ed3ce4 (diff) | |
download | qtlocation-mapboxgl-fe934717192863a47286efa364178842a44bc1bf.tar.gz |
[core] Add a helper class to generate geometry buffer for lines
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | include/mbgl/util/geometry_buffer.hpp | 283 | ||||
-rw-r--r-- | platform/glfw/glfw_view.cpp | 64 | ||||
-rw-r--r-- | platform/glfw/helsinki_route.hpp | 273 |
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 |