627 lines
17 KiB
C++
627 lines
17 KiB
C++
/* This file is part of Mapnik (c++ mapping toolkit)
|
|
*
|
|
* Copyright (C) 2024 Artem Pavlenko
|
|
*
|
|
* Mapnik is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <QtGui>
|
|
|
|
#include <boost/bind.hpp>
|
|
#include <mapnik/agg_renderer.hpp>
|
|
#include <mapnik/layer.hpp>
|
|
#include <mapnik/proj_transform.hpp>
|
|
#include <mapnik/scale_denominator.hpp>
|
|
#include <mapnik/view_transform.hpp>
|
|
#include <mapnik/transform_path_adapter.hpp>
|
|
#include <mapnik/memory_datasource.hpp>
|
|
#include <mapnik/feature_kv_iterator.hpp>
|
|
#include <mapnik/image_util.hpp>
|
|
#include <mapnik/util/timer.hpp>
|
|
|
|
#ifdef HAVE_CAIRO
|
|
// cairo
|
|
#include <mapnik/cairo/cairo_image_util.hpp>
|
|
#include <mapnik/cairo/cairo_renderer.hpp>
|
|
#endif
|
|
|
|
#include "mapwidget.hpp"
|
|
#include "info_dialog.hpp"
|
|
|
|
using mapnik::box2d;
|
|
using mapnik::coord2d;
|
|
using mapnik::feature_kv_iterator;
|
|
using mapnik::feature_ptr;
|
|
using mapnik::image_rgba8;
|
|
using mapnik::layer;
|
|
using mapnik::Map;
|
|
using mapnik::projection;
|
|
using mapnik::scale_denominator;
|
|
using mapnik::view_transform;
|
|
|
|
double scales[] = {279541132.014, 139770566.007, 69885283.0036, 34942641.5018, 17471320.7509,
|
|
8735660.37545, 4367830.18772, 2183915.09386, 1091957.54693, 545978.773466,
|
|
272989.386733, 136494.693366, 68247.3466832, 34123.6733416, 17061.8366708,
|
|
8530.9183354, 4265.4591677, 2132.72958385, 1066.36479192, 533.182395962};
|
|
|
|
MapWidget::MapWidget(QWidget* parent)
|
|
: QWidget(parent)
|
|
, map_()
|
|
, selected_(1)
|
|
, extent_()
|
|
, cur_tool_(ZoomToBox)
|
|
, start_x_(0)
|
|
, start_y_(0)
|
|
, end_x_(0)
|
|
, end_y_(0)
|
|
, drag_(false)
|
|
, first_(true)
|
|
, pen_(QColor(0, 0, 255, 96))
|
|
, selectedLayer_(-1)
|
|
, scaling_factor_(1.0)
|
|
, cur_renderer_(AGG)
|
|
{
|
|
pen_.setWidth(3);
|
|
pen_.setCapStyle(Qt::RoundCap);
|
|
pen_.setJoinStyle(Qt::RoundJoin);
|
|
}
|
|
|
|
void MapWidget::setTool(eTool tool)
|
|
{
|
|
cur_tool_ = tool;
|
|
}
|
|
|
|
void MapWidget::paintEvent(QPaintEvent*)
|
|
{
|
|
QPainter painter(this);
|
|
|
|
if (drag_)
|
|
{
|
|
if (cur_tool_ == ZoomToBox)
|
|
{
|
|
unsigned width = end_x_ - start_x_;
|
|
unsigned height = end_y_ - start_y_;
|
|
painter.drawPixmap(QPoint(0, 0), pix_);
|
|
painter.setPen(pen_);
|
|
painter.setBrush(QColor(200, 200, 255, 128));
|
|
painter.drawRect(start_x_, start_y_, width, height);
|
|
}
|
|
else if (cur_tool_ == Pan)
|
|
{
|
|
int dx = end_x_ - start_x_;
|
|
int dy = end_y_ - start_y_;
|
|
painter.setBrush(QColor(200, 200, 200, 128));
|
|
painter.drawRect(0, 0, width(), height());
|
|
painter.drawPixmap(QPoint(dx, dy), pix_);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
painter.drawPixmap(QPoint(0, 0), pix_);
|
|
}
|
|
painter.end();
|
|
}
|
|
|
|
void MapWidget::resizeEvent(QResizeEvent* ev)
|
|
{
|
|
if (map_)
|
|
{
|
|
map_->resize(ev->size().width(), ev->size().height());
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::mousePressEvent(QMouseEvent* e)
|
|
{
|
|
if (e->button() == Qt::LeftButton)
|
|
{
|
|
if (cur_tool_ == ZoomToBox || cur_tool_ == Pan)
|
|
{
|
|
start_x_ = e->x();
|
|
start_y_ = e->y();
|
|
drag_ = true;
|
|
}
|
|
else if (cur_tool_ == Info)
|
|
{
|
|
if (map_)
|
|
{
|
|
QVector<QPair<QString, QString>> info;
|
|
|
|
projection map_proj(map_->srs(), true); // map projection
|
|
double scale_denom = scale_denominator(map_->scale(), map_proj.is_geographic());
|
|
view_transform t(map_->width(), map_->height(), map_->get_current_extent());
|
|
|
|
for (unsigned index = 0; index < map_->layer_count(); ++index)
|
|
{
|
|
if (int(index) != selectedLayer_)
|
|
continue;
|
|
|
|
layer& layer = map_->layers()[index];
|
|
if (!layer.visible(scale_denom))
|
|
continue;
|
|
std::string name = layer.name();
|
|
double x = e->x();
|
|
double y = e->y();
|
|
std::cout << "query at " << x << "," << y << "\n";
|
|
projection layer_proj(layer.srs(), true);
|
|
mapnik::proj_transform prj_trans(map_proj, layer_proj);
|
|
// std::auto_ptr<mapnik::memory_datasource> data(new mapnik::memory_datasource);
|
|
mapnik::featureset_ptr fs = map_->query_map_point(index, x, y);
|
|
|
|
if (fs)
|
|
{
|
|
feature_ptr feat = fs->next();
|
|
if (feat)
|
|
{
|
|
feature_kv_iterator itr(*feat, true);
|
|
feature_kv_iterator end(*feat);
|
|
|
|
for (; itr != end; ++itr)
|
|
{
|
|
info.push_back(QPair<QString, QString>(QString(std::get<0>(*itr).c_str()),
|
|
std::get<1>(*itr).to_string().c_str()));
|
|
}
|
|
|
|
#if 0 //
|
|
using path_type = mapnik::transform_path_adapter<mapnik::view_transform,mapnik::vertex_adapter>;
|
|
|
|
for (unsigned i=0; i < feat->num_geometries();++i)
|
|
{
|
|
mapnik::geometry_type const& geom = feat->get_geometry(i);
|
|
mapnik::vertex_adapter va(geom);
|
|
path_type path(t,va,prj_trans);
|
|
if (va.size() > 0)
|
|
{
|
|
QPainterPath qpath;
|
|
double x,y;
|
|
va.vertex(&x,&y);
|
|
qpath.moveTo(x,y);
|
|
for (unsigned j = 1; j < geom.size(); ++j)
|
|
{
|
|
va.vertex(&x,&y);
|
|
qpath.lineTo(x,y);
|
|
}
|
|
QPainter painter(&pix_);
|
|
QPen pen(QColor(255,0,0,96));
|
|
pen.setWidth(3);
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
|
painter.setPen(pen);
|
|
painter.drawPath(qpath);
|
|
update();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (info.size() > 0)
|
|
{
|
|
info_dialog info_dlg(info, this);
|
|
info_dlg.exec();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// remove annotation layer
|
|
map_->layers().erase(
|
|
remove_if(map_->layers().begin(), map_->layers().end(), bind(&layer::name, _1) == "*annotations*"),
|
|
map_->layers().end());
|
|
}
|
|
}
|
|
}
|
|
else if (e->button() == Qt::RightButton)
|
|
{
|
|
// updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::mouseMoveEvent(QMouseEvent* e)
|
|
{
|
|
if (cur_tool_ == ZoomToBox || cur_tool_ == Pan)
|
|
{
|
|
end_x_ = e->x();
|
|
end_y_ = e->y();
|
|
update();
|
|
}
|
|
}
|
|
|
|
void MapWidget::mouseReleaseEvent(QMouseEvent* e)
|
|
{
|
|
if (e->button() == Qt::LeftButton)
|
|
{
|
|
end_x_ = e->x();
|
|
end_y_ = e->y();
|
|
if (cur_tool_ == ZoomToBox)
|
|
{
|
|
drag_ = false;
|
|
if (map_)
|
|
{
|
|
view_transform t(map_->width(), map_->height(), map_->get_current_extent());
|
|
box2d<double> box = t.backward(box2d<double>(start_x_, start_y_, end_x_, end_y_));
|
|
map_->zoom_to_box(box);
|
|
updateMap();
|
|
}
|
|
}
|
|
else if (cur_tool_ == Pan)
|
|
{
|
|
drag_ = false;
|
|
if (map_)
|
|
{
|
|
int cx = int(0.5 * map_->width());
|
|
int cy = int(0.5 * map_->height());
|
|
int dx = end_x_ - start_x_;
|
|
int dy = end_y_ - start_y_;
|
|
map_->pan(cx - dx, cy - dy);
|
|
updateMap();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MapWidget::wheelEvent(QWheelEvent* e)
|
|
{
|
|
if (!map_)
|
|
{
|
|
return;
|
|
}
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
QPointF corner(map_->width(), map_->height());
|
|
QPointF zoomCoords;
|
|
double zoom;
|
|
if (e->angleDelta().y() > 0)
|
|
{
|
|
zoom = 0.5;
|
|
QPointF center = corner / 2;
|
|
QPointF delta = e->position() - center;
|
|
zoomCoords = zoom * delta + center;
|
|
}
|
|
else
|
|
{
|
|
zoom = 2.0;
|
|
zoomCoords = corner - e->position();
|
|
}
|
|
#else
|
|
QPoint corner(map_->width(), map_->height());
|
|
QPoint zoomCoords;
|
|
double zoom;
|
|
if (e->delta() > 0)
|
|
{
|
|
zoom = 0.5;
|
|
QPoint center = corner / 2;
|
|
QPoint delta = e->pos() - center;
|
|
zoomCoords = zoom * delta + center;
|
|
}
|
|
else
|
|
{
|
|
zoom = 2.0;
|
|
zoomCoords = corner - e->pos();
|
|
}
|
|
#endif
|
|
|
|
map_->pan_and_zoom(zoomCoords.x(), zoomCoords.y(), zoom);
|
|
updateMap();
|
|
}
|
|
|
|
void MapWidget::keyPressEvent(QKeyEvent* e)
|
|
{
|
|
std::cout << "key pressed:" << e->key() << "\n";
|
|
switch (e->key())
|
|
{
|
|
case Qt::Key_Minus:
|
|
zoomOut();
|
|
break;
|
|
case Qt::Key_Plus:
|
|
case 61:
|
|
zoomIn();
|
|
break;
|
|
case 65:
|
|
defaultView();
|
|
break;
|
|
case Qt::Key_Up:
|
|
panUp();
|
|
break;
|
|
case Qt::Key_Down:
|
|
panDown();
|
|
break;
|
|
case Qt::Key_Left:
|
|
panLeft();
|
|
break;
|
|
case Qt::Key_Right:
|
|
panRight();
|
|
break;
|
|
case 49:
|
|
zoomToLevel(10);
|
|
break;
|
|
case 50:
|
|
zoomToLevel(11);
|
|
break;
|
|
case 51:
|
|
zoomToLevel(12);
|
|
break;
|
|
case 52:
|
|
zoomToLevel(13);
|
|
break;
|
|
case 53:
|
|
zoomToLevel(14);
|
|
break;
|
|
case 54:
|
|
zoomToLevel(15);
|
|
break;
|
|
case 55:
|
|
zoomToLevel(16);
|
|
break;
|
|
case 56:
|
|
zoomToLevel(17);
|
|
break;
|
|
case 57:
|
|
zoomToLevel(18);
|
|
break;
|
|
default:
|
|
QWidget::keyPressEvent(e);
|
|
}
|
|
}
|
|
|
|
void MapWidget::zoomToBox(mapnik::box2d<double> const& bbox)
|
|
{
|
|
if (map_)
|
|
{
|
|
map_->zoom_to_box(bbox);
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::defaultView()
|
|
{
|
|
if (map_)
|
|
{
|
|
map_->resize(width(), height());
|
|
map_->zoom_all();
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::zoomIn()
|
|
{
|
|
if (map_)
|
|
{
|
|
map_->zoom(0.5);
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::zoomOut()
|
|
{
|
|
if (map_)
|
|
{
|
|
map_->zoom(2.0);
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::panUp()
|
|
{
|
|
if (map_)
|
|
{
|
|
double cx = 0.5 * map_->width();
|
|
double cy = 0.5 * map_->height();
|
|
map_->pan(int(cx), int(cy - cy * 0.25));
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::panDown()
|
|
{
|
|
if (map_)
|
|
{
|
|
double cx = 0.5 * map_->width();
|
|
double cy = 0.5 * map_->height();
|
|
map_->pan(int(cx), int(cy + cy * 0.25));
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::panLeft()
|
|
{
|
|
if (map_)
|
|
{
|
|
double cx = 0.5 * map_->width();
|
|
double cy = 0.5 * map_->height();
|
|
map_->pan(int(cx - cx * 0.25), int(cy));
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::panRight()
|
|
{
|
|
if (map_)
|
|
{
|
|
double cx = 0.5 * map_->width();
|
|
double cy = 0.5 * map_->height();
|
|
map_->pan(int(cx + cx * 0.25), int(cy));
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::zoomToLevel(int level)
|
|
{
|
|
if (map_ && level >= 0 && level < 19)
|
|
{
|
|
double scale_denom = scales[level];
|
|
std::cerr << "scale denominator = " << scale_denom << "\n";
|
|
mapnik::box2d<double> ext = map_->get_current_extent();
|
|
double width = static_cast<double>(map_->width());
|
|
double height = static_cast<double>(map_->height());
|
|
mapnik::coord2d pt = ext.center();
|
|
|
|
double res = scale_denom * 0.00028;
|
|
|
|
mapnik::box2d<double> box(pt.x - 0.5 * width * res,
|
|
pt.y - 0.5 * height * res,
|
|
pt.x + 0.5 * width * res,
|
|
pt.y + 0.5 * height * res);
|
|
map_->zoom_to_box(box);
|
|
updateMap();
|
|
}
|
|
}
|
|
|
|
void MapWidget::export_to_file(unsigned, unsigned, std::string const&, std::string const&)
|
|
{
|
|
// image_rgba8 image(width,height);
|
|
// agg_renderer renderer(map,image);
|
|
// renderer.apply();
|
|
// image.saveToFile(filename,type);
|
|
std::cout << "Export to file .." << std::endl;
|
|
}
|
|
|
|
void MapWidget::set_scaling_factor(double scaling_factor)
|
|
{
|
|
scaling_factor_ = scaling_factor;
|
|
}
|
|
|
|
void render_agg(mapnik::Map const& map, double scaling_factor, QPixmap& pix)
|
|
{
|
|
unsigned width = map.width();
|
|
unsigned height = map.height();
|
|
|
|
image_rgba8 buf(width, height);
|
|
mapnik::agg_renderer<image_rgba8> ren(map, buf, scaling_factor);
|
|
|
|
try
|
|
{
|
|
mapnik::auto_cpu_timer t(std::clog, "rendering took: ");
|
|
ren.apply();
|
|
QImage image((uchar*)buf.data(), width, height, QImage::Format_ARGB32);
|
|
pix = QPixmap::fromImage(image.rgbSwapped());
|
|
}
|
|
// catch (mapnik::config_error & ex)
|
|
//{
|
|
// std::cerr << ex.what() << std::endl;
|
|
// }
|
|
catch (std::exception const& ex)
|
|
{
|
|
std::cerr << "exception: " << ex.what() << std::endl;
|
|
}
|
|
catch (...)
|
|
{
|
|
std::cerr << "Unknown exception caught!\n";
|
|
}
|
|
}
|
|
|
|
void render_grid(mapnik::Map const& map, double scaling_factor, QPixmap& pix)
|
|
{
|
|
std::cerr << "Not supported" << std::endl;
|
|
}
|
|
|
|
void render_cairo(mapnik::Map const& map, double scaling_factor, QPixmap& pix)
|
|
{
|
|
// FIXME
|
|
#ifdef HAVE_CAIRO
|
|
mapnik::cairo_surface_ptr image_surface(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, map.width(), map.height()),
|
|
mapnik::cairo_surface_closer());
|
|
mapnik::cairo_ptr cairo = mapnik::create_context(image_surface);
|
|
if (cairo)
|
|
{
|
|
mapnik::auto_cpu_timer t(std::clog, "rendering took: ");
|
|
mapnik::cairo_renderer<mapnik::cairo_ptr> renderer(map, cairo, scaling_factor);
|
|
renderer.apply();
|
|
}
|
|
mapnik::image_rgba8 data(map.width(), map.height());
|
|
mapnik::cairo_image_to_rgba8(data, image_surface);
|
|
QImage image((uchar*)data.bytes(), data.width(), data.height(), QImage::Format_ARGB32);
|
|
pix = QPixmap::fromImage(image.rgbSwapped());
|
|
#endif
|
|
}
|
|
|
|
void MapWidget::updateRenderer(int index)
|
|
{
|
|
std::cerr << "updateRenderer:" << index << std::endl;
|
|
if (index == 0)
|
|
cur_renderer_ = AGG;
|
|
else if (index == 1)
|
|
cur_renderer_ = Cairo;
|
|
else if (index == 2)
|
|
cur_renderer_ = Grid;
|
|
updateMap();
|
|
}
|
|
|
|
void MapWidget::updateScaleFactor(double scale_factor)
|
|
{
|
|
set_scaling_factor(scale_factor);
|
|
updateMap();
|
|
}
|
|
|
|
void MapWidget::updateMap()
|
|
{
|
|
if (map_)
|
|
{
|
|
if (cur_renderer_ == AGG)
|
|
{
|
|
render_agg(*map_, scaling_factor_, pix_);
|
|
}
|
|
else if (cur_renderer_ == Cairo)
|
|
{
|
|
render_cairo(*map_, scaling_factor_, pix_);
|
|
}
|
|
else if (cur_renderer_ == Grid)
|
|
{
|
|
render_grid(*map_, scaling_factor_, pix_);
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Unknown renderer..." << std::endl;
|
|
}
|
|
|
|
try
|
|
{
|
|
projection prj(map_->srs(), true); // map projection
|
|
box2d<double> ext = map_->get_current_extent();
|
|
double x0 = ext.minx();
|
|
double y0 = ext.miny();
|
|
double x1 = ext.maxx();
|
|
double y1 = ext.maxy();
|
|
double z = 0;
|
|
std::string dest_srs = {"epsg:4326"};
|
|
mapnik::proj_transform proj_tr(map_->srs(), dest_srs);
|
|
|
|
proj_tr.forward(x0, y0, z);
|
|
proj_tr.forward(x1, y1, z);
|
|
std::cout << "MAP SIZE:" << map_->width() << "," << map_->height() << std::endl;
|
|
std::cout << "BBOX (WGS84): " << x0 << "," << y0 << "," << x1 << "," << y1 << "\n";
|
|
update();
|
|
// emit signal to interested widgets
|
|
emit mapViewChanged();
|
|
}
|
|
catch (...)
|
|
{
|
|
std::cerr << "Unknown exception caught!\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Map> MapWidget::getMap()
|
|
{
|
|
return map_;
|
|
}
|
|
|
|
void MapWidget::setMap(std::shared_ptr<Map> map)
|
|
{
|
|
map_ = map;
|
|
}
|
|
|
|
void MapWidget::layerSelected(int index)
|
|
{
|
|
selectedLayer_ = index;
|
|
}
|