geometry/example/x04_wxwidgets_world_mapper.cpp
2010-05-11 04:18:22 +00:00

417 lines
11 KiB
C++

// Boost.Geometry (aka GGL, Generic Geometry Library)
//
// Copyright Barend Gehrels 2010, 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)
//
// wxWidgets World Mapper example
// #define EXAMPLE_WX_USE_GRAPHICS_CONTEXT 1
// For Boost/uBLAS:
#define _SCL_SECURE_NO_WARNINGS 1
#include <fstream>
#include <sstream>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
#include <boost/geometry/multi/multi.hpp>
#include <boost/geometry/geometries/cartesian2d.hpp>
#include <boost/geometry/extensions/algorithms/selected.hpp>
#include <boost/geometry/extensions/gis/io/wkt/read_wkt.hpp>
#include <boost/geometry/extensions/gis/io/wkt/read_wkt_multi.hpp>
// wxWidgets, if these headers are NOT found, adapt include path (and lib path)
#include "wx/wx.h"
#include "wx/math.h"
#include "wx/stockitem.h"
#ifdef EXAMPLE_WX_USE_GRAPHICS_CONTEXT
#include "wx/graphics.h"
#endif
// wxWidgets draws using wxPoint*
// So we have to make a wxPoint* array compatible with Boost.Range
// and with std::back_inserter
// So we define an iterator pair:
typedef std::pair<wxPoint*,wxPoint*> wxPointPointerPair;
// Adapt wxWidgets points to Boost.Geometry points such that they can be used
// in e.g. transformations (see below)
BOOST_GEOMETRY_REGISTER_POINT_2D(wxPoint, int, cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_POINT_2D(wxRealPoint, double, cs::cartesian, x, y)
// Done explicitly below to implement "clear"
//BOOST_GEOMETRY_REGISTER_RING(wxPointPointerPair);
// Implement a draft back_insert_iterator for such a pair of pointers
// It might exist somewhere in Boost yet.
namespace std
{
template <>
class back_insert_iterator<wxPointPointerPair>
{
public:
explicit back_insert_iterator(wxPointPointerPair& x)
: current(boost::begin(x))
, end(boost::end(x))
{}
back_insert_iterator<wxPointPointerPair>&
operator=(const wxPoint& value)
{
// Check if not passed beyond
if (current != end)
{
*current++ = value;
}
return *this;
}
// Boiler-plate
back_insert_iterator<wxPointPointerPair>& operator*() { return *this; }
back_insert_iterator<wxPointPointerPair>& operator++() { return *this; }
back_insert_iterator<wxPointPointerPair>& operator++(int) { return *this; }
typedef wxPointPointerPair container_type;
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
protected:
boost::range_iterator<wxPointPointerPair>::type current, end;
};
}
namespace boost { namespace geometry { namespace traits
{
template <>
struct tag< wxPointPointerPair > { typedef ring_tag type; };
template <>
struct use_std< wxPointPointerPair >
{
static bool const value = false;
};
template <>
struct clear< wxPointPointerPair >
{
static inline void apply(wxPointPointerPair& ls)
{
// Empty on purpose, fixed size, cannot be cleared
!!! /*TODO*/ 0;
}
};
}}} // namespace boost::geometry::traits
typedef boost::geometry::multi_polygon<boost::geometry::polygon_2d> country_type;
// ----------------------------------------------------------------------------
// Read an ASCII file containing WKT's
// ----------------------------------------------------------------------------
template <typename Geometry, typename Box>
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::combine(box, boost::geometry::make_envelope<Box>(geometry));
}
}
}
}
// ----------------------------------------------------------------------------
class HelloWorldFrame: public wxFrame
{
public:
HelloWorldFrame(wxFrame *frame, wxString const& title, wxPoint const& pos, wxSize const& size);
void OnCloseWindow(wxCloseEvent& );
void OnExit(wxCommandEvent& );
DECLARE_EVENT_TABLE()
};
// ----------------------------------------------------------------------------
class HelloWorldCanvas: public wxWindow
{
public:
HelloWorldCanvas(wxFrame *frame);
private:
void DrawCountries(wxDC& dc);
void DrawCountry(wxDC& dc, country_type const& country);
void OnPaint(wxPaintEvent& );
void OnMouseMove(wxMouseEvent&);
typedef boost::geometry::strategy::transform::map_transformer
<
boost::geometry::point_2d, wxPoint,
true, true
> map_transformer_type;
typedef boost::geometry::strategy::transform::inverse_transformer
<
wxPoint, boost::geometry::point_2d
> inverse_transformer_type;
boost::shared_ptr<map_transformer_type> m_map_transformer;
boost::shared_ptr<inverse_transformer_type> m_inverse_transformer;
boost::geometry::box_2d m_box;
std::vector<country_type> m_countries;
int m_focus;
wxBrush m_orange;
wxFrame* m_owner;
DECLARE_EVENT_TABLE()
};
// ----------------------------------------------------------------------------
class HelloWorldApp: public wxApp
{
public:
bool OnInit()
{
// Create the main frame window
HelloWorldFrame *frame = new HelloWorldFrame(NULL, _T("Boost.Geometry for wxWidgets - Hello World!"), wxDefaultPosition, wxSize(640, 480));
wxMenu *file_menu = new wxMenu;
file_menu->Append(wxID_EXIT, wxGetStockLabel(wxID_EXIT));
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(file_menu, _T("&File"));
frame->SetMenuBar(menuBar);
int width, height;
frame->GetClientSize(&width, &height);
(void) new HelloWorldCanvas(frame);
// Show the frame
frame->Show(true);
return true;
}
};
// ----------------------------------------------------------------------------
HelloWorldFrame::HelloWorldFrame(wxFrame *frame, wxString const& title, wxPoint const& pos, wxSize const& size)
: wxFrame(frame, wxID_ANY, title, pos, size, wxDEFAULT_FRAME_STYLE | wxFULL_REPAINT_ON_RESIZE )
{
CreateStatusBar(2);
}
void HelloWorldFrame::OnExit(wxCommandEvent& )
{
this->Destroy();
}
void HelloWorldFrame::OnCloseWindow(wxCloseEvent& )
{
static bool destroyed = false;
if (! destroyed)
{
this->Destroy();
destroyed = true;
}
}
// ----------------------------------------------------------------------------
HelloWorldCanvas::HelloWorldCanvas(wxFrame *frame)
: wxWindow(frame, wxID_ANY)
, m_owner(frame)
, m_focus(-1)
{
boost::geometry::assign_inverse(m_box);
read_wkt("data/world.wkt", m_countries, m_box);
m_orange = wxBrush(wxColour(255, 128, 0), wxSOLID);
}
void HelloWorldCanvas::OnMouseMove(wxMouseEvent &event)
{
namespace bg = boost::geometry;
if (m_inverse_transformer)
{
// Boiler-plate wxWidgets code
wxClientDC dc(this);
PrepareDC(dc);
m_owner->PrepareDC(dc);
// Transform the point to Lon/Lat
bg::point_2d point;
bg::transform(event.GetPosition(), point, *m_inverse_transformer);
// Determine selected object
int i = 0;
int previous_focus = m_focus;
m_focus = -1;
BOOST_FOREACH(country_type const& country, m_countries)
{
if (bg::selected(country, point, 0))
{
m_focus = i;
}
i++;
}
// On change:
if (m_focus != previous_focus)
{
// Undraw old focus
if (previous_focus >= 0)
{
dc.SetBrush(*wxWHITE_BRUSH);
DrawCountry(dc, m_countries[previous_focus]);
}
// Draw new focus
if (m_focus >= 0)
{
dc.SetBrush(m_orange);
DrawCountry(dc, m_countries[m_focus]);
}
}
// Create a string and set it in the status text
std::ostringstream out;
out << "Position: " << point.x() << ", " << point.y();
m_owner->SetStatusText(wxString(out.str().c_str(), wxConvUTF8));
}
}
void HelloWorldCanvas::OnPaint(wxPaintEvent& )
{
#if defined(EXAMPLE_WX_USE_GRAPHICS_CONTEXT)
wxPaintDC pdc(this);
wxGCDC gdc(pdc);
wxDC& dc = (wxDC&) gdc;
#else
wxPaintDC dc(this);
#endif
PrepareDC(dc);
static bool running = false;
if (! running)
{
running = true;
// Update the transformers
wxSize sz = dc.GetSize();
m_map_transformer.reset(new map_transformer_type(m_box, sz.x, sz.y));
m_inverse_transformer.reset(new inverse_transformer_type(*m_map_transformer));
DrawCountries(dc);
running = false;
}
}
void HelloWorldCanvas::DrawCountries(wxDC& dc)
{
namespace bg = boost::geometry;
dc.SetBackground(*wxLIGHT_GREY_BRUSH);
dc.Clear();
BOOST_FOREACH(country_type const& country, m_countries)
{
DrawCountry(dc, country);
}
if (m_focus != -1)
{
dc.SetBrush(m_orange);
DrawCountry(dc, m_countries[m_focus]);
}
}
void HelloWorldCanvas::DrawCountry(wxDC& dc, country_type const& country)
{
namespace bg = boost::geometry;
BOOST_FOREACH(bg::polygon_2d const& poly, country)
{
// Use only outer, holes are (for the moment) ignored. This would need
// a holey-polygon compatible wx object
std::size_t n = boost::size(poly.outer());
boost::scoped_array<wxPoint> points(new wxPoint[n]);
bg::transform(poly.outer(),
std::make_pair(points.get(), points.get() + n),
*m_map_transformer);
dc.DrawPolygon(n, points.get());
}
}
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(HelloWorldFrame, wxFrame)
EVT_CLOSE(HelloWorldFrame::OnCloseWindow)
EVT_MENU(wxID_EXIT, HelloWorldFrame::OnExit)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(HelloWorldCanvas, wxWindow)
EVT_PAINT(HelloWorldCanvas::OnPaint)
EVT_MOTION(HelloWorldCanvas::OnMouseMove)
END_EVENT_TABLE()
IMPLEMENT_APP(HelloWorldApp)