/* This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2021 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 #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CAIRO // cairo #include #include #endif #include "mapwidget.hpp" #include "info_dialog.hpp" using mapnik::image_rgba8; using mapnik::Map; using mapnik::layer; using mapnik::box2d; using mapnik::coord2d; using mapnik::feature_ptr; using mapnik::view_transform; using mapnik::projection; using mapnik::scale_denominator; using mapnik::feature_kv_iterator; 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 > 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 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(std::get<0>(*itr).c_str()), std::get<1>(*itr).to_string().c_str())); } #if 0 // using path_type = mapnik::transform_path_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 box = t.backward(box2d(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 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 ext = map_->get_current_extent(); double width = static_cast(map_->width()); double height= static_cast(map_->height()); mapnik::coord2d pt = ext.center(); double res = scale_denom * 0.00028; mapnik::box2d 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 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 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(QString const& txt) { if (txt == "AGG") cur_renderer_ = AGG; else if (txt == "Cairo") cur_renderer_ = Cairo; else if (txt == "Grid") cur_renderer_ = Grid; std::cerr << "Update renderer called" << std::endl; 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 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 MapWidget::getMap() { return map_; } void MapWidget::setMap(std::shared_ptr map) { map_ = map; } void MapWidget::layerSelected(int index) { selectedLayer_ = index; }