Skip to content

Commit

Permalink
[examples] Update qt example
Browse files Browse the repository at this point in the history
  • Loading branch information
barendgehrels committed Sep 13, 2024
1 parent e8d8c39 commit a726a95
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 306 deletions.
66 changes: 66 additions & 0 deletions example/with_external_libs/common/read_countries.hpp
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions example/with_external_libs/qt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
..
../../../../..
)
64 changes: 64 additions & 0 deletions example/with_external_libs/qt/README.md
Original file line number Diff line number Diff line change
@@ -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`
77 changes: 77 additions & 0 deletions example/with_external_libs/qt/qt_world_mapper.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
47 changes: 47 additions & 0 deletions example/with_external_libs/qt/qt_world_mapper.hpp
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions example/with_external_libs/wxwidgets/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

// #define EXAMPLE_WX_USE_GRAPHICS_CONTEXT 1

#include <fstream>
#include <sstream>

#include <boost/geometry/geometry.hpp>
Expand All @@ -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
<
Expand All @@ -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
Expand Down Expand Up @@ -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);
}


Expand Down
Loading

0 comments on commit a726a95

Please sign in to comment.