[examples] Update qt example

This commit is contained in:
Barend Gehrels 2024-09-08 13:03:20 +02:00
parent 12cac83034
commit 6c173505d9
10 changed files with 296 additions and 306 deletions

View File

@ -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 <fstream>
#include <boost/geometry/geometry.hpp>
// ----------------------------------------------------------------------------
// Read an ASCII file containing WKT's of either POLYGON or MULTIPOLYGON
// ----------------------------------------------------------------------------
template <typename Geometry>
std::vector<Geometry> read_countries(std::string const& filename)
{
std::vector<Geometry> 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<decltype(*geometry.begin())>;
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 <typename Box, typename Countries>
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<Box>(country));
}
return box;
}
#endif

View File

@ -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
..
../../../../..
)

View File

@ -0,0 +1,64 @@
# ![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
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)
```
And then you can use the standard CMake workflow.
If you want to use Qt with Vulkan, or get compiler errors or warnings because of Vulkan missing,
and you want to solve that, then install Vulkan as described [here](https://vulkan.lunarg.com/doc/sdk/1.3.239.0/linux/getting_started_ubuntu.html).
### 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, an 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`

View File

@ -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 <QApplication>
#include <QPainter>
#include <QTime>
#include <QTimer>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
// 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<country_type> const& countries, boost::geometry::model::box<point_2d> 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<country_type>(filename);
if (countries.empty())
{
std::cout << "No countries read" << std::endl;
return 1;
}
const auto box = calculate_envelope<boost::geometry::model::box<point_2d>>(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();
}

View File

@ -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 <QWidget>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
using point_2d = boost::geometry::model::d2::point_xy<double>;
using country_type = boost::geometry::model::multi_polygon
<
boost::geometry::model::polygon<point_2d>
>;
class qt_world_mapper : public QWidget
{
Q_OBJECT
public:
qt_world_mapper(std::vector<country_type> const& countries, boost::geometry::model::box<point_2d> 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<country_type> m_countries;
boost::geometry::model::box<point_2d> m_box;
};
#endif

View File

@ -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

View File

@ -13,7 +13,6 @@
// #define EXAMPLE_WX_USE_GRAPHICS_CONTEXT 1
#include <fstream>
#include <sstream>
#include <boost/geometry/geometry.hpp>
@ -34,6 +33,8 @@
#include "wx/dcgraph.h"
#endif
#include "common/read_countries.hpp"
using point_2d = boost::geometry::model::d2::point_xy<double>;
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 <typename Geometry, typename Box>
inline void read_wkt(std::string const& filename, std::vector<Geometry>& 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<point_2d> 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<Box>(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<country_type>(m_filename);
m_box = calculate_envelope<boost::geometry::model::box<point_2d>>(m_countries);
}

View File

@ -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 <sstream>
#include <QtGui>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
// 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();
}

View File

@ -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 <fstream>
#include <QtGui>
#include <QWidget>
#include <QObject>
#include <QPainter>
#include <boost/foreach.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/geometries/multi_geometries.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
#include <boost/geometry/extensions/algorithms/selected.hpp>
// 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<double> point_2d;
typedef boost::geometry::model::multi_polygon
<
boost::geometry::model::polygon<point_2d>
> country_type;
class WorldMapper : public QWidget
{
public:
WorldMapper(std::vector<country_type> const& countries, boost::geometry::model::box<point_2d> 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<country_type>::type polygon_type;
BOOST_FOREACH(polygon_type const& polygon, country)
{
typedef boost::geometry::ring_type<polygon_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<country_type> const& m_countries;
boost::geometry::model::box<point_2d> const& m_box;
};
class MapperWidget : public QWidget
{
public:
MapperWidget(std::vector<country_type> const& countries, boost::geometry::model::box<point_2d> 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 <typename Geometry, typename Box>
inline void read_wkt(std::string const& filename, std::vector<Geometry>& 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<Box>(geometry));
}
}
}
}
int main(int argc, char *argv[])
{
std::vector<country_type> countries;
boost::geometry::model::box<point_2d> 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();
}

View File

@ -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