diff --git a/example/with_external_libs/common/read_countries.hpp b/example/with_external_libs/common/read_countries.hpp new file mode 100644 index 0000000000..024a57d945 --- /dev/null +++ b/example/with_external_libs/common/read_countries.hpp @@ -0,0 +1,66 @@ +// Boost.Geometry +// +// Copyright (c) 2007-2024 Barend Gehrels, Amsterdam, the Netherlands. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef READ_COUNTRIES_HPP +#define READ_COUNTRIES_HPP + +#include +#include + +// ---------------------------------------------------------------------------- +// Read an ASCII file containing WKT's of either POLYGON or MULTIPOLYGON +// ---------------------------------------------------------------------------- +template +std::vector read_countries(std::string const& filename) +{ + std::vector geometries; + std::ifstream cpp_file(filename.c_str()); + if (!cpp_file.is_open()) + { + return geometries; + } + while (! cpp_file.eof() ) + { + std::string line; + std::getline(cpp_file, line); + if (line.empty()) + { + continue; + } + Geometry geometry; + if (line.substr(0, 4) == "POLY") + { + using polygon_t = std::decay_t; + polygon_t polygon; + boost::geometry::read_wkt(line, polygon); + geometry.push_back(polygon); + } + else + { + boost::geometry::read_wkt(line, geometry); + } + + geometries.push_back(geometry); + } + return geometries; +} + +// Returns the envelope of a collection of geometries +template +Box calculate_envelope(Countries const& countries) +{ + Box box; + boost::geometry::assign_inverse(box); + + for (auto const& country : countries) + { + boost::geometry::expand(box, boost::geometry::return_envelope(country)); + } + return box; +} + +#endif diff --git a/example/with_external_libs/qt/CMakeLists.txt b/example/with_external_libs/qt/CMakeLists.txt new file mode 100644 index 0000000000..007c7c0604 --- /dev/null +++ b/example/with_external_libs/qt/CMakeLists.txt @@ -0,0 +1,36 @@ +# Boost.Geometry +# Example CMakeLists.txt building the Boost.Geometry with Qt example +# +# Copyright (c) 2021-2024 Barend Gehrels, Amsterdam, the Netherlands. + +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +cmake_minimum_required(VERSION 3.16) +project(qt_world_mapper LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(${PROJECT_NAME} + qt_world_mapper.cpp + qt_world_mapper.hpp +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(${PROJECT_NAME} PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +target_include_directories(${PROJECT_NAME} PRIVATE + .. + ../../../../.. +) diff --git a/example/with_external_libs/qt/README.md b/example/with_external_libs/qt/README.md new file mode 100644 index 0000000000..dd05c659d3 --- /dev/null +++ b/example/with_external_libs/qt/README.md @@ -0,0 +1,61 @@ +# ![Boost.Geometry](../../../doc/other/logo/logo_bkg.png) + +# Qt + +## Introduction + +[Qt](https://www.qt.io/product/framework) is a stable and powerful open source framework for developing native cross-platform GUI applications in C++. + +## Installing Qt + +### Mac + +On a Mac, installing Qt for this purpose is trivial: + +`brew install qt` + +And then you can use the standard CMake workflow. + +During that, either ignore this warning: `Could NOT find WrapVulkanHeaders (missing: Vulkan_INCLUDE_DIR)` + +or install vulkan-tools (`brew install vulkan-tools`), though for 2D applications, it might not be necessary. + +### Linux Ubuntu + +Install Vulkan as described [here](https://vulkan.lunarg.com/doc/sdk/1.3.239.0/linux/getting_started_ubuntu.html). + +Since Qt 6.3 there is `qt_standard_project_setup()` used in the `CMakeLists.txt`. + +If you have an older Qt version (on Ubuntu 22 you might have `QT6.2.4`), replace `qt_standard_project_setup` with: + +``` +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) +``` + +### Other platforms + +Either you have Qt installed already, or you install it according to the [Qt documentation](https://doc.qt.io/qt-6/get-and-install-qt.html), +and change the `CMakeLists.txt` if necessary. + +## Building this example + +Assuming you want to build it with CMake, and example workflow is: + +``` +cd example/with_external_libs/qt +mkdir my_build_folder +cd my_build_folder +cmake .. -G Ninja +ninja +``` + +## Running this example + +You can pass an Ascii file with WKT polygons as the first command line argument. There are several +packed with Boost.Geometry as examples and as test data. + +For example: `././qt_world_mapper.app/Contents/MacOS/qt_world_mapper ../../../data/world.wkt` diff --git a/example/with_external_libs/qt/qt_world_mapper.cpp b/example/with_external_libs/qt/qt_world_mapper.cpp new file mode 100644 index 0000000000..f0a12602a5 --- /dev/null +++ b/example/with_external_libs/qt/qt_world_mapper.cpp @@ -0,0 +1,77 @@ +// Boost.Geometry +// +// Copyright (c) 2007-2024 Barend Gehrels, Amsterdam, the Netherlands. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Qt World Mapper Example + +#include "qt_world_mapper.hpp" +#include "common/read_countries.hpp" + +#include +#include +#include +#include + +#include +#include + +// Adapt a QPointF such that it can be handled by Boost.Geometry +BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(QPointF, double, cs::cartesian, x, y, setX, setY) + +// Adapt a QPolygonF as well. +// A QPolygonF has no holes (interiors) so it is similar to a Boost.Geometry ring +BOOST_GEOMETRY_REGISTER_RING(QPolygonF) + + +qt_world_mapper::qt_world_mapper(std::vector const& countries, boost::geometry::model::box const& box, QWidget *parent) + : QWidget(parent) + , m_countries(countries) + , m_box(box) +{ + setPalette(QPalette(Qt::blue)); + setAutoFillBackground(true); +} + +void qt_world_mapper::paintEvent(QPaintEvent *) +{ + map_transformer_type transformer(m_box, this->width(), this->height()); + + QPainter painter(this); + painter.setBrush(Qt::green); + painter.setRenderHint(QPainter::Antialiasing); + + for(auto const& country : m_countries) + { + for(auto const& polygon : country) + { + // This is the essention: + // Directly transform from a multi_polygon (ring-type) to a QPolygonF + QPolygonF qring; + boost::geometry::transform(boost::geometry::exterior_ring(polygon), qring, transformer); + painter.drawPolygon(qring); + } + } +} + +int main(int argc, char *argv[]) +{ + const std::string filename = argc > 1 ? argv[1] : "../../../data/world.wkt"; + const auto countries = read_countries(filename); + if (countries.empty()) + { + std::cout << "No countries read" << std::endl; + return 1; + } + + const auto box = calculate_envelope>(countries); + + QApplication app(argc, argv); + qt_world_mapper mapper(countries, box); + mapper.setWindowTitle("Boost.Geometry for Qt - Hello World!"); + mapper.setGeometry(100, 100, 1024, 768); + mapper.show(); + return app.exec(); +} diff --git a/example/with_external_libs/qt/qt_world_mapper.hpp b/example/with_external_libs/qt/qt_world_mapper.hpp new file mode 100644 index 0000000000..bccfa9fa95 --- /dev/null +++ b/example/with_external_libs/qt/qt_world_mapper.hpp @@ -0,0 +1,47 @@ +// Boost.Geometry +// +// Copyright (c) 2007-2024 Barend Gehrels, Amsterdam, the Netherlands. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Qt World Mapper Example + +#ifndef QT_WORLD_MAPPER_H +#define QT_WORLD_MAPPER_H + +#include + +#include + +#include + +using point_2d = boost::geometry::model::d2::point_xy; +using country_type = boost::geometry::model::multi_polygon + < + boost::geometry::model::polygon + >; + +class qt_world_mapper : public QWidget +{ + Q_OBJECT + +public: + qt_world_mapper(std::vector const& countries, boost::geometry::model::box const& box, QWidget *parent = nullptr); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + using map_transformer_type = boost::geometry::strategy::transform::map_transformer + < + double, 2, 2, + true, true + >; + + std::vector m_countries; + boost::geometry::model::box m_box; +}; + + +#endif diff --git a/example/with_external_libs/wxwidgets/CMakeLists.txt b/example/with_external_libs/wxwidgets/CMakeLists.txt index ad06d01949..dda598699c 100644 --- a/example/with_external_libs/wxwidgets/CMakeLists.txt +++ b/example/with_external_libs/wxwidgets/CMakeLists.txt @@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 3.8...3.20) project(wx_widgets_world_mapper) -add_executable(${PROJECT_NAME} x04_wxwidgets_world_mapper.cpp) +add_executable(${PROJECT_NAME} wxwidgets_world_mapper.cpp) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # Link the wxWidgets libraries to our executable @@ -21,7 +21,7 @@ target_link_libraries(${PROJECT_NAME} wxWidgets::wxWidgets) # Link the Boost.Geometry library to our executable # By default, it is assumed to be relative to this directory. -target_include_directories(${PROJECT_NAME} PRIVATE ../../../../..) +target_include_directories(${PROJECT_NAME} PRIVATE .. ../../../../..) # If this does not work, or you build from elsewhere # First set BOOST_ROOT diff --git a/example/with_external_libs/wxwidgets/x04_wxwidgets_world_mapper.cpp b/example/with_external_libs/wxwidgets/wxwidgets_world_mapper.cpp similarity index 86% rename from example/with_external_libs/wxwidgets/x04_wxwidgets_world_mapper.cpp rename to example/with_external_libs/wxwidgets/wxwidgets_world_mapper.cpp index 40074108bb..c7843d4c33 100644 --- a/example/with_external_libs/wxwidgets/x04_wxwidgets_world_mapper.cpp +++ b/example/with_external_libs/wxwidgets/wxwidgets_world_mapper.cpp @@ -13,7 +13,6 @@ // #define EXAMPLE_WX_USE_GRAPHICS_CONTEXT 1 -#include #include #include @@ -34,6 +33,8 @@ #include "wx/dcgraph.h" #endif +#include "common/read_countries.hpp" + using point_2d = boost::geometry::model::d2::point_xy; using country_type = boost::geometry::model::multi_polygon < @@ -45,41 +46,6 @@ using country_type = boost::geometry::model::multi_polygon BOOST_GEOMETRY_REGISTER_POINT_2D(wxPoint, int, cs::cartesian, x, y) BOOST_GEOMETRY_REGISTER_POINT_2D(wxRealPoint, double, cs::cartesian, x, y) -// ---------------------------------------------------------------------------- -// Read an ASCII file containing WKT's of either POLYGON or MULTIPOLYGON -// ---------------------------------------------------------------------------- -template -inline void read_wkt(std::string const& filename, std::vector& geometries, Box& box) -{ - std::ifstream cpp_file(filename.c_str()); - if (cpp_file.is_open()) - { - while (! cpp_file.eof() ) - { - std::string line; - std::getline(cpp_file, line); - if (line.empty()) - { - continue; - } - Geometry geometry; - if (line.substr(0, 4) == "POLY") - { - boost::geometry::model::polygon polygon; - boost::geometry::read_wkt(line, polygon); - geometry.push_back(polygon); - } - else - { - boost::geometry::read_wkt(line, geometry); - } - - geometries.push_back(geometry); - boost::geometry::expand(box, boost::geometry::return_envelope(geometry)); - } - } -} - // ---------------------------------------------------------------------------- class HelloWorldFrame: public wxFrame @@ -195,8 +161,8 @@ HelloWorldCanvas::HelloWorldCanvas(wxFrame *frame, const std::string& filename) , m_owner(frame) , m_filename(filename) { - boost::geometry::assign_inverse(m_box); - read_wkt(m_filename, m_countries, m_box); + m_countries = read_countries(m_filename); + m_box = calculate_envelope>(m_countries); } diff --git a/example/with_external_libs/x01_qt_example.cpp b/example/with_external_libs/x01_qt_example.cpp deleted file mode 100644 index 4ccade27a3..0000000000 --- a/example/with_external_libs/x01_qt_example.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) -// -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -// Qt Example - -// Qt is a well-known and often used platform independent windows library - -// To build and run this example: -// 1) download (from http://qt.nokia.com), configure and make QT -// 2) if necessary, adapt Qt clause in include path (note there is a Qt property sheet) - -#include - -#include - -#include -#include -#include - - -// Adapt a QPointF such that it can be handled by Boost.Geometry -BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(QPointF, double, cs::cartesian, x, y, setX, setY) - -// Adapt a QPolygonF as well. -// A QPolygonF has no holes (interiors) so it is similar to a Boost.Geometry ring -BOOST_GEOMETRY_REGISTER_RING(QPolygonF) - - -int main(int argc, char *argv[]) -{ - // This usage QApplication and QLabel is adapted from - // http://en.wikipedia.org/wiki/Qt_(toolkit)#Qt_hello_world - QApplication app(argc, argv); - - // Declare a Qt polygon. The Qt Polygon can be used - // in Boost.Geometry, just by its oneline registration above. - QPolygonF polygon; - - // Use Qt to add points to polygon - polygon - << QPointF(10, 20) << QPointF(20, 30) - << QPointF(30, 20) << QPointF(20, 10) - << QPointF(10, 20); - - // Use Boost.Geometry e.g. to calculate area - std::ostringstream out; - out << "Boost.Geometry area: " << boost::geometry::area(polygon) << std::endl; - - // Some functionality is defined in both Qt and Boost.Geometry - QPointF p(20,20); - out << "Qt contains: " - << (polygon.containsPoint(p, Qt::WindingFill) ? "yes" : "no") - << std::endl - << "Boost.Geometry within: " - << (boost::geometry::within(p, polygon) ? "yes" : "no") - << std::endl; - // Detail: if point is ON boundary, Qt says yes, Boost.Geometry says no. - - // Qt defines an iterator - // (which is required for of the Boost.Geometry ring-concept) - // such that Boost.Geometry can use the points of this polygon - QPolygonF::const_iterator it; - for (it = polygon.begin(); it != polygon.end(); ++it) - { - // Stream Delimiter-Separated, just to show something Boost.Geometry can do - out << boost::geometry::dsv(*it) << std::endl; - } - - // Stream the polygon as well - out << boost::geometry::dsv(polygon) << std::endl; - - // Just show what we did in a label - QLabel label(out.str().c_str()); - label.show(); - return app.exec(); -} - diff --git a/example/with_external_libs/x06_qt_world_mapper.cpp b/example/with_external_libs/x06_qt_world_mapper.cpp deleted file mode 100644 index 67bd9067ce..0000000000 --- a/example/with_external_libs/x06_qt_world_mapper.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) -// -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -// Qt World Mapper Example - -// Qt is a well-known and often used platform independent windows library - -// To build and run this example: -// 1) download (from http://qt.nokia.com), configure and make QT -// 2) if necessary, adapt Qt clause in include path (note there is a Qt property sheet) - -#include - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include -#include - -#include - - - -// Adapt a QPointF such that it can be handled by Boost.Geometry -BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(QPointF, double, cs::cartesian, x, y, setX, setY) - -// Adapt a QPolygonF as well. -// A QPolygonF has no holes (interiors) so it is similar to a Boost.Geometry ring -BOOST_GEOMETRY_REGISTER_RING(QPolygonF) - - -typedef boost::geometry::model::d2::point_xy point_2d; -typedef boost::geometry::model::multi_polygon - < - boost::geometry::model::polygon - > country_type; - - -class WorldMapper : public QWidget -{ - public: - WorldMapper(std::vector const& countries, boost::geometry::model::box const& box) - : m_countries(countries) - , m_box(box) - { - setPalette(QPalette(QColor(200, 250, 250))); - setAutoFillBackground(true); - } - - protected: - void paintEvent(QPaintEvent*) - { - map_transformer_type transformer(m_box, this->width(), this->height()); - - QPainter painter(this); - painter.setBrush(Qt::green); - painter.setRenderHint(QPainter::Antialiasing); - - BOOST_FOREACH(country_type const& country, m_countries) - { - typedef boost::range_value::type polygon_type; - BOOST_FOREACH(polygon_type const& polygon, country) - { - typedef boost::geometry::ring_type::type ring_type; - ring_type const& ring = boost::geometry::exterior_ring(polygon); - - // This is the essention: - // Directly transform from a multi_polygon (ring-type) to a QPolygonF - QPolygonF qring; - boost::geometry::transform(ring, qring, transformer); - - painter.drawPolygon(qring); - } - } - } - - private: - typedef boost::geometry::strategy::transform::map_transformer - < - double, 2, 2, - true, true - > map_transformer_type; - - std::vector const& m_countries; - boost::geometry::model::box const& m_box; - }; - - -class MapperWidget : public QWidget -{ - public: - MapperWidget(std::vector const& countries, boost::geometry::model::box const& box, QWidget *parent = 0) - : QWidget(parent) - { - WorldMapper* mapper = new WorldMapper(countries, box); - - QPushButton *quit = new QPushButton(tr("Quit")); - quit->setFont(QFont("Times", 18, QFont::Bold)); - connect(quit, SIGNAL(clicked()), qApp, SLOT(quit())); - - QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(mapper); - layout->addWidget(quit); - setLayout(layout); - } - -}; - - -// ---------------------------------------------------------------------------- -// Read an ASCII file containing WKT's -// ---------------------------------------------------------------------------- -template -inline void read_wkt(std::string const& filename, std::vector& geometries, Box& box) -{ - std::ifstream cpp_file(filename.c_str()); - if (cpp_file.is_open()) - { - while (! cpp_file.eof() ) - { - std::string line; - std::getline(cpp_file, line); - if (! line.empty()) - { - Geometry geometry; - boost::geometry::read_wkt(line, geometry); - geometries.push_back(geometry); - boost::geometry::expand(box, boost::geometry::return_envelope(geometry)); - } - } - } -} - - -int main(int argc, char *argv[]) -{ - std::vector countries; - boost::geometry::model::box box; - boost::geometry::assign_inverse(box); - read_wkt("../data/world.wkt", countries, box); - - QApplication app(argc, argv); - MapperWidget widget(countries, box); - widget.setWindowTitle("Boost.Geometry for Qt - Hello World!"); - widget.setGeometry(50, 50, 800, 500); - widget.show(); - return app.exec(); -} diff --git a/example/with_external_libs/x06_qt_world_mapper_readme.txt b/example/with_external_libs/x06_qt_world_mapper_readme.txt deleted file mode 100644 index 131bcca211..0000000000 --- a/example/with_external_libs/x06_qt_world_mapper_readme.txt +++ /dev/null @@ -1,23 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) -// -// Copyright Barend Gehrels 2011, Geodan, Amsterdam, the Netherlands -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -Qt World Mapper example - -It will show a basic Qt Widget, displaying world countries - -To compile this program: - -Install Qt (if not done before) - -Using Linux/gcc - - install Qt with sudo apt-get install libqt4-dev - - run qmake -project - - edit the generated file "with_external_libs.pro" and delete all lines but the x06_qt_world_mapper.cpp - - run qmake - - edit the generated Makefile, if necessary, and add -I../../../.. to include Boost and Boost.Geometry - - run make -