diff --git a/src/core/map.cpp b/src/core/map.cpp index d8e6abd2..f3255704 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1622,6 +1623,16 @@ void Map::setConnectionEstablished() { have changed. This can be caused by a style change or adding a new source. */ +std::vector Map::queryRenderedFeatures(const mbgl::ScreenCoordinate &point, + const mbgl::RenderedQueryOptions &options) const { + return d_ptr->queryRenderedFeatures(point, options); +} + +std::vector Map::queryRenderedFeatures(const mbgl::ScreenBox &screenBox, + const mbgl::RenderedQueryOptions &options) const { + return d_ptr->queryRenderedFeatures(screenBox, options); +} + /*! \cond PRIVATE */ MapPrivate::MapPrivate(Map *map, const Settings &settings, const QSize &size, qreal pixelRatio_) @@ -1791,6 +1802,24 @@ bool MapPrivate::setProperty(const PropertySetter &setter, return true; } +std::vector MapPrivate::queryRenderedFeatures(const mbgl::ScreenCoordinate &point, + const mbgl::RenderedQueryOptions &options) const { + if (m_mapRenderer == nullptr) { + return {}; + } + + return m_mapRenderer->queryRenderedFeatures(point, options); +} + +std::vector MapPrivate::queryRenderedFeatures(const mbgl::ScreenBox &box, + const mbgl::RenderedQueryOptions &options) const { + if (m_mapRenderer == nullptr) { + return {}; + } + + return m_mapRenderer->queryRenderedFeatures(box, options); +} + /*! \endcond */ } // namespace QMapLibre diff --git a/src/core/map.hpp b/src/core/map.hpp index 6654867a..0af407b5 100644 --- a/src/core/map.hpp +++ b/src/core/map.hpp @@ -10,6 +10,9 @@ #include #include +#include +#include + #include #include #include @@ -175,6 +178,12 @@ class Q_MAPLIBRE_CORE_EXPORT Map : public QObject { void destroyRenderer(); void setOpenGLFramebufferObject(quint32 fbo, const QSize &size); + std::vector queryRenderedFeatures(const mbgl::ScreenCoordinate &, + const mbgl::RenderedQueryOptions &options = {}) const; + + std::vector queryRenderedFeatures(const mbgl::ScreenBox &screenBox, + const mbgl::RenderedQueryOptions &options = {}) const; + public slots: void render(); void setConnectionEstablished(); diff --git a/src/core/map_p.hpp b/src/core/map_p.hpp index d8930b73..26698308 100644 --- a/src/core/map_p.hpp +++ b/src/core/map_p.hpp @@ -51,6 +51,12 @@ class MapPrivate : public QObject, public mbgl::RendererFrontend { mbgl::EdgeInsets margins; std::unique_ptr mapObj{}; + std::vector queryRenderedFeatures(const mbgl::ScreenCoordinate &, + const mbgl::RenderedQueryOptions &options = {}) const; + + std::vector queryRenderedFeatures(const mbgl::ScreenBox &screenBox, + const mbgl::RenderedQueryOptions &options = {}) const; + public slots: void requestRendering(); diff --git a/src/core/map_renderer.cpp b/src/core/map_renderer.cpp index 0185f87d..6635721b 100644 --- a/src/core/map_renderer.cpp +++ b/src/core/map_renderer.cpp @@ -8,6 +8,7 @@ #include "utils/scheduler.hpp" #include +#include #include @@ -105,6 +106,16 @@ void MapRenderer::setObserver(mbgl::RendererObserver *observer) { m_renderer->setObserver(observer); } +std::vector MapRenderer::queryRenderedFeatures(const mbgl::ScreenCoordinate &point, + const mbgl::RenderedQueryOptions &options) const { + return m_renderer->queryRenderedFeatures(point, options); +} + +std::vector MapRenderer::queryRenderedFeatures(const mbgl::ScreenBox &box, + const mbgl::RenderedQueryOptions &options) const { + return m_renderer->queryRenderedFeatures(box, options); +} + /*! \endcond */ } // namespace QMapLibre diff --git a/src/core/map_renderer_p.hpp b/src/core/map_renderer_p.hpp index b9a087c3..840a916c 100644 --- a/src/core/map_renderer_p.hpp +++ b/src/core/map_renderer_p.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -33,16 +34,21 @@ class MapRenderer : public QObject { Q_OBJECT public: - MapRenderer(qreal pixelRatio, Settings::GLContextMode, const QString &localFontFamily); + MapRenderer(qreal pixelRatio, Settings::GLContextMode, const QString& localFontFamily); ~MapRenderer() override; void render(); - void updateFramebuffer(quint32 fbo, const mbgl::Size &size); - void setObserver(mbgl::RendererObserver *observer); + void updateFramebuffer(quint32 fbo, const mbgl::Size& size); + void setObserver(mbgl::RendererObserver* observer); // Thread-safe, called by the Frontend void updateParameters(std::shared_ptr parameters); + std::vector queryRenderedFeatures(const mbgl::ScreenCoordinate&, + const mbgl::RenderedQueryOptions& options = {}) const; + + std::vector queryRenderedFeatures(const mbgl::ScreenBox& screenBox, + const mbgl::RenderedQueryOptions& options = {}) const; signals: void needsRendering(); diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 79a92f4d..32ca9948 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -1,3 +1,5 @@ +set(MLN_CORE_PATH "${PROJECT_SOURCE_DIR}/vendor/maplibre-native" CACHE STRING "MapLibre Native Core source path" FORCE) + # Public headers string(TOLOWER ${MLN_QT_NAME}Widgets MLN_QT_WIDGETS_LOWERCASE) set(MLNQtWidgets_Headers @@ -73,6 +75,10 @@ target_include_directories( ${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_SOURCE_DIR}/src/core ${CMAKE_BINARY_DIR}/src/core/include + ${MLN_CORE_PATH}/include + ${MLN_CORE_PATH}/vendor/mapbox-base/deps/variant/include + ${MLN_CORE_PATH}/vendor/mapbox-base/include + ${MLN_CORE_PATH}/vendor/mapbox-base/deps/geometry.hpp/include ) # Common link libraries diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 03ccaafd..fadc7f70 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -1,3 +1,5 @@ +# set(MLN_CORE_PATH "${PROJECT_SOURCE_DIR}/vendor/maplibre-native" CACHE STRING "MapLibre Native Core source path" FORCE) + if(Qt5_FOUND) return() endif() @@ -19,6 +21,10 @@ target_include_directories( ${CMAKE_SOURCE_DIR}/src/core ${CMAKE_BINARY_DIR}/src/core/include ${CMAKE_SOURCE_DIR}/test/common + ${MLN_CORE_PATH}/include + ${MLN_CORE_PATH}/vendor/mapbox-base/deps/variant/include + ${MLN_CORE_PATH}/vendor/mapbox-base/include + ${MLN_CORE_PATH}/vendor/mapbox-base/deps/geometry.hpp/include ) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Test REQUIRED) diff --git a/test/widgets/CMakeLists.txt b/test/widgets/CMakeLists.txt index 949305e2..971e1c6f 100644 --- a/test/widgets/CMakeLists.txt +++ b/test/widgets/CMakeLists.txt @@ -1,3 +1,5 @@ +set(MLN_CORE_PATH "${PROJECT_SOURCE_DIR}/vendor/maplibre-native" CACHE STRING "MapLibre Native Core source path" FORCE) + set(test_sources ../common/test_rendering.cpp ../common/test_rendering.hpp gl_tester.cpp gl_tester.hpp @@ -17,6 +19,11 @@ target_include_directories( ${CMAKE_SOURCE_DIR}/src/widgets ${CMAKE_BINARY_DIR}/src/widgets/include ${CMAKE_SOURCE_DIR}/test/common + ${CMAKE_BINARY_DIR}/include + ${MLN_CORE_PATH}/include + ${MLN_CORE_PATH}/vendor/mapbox-base/deps/variant/include + ${MLN_CORE_PATH}/vendor/mapbox-base/include + ${MLN_CORE_PATH}/vendor/mapbox-base/deps/geometry.hpp/include ) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Test REQUIRED) diff --git a/test/widgets/gl_tester.cpp b/test/widgets/gl_tester.cpp index 6cde942a..f974942f 100644 --- a/test/widgets/gl_tester.cpp +++ b/test/widgets/gl_tester.cpp @@ -12,9 +12,64 @@ constexpr int kAnimationDuration = 5000; namespace QMapLibre { -GLTester::GLTester(const QMapLibre::Settings &settings) +struct IdentifierToQStringVisitor { + QString operator()(const std::string& value) const { return QString::fromStdString(value); } + QString operator()(uint64_t value) const { return QString::number(value); } + QString operator()(int64_t value) const { return QString::number(value); } + QString operator()(double value) const { return QString::number(value); } + QString operator()(mapbox::feature::null_value_t) const { return QStringLiteral("null"); } +}; + +QString identifierToQString(const mapbox::feature::identifier& id) { + return mapbox::util::apply_visitor(IdentifierToQStringVisitor(), id); +} + +GLTester::GLTester(const QMapLibre::Settings& settings) : GLWidget(settings) {} +QString valueToQString(const mapbox::feature::value& val) { + if (val.is()) { + return QString::fromStdString(val.get()); + } else if (val.is()) { + return val.get() ? "true" : "false"; + } else if (val.is()) { + return QString::number(val.get()); + } else if (val.is()) { + return QString::number(val.get()); + } else if (val.is()) { + return QString::number(val.get()); + } else if (val.is()) { + return "(null)"; + } else { + return "(unsupported)"; + } +} + +void GLTester::initializeQuery() { + connect(this, &GLTester::onMousePressEvent, [this](Coordinate coordinate) { + qDebug() << "onMousePressEvent" << coordinate; + + /* This feels stupid, need to change the interface. */ + QPointF pixel = map()->pixelForCoordinate(coordinate); + mbgl::ScreenCoordinate screenPoint(pixel.x(), pixel.y()); + + mbgl::RenderedQueryOptions options; + options.layerIDs = {"countries-fill"}; + + std::vector features = map()->queryRenderedFeatures(screenPoint, options); + + for (const auto& feature : features) { + qDebug() << "Feature ID:" << identifierToQString(feature.id); + + for (const auto& [key, value] : feature.properties) { + QString keyStr = QString::fromStdString(key); + QString valueStr = valueToQString(value); + qDebug() << keyStr << ":" << valueStr; + } + } + }); +} + void GLTester::initializeAnimation() { m_bearingAnimation = std::make_unique(map(), "bearing"); m_zoomAnimation = std::make_unique(map(), "zoom"); diff --git a/test/widgets/gl_tester.hpp b/test/widgets/gl_tester.hpp index 5e3e8ae9..99cd0e0a 100644 --- a/test/widgets/gl_tester.hpp +++ b/test/widgets/gl_tester.hpp @@ -19,6 +19,7 @@ class GLTester : public GLWidget { explicit GLTester(const QMapLibre::Settings &); void initializeAnimation(); + void initializeQuery(); int selfTest(); private slots: diff --git a/test/widgets/test_widgets.cpp b/test/widgets/test_widgets.cpp index 380a0a76..9c264fd1 100644 --- a/test/widgets/test_widgets.cpp +++ b/test/widgets/test_widgets.cpp @@ -17,6 +17,7 @@ private slots: void testGLWidgetNoProvider(); void testGLWidgetMapLibreProvider(); void testGLWidgetStyle(); + void testGLWidgetQuery(); }; void TestWidgets::testGLWidgetNoProvider() { @@ -69,6 +70,26 @@ void TestWidgets::testGLWidgetStyle() { QTest::qWait(tester->selfTest()); } +void TestWidgets::testGLWidgetQuery() { + const std::unique_ptr surface = QMapLibre::createTestSurface(QSurface::Window); + QVERIFY(surface != nullptr); + + QOpenGLContext ctx; + QVERIFY(ctx.create()); + QVERIFY(ctx.makeCurrent(surface.get())); + + QMapLibre::Styles styles; + styles.append(QMapLibre::Style("https://demotiles.maplibre.org/style.json", "Demo tiles")); + + QMapLibre::Settings settings; + settings.setStyles(styles); + auto tester = std::make_unique(settings); + tester->show(); + QTest::qWait(100); + tester->initializeQuery(); + QTest::qWait(10000); +} + // NOLINTNEXTLINE(misc-const-correctness) QTEST_MAIN(TestWidgets) #include "test_widgets.moc"