version 2.13.0

add Map library (MapView with OSM maps and items) and mapviewer util
This commit is contained in:
2023-01-20 09:16:42 +03:00
parent 10212e2ebd
commit 958c81fb1d
46 changed files with 2383 additions and 1 deletions

View File

@@ -3,7 +3,7 @@ cmake_policy(SET CMP0017 NEW) # need include() with .cmake
cmake_policy(SET CMP0072 NEW) # FindOpenGL prefers GLVND by default cmake_policy(SET CMP0072 NEW) # FindOpenGL prefers GLVND by default
project(QAD) project(QAD)
set(QAD_MAJOR 2) set(QAD_MAJOR 2)
set(QAD_MINOR 12) set(QAD_MINOR 13)
set(QAD_REVISION 0) set(QAD_REVISION 0)
set(QAD_SUFFIX ) set(QAD_SUFFIX )
set(QAD_COMPANY SHS) set(QAD_COMPANY SHS)

BIN
icons/maps.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

1
libs/map/CMakeLists.txt Normal file
View File

@@ -0,0 +1 @@
qad_library(map "Gui;Widgets;Network" "qad_widgets")

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>AboutWindow</name>
<message>
<location filename="../aboutwindow.ui" line="14"/>
<source> - About</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="41"/>
<source>Versions</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="56"/>
<source>Build</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="71"/>
<source>Authors</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="100"/>
<source>About Qt...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="111"/>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutwindow.cpp" line="33"/>
<location filename="../aboutwindow.cpp" line="157"/>
<source>About</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EMainWindow</name>
<message>
<location filename="../emainwindow.cpp" line="12"/>
<location filename="../emainwindow.cpp" line="130"/>
<source>Clear recent list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="15"/>
<location filename="../emainwindow.cpp" line="16"/>
<location filename="../emainwindow.cpp" line="126"/>
<location filename="../emainwindow.cpp" line="127"/>
<source>Show all</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="17"/>
<location filename="../emainwindow.cpp" line="18"/>
<location filename="../emainwindow.cpp" line="128"/>
<location filename="../emainwindow.cpp" line="129"/>
<source>Hide all</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="152"/>
<source>Toolbars</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="177"/>
<source>Docks</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="395"/>
<source>Select file to open</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="404"/>
<source>Select files to open</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="414"/>
<source>Save changes%1?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="414"/>
<source> in</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="427"/>
<source>Select file to save</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
<location filename="../historyview.cpp" line="17"/>
<location filename="../historyview.cpp" line="97"/>
<source>History cleared</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogView</name>
<message>
<location filename="../logview.ui" line="92"/>
<source>Category:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../logview.ui" line="119"/>
<location filename="../logview.cpp" line="37"/>
<location filename="../logview.cpp" line="133"/>
<source>Clear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../logview.cpp" line="35"/>
<location filename="../logview.cpp" line="131"/>
<source>Select All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../logview.cpp" line="36"/>
<location filename="../logview.cpp" line="132"/>
<source>Copy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../logview.cpp" line="46"/>
<source>All</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
<name>AboutWindow</name>
<message>
<location filename="../aboutwindow.ui" line="14"/>
<source> - About</source>
<translation> - О программе</translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="41"/>
<source>Versions</source>
<translation>Версии</translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="56"/>
<source>Build</source>
<translation>Сборка</translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="71"/>
<source>Authors</source>
<translation>Авторы</translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="100"/>
<source>About Qt...</source>
<translation>О Qt ...</translation>
</message>
<message>
<location filename="../aboutwindow.ui" line="111"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../aboutwindow.cpp" line="33"/>
<location filename="../aboutwindow.cpp" line="157"/>
<source>About</source>
<translation>О программе</translation>
</message>
</context>
<context>
<name>EMainWindow</name>
<message>
<location filename="../emainwindow.cpp" line="12"/>
<location filename="../emainwindow.cpp" line="130"/>
<source>Clear recent list</source>
<translation>Очистить список недавних</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="15"/>
<location filename="../emainwindow.cpp" line="16"/>
<location filename="../emainwindow.cpp" line="126"/>
<location filename="../emainwindow.cpp" line="127"/>
<source>Show all</source>
<translation>Показать все</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="17"/>
<location filename="../emainwindow.cpp" line="18"/>
<location filename="../emainwindow.cpp" line="128"/>
<location filename="../emainwindow.cpp" line="129"/>
<source>Hide all</source>
<translation>Скрыть все</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="152"/>
<source>Toolbars</source>
<translation>Панели инструментов</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="177"/>
<source>Docks</source>
<translation>Окна</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="395"/>
<source>Select file to open</source>
<translation>Выбрать файл для открытия</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="404"/>
<source>Select files to open</source>
<translation>Выберите файлы для открытия</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="414"/>
<source>Save changes%1?</source>
<translation>Сохранить изменения%1?</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="414"/>
<source> in</source>
<translation> в</translation>
</message>
<message>
<location filename="../emainwindow.cpp" line="427"/>
<source>Select file to save</source>
<translation>Выберите файл для сохранения</translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
<location filename="../historyview.cpp" line="17"/>
<location filename="../historyview.cpp" line="97"/>
<source>History cleared</source>
<translation>История очищена</translation>
</message>
</context>
<context>
<name>LogView</name>
<message>
<location filename="../logview.ui" line="92"/>
<source>Category:</source>
<translation>Категория:</translation>
</message>
<message>
<location filename="../logview.ui" line="119"/>
<location filename="../logview.cpp" line="37"/>
<location filename="../logview.cpp" line="133"/>
<source>Clear</source>
<translation>Очистить</translation>
</message>
<message>
<location filename="../logview.cpp" line="35"/>
<location filename="../logview.cpp" line="131"/>
<source>Select All</source>
<translation>Выделить всё</translation>
</message>
<message>
<location filename="../logview.cpp" line="36"/>
<location filename="../logview.cpp" line="132"/>
<source>Copy</source>
<translation>Копировать</translation>
</message>
<message>
<location filename="../logview.cpp" line="46"/>
<source>All</source>
<translation>Все</translation>
</message>
</context>
</TS>

2
libs/map/lang/update.bat Normal file
View File

@@ -0,0 +1,2 @@
lupdate ../ -ts qad_application_ru.ts
lupdate ../ -ts qad_application_en.ts

95
libs/map/mapitembase.cpp Normal file
View File

@@ -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;
}

94
libs/map/mapitembase.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef mapitembase_h
#define mapitembase_h
#include "qad_map_export.h"
#include <QCursor>
#include <QMap>
#include <QPainter>
#include <QRectF>
#include <QVariant>
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<int, QVariant> data_;
};
#endif

View File

@@ -0,0 +1,77 @@
#include "mapitemellipse.h"
#include <math.h>
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;
}

53
libs/map/mapitemellipse.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@@ -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();
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef mapitemgeometrybase_h
#define mapitemgeometrybase_h
#include "mapitembase.h"
#include <QBrush>
#include <QPen>
#include <QPolygonF>
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

View File

@@ -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();
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@@ -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();
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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

38
libs/map/mapitemimage.cpp Normal file
View File

@@ -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);
}

46
libs/map/mapitemimage.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef mapitemimage_h
#define mapitemimage_h
#include "mapitembase.h"
#include <QPixmap>
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

View File

@@ -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.;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@@ -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());
}

43
libs/map/mapitempolygon.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@@ -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());
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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

50
libs/map/mapitemtext.cpp Normal file
View File

@@ -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);
}

51
libs/map/mapitemtext.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef mapitemtext_h
#define mapitemtext_h
#include "mapitemgeometrybase.h"
#include <QFont>
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

317
libs/map/mapview.cpp Normal file
View File

@@ -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 <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QPainter>
#include <QPixmap>
#include <QScrollBar>
#include <QWheelEvent>
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();
}

104
libs/map/mapview.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef mapview_h
#define mapview_h
#include "mapitembase.h"
#include "qad_map_export.h"
#include <QImage>
#include <QWidget>
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<MapItemBase *> 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

View File

@@ -0,0 +1,73 @@
#include "mapview.h"
#include "osm_downloader_p.h"
#include "osm_tile_cache_p.h"
OSMDownloader::OSMDownloader(MapView * p): QThread() {
qRegisterMetaType<OSM::TileIndex>();
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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef osm_downloader_h
#define osm_downloader_h
#include "osm_types_p.h"
#include <QMutex>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPixmap>
#include <QQueue>
#include <QThread>
#include <QWaitCondition>
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<OSM::TileIndex> queue;
QSet<quint64> in_progress;
public slots:
private slots:
signals:
};
#endif

66
libs/map/osm_math_p.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef osm_math_p_h
#define osm_math_p_h
#include <QPointF>
#include <math.h>
// 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

100
libs/map/osm_tile_cache.cpp Normal file
View File

@@ -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<OSM::TileIndex>();
qRegisterMetaType<OSM::TilePixmap>();
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);
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef osm_tile_cache_h
#define osm_tile_cache_h
#include "osm_types_p.h"
#include <QCache>
#include <QDir>
#include <QMutex>
#include <QQueue>
#include <QThread>
#include <QWaitCondition>
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<OSM::TilePixmap> queue;
QCache<quint64, OSM::TilePixmap> tile_cache;
public slots:
private slots:
signals:
void tileReady(OSM::TilePixmap);
};
#endif

62
libs/map/osm_types.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include "osm_types_p.h"
#include <QString>
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;
}

56
libs/map/osm_types_p.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef osm_types_h
#define osm_types_h
#include <QPixmap>
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

View File

@@ -0,0 +1 @@
qad_plugin(map "Gui;Widgets" "")

View File

@@ -0,0 +1,70 @@
#include "mapplugin.h"
#include "mapview.h"
#include <QtCore/QtPlugin>
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("<widget class=\"MapView\" name=\"map\">\n</widget>\n");
}
QString MapPlugin::includeFile() const {
return QLatin1String("mapview.h");
}

View File

@@ -0,0 +1,33 @@
#ifndef MAPPLUGIN_H
#define MAPPLUGIN_H
#include <QObject>
#include <QtUiPlugin/QDesignerCustomWidgetInterface>
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

View File

@@ -0,0 +1,13 @@
#include "mapview_designerplugin.h"
#include "mapplugin.h"
MapDesignerPlugin::MapDesignerPlugin(QObject * parent): QObject(parent) {
m_widgets.append(new MapPlugin(this));
}
QList<QDesignerCustomWidgetInterface *> MapDesignerPlugin::customWidgets() const {
return m_widgets;
}

View File

@@ -0,0 +1,23 @@
#ifndef MAP_DESIGNERPLUGIN_H
#define MAP_DESIGNERPLUGIN_H
#include <QtCore/qplugin.h>
#include <QtDesigner/QtDesigner>
class MapDesignerPlugin
: public QObject
, public QDesignerCustomWidgetCollectionInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID "qad.map")
Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
public:
MapDesignerPlugin(QObject * parent = 0);
virtual QList<QDesignerCustomWidgetInterface *> customWidgets() const;
private:
QList<QDesignerCustomWidgetInterface *> m_widgets;
};
#endif

5
libs/map/qad_map.qrc Normal file
View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>../../icons/maps.png</file>
</qresource>
</RCC>

View File

@@ -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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

72
utils/mapviewer/main.cpp Normal file
View File

@@ -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 <QApplication>
#include <QDateTime>
#include <QDebug>
#include <QTimer>
#include <math.h>
int main(int argc, char * argv[]) {
QApplication a(argc, argv);
enableHighDPI();
MapView w;
w.resize(800, 600);
w.show();
/*QVector<QPointF> g = {QPointF(55.583055, 37.580008), QPointF(55.583055, 37.590008), QPointF(55.593055, 37.580008)};
QVector<QPointF> 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();
}

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>icons/maps.png</file>
</qresource>
</RCC>