diff --git a/CMakeLists.txt b/CMakeLists.txt index b5571ac..563b6df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_policy(SET CMP0017 NEW) # need include() with .cmake cmake_policy(SET CMP0072 NEW) # FindOpenGL prefers GLVND by default project(QAD) set(QAD_MAJOR 2) -set(QAD_MINOR 12) +set(QAD_MINOR 13) set(QAD_REVISION 0) set(QAD_SUFFIX ) set(QAD_COMPANY SHS) diff --git a/icons/maps.png b/icons/maps.png new file mode 100644 index 0000000..ae68d54 Binary files /dev/null and b/icons/maps.png differ diff --git a/libs/map/CMakeLists.txt b/libs/map/CMakeLists.txt new file mode 100644 index 0000000..ff89440 --- /dev/null +++ b/libs/map/CMakeLists.txt @@ -0,0 +1 @@ +qad_library(map "Gui;Widgets;Network" "qad_widgets") diff --git a/libs/map/lang/qad_application_en.ts b/libs/map/lang/qad_application_en.ts new file mode 100644 index 0000000..cc85e91 --- /dev/null +++ b/libs/map/lang/qad_application_en.ts @@ -0,0 +1,144 @@ + + + + + AboutWindow + + + - About + + + + + Versions + + + + + Build + + + + + Authors + + + + + About Qt... + + + + + OK + + + + + + About + + + + + EMainWindow + + + + Clear recent list + + + + + + + + Show all + + + + + + + + Hide all + + + + + Toolbars + + + + + Docks + + + + + Select file to open + + + + + Select files to open + + + + + Save changes%1? + + + + + in + + + + + Select file to save + + + + + HistoryView + + + + History cleared + + + + + LogView + + + Category: + + + + + + + Clear + + + + + + Select All + + + + + + Copy + + + + + All + + + + diff --git a/libs/map/lang/qad_application_ru.ts b/libs/map/lang/qad_application_ru.ts new file mode 100644 index 0000000..bb4ba21 --- /dev/null +++ b/libs/map/lang/qad_application_ru.ts @@ -0,0 +1,144 @@ + + + + + AboutWindow + + + - About + - О программе + + + + Versions + Версии + + + + Build + Сборка + + + + Authors + Авторы + + + + About Qt... + О Qt ... + + + + OK + + + + + + About + О программе + + + + EMainWindow + + + + Clear recent list + Очистить список недавних + + + + + + + Show all + Показать все + + + + + + + Hide all + Скрыть все + + + + Toolbars + Панели инструментов + + + + Docks + Окна + + + + Select file to open + Выбрать файл для открытия + + + + Select files to open + Выберите файлы для открытия + + + + Save changes%1? + Сохранить изменения%1? + + + + in + в + + + + Select file to save + Выберите файл для сохранения + + + + HistoryView + + + + History cleared + История очищена + + + + LogView + + + Category: + Категория: + + + + + + Clear + Очистить + + + + + Select All + Выделить всё + + + + + Copy + Копировать + + + + All + Все + + + diff --git a/libs/map/lang/update.bat b/libs/map/lang/update.bat new file mode 100644 index 0000000..34f3eee --- /dev/null +++ b/libs/map/lang/update.bat @@ -0,0 +1,2 @@ +lupdate ../ -ts qad_application_ru.ts +lupdate ../ -ts qad_application_en.ts diff --git a/libs/map/mapitembase.cpp b/libs/map/mapitembase.cpp new file mode 100644 index 0000000..b9089d0 --- /dev/null +++ b/libs/map/mapitembase.cpp @@ -0,0 +1,95 @@ +#include "mapitembase.h" + +#include "mapview.h" +#include "osm_math_p.h" + + +MapItemBase::MapItemBase() {} + + +MapItemBase::~MapItemBase() { + if (!parent) return; + parent->removeItem(this); +} + + +void MapItemBase::setInteracive(bool newInteracive) { + m_interacive = newInteracive; + updateParent(); +} + + +void MapItemBase::setVisible(bool newVisible) { + m_visible = newVisible; + updateParent(); +} + + +void MapItemBase::setRotation(double deg) { + m_rotation = deg; + updateParent(); +} + + +void MapItemBase::rotate(double deg) { + m_rotation += deg; + while (deg < 0) + deg += 360.; + while (deg > 360.) + deg -= 360.; + updateParent(); +} + + +void MapItemBase::scale(QPointF s) { + m_scale = {m_scale.x() * s.x(), m_scale.y() * s.y()}; + updateParent(); +} + + +void MapItemBase::setScale(QPointF s) { + m_scale = s; + updateParent(); +} + + +void MapItemBase::setPosition(QPointF geo) { + m_position = geo; + updatePosition(); + updateParent(); +} + + +void MapItemBase::setCursor(const QCursor & newCursor) { + m_cursor = newCursor; + updateParent(); +} + + +QPointF MapItemBase::mapToView(QPointF norm) const { + if (!parent) return norm; + return parent->mapFromNorm(norm); +} + + +double MapItemBase::scalePx2M(QPointF norm) const { + if (!parent) return 1.; + return parent->scalePx2M(norm); +} + + +void MapItemBase::updatePosition() { + norm_pos = OSM::geo2xy(m_position); + potitionChanged(); + zoomChanged(); +} + + +void MapItemBase::updateParent() { + if (parent) parent->update(); +} + + +void MapItemBase::setBoundingRect(QRectF b) { + m_bounding = b; +} diff --git a/libs/map/mapitembase.h b/libs/map/mapitembase.h new file mode 100644 index 0000000..55ba0ba --- /dev/null +++ b/libs/map/mapitembase.h @@ -0,0 +1,94 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitembase_h +#define mapitembase_h + +#include "qad_map_export.h" + +#include +#include +#include +#include +#include + +class MapView; + +class QAD_MAP_EXPORT MapItemBase { + friend class MapView; + +public: + explicit MapItemBase(); + virtual ~MapItemBase(); + + + bool isInteracive() const { return m_interacive; } + void setInteracive(bool newInteracive); + + bool isVisible() const { return m_visible; } + void setVisible(bool newVisible); + + double getRotation() const { return m_rotation; } + void setRotation(double deg); + void rotate(double deg); + + QPointF getScale() const { return m_scale; } + void scale(double s) { scale({s, s}); } + void scale(double sx, double sy) { scale({sx, sy}); } + void scale(QPointF s); + void setScale(double s) { setScale({s, s}); } + void setScale(double sx, double sy) { scale({sx, sy}); } + void setScale(QPointF s); + + QPointF position() const { return m_position; } + void setPosition(QPointF geo); + void setPosition(double lat, double lng) { setPosition({lat, lng}); } + + const QCursor & cursor() const { return m_cursor; } + void setCursor(const QCursor & newCursor); + + QVariant data(int id) const { return data_.value(id); } + void setData(int id, const QVariant & d) { data_[id] = d; } + +protected: + QPointF mapToView(QPointF norm) const; + double scalePx2M(QPointF norm) const; + virtual void draw(QPainter * p) = 0; + void updateParent(); + void setBoundingRect(QRectF b); + + QPointF norm_pos; + bool ignore_scale = false; + +private: + void updatePosition(); + virtual void potitionChanged() {} + virtual void zoomChanged() {} + + MapView * parent = nullptr; + bool m_visible = true, m_interacive = false; + double m_rotation = 0.; + QPointF m_position, m_scale = {1., 1.}; + QRectF m_bounding; + QCursor m_cursor; + QMap data_; +}; + + +#endif diff --git a/libs/map/mapitemellipse.cpp b/libs/map/mapitemellipse.cpp new file mode 100644 index 0000000..be7ad8b --- /dev/null +++ b/libs/map/mapitemellipse.cpp @@ -0,0 +1,77 @@ +#include "mapitemellipse.h" + +#include + + +MapItemEllipse::MapItemEllipse(const QRectF & r) { + setEllipse(r); +} + + +MapItemEllipse::MapItemEllipse(const QPointF & p, double rx, double ry) { + setEllipse(p, rx, ry); +} + + +void MapItemEllipse::setStartAngle(double a) { + sa = a; + rebuild(); + updateParent(); +} + + +void MapItemEllipse::setEndAngle(double a) { + ea = a; + rebuild(); + updateParent(); +} + + +void MapItemEllipse::setEllipse(const QRectF & r) { + rct = r; + rebuild(); + updateParent(); +} + + +void MapItemEllipse::setEllipse(const QPointF & p, double rx, double ry) { + rct.setWidth(rx * 2.); + rct.setHeight(ry * 2.); + rct.moveCenter(p); + rebuild(); + updateParent(); +} + + +void MapItemEllipse::draw(QPainter * p) { + p->setPen(pen()); + p->setBrush(brush()); + QPolygonF dp(pol); + double us = unitScale(); + for (auto & i: dp) + i = QPointF(i.x(), -i.y()) / us; + p->drawPolygon(dp); + setBoundingRect(dp.boundingRect()); +} + + +void MapItemEllipse::rebuild() { + pol.clear(); + QPointF c = rct.center(); + bool full = sa == ea; + if (!full) pol << c; + double w = rct.width() / 2., h = rct.height() / 2.; + static double deg2rad = M_PI / 180.; + if (full) { + sa = 0.; + ea = 360.; + } else { + if (ea < sa) qSwap(sa, ea); + } + for (double a = sa; a <= ea; a += 3.) { + if (a > ea) a = ea; + QPointF p(sin(a * deg2rad) * w, cos(a * deg2rad) * h); + pol << (c + p); + } + if (!full) pol << c; +} diff --git a/libs/map/mapitemellipse.h b/libs/map/mapitemellipse.h new file mode 100644 index 0000000..6dbd132 --- /dev/null +++ b/libs/map/mapitemellipse.h @@ -0,0 +1,53 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitemellipse_h +#define mapitemellipse_h + +#include "mapitemnongeogeometrybase.h" + + +/// ellipse in meters, based on map scale at "position", or pixels +/// north = 0 deg, positive is clockwise +class QAD_MAP_EXPORT MapItemEllipse: public MapItemNonGeoGeometryBase { +public: + MapItemEllipse(const QRectF & r = QRectF()); + MapItemEllipse(const QPointF & p, double rx, double ry); + + + QRectF rect() const { return rct; } + double startAngle() const { return sa; } + double endAngle() const { return ea; } + void setStartAngle(double a); + void setEndAngle(double a); + void setEllipse(const QRectF & r); + void setEllipse(const QPointF & p, double rx, double ry); + +protected: + void draw(QPainter * p) override; + void rebuild(); + +private: + QRectF rct; + QPolygonF pol; + double sa = 0., ea = 0.; +}; + + +#endif diff --git a/libs/map/mapitemgeometrybase.cpp b/libs/map/mapitemgeometrybase.cpp new file mode 100644 index 0000000..53dcfba --- /dev/null +++ b/libs/map/mapitemgeometrybase.cpp @@ -0,0 +1,16 @@ +#include "mapitemgeometrybase.h" + + +MapItemGeometryBase::MapItemGeometryBase() {} + + +void MapItemGeometryBase::setPen(QPen newPen) { + m_pen = newPen; + updateParent(); +} + + +void MapItemGeometryBase::setBrush(QBrush newBrush) { + m_brush = newBrush; + updateParent(); +} diff --git a/libs/map/mapitemgeometrybase.h b/libs/map/mapitemgeometrybase.h new file mode 100644 index 0000000..d87b92c --- /dev/null +++ b/libs/map/mapitemgeometrybase.h @@ -0,0 +1,53 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitemgeometrybase_h +#define mapitemgeometrybase_h + +#include "mapitembase.h" + +#include +#include +#include + +class QAD_MAP_EXPORT MapItemGeometryBase: public MapItemBase { + friend class MapView; + +public: + explicit MapItemGeometryBase(); + + + QPen pen() const { return m_pen; } + void setPen(QPen newPen); + + QBrush brush() const { return m_brush; } + void setBrush(QBrush newBrush); + +protected: + virtual void updatePolygon() {} + + QPolygonF norm_polygon; + +private: + QPen m_pen = QPen(Qt::black, 1); + QBrush m_brush = QBrush(Qt::white); +}; + + +#endif diff --git a/libs/map/mapitemgeopolygon.cpp b/libs/map/mapitemgeopolygon.cpp new file mode 100644 index 0000000..0d886ed --- /dev/null +++ b/libs/map/mapitemgeopolygon.cpp @@ -0,0 +1,35 @@ +#include "mapitemgeopolygon.h" + +#include "osm_math_p.h" + + +MapItemGeoPolygon::MapItemGeoPolygon(const QPolygonF & p) { + ignore_scale = true; + setPolygon(p); +} + + +void MapItemGeoPolygon::setPolygon(const QPolygonF & p) { + pol = p; + updatePolygon(); +} + + +void MapItemGeoPolygon::draw(QPainter * p) { + p->setPen(pen()); + p->setBrush(brush()); + QPolygonF dp(norm_polygon); + QPointF pos_offset = mapToView(norm_pos); + for (auto & i: dp) + i = mapToView(i) - pos_offset; + p->drawPolygon(dp); + setBoundingRect(dp.boundingRect()); +} + + +void MapItemGeoPolygon::updatePolygon() { + norm_polygon.resize(pol.size()); + for (int i = 0; i < pol.size(); ++i) + norm_polygon[i] = OSM::geo2xy(pol[i]); + updateParent(); +} diff --git a/libs/map/mapitemgeopolygon.h b/libs/map/mapitemgeopolygon.h new file mode 100644 index 0000000..5af0487 --- /dev/null +++ b/libs/map/mapitemgeopolygon.h @@ -0,0 +1,44 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitemgeopolygon_h +#define mapitemgeopolygon_h + +#include "mapitemgeometrybase.h" + + +/// polygon in geo-coordinates +class QAD_MAP_EXPORT MapItemGeoPolygon: public MapItemGeometryBase { +public: + explicit MapItemGeoPolygon(const QPolygonF & p = QPolygonF()); + + + QPolygonF polygon() const { return pol; } + void setPolygon(const QPolygonF & p); + +protected: + void draw(QPainter * p) override; + void updatePolygon() override; + +private: + QPolygonF pol; +}; + + +#endif diff --git a/libs/map/mapitemgeopolyline.cpp b/libs/map/mapitemgeopolyline.cpp new file mode 100644 index 0000000..19b6bc7 --- /dev/null +++ b/libs/map/mapitemgeopolyline.cpp @@ -0,0 +1,34 @@ +#include "mapitemgeopolyline.h" + +#include "osm_math_p.h" + + +MapItemGeoPolyline::MapItemGeoPolyline(const QPolygonF & p) { + ignore_scale = true; + setPolyline(p); +} + + +void MapItemGeoPolyline::setPolyline(const QPolygonF & p) { + pol = p; + updatePolygon(); +} + + +void MapItemGeoPolyline::draw(QPainter * p) { + p->setPen(pen()); + QPolygonF dp(norm_polygon); + QPointF pos_offset = mapToView(norm_pos); + for (auto & i: dp) + i = mapToView(i) - pos_offset; + p->drawPolyline(dp); + setBoundingRect(dp.boundingRect()); +} + + +void MapItemGeoPolyline::updatePolygon() { + norm_polygon.resize(pol.size()); + for (int i = 0; i < pol.size(); ++i) + norm_polygon[i] = OSM::geo2xy(pol[i]); + updateParent(); +} diff --git a/libs/map/mapitemgeopolyline.h b/libs/map/mapitemgeopolyline.h new file mode 100644 index 0000000..6b87e87 --- /dev/null +++ b/libs/map/mapitemgeopolyline.h @@ -0,0 +1,44 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitemgeopolyline_h +#define mapitemgeopolyline_h + +#include "mapitemgeometrybase.h" + + +/// polyline in geo-coordinates +class QAD_MAP_EXPORT MapItemGeoPolyline: public MapItemGeometryBase { +public: + explicit MapItemGeoPolyline(const QPolygonF & p = QPolygonF()); + + + QPolygonF polyline() const { return pol; } + void setPolyline(const QPolygonF & p); + +protected: + void draw(QPainter * p) override; + void updatePolygon() override; + +private: + QPolygonF pol; +}; + + +#endif diff --git a/libs/map/mapitemimage.cpp b/libs/map/mapitemimage.cpp new file mode 100644 index 0000000..8e2f3e1 --- /dev/null +++ b/libs/map/mapitemimage.cpp @@ -0,0 +1,38 @@ +#include "mapitemimage.h" + + +MapItemImage::MapItemImage(const QPixmap & p) { + setPixmap(p); +} + + +void MapItemImage::setPixmap(const QPixmap & p) { + m_pixmap = p; + updateParent(); +} + + +void MapItemImage::setAlignment(Qt::Alignment a) { + m_alignment = a; + updateParent(); +} + + +void MapItemImage::draw(QPainter * p) { + if (m_pixmap.isNull()) return; + QRectF pr(QPointF(0, 0), QSizeF(m_pixmap.size())); + if (m_alignment.testFlag(Qt::AlignHCenter)) { + pr.translate(-pr.center().x(), 0.); + } + if (m_alignment.testFlag(Qt::AlignRight)) { + pr.translate(-pr.width(), 0.); + } + if (m_alignment.testFlag(Qt::AlignVCenter)) { + pr.translate(0., -pr.center().y()); + } + if (m_alignment.testFlag(Qt::AlignBottom)) { + pr.translate(0., -pr.height()); + } + p->drawPixmap(pr, m_pixmap, m_pixmap.rect()); + setBoundingRect(pr); +} diff --git a/libs/map/mapitemimage.h b/libs/map/mapitemimage.h new file mode 100644 index 0000000..9c3a36b --- /dev/null +++ b/libs/map/mapitemimage.h @@ -0,0 +1,46 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitemimage_h +#define mapitemimage_h + +#include "mapitembase.h" + +#include + +class QAD_MAP_EXPORT MapItemImage: public MapItemBase { +public: + explicit MapItemImage(const QPixmap & p = QPixmap()); + + QPixmap pixmap() const { return m_pixmap; } + void setPixmap(const QPixmap & p); + + Qt::Alignment alignment() const { return m_alignment; } + void setAlignment(Qt::Alignment a); + +protected: + void draw(QPainter * p) override; + +private: + QPixmap m_pixmap; + Qt::Alignment m_alignment = Qt::AlignCenter; +}; + + +#endif diff --git a/libs/map/mapitemnongeogeometrybase.cpp b/libs/map/mapitemnongeogeometrybase.cpp new file mode 100644 index 0000000..59d58c2 --- /dev/null +++ b/libs/map/mapitemnongeogeometrybase.cpp @@ -0,0 +1,21 @@ +#include "mapitemnongeogeometrybase.h" + + +void MapItemNonGeoGeometryBase::setUnits(Units u) { + m_units = u; + updateParent(); +} + + +void MapItemNonGeoGeometryBase::zoomChanged() { + px2m = scalePx2M(norm_pos); +} + + +double MapItemNonGeoGeometryBase::unitScale() const { + switch (m_units) { + case Meters: return px2m; + default: break; + } + return 1.; +} diff --git a/libs/map/mapitemnongeogeometrybase.h b/libs/map/mapitemnongeogeometrybase.h new file mode 100644 index 0000000..ee34e92 --- /dev/null +++ b/libs/map/mapitemnongeogeometrybase.h @@ -0,0 +1,49 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitemnongeogeometrybase_h +#define mapitemnongeogeometrybase_h + +#include "mapitemgeometrybase.h" + + +/// ellipse in meters, based on map scale at "position" +/// north = 0 deg, positive is clockwise +class QAD_MAP_EXPORT MapItemNonGeoGeometryBase: public MapItemGeometryBase { +public: + enum Units { + Meters, + Pixels + }; + + Units units() const { return m_units; } + void setUnits(Units u); + +protected: + void zoomChanged() override; + double unitScale() const; + + +private: + Units m_units = Meters; + double px2m = 1.; +}; + + +#endif diff --git a/libs/map/mapitempolygon.cpp b/libs/map/mapitempolygon.cpp new file mode 100644 index 0000000..1a97ada --- /dev/null +++ b/libs/map/mapitempolygon.cpp @@ -0,0 +1,24 @@ +#include "mapitempolygon.h" + + +MapItemPolygon::MapItemPolygon(const QPolygonF & p) { + setPolygon(p); +} + + +void MapItemPolygon::setPolygon(const QPolygonF & p) { + pol = p; + updateParent(); +} + + +void MapItemPolygon::draw(QPainter * p) { + p->setPen(pen()); + p->setBrush(brush()); + QPolygonF dp(pol); + double us = unitScale(); + for (auto & i: dp) + i = QPointF(i.x(), -i.y()) / us; + p->drawPolygon(dp); + setBoundingRect(dp.boundingRect()); +} diff --git a/libs/map/mapitempolygon.h b/libs/map/mapitempolygon.h new file mode 100644 index 0000000..77e140d --- /dev/null +++ b/libs/map/mapitempolygon.h @@ -0,0 +1,43 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitempolygon_h +#define mapitempolygon_h + +#include "mapitemnongeogeometrybase.h" + + +/// polygon in meters, based on map scale at "position", or pixels +class QAD_MAP_EXPORT MapItemPolygon: public MapItemNonGeoGeometryBase { +public: + explicit MapItemPolygon(const QPolygonF & p = QPolygonF()); + + + QPolygonF polygon() const { return pol; } + void setPolygon(const QPolygonF & p); + +protected: + void draw(QPainter * p) override; + +private: + QPolygonF pol; +}; + + +#endif diff --git a/libs/map/mapitempolyline.cpp b/libs/map/mapitempolyline.cpp new file mode 100644 index 0000000..de9611a --- /dev/null +++ b/libs/map/mapitempolyline.cpp @@ -0,0 +1,23 @@ +#include "mapitempolyline.h" + + +MapItemPolyline::MapItemPolyline(const QPolygonF & p) { + setPolyline(p); +} + + +void MapItemPolyline::setPolyline(const QPolygonF & p) { + pol = p; + updateParent(); +} + + +void MapItemPolyline::draw(QPainter * p) { + p->setPen(pen()); + QPolygonF dp(pol); + double us = unitScale(); + for (auto & i: dp) + i = QPointF(i.x(), -i.y()) / us; + p->drawPolyline(dp); + setBoundingRect(dp.boundingRect()); +} diff --git a/libs/map/mapitempolyline.h b/libs/map/mapitempolyline.h new file mode 100644 index 0000000..1f4dcac --- /dev/null +++ b/libs/map/mapitempolyline.h @@ -0,0 +1,43 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitempolyline_h +#define mapitempolyline_h + +#include "mapitemnongeogeometrybase.h" + + +/// polyline in meters, based on map scale at "position", or pixels +class QAD_MAP_EXPORT MapItemPolyline: public MapItemNonGeoGeometryBase { +public: + explicit MapItemPolyline(const QPolygonF & p = QPolygonF()); + + + QPolygonF polyline() const { return pol; } + void setPolyline(const QPolygonF & p); + +protected: + void draw(QPainter * p) override; + +private: + QPolygonF pol; +}; + + +#endif diff --git a/libs/map/mapitemtext.cpp b/libs/map/mapitemtext.cpp new file mode 100644 index 0000000..401a2a8 --- /dev/null +++ b/libs/map/mapitemtext.cpp @@ -0,0 +1,50 @@ +#include "mapitemtext.h" + + +MapItemText::MapItemText(const QString & t) { + setBrush(Qt::NoBrush); + setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + setText(t); +} + + +void MapItemText::setAlignment(Qt::Alignment a) { + m_alignment = a; + updateParent(); +} + + +void MapItemText::setText(const QString & s) { + str = s; + updateParent(); +} + + +void MapItemText::setFont(const QFont & f) { + m_font = f; + updateParent(); +} + + +void MapItemText::draw(QPainter * p) { + if (str.isEmpty()) return; + p->setPen(pen()); + p->setFont(font()); + auto br = p->fontMetrics().boundingRect(str); + br.translate(-br.topLeft()); + if (m_alignment.testFlag(Qt::AlignHCenter)) { + br.translate(-br.center().x(), 0.); + } + if (m_alignment.testFlag(Qt::AlignRight)) { + br.translate(-br.width(), 0.); + } + if (m_alignment.testFlag(Qt::AlignVCenter)) { + br.translate(0., -br.center().y()); + } + if (m_alignment.testFlag(Qt::AlignBottom)) { + br.translate(0., -br.height()); + } + p->fillRect(br.adjusted(-2, 0, 2, 0), brush()); + p->drawText(br, Qt::TextSingleLine, str); + setBoundingRect(br); +} diff --git a/libs/map/mapitemtext.h b/libs/map/mapitemtext.h new file mode 100644 index 0000000..4763379 --- /dev/null +++ b/libs/map/mapitemtext.h @@ -0,0 +1,51 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapitemtext_h +#define mapitemtext_h + +#include "mapitemgeometrybase.h" + +#include + + +class QAD_MAP_EXPORT MapItemText: public MapItemGeometryBase { +public: + explicit MapItemText(const QString & t = QString()); + + Qt::Alignment alignment() const { return m_alignment; } + void setAlignment(Qt::Alignment a); + + QString text() const { return str; } + void setText(const QString & s); + + QFont font() const { return m_font; } + void setFont(const QFont & f); + +protected: + void draw(QPainter * p) override; + +private: + Qt::Alignment m_alignment = Qt::AlignCenter; + QString str; + QFont m_font; +}; + + +#endif diff --git a/libs/map/mapview.cpp b/libs/map/mapview.cpp new file mode 100644 index 0000000..dc2d9e2 --- /dev/null +++ b/libs/map/mapview.cpp @@ -0,0 +1,317 @@ +#include "mapview.h" + +#include "osm_downloader_p.h" +#include "osm_math_p.h" +#include "osm_tile_cache_p.h" +#include "qad_types.h" + +#include +#include +#include +#include +#include +#include + +const int tileSize = 256; + + +MapView::MapView(QWidget * parent): QWidget(parent) { + downloader = new OSMDownloader(this); + cache = new OSMTileCache(this); + setMouseTracking(true); + connect(cache, &OSMTileCache::tileReady, this, [this]() { drawBackground(); }); + int size = 16; + QPixmap px(QSize(size, size) * 2); + QPainter p(&px); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + p.fillRect(i * size, j * size, size, size, (((i + j) % 2) == 0) ? Qt::white : Qt::lightGray); + } + } + p.end(); + brush_tr.setTexture(px); + zoomTo(0); +} + + +MapView::~MapView() { + for (auto * i: items_) + i->parent = nullptr; + qDeleteAll(items_); + delete cache; + delete downloader; +} + + +void MapView::addItem(MapItemBase * item) { + if (items_.contains(item)) return; + items_ << item; + item->parent = this; + item->updatePosition(); + update(); +} + + +void MapView::removeItem(MapItemBase * item) { + if (!items_.contains(item)) return; + items_.removeOne(item); + if (hover == item) hover = nullptr; +} + + +QPointF MapView::center() const { + return OSM::xy2geo(center_); +} + + +void MapView::setCenter(QPointF c) { + center_ = OSM::geo2xy(c); + updateViewRect(); + drawBackground(); +} + + +void MapView::setZoom(double z) { + zoomTo(z); +} + + +void MapView::mousePressEvent(QMouseEvent * e) { + is_pan = false; + press_point = e->pos(); +} + + +void MapView::mouseReleaseEvent(QMouseEvent * e) { + if (!is_pan) { + if (hover) emit itemClicked(hover); + } + is_pan = false; +} + + +void MapView::mouseDoubleClickEvent(QMouseEvent * e) {} + + +void MapView::mouseMoveEvent(QMouseEvent * e) { + // QPointF geo = OSM::xy2geo(mapToNorm(e->pos())); + // qDebug() << QString("%1 , %2").arg(geo.x(), 0, 'f', 5).arg(geo.y(), 0, 'f', 5); + if (e->buttons() == Qt::LeftButton) { + if (is_pan) { + double mins = qMin(width(), height()); + center_ -= QPointF(e->pos() - press_point) / scale_ / mins; + press_point = e->pos(); + updateViewRect(); + drawBackground(); + } else { + if ((e->pos() - press_point).manhattanLength() >= QApplication::startDragDistance()) is_pan = true; + } + } + updateMouse(e->pos()); +} + + +void MapView::wheelEvent(QWheelEvent * e) { + double scl = e->angleDelta().x() == 0 ? e->angleDelta().y() : e->angleDelta().x(); + scl = 1. + (scl / 400.); + QPoint mp; +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + mp = e->pos(); +#else + mp = e->position().toPoint(); +#endif + zoom(scl, mp); +} + + +void MapView::paintEvent(QPaintEvent *) { + QPainter p(this); + p.drawPixmap(0, 0, background); + drawItems(p); + updateMouse(mapFromGlobal(QCursor::pos())); +} + + +void MapView::resizeEvent(QResizeEvent *) { + cache->setMinimumCacheSize((width() + tileSize) * (height() + tileSize) * 4); + checkZoom(); + updateZoomLevel(); + updateViewRect(); + drawBackground(); +} + + +void MapView::drawBackground() { + if (size() != background.size()) background = QPixmap(size()); + background.fill(palette().window().color()); + QPainter p(&background); + double mins = qMin(width(), height()); + int sx = qMax(0, (int)floor(view_rect.left() * tiles_side)); + int sy = qMax(0, (int)floor(view_rect.top() * tiles_side)); + int ex = qMin(tiles_side, (int)ceil(view_rect.right() * tiles_side)); + int ey = qMin(tiles_side, (int)ceil(view_rect.bottom() * tiles_side)); + double ts = 1. / tiles_side / qMax(view_rect.width(), view_rect.height()) * qMax(width(), height()); + QPointF offset = view_rect.topLeft() * mins * scale_; + p.setRenderHint(QPainter::SmoothPixmapTransform); + p.setPen(Qt::white); + for (int ix = sx; ix < ex; ++ix) { + for (int iy = sy; iy < ey; ++iy) { + QRectF r((ix)*ts, (iy)*ts, ts, ts); + r.translate(-offset); + auto tile = cache->getTile((OSM::TileIndex){zoom_level, ix, iy}); + if (!tile.isEmpty()) { + p.drawPixmap(r, tile.pixmap, QRectF(tile.pixmap.rect())); + } else { + p.fillRect(r, brush_tr); + } + // p.setPen(Qt::white); + // p.drawText(r, Qt::AlignCenter, QString("%1x%2").arg(ix).arg(iy)); + } + } + // qDebug() << sx << sy << ex << ey << ts; + update(); +} + + +void MapView::drawItems(QPainter & p) { + auto src_tr = p.transform(); + p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::SmoothPixmapTransform); + QPoint mouse = mapFromGlobal(QCursor::pos()); + MapItemBase * hover = nullptr; + for (auto * i: items_) { + if (!i->isVisible()) continue; + QPointF ipos = mapFromNorm(i->norm_pos); + p.setTransform(src_tr); + p.translate(ipos); + p.rotate(i->getRotation()); + if (!i->ignore_scale) p.scale(i->getScale().x(), i->getScale().y()); + i->draw(&p); + QTransform mtr; + mtr.rotate(-i->getRotation()); + mtr.translate(-ipos.x(), -ipos.y()); + QPoint m = mtr.map(mouse); + if (i->m_bounding.contains(m)) { + hover = i; + } + } + if (hover) + setCursor(hover->cursor()); + else + unsetCursor(); +} + + +void MapView::checkZoom() { + static const double max = 20; + if (zoom_ < 0.) zoom_ = 0.; + if (zoom_ > max) zoom_ = max; + double mins = qMin(width(), height()) / appScale(this); + scale_ = std::pow(2., zoom_) * tileSize / mins; + updateViewRect(); +} + + +void MapView::zoomLevelChanged(int new_level) { + zoom_level = qBound(0, new_level, max_level); + // qDebug() << "level changed" << new_level << zoom_level; + tiles_side = 1 << zoom_level; + // for (int x = 0; x < tiles_side; ++x) + // for (int y = 0; y < tiles_side; ++y) + // downloader->queueTile(OSM::TileIndex{zoom_level, x, y}); +} + + +void MapView::updateZoomLevel() { + int zl = qRound(zoom_); + if (zl != zoom_level) zoomLevelChanged(zl); +} + + +void MapView::updateViewRect() { + double mins = qMin(width(), height()); + if (mins <= 0) return; + view_rect.setRect(0, 0, (width() / mins) / scale_, (height() / mins) / scale_); + view_rect.moveCenter(center_); + for (auto * i: items_) + i->zoomChanged(); +} + + +void MapView::updateMouse(QPoint mouse) { + MapItemBase * new_hover = nullptr; + for (auto * i: items_) { + if (!i->isVisible() || !i->isInteracive()) continue; + QPointF ipos = mapFromNorm(i->norm_pos); + QTransform mtr; + if (!i->ignore_scale) mtr.scale(1. / i->getScale().x(), 1. / i->getScale().y()); + mtr.rotate(-i->getRotation()); + mtr.translate(-ipos.x(), -ipos.y()); + QPoint m = mtr.map(mouse); + if (i->m_bounding.contains(m)) { + new_hover = i; + } + } + if (new_hover) + setCursor(new_hover->cursor()); + else + unsetCursor(); + if (new_hover != hover) { + if (hover) emit itemLeaved(hover); + if (new_hover) emit itemEntered(new_hover); + } + hover = new_hover; +} + + +double MapView::scalePx2M(QPointF norm) { + double lat = OSM::y2lat(norm.y()) * M_PI / 180.; + px2m = qMax(view_rect.width(), view_rect.height()) / qMax(width(), height()) * tileSize; + px2m *= 156543.03 * cos(lat); + // qDebug() << px2m << lat; + return px2m; +} + + +QPointF MapView::mapToNorm(QPoint screen) const { + QPointF s(screen.x() / (double)width(), screen.y() / (double)height()); + return view_rect.topLeft() + QPointF(view_rect.width() * s.x(), view_rect.height() * s.y()); +} + + +QPoint MapView::mapFromNorm(QPointF norm) const { + QPointF s = norm - view_rect.topLeft(); + return QPoint(qRound((s.x() / view_rect.width()) * width()), qRound((s.y() / view_rect.height()) * height())); +} + + +void MapView::zoom(double factor) { + zoom(factor, rect().center()); +} + + +void MapView::zoom(double factor, QPoint anchor) { + QPointF prev_center = mapToNorm(anchor); + zoom_ += (factor - 1.); + checkZoom(); + QPointF new_center = mapToNorm(anchor); + center_ += prev_center - new_center; + view_rect.translate(prev_center - new_center); + updateZoomLevel(); + drawBackground(); +} + + +void MapView::zoomTo(double level) { + zoom_ = level; + checkZoom(); + updateZoomLevel(); + drawBackground(); +} + + +void MapView::centerTo(QPointF coord) { + center_ = OSM::geo2xy(coord); + updateViewRect(); + drawBackground(); +} diff --git a/libs/map/mapview.h b/libs/map/mapview.h new file mode 100644 index 0000000..b3e68e8 --- /dev/null +++ b/libs/map/mapview.h @@ -0,0 +1,104 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef mapview_h +#define mapview_h + +#include "mapitembase.h" +#include "qad_map_export.h" + +#include +#include + + +class OSMDownloader; +class OSMTileCache; + +class QAD_MAP_EXPORT MapView: public QWidget { + Q_OBJECT + Q_PROPERTY(QPointF center READ center WRITE setCenter) + Q_PROPERTY(double zoom READ getZoom WRITE setZoom) + + friend class OSMDownloader; + friend class OSMTileCache; + friend class MapItemBase; + +public: + explicit MapView(QWidget * parent = 0); + ~MapView(); + + void addItem(MapItemBase * item); + void removeItem(MapItemBase * item); + + QPointF center() const; + void setCenter(QPointF c); + + double getZoom() const { return zoom_; } + void setZoom(double z); + +private: + QSize sizeHint() const override { return QSize(200, 200); } + void mousePressEvent(QMouseEvent * e) override; + void mouseReleaseEvent(QMouseEvent * e) override; + void mouseDoubleClickEvent(QMouseEvent * e) override; + void mouseMoveEvent(QMouseEvent * e) override; + void wheelEvent(QWheelEvent * e) override; + void paintEvent(QPaintEvent *) override; + void resizeEvent(QResizeEvent *) override; + void drawBackground(); + void drawItems(QPainter & p); + void checkZoom(); + void zoomLevelChanged(int new_level); + void updateZoomLevel(); + void updateViewRect(); + void updateMouse(QPoint mouse); + double scalePx2M(QPointF norm); + + QPointF mapToNorm(QPoint screen) const; + QPoint mapFromNorm(QPointF norm) const; + + OSMDownloader * downloader = nullptr; + OSMTileCache * cache = nullptr; + MapItemBase * hover = nullptr; + QPointF center_ = QPointF(0.5, 0.5); + QPoint press_point; + QPixmap background; + QRectF view_rect; + QVector items_; + QBrush brush_tr; + bool is_pan = false; + double zoom_ = 1., scale_ = 1., px2m = 1.; + int zoom_level = 0, tiles_side = 1, max_level = 19; + +public slots: + void zoom(double factor); + void zoom(double factor, QPoint anchor); + void zoomTo(double level); + void centerTo(QPointF coord); + +private slots: + +signals: + void itemClicked(MapItemBase * item); + void itemEntered(MapItemBase * item); + void itemLeaved(MapItemBase * item); +}; + + +#endif diff --git a/libs/map/osm_downloader.cpp b/libs/map/osm_downloader.cpp new file mode 100644 index 0000000..5229ed6 --- /dev/null +++ b/libs/map/osm_downloader.cpp @@ -0,0 +1,73 @@ +#include "mapview.h" +#include "osm_downloader_p.h" +#include "osm_tile_cache_p.h" + + +OSMDownloader::OSMDownloader(MapView * p): QThread() { + qRegisterMetaType(); + nam = new QNetworkAccessManager(); + parent = p; + provider = "http://tile.openstreetmap.org"; + start(); +} + + +OSMDownloader::~OSMDownloader() { + requestInterruption(); + cond.wakeAll(); + wait(); + delete nam; +} + + +void OSMDownloader::queueTile(OSM::TileIndex index) { + // auto hash = tile.hash(); + cond_mutex.lock(); + if (!queue.contains(index) && !in_progress.contains(index.hash())) { + queue.enqueue(index); + cond.wakeOne(); + } + cond_mutex.unlock(); +} + + +void OSMDownloader::requestTile(OSM::TileIndex index) { + QNetworkRequest req(provider + QString("/%1/%2/%3.png").arg(index.z).arg(index.x).arg(index.y)); + req.setHeader(QNetworkRequest::UserAgentHeader, "Qt/5"); + auto * r = nam->get(req); + if (!r) return; + connect(r, &QNetworkReply::finished, this, [this, r, index]() { + r->deleteLater(); + if (r->error() != QNetworkReply::NoError) { + qDebug() << "Error:" << r->error(); + } else { + QByteArray data = r->readAll(); + if (!data.isEmpty()) { + QPixmap tim; + tim.loadFromData(data, "png"); + if (!tim.isNull()) parent->cache->tileDownloaded(index, tim); + } + } + cond_mutex.lock(); + in_progress.remove(index.hash()); + cond_mutex.unlock(); + }); +} + + +void OSMDownloader::run() { + while (!isInterruptionRequested()) { + cond_mutex.lock(); + if (queue.isEmpty()) + cond.wait(&cond_mutex); + else { + auto t = queue.dequeue(); + in_progress.insert(t.hash()); + QMetaObject::invokeMethod( + parent, + [this, t]() { requestTile(t); }, + Qt::QueuedConnection); + } + cond_mutex.unlock(); + } +} diff --git a/libs/map/osm_downloader_p.h b/libs/map/osm_downloader_p.h new file mode 100644 index 0000000..18fd9cd --- /dev/null +++ b/libs/map/osm_downloader_p.h @@ -0,0 +1,66 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef osm_downloader_h +#define osm_downloader_h + +#include "osm_types_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class MapView; + +class OSMDownloader: public QThread { + Q_OBJECT + +public: + explicit OSMDownloader(MapView * p); + ~OSMDownloader(); + + void queueTile(OSM::TileIndex index); + + +private: + void run() override; + void requestTile(OSM::TileIndex index); + + MapView * parent; + QNetworkAccessManager * nam = nullptr; + QString provider; + QWaitCondition cond; + QMutex cond_mutex; + QQueue queue; + QSet in_progress; + +public slots: + +private slots: + +signals: +}; + + +#endif diff --git a/libs/map/osm_math_p.h b/libs/map/osm_math_p.h new file mode 100644 index 0000000..df52275 --- /dev/null +++ b/libs/map/osm_math_p.h @@ -0,0 +1,66 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef osm_math_p_h +#define osm_math_p_h + +#include +#include + +// lon -> -180..+180 +// lat -> -85.0511..+85.0511 + +namespace OSM { + + +inline double lng2x(double lon) { + return (lon + 180.0) / 360.0; +} + + +inline double lat2y(double lat) { + double latrad = lat * M_PI / 180.0; + return (1.0 - asinh(tan(latrad)) / M_PI) / 2.0; +} + + +inline double x2lng(double x) { + return x * 360.0 - 180; +} + + +inline double y2lat(double y) { + double n = M_PI - 2.0 * M_PI * y; + return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); +} + + +inline QPointF xy2geo(QPointF p) { + return QPointF(y2lat(p.y()), x2lng(p.x())); +} + + +inline QPointF geo2xy(QPointF p) { + return QPointF(lng2x(p.y()), lat2y(p.x())); +} + + +} // namespace OSM + +#endif diff --git a/libs/map/osm_tile_cache.cpp b/libs/map/osm_tile_cache.cpp new file mode 100644 index 0000000..0f63253 --- /dev/null +++ b/libs/map/osm_tile_cache.cpp @@ -0,0 +1,100 @@ +#include "mapview.h" +#include "osm_downloader_p.h" +#include "osm_tile_cache_p.h" +#include "qad_locations.h" + + +OSMTileCache::OSMTileCache(MapView * p): QThread() { + qRegisterMetaType(); + qRegisterMetaType(); + parent = p; + cache_root = QAD::userPath(QAD::ltCache, "map_osm") + "/"; + cache_dir.setPath(cache_root); + qDebug() << "[OSMTileCache] save cache to" << cache_root; + if (!cache_dir.exists()) cache_dir.mkpath("."); + setAdditionalCacheSize(64); + start(); +} + + +OSMTileCache::~OSMTileCache() { + requestInterruption(); + cond.wakeAll(); + wait(); +} + + +void OSMTileCache::tileDownloaded(OSM::TileIndex index, const QPixmap & pixmap) { + auto * tile = new OSM::TilePixmap(); + tile->index = index; + tile->pixmap = pixmap; + saveTile(tile); +} + + +OSM::TilePixmap OSMTileCache::getTile(OSM::TileIndex index) { + OSM::TilePixmap ret; + ret.index = index; + auto * tile = tile_cache[index.hash()]; + if (tile) { + ret.pixmap = tile->pixmap; + } else { + QString hashdir = index.cacheDir(); + if (cache_dir.exists(hashdir)) { + QString hashname = hashdir + "/" + index.hashName(); + if (cache_dir.exists(hashname)) { + ret.pixmap.load(cache_dir.absoluteFilePath(hashname), "png"); + tile = new OSM::TilePixmap(); + tile->index = index; + tile->pixmap = ret.pixmap; + tile_cache.insert(tile->index.hash(), tile, tile->pixmapBytes()); + return ret; + } + } + parent->downloader->queueTile(index); + } + return ret; +} + + +void OSMTileCache::updateCacheSize() { + tile_cache.setMaxCost(fixed_size_b + add_size_b); +} + + +void OSMTileCache::saveTile(OSM::TilePixmap * tile) { + tile_cache.insert(tile->index.hash(), tile, tile->pixmapBytes()); + emit tileReady(*tile); + cond_mutex.lock(); + if (!queue.contains(*tile)) { + queue.enqueue(*tile); + cond.wakeOne(); + } + cond_mutex.unlock(); +} + + +void OSMTileCache::writeToDisk(const OSM::TilePixmap & tile) { + QString hashdir = tile.index.cacheDir(); + if (!cache_dir.exists(hashdir)) cache_dir.mkdir(hashdir); + QFile f(cache_dir.absoluteFilePath(hashdir + "/" + tile.index.hashName())); + if (!f.open(QIODevice::ReadWrite)) return; + f.resize(0); + tile.pixmap.save(&f, "png"); +} + + +void OSMTileCache::run() { + while (!isInterruptionRequested()) { + OSM::TilePixmap tile; + cond_mutex.lock(); + if (queue.isEmpty()) { + cond.wait(&cond_mutex); + cond_mutex.unlock(); + } else { + tile = queue.dequeue(); + cond_mutex.unlock(); + writeToDisk(tile); + } + } +} diff --git a/libs/map/osm_tile_cache_p.h b/libs/map/osm_tile_cache_p.h new file mode 100644 index 0000000..b3bcfe1 --- /dev/null +++ b/libs/map/osm_tile_cache_p.h @@ -0,0 +1,77 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef osm_tile_cache_h +#define osm_tile_cache_h + +#include "osm_types_p.h" + +#include +#include +#include +#include +#include +#include + +class MapView; + +class OSMTileCache: public QThread { + Q_OBJECT + +public: + explicit OSMTileCache(MapView * p); + ~OSMTileCache(); + + void setMinimumCacheSize(int bytes) { + fixed_size_b = bytes; + updateCacheSize(); + } + void setAdditionalCacheSize(int mb) { + add_size_b = mb * 1024 * 1024; + updateCacheSize(); + } + + void tileDownloaded(OSM::TileIndex index, const QPixmap & pixmap); + OSM::TilePixmap getTile(OSM::TileIndex index); + +private: + void updateCacheSize(); + void saveTile(OSM::TilePixmap * tile); + void writeToDisk(const OSM::TilePixmap & tile); + void run() override; + + MapView * parent; + QString cache_root; + QDir cache_dir; + QWaitCondition cond; + QMutex cond_mutex; + int fixed_size_b = 0, add_size_b = 0; + QQueue queue; + QCache tile_cache; + +public slots: + +private slots: + +signals: + void tileReady(OSM::TilePixmap); +}; + + +#endif diff --git a/libs/map/osm_types.cpp b/libs/map/osm_types.cpp new file mode 100644 index 0000000..e9ea923 --- /dev/null +++ b/libs/map/osm_types.cpp @@ -0,0 +1,62 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "osm_types_p.h" + +#include + +using namespace OSM; + + +QString TileIndex ::hashName() const { + return QString("%1.%2.%3").arg(z).arg(x).arg(y); +} + + +quint64 TileIndex::hash() const { + union _Hash { + quint64 ret; + struct { + quint64 z: 8; + quint64 x: 24; + quint64 y: 24; + }; + }; + _Hash h; + h.ret = 0; + h.z = z; + h.x = x; + h.y = y; + return h.ret; +} + + +QString TileIndex::cacheDir() const { + return QString::number(qHash(hash()) % 256); +}; + + +bool TilePixmap::isEmpty() const { + return pixmap.isNull(); +} + + +int TilePixmap::pixmapBytes() const { + return pixmap.width() * pixmap.height() * pixmap.depth() / 8; +} diff --git a/libs/map/osm_types_p.h b/libs/map/osm_types_p.h new file mode 100644 index 0000000..4203b26 --- /dev/null +++ b/libs/map/osm_types_p.h @@ -0,0 +1,56 @@ +/* + QAD - Qt ADvanced + + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef osm_types_h +#define osm_types_h + +#include + +namespace OSM { + +struct TileIndex { + int z; + int x; + int y; + QString hashName() const; + quint64 hash() const; + QString cacheDir() const; +}; + +struct TilePixmap { + TileIndex index; + QPixmap pixmap; + bool isEmpty() const; + int pixmapBytes() const; +}; + +} // namespace OSM + +inline bool operator==(const OSM::TileIndex & v0, const OSM::TileIndex & v1) { + return (v0.z == v1.z) && (v0.x == v1.x) && (v0.y == v1.y); +} + +inline bool operator==(const OSM::TilePixmap & v0, const OSM::TilePixmap & v1) { + return v0.index == v1.index; +} + +Q_DECLARE_METATYPE(OSM::TileIndex); +Q_DECLARE_METATYPE(OSM::TilePixmap); + +#endif diff --git a/libs/map/plugin/CMakeLists.txt b/libs/map/plugin/CMakeLists.txt new file mode 100644 index 0000000..ff0f90e --- /dev/null +++ b/libs/map/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(map "Gui;Widgets" "") diff --git a/libs/map/plugin/mapplugin.cpp b/libs/map/plugin/mapplugin.cpp new file mode 100644 index 0000000..6da93f5 --- /dev/null +++ b/libs/map/plugin/mapplugin.cpp @@ -0,0 +1,70 @@ +#include "mapplugin.h" + +#include "mapview.h" + +#include + + +MapPlugin::MapPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void MapPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool MapPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * MapPlugin::createWidget(QWidget * parent) { + MapView * w = new MapView(parent); + return w; +} + + +QString MapPlugin::name() const { + return QLatin1String("MapView"); +} + + +QString MapPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon MapPlugin::icon() const { + return QIcon("://icons/maps.png"); +} + + +QString MapPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString MapPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool MapPlugin::isContainer() const { + return false; +} + + +QString MapPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString MapPlugin::includeFile() const { + return QLatin1String("mapview.h"); +} diff --git a/libs/map/plugin/mapplugin.h b/libs/map/plugin/mapplugin.h new file mode 100644 index 0000000..ecfddb6 --- /dev/null +++ b/libs/map/plugin/mapplugin.h @@ -0,0 +1,33 @@ +#ifndef MAPPLUGIN_H +#define MAPPLUGIN_H + +#include +#include + + +class MapPlugin + : public QObject + , public QDesignerCustomWidgetInterface { + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit MapPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; +}; + +#endif diff --git a/libs/map/plugin/mapview_designerplugin.cpp b/libs/map/plugin/mapview_designerplugin.cpp new file mode 100644 index 0000000..0523614 --- /dev/null +++ b/libs/map/plugin/mapview_designerplugin.cpp @@ -0,0 +1,13 @@ +#include "mapview_designerplugin.h" + +#include "mapplugin.h" + + +MapDesignerPlugin::MapDesignerPlugin(QObject * parent): QObject(parent) { + m_widgets.append(new MapPlugin(this)); +} + + +QList MapDesignerPlugin::customWidgets() const { + return m_widgets; +} diff --git a/libs/map/plugin/mapview_designerplugin.h b/libs/map/plugin/mapview_designerplugin.h new file mode 100644 index 0000000..b7ab702 --- /dev/null +++ b/libs/map/plugin/mapview_designerplugin.h @@ -0,0 +1,23 @@ +#ifndef MAP_DESIGNERPLUGIN_H +#define MAP_DESIGNERPLUGIN_H + +#include +#include + + +class MapDesignerPlugin + : public QObject + , public QDesignerCustomWidgetCollectionInterface { + Q_OBJECT + Q_PLUGIN_METADATA(IID "qad.map") + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) + +public: + MapDesignerPlugin(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; +}; + +#endif diff --git a/libs/map/qad_map.qrc b/libs/map/qad_map.qrc new file mode 100644 index 0000000..6546225 --- /dev/null +++ b/libs/map/qad_map.qrc @@ -0,0 +1,5 @@ + + + ../../icons/maps.png + + diff --git a/utils/mapviewer/CMakeLists.txt b/utils/mapviewer/CMakeLists.txt new file mode 100644 index 0000000..6d5a248 --- /dev/null +++ b/utils/mapviewer/CMakeLists.txt @@ -0,0 +1,15 @@ +find_package(PIP) +if (PIP_FOUND) + + project(mapviewer) + if(APPLE) + set(APP_ICON "") + elseif(WIN32) + set(APP_ICON "icons/maps.ico") + else() + set(APP_ICON "icons/maps.png") + endif() + set(APP_INFO "Map viewer") + qad_application(${PROJECT_NAME} "Gui;Widgets" "qad_map") + +endif() diff --git a/utils/mapviewer/icons/maps.ico b/utils/mapviewer/icons/maps.ico new file mode 100644 index 0000000..6853c7e Binary files /dev/null and b/utils/mapviewer/icons/maps.ico differ diff --git a/utils/mapviewer/icons/maps.png b/utils/mapviewer/icons/maps.png new file mode 100644 index 0000000..ae68d54 Binary files /dev/null and b/utils/mapviewer/icons/maps.png differ diff --git a/utils/mapviewer/main.cpp b/utils/mapviewer/main.cpp new file mode 100644 index 0000000..054a865 --- /dev/null +++ b/utils/mapviewer/main.cpp @@ -0,0 +1,72 @@ +#include "mapitemellipse.h" +#include "mapitemgeopolygon.h" +#include "mapitemgeopolyline.h" +#include "mapitemimage.h" +#include "mapitempolygon.h" +#include "mapitemtext.h" +#include "mapview.h" +#include "qad_types.h" + +#include +#include +#include +#include +#include + +int main(int argc, char * argv[]) { + QApplication a(argc, argv); + enableHighDPI(); + MapView w; + w.resize(800, 600); + w.show(); + /*QVector g = {QPointF(55.583055, 37.580008), QPointF(55.583055, 37.590008), QPointF(55.593055, 37.580008)}; + QVector p = {QPointF(0, 0), QPointF(200, 0), QPointF(0, 100)}; + MapItemPolygon * pol = new MapItemPolygon(QPolygonF(p)); + MapItemGeoPolyline * gpol = new MapItemGeoPolyline(QPolygonF(g)); + MapItemEllipse * ell = new MapItemEllipse(QPointF(), 50, 50); + MapItemImage * im = new MapItemImage(QPixmap(":/icons/maps.png")); + MapItemText * it = new MapItemText(QString::fromUtf8("Это Ваня!")); + im->setPosition({55.583055, 37.580008}); + im->setScale(0.2, 0.5); + im->setAlignment(Qt::AlignRight | Qt::AlignTop); + im->setCursor(Qt::PointingHandCursor); + im->setInteracive(true); + it->setPosition({55.583055, 37.580008}); + it->setBrush(QColor(127, 0, 0, 127)); + it->setPen(QColor(64, 255, 64)); + it->setFont(QFont("times", 18)); + it->setCursor(Qt::OpenHandCursor); + it->setInteracive(true); + pol->setPosition({55.583055, 37.580008}); + pol->setUnits(MapItemNonGeoGeometryBase::Pixels); + gpol->setBrush(QColor(0, 0, 255, 64)); + gpol->setPen(QPen(QColor(64, 64, 255), 3)); + ell->setPosition({55.583055, 37.580008}); + ell->setStartAngle(-20); + ell->setEndAngle(20); + ell->setEllipse(QPointF(100, 0), 50, 50); + ell->setInteracive(true); + QTimer t; + QObject::connect(&w, &MapView::itemClicked, [](MapItemBase * item) { qDebug() << "click" << item; }); + QObject::connect(&w, &MapView::itemEntered, [](MapItemBase * item) { qDebug() << "enter" << item; }); + QObject::connect(&w, &MapView::itemLeaved, [](MapItemBase * item) { qDebug() << "leave" << item; }); + + QObject::connect(&t, &QTimer::timeout, [im, it, pol, ell]() { + im->rotate(1); + it->rotate(-0.1); + // pol->rotate(0.2); + ell->rotate(-0.2); + static double t = 0.; + t += 0.025; + ell->setScale((sin(t) / 2. + 1.)); + }); + t.start(100); + w.addItem(im); + w.addItem(gpol); + w.addItem(pol); + w.addItem(it); + w.addItem(ell);*/ + w.centerTo({55.583055, 37.580008}); + w.zoomTo(17); + return a.exec(); +} diff --git a/utils/mapviewer/mapviewer.qrc b/utils/mapviewer/mapviewer.qrc new file mode 100644 index 0000000..f49b0df --- /dev/null +++ b/utils/mapviewer/mapviewer.qrc @@ -0,0 +1,5 @@ + + + icons/maps.png + +