Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kosmindoormap for openSUSE:Factory checked in at 2023-01-07 17:18:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kosmindoormap (Old) and /work/SRC/openSUSE:Factory/.kosmindoormap.new.1563 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kosmindoormap" Sat Jan 7 17:18:44 2023 rev:26 rq:1056570 version:22.12.1 Changes: -------- --- /work/SRC/openSUSE:Factory/kosmindoormap/kosmindoormap.changes 2022-12-09 17:00:37.260653905 +0100 +++ /work/SRC/openSUSE:Factory/.kosmindoormap.new.1563/kosmindoormap.changes 2023-01-07 17:21:35.066825346 +0100 @@ -1,0 +2,8 @@ +Tue Jan 3 10:20:37 UTC 2023 - Christophe Marin <[email protected]> + +- Update to 22.12.1 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/gear/22.12.1/ + +------------------------------------------------------------------- Old: ---- kosmindoormap-22.12.0.tar.xz kosmindoormap-22.12.0.tar.xz.sig New: ---- kosmindoormap-22.12.1.tar.xz kosmindoormap-22.12.1.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kosmindoormap.spec ++++++ --- /var/tmp/diff_new_pack.Qr7xc6/_old 2023-01-07 17:21:35.562828305 +0100 +++ /var/tmp/diff_new_pack.Qr7xc6/_new 2023-01-07 17:21:35.570828353 +0100 @@ -21,7 +21,7 @@ %{!?_kapp_version: %define _kapp_version %(echo %{version}| awk -F. '{print $1"."$2}')} %bcond_without released Name: kosmindoormap -Version: 22.12.0 +Version: 22.12.1 Release: 0 Summary: OSM indoor map QML component License: LGPL-2.0-or-later AND CC0-1.0 ++++++ kosmindoormap-22.12.0.tar.xz -> kosmindoormap-22.12.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/CMakeLists.txt new/kosmindoormap-22.12.1/CMakeLists.txt --- old/kosmindoormap-22.12.0/CMakeLists.txt 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/CMakeLists.txt 2023-01-03 01:06:39.000000000 +0100 @@ -6,7 +6,7 @@ # KDE Application Version, managed by release script set (RELEASE_SERVICE_VERSION_MAJOR "22") set (RELEASE_SERVICE_VERSION_MINOR "12") -set (RELEASE_SERVICE_VERSION_MICRO "0") +set (RELEASE_SERVICE_VERSION_MICRO "1") set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") project(KOSMIndoorMap VERSION ${RELEASE_SERVICE_VERSION}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/autotests/mapviewtest.cpp new/kosmindoormap-22.12.1/autotests/mapviewtest.cpp --- old/kosmindoormap-22.12.0/autotests/mapviewtest.cpp 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/autotests/mapviewtest.cpp 2023-01-03 01:06:39.000000000 +0100 @@ -4,7 +4,7 @@ SPDX-License-Identifier: LGPL-2.0-or-later */ -#include <map/renderer/view.h> +#include <map/scene/view.h> #include <QTest> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/po/fr/kosmindoormap.po new/kosmindoormap-22.12.1/po/fr/kosmindoormap.po --- old/kosmindoormap-22.12.0/po/fr/kosmindoormap.po 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/po/fr/kosmindoormap.po 2023-01-03 01:06:39.000000000 +0100 @@ -12,7 +12,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Lokalize 22.11.80\n" +"X-Generator: Lokalize 22.11.90\n" #: app/main.cpp:44 #, kde-format diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/po/tr/kosmindoormap.po new/kosmindoormap-22.12.1/po/tr/kosmindoormap.po --- old/kosmindoormap-22.12.0/po/tr/kosmindoormap.po 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/po/tr/kosmindoormap.po 2023-01-03 01:06:39.000000000 +0100 @@ -7,7 +7,7 @@ "Project-Id-Version: kosmindoormap\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-06-29 00:43+0000\n" -"PO-Revision-Date: 2022-11-22 16:35+0300\n" +"PO-Revision-Date: 2022-12-11 13:50+0300\n" "Last-Translator: Emir SARI <[email protected]>\n" "Language-Team: Turkish <[email protected]>\n" "Language: tr\n" @@ -15,7 +15,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Lokalize 22.08.3\n" +"X-Generator: Lokalize 22.12.0\n" #: app/main.cpp:44 #, kde-format @@ -1090,7 +1090,7 @@ #: map-quick/osmelementinformationmodel_data.cpp:194 msgctxt "OSM::cuisine" msgid "Indonesian" -msgstr "Endonezya" +msgstr "Endonezce" #: map-quick/osmelementinformationmodel_data.cpp:195 msgctxt "OSM::cuisine" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/po/zh_CN/kosmindoormap.po new/kosmindoormap-22.12.1/po/zh_CN/kosmindoormap.po --- old/kosmindoormap-22.12.0/po/zh_CN/kosmindoormap.po 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/po/zh_CN/kosmindoormap.po 2023-01-03 01:06:39.000000000 +0100 @@ -3,7 +3,7 @@ "Project-Id-Version: kdeorg\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-06-29 00:43+0000\n" -"PO-Revision-Date: 2022-11-19 14:51\n" +"PO-Revision-Date: 2022-12-24 11:45\n" "Last-Translator: \n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/app/org.kde.kosmindoormap.appdata.xml new/kosmindoormap-22.12.1/src/app/org.kde.kosmindoormap.appdata.xml --- old/kosmindoormap-22.12.0/src/app/org.kde.kosmindoormap.appdata.xml 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/app/org.kde.kosmindoormap.appdata.xml 2023-01-03 01:06:39.000000000 +0100 @@ -87,9 +87,9 @@ </provides> <launchable type="desktop-id">org.kde.kosmindoormap.desktop</launchable> <releases> + <release version="22.12.1" date="2023-01-05"/> <release version="22.12.0" date="2022-12-08"/> <release version="22.08.3" date="2022-11-03"/> <release version="22.08.2" date="2022-10-13"/> - <release version="22.08.1" date="2022-09-08"/> </releases> </component> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/CMakeLists.txt new/kosmindoormap-22.12.1/src/map/CMakeLists.txt --- old/kosmindoormap-22.12.0/src/map/CMakeLists.txt 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/CMakeLists.txt 2023-01-03 01:06:39.000000000 +0100 @@ -41,7 +41,6 @@ renderer/hitdetector.cpp renderer/painterrenderer.cpp renderer/stackblur.cpp - renderer/view.cpp scene/iconloader.cpp scene/openinghourscache.cpp @@ -52,6 +51,7 @@ scene/scenegeometry.cpp scene/scenegraph.cpp scene/scenegraphitem.cpp + scene/view.cpp style/mapcsscondition.cpp style/mapcssdeclaration.cpp @@ -127,7 +127,6 @@ HEADER_NAMES HitDetector PainterRenderer - View PREFIX KOSMIndoorMap REQUIRED_HEADERS KOSMIndoorMap_Renderer_HEADERS RELATIVE renderer @@ -138,6 +137,7 @@ SceneController SceneGraph SceneGraphItem + View PREFIX KOSMIndoorMap REQUIRED_HEADERS KOSMIndoorMap_Scene_HEADERS RELATIVE scene diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/renderer/hitdetector.cpp new/kosmindoormap-22.12.1/src/map/renderer/hitdetector.cpp --- old/kosmindoormap-22.12.0/src/map/renderer/hitdetector.cpp 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/renderer/hitdetector.cpp 2023-01-03 01:06:39.000000000 +0100 @@ -9,7 +9,7 @@ #include "../scene/scenegraph.h" #include "../scene/scenegraphitem.h" #include "../scene/scenegeometry_p.h" -#include "view.h" +#include "../scene/view.h" #include <QBrush> #include <QFontMetrics> @@ -35,9 +35,9 @@ } // (2) in presence of transparency, use the smallest item at this position - std::sort(items.begin(), items.end(), [](auto lhs, auto rhs) { - const auto lhsBbox = lhs->payload->boundingRect(); - const auto rhsBbox = rhs->payload->boundingRect(); + std::sort(items.begin(), items.end(), [view](auto lhs, auto rhs) { + const auto lhsBbox = lhs->payload->boundingRect(view); + const auto rhsBbox = rhs->payload->boundingRect(view); return (lhsBbox.width() * lhsBbox.height()) < (rhsBbox.width() * rhsBbox.height()); }); return items.front(); @@ -47,7 +47,7 @@ { std::vector<const SceneGraphItem*> result; for (const auto &item : sg.items()) { - if (item.payload->renderPhases() == SceneGraphItemPayload::NoPhase || !item.payload->boundingRect().contains(view->mapScreenToScene(pos))) { + if (item.payload->renderPhases() == SceneGraphItemPayload::NoPhase || !item.payload->boundingRect(view).contains(view->mapScreenToScene(pos))) { continue; } if (!itemContainsPoint(item, pos, view)) { @@ -108,7 +108,7 @@ bool HitDetector::itemContainsPoint(const LabelItem *item, QPointF screenPos, const View *view) const { - auto hitBox = item->boundingRect(); + auto hitBox = item->boundingRect(view); // QStaticText::size doesn't return the actual bounding box with QStaticText::textWidth is set, // so we need to compute that manually here to not end up with overly large hitboxes if (item->text.textWidth() > 0) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/renderer/painterrenderer.cpp new/kosmindoormap-22.12.1/src/map/renderer/painterrenderer.cpp --- old/kosmindoormap-22.12.0/src/map/renderer/painterrenderer.cpp 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/renderer/painterrenderer.cpp 2023-01-03 01:06:39.000000000 +0100 @@ -6,10 +6,10 @@ #include "painterrenderer.h" #include "stackblur_p.h" -#include "view.h" #include "render-logging.h" #include <KOSMIndoorMap/SceneGraph> +#include <KOSMIndoorMap/View> #include <QDebug> #include <QElapsedTimer> @@ -48,11 +48,11 @@ m_renderBatch.reserve(layerOffsets.second - layerOffsets.first); const QRectF screenRect(QPointF(0, 0), QSizeF(m_view->screenWidth(), m_view->screenHeight())); for (auto it = layerBegin; it != layerEnd; ++it) { - if ((*it).payload->inSceneSpace() && m_view->viewport().intersects((*it).payload->boundingRect())) { + if ((*it).payload->inSceneSpace() && m_view->viewport().intersects((*it).payload->boundingRect(view))) { m_renderBatch.push_back((*it).payload.get()); } if ((*it).payload->inHUDSpace()) { - auto bbox = (*it).payload->boundingRect(); + auto bbox = (*it).payload->boundingRect(view); bbox.moveCenter(m_view->mapSceneToScreen(bbox.center())); if (screenRect.intersects(bbox)) { m_renderBatch.push_back((*it).payload.get()); @@ -172,7 +172,7 @@ m_painter->translate(m_view->mapSceneToScreen(item->pos)); m_painter->rotate(item->angle); - auto box = item->boundingRect(); + auto box = item->boundingRect(m_view); box.moveCenter({0.0, item->offset}); // draw shield diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/renderer/view.cpp new/kosmindoormap-22.12.1/src/map/renderer/view.cpp --- old/kosmindoormap-22.12.0/src/map/renderer/view.cpp 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/renderer/view.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,345 +0,0 @@ -/* - SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]> - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ - -#include "view.h" - -#include <osm/geomath.h> - -#include <cmath> - -using namespace KOSMIndoorMap; - -static constexpr const double SceneWorldSize = 256.0; // size of the scene when containing the full world -static constexpr const double LatitudeLimit = 85.05112879806592; // invtan(sinh(pi)) + radToDeg -static constexpr const auto MaxZoomFactor = 21; // 2^MaxZoomFactor subdivisions of the scene space - -View::View(QObject *parent) - : QObject(parent) -{ - setBeginTime(QDateTime::currentDateTime()); -} - -View::~View() = default; - -QPointF View::mapGeoToScene(OSM::Coordinate coord) const -{ - const auto lat = qBound(-LatitudeLimit, coord.latF(), LatitudeLimit); - return QPointF( - (coord.lonF() + 180.0) / 360.0 * SceneWorldSize, - SceneWorldSize / (2.0 * M_PI) * (M_PI - std::log(std::tan((M_PI / 4.0) + ((OSM::degToRad(lat) / 2.0))))) - ); -} - -QRectF View::mapGeoToScene(OSM::BoundingBox box) const -{ - const auto p1 = mapGeoToScene(box.min); - const auto p2 = mapGeoToScene(box.max); - return QRectF(QPointF(p1.x(), p2.y()), QPointF(p2.x(), p1.y())); -} - -OSM::Coordinate View::mapSceneToGeo(QPointF p) const -{ - return OSM::Coordinate( - OSM::radToDeg(std::atan(std::sinh(M_PI * (1 - 2 * (p.y() / SceneWorldSize))))), - (p.x() / SceneWorldSize) * 360.0 - 180.0 - ); -} - -OSM::BoundingBox View::mapSceneToGeo(const QRectF &box) const -{ - const auto c1 = mapSceneToGeo(box.bottomLeft()); - const auto c2 = mapSceneToGeo(box.topRight()); - return OSM::BoundingBox(c1, c2); -} - -int View::screenHeight() const -{ - return m_screenSize.height(); -} - -int View::screenWidth() const -{ - return m_screenSize.width(); -} - -void View::setScreenSize(QSize size) -{ - if (size.width() <= 0.0 || size.height() <= 0.0 || size == m_screenSize) { - return; - } - - const auto dx = (double)size.width() / (double)screenWidth(); - const auto dy = (double)size.height() / (double)screenHeight(); - m_screenSize = size; - - m_viewport.setWidth(m_viewport.width() * dx); - m_viewport.setHeight(m_viewport.height() * dy); - constrainViewToScene(); - Q_EMIT transformationChanged(); -} - -int View::level() const -{ - return m_level; -} - -void View::setLevel(int level) -{ - if (m_level == level) { - return; - } - - m_level = level; - Q_EMIT floorLevelChanged(); -} - -double View::zoomLevel() const -{ - const auto dx = m_viewport.width() / (screenWidth() / SceneWorldSize) / 360.0; - return - std::log2(dx); -} - -void View::setZoomLevel(double zoom, QPointF screenCenter) -{ - auto z = std::pow(2.0, - std::min(zoom, (double)MaxZoomFactor)); - const auto dx = ((screenWidth() / SceneWorldSize) * 360.0 * z) - m_viewport.width(); - const auto dy = ((screenHeight() / SceneWorldSize) * 360.0 * z) - m_viewport.height(); - - const auto centerScene = mapScreenToScene(screenCenter); - if (!m_viewport.contains(centerScene)) { - return; // invalid input - } - - const auto xr = (centerScene.x() - m_viewport.x()) / m_viewport.width(); - const auto yr = (centerScene.y() - m_viewport.y()) / m_viewport.height(); - - m_viewport.adjust(-xr * dx, -yr * dy, (1-xr) * dx, (1-yr) * dy); - constrainViewToScene(); - Q_EMIT transformationChanged(); -} - -QRectF View::viewport() const -{ - return m_viewport; -} - -void View::setViewport(const QRectF &viewport) -{ - m_viewport = viewport; - constrainViewToScene(); -} - -QRectF View::sceneBoundingBox() const -{ - return m_bbox; -} - -void View::setSceneBoundingBox(OSM::BoundingBox bbox) -{ - setSceneBoundingBox(mapGeoToScene(bbox)); -} - -void View::setSceneBoundingBox(const QRectF &bbox) -{ - if (m_bbox == bbox) { - return; - } - m_bbox = bbox; - - // scale to fit horizontally - m_viewport = bbox; - const auto screenAspectRatio = (double)screenWidth() / (double)screenHeight(); - m_viewport.setHeight(m_viewport.width() / screenAspectRatio); - - // if necessary, scale to fit vertically - if (m_viewport.height() > m_bbox.height()) { - const auto dy = (double)m_bbox.height() / (double)m_viewport.height(); - m_viewport.setHeight(m_viewport.height() * dy); - m_viewport.setWidth(m_viewport.width() * dy); - } - - Q_EMIT transformationChanged(); -} - - -QPointF View::mapSceneToScreen(QPointF scenePos) const -{ - return sceneToScreenTransform().map(scenePos); -} - -QRectF View::mapSceneToScreen(const QRectF &sceneRect) const -{ - return QRectF(mapSceneToScreen(sceneRect.topLeft()), mapSceneToScreen(sceneRect.bottomRight())); -} - -QPointF View::mapScreenToScene(QPointF screenPos) const -{ - // TODO this can be implemented more efficiently - return sceneToScreenTransform().inverted().map(screenPos); -} - -double View::mapScreenDistanceToSceneDistance(double distance) const -{ - const auto p1 = mapScreenToScene(m_viewport.center()); - const auto p2 = mapScreenToScene(m_viewport.center() + QPointF(1.0, 0)); - // ### does not consider rotations, needs to take the actual distance between p1 and p2 for that - return std::abs(p2.x() - p1.x()) * distance; -} - -void View::panScreenSpace(QPoint offset) -{ - auto dx = offset.x() * (m_viewport.width() / screenWidth()); - auto dy = offset.y() * (m_viewport.height() / screenHeight()); - m_viewport.adjust(dx, dy, dx, dy); - constrainViewToScene(); -} - -QTransform View::sceneToScreenTransform() const -{ - QTransform t; - t.scale(screenWidth() / (m_viewport.width()), screenHeight() / (m_viewport.height())); - t.translate(-m_viewport.x(), -m_viewport.y()); - return t; -} - -void View::zoomIn(QPointF screenCenter) -{ - setZoomLevel(zoomLevel() + 1, screenCenter); -} - -void View::zoomOut(QPointF screenCenter) -{ - setZoomLevel(zoomLevel() - 1, screenCenter); -} - -void View::constrainViewToScene() -{ - // ensure we don't scale smaller than the bounding box - const auto s = std::min(m_viewport.width() / m_bbox.width(), m_viewport.height() / m_bbox.height()); - if (s > 1.0) { - m_viewport.setWidth(m_viewport.width() / s); - m_viewport.setHeight(m_viewport.height() / s); - } - - // ensure we don't pan outside of the bounding box - if (m_bbox.left() < m_viewport.left() && m_bbox.right() < m_viewport.right()) { - const auto dx = std::min(m_viewport.left() - m_bbox.left(), m_viewport.right() - m_bbox.right()); - m_viewport.adjust(-dx, 0, -dx, 0); - } else if (m_bbox.right() > m_viewport.right() && m_bbox.left() > m_viewport.left()) { - const auto dx = std::min(m_bbox.right() - m_viewport.right(), m_bbox.left() - m_viewport.left()); - m_viewport.adjust(dx, 0, dx, 0); - } - - if (m_bbox.top() < m_viewport.top() && m_bbox.bottom() < m_viewport.bottom()) { - const auto dy = std::min(m_viewport.top() - m_bbox.top(), m_viewport.bottom() - m_bbox.bottom()); - m_viewport.adjust(0, -dy, 0, -dy); - } else if (m_bbox.bottom() > m_viewport.bottom() && m_bbox.top() > m_viewport.top()) { - const auto dy = std::min(m_bbox.bottom() - m_viewport.bottom(), m_bbox.top() - m_viewport.top()); - m_viewport.adjust(0, dy, 0, dy); - } -} - -double View::mapMetersToScene(double meters) const -{ - // ### this fails for distances above 180° due to OSM::distance wrapping around - // doesn't matter for our use-case though, we are looking at much much smaller areas - const auto d = OSM::distance(mapSceneToGeo(QPointF(m_viewport.left(), m_viewport.center().y())), mapSceneToGeo(QPointF(m_viewport.right(), m_viewport.center().y()))); - const auto scale = m_viewport.width() / d; - return meters * scale; -} - -double View::mapMetersToScreen(double meters) const -{ - const auto d = OSM::distance(mapSceneToGeo(QPointF(m_viewport.left(), m_viewport.center().y())), mapSceneToGeo(QPointF(m_viewport.right(), m_viewport.center().y()))); - const auto r = meters / d; - return r * m_screenSize.width(); -} - -double View::mapScreenToMeters(int pixels) const -{ - const auto d = OSM::distance(mapSceneToGeo(QPointF(m_viewport.left(), m_viewport.center().y())), mapSceneToGeo(QPointF(m_viewport.right(), m_viewport.center().y()))); - const auto r = (double)pixels / (double)m_screenSize.width(); - return d * r; -} - -double View::panX() const -{ - const auto r = (m_viewport.left() - m_bbox.left()) / m_bbox.width(); - return panWidth() * r; -} - -double View::panY() const -{ - const auto r = (m_viewport.top() - m_bbox.top()) / m_bbox.height(); - return panHeight() * r; -} - -double View::panWidth() const -{ - const auto r = m_bbox.width() / m_viewport.width(); - return screenWidth() * r; -} - -double View::panHeight() const -{ - const auto r = m_bbox.height() / m_viewport.height(); - return screenHeight() * r; -} - -void View::panTopLeft(double x, double y) -{ - m_viewport.moveLeft(m_bbox.x() + m_bbox.width() * (x / panWidth())); - m_viewport.moveTop(m_bbox.y() + m_bbox.height() * (y / panHeight())); - constrainViewToScene(); -} - -QTransform View::deviceTransform() const -{ - return m_deviceTransform; -} - -void View::setDeviceTransform(const QTransform &t) -{ - m_deviceTransform = t; -} - -void View::centerOnGeoCoordinate(QPointF geoCoord) -{ - const auto sceneCenter = mapGeoToScene(OSM::Coordinate(geoCoord.y(), geoCoord.x())); - m_viewport.moveCenter(sceneCenter); - constrainViewToScene(); - Q_EMIT transformationChanged(); -} - -QDateTime View::beginTime() const -{ - return m_beginTime; -} - -void View::setBeginTime(const QDateTime &beginTime) -{ - const auto alignedTime = QDateTime(beginTime.date(), {beginTime.time().hour(), beginTime.time().minute()}); - if (m_beginTime == alignedTime) { - return; - } - m_beginTime = alignedTime; - Q_EMIT timeChanged(); -} - -QDateTime View::endTime() const -{ - return m_endTime; -} - -void View::setEndTime(const QDateTime& endTime) -{ - const auto alignedTime = QDateTime(endTime.date(), {endTime.time().hour(), endTime.time().minute()}); - if (m_endTime == alignedTime) { - return; - } - m_endTime = alignedTime; - Q_EMIT timeChanged(); -} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/renderer/view.h new/kosmindoormap-22.12.1/src/map/renderer/view.h --- old/kosmindoormap-22.12.0/src/map/renderer/view.h 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/renderer/view.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,161 +0,0 @@ -/* - SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]> - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ - -#ifndef KOSMINDOORMAP_VIEW_H -#define KOSMINDOORMAP_VIEW_H - -#include "kosmindoormap_export.h" - -#include <KOSM/Datatypes> - -#include <QDateTime> -#include <QObject> -#include <QRectF> -#include <QSize> -#include <QTransform> - -namespace KOSMIndoorMap { - -/** View transformations and transformation manipulation. - * There are three different coordinate systems involved here: - * - The geographic world coordinates of the OSM input data. - * This uses OSM::Coordinate. - * - The scene coordinates which have a the Web Mercator projection applied (see https://en.wikipedia.org/wiki/Mercator_projection). - * This uses QPointF ranging from 0x0 to 256x256 - * - The screen coordinates (ie. visible pixels on screen). - * This uses QPoint. - * Further, there's also three slight variations of those in use here: - * - "HUD" coordinates: elements that follow the scene coordinates for their positioning, - * but the screen coordinates regarding scaling and rotation. This is used for map labels. - * - Geographic distances. This is needed to display things in a fixed width in meters in the scene, - * or to compute the map scale. Note that this only works due to the relatively high zoom levels, - * so that earth curvature or map projection effects are negligible. - * - "pan space": same transform as screen space, but with the origin at the origin of the scene bounding box - * This is useful for implementing scene-wide panning and showing scroll bars. - */ -class KOSMINDOORMAP_EXPORT View : public QObject -{ - Q_OBJECT - Q_PROPERTY(double panX READ panX NOTIFY transformationChanged) - Q_PROPERTY(double panY READ panY NOTIFY transformationChanged) - Q_PROPERTY(double panWidth READ panWidth NOTIFY transformationChanged) - Q_PROPERTY(double panHeight READ panHeight NOTIFY transformationChanged) - Q_PROPERTY(int floorLevel READ level WRITE setLevel NOTIFY floorLevelChanged) - Q_PROPERTY(double zoomLevel READ zoomLevel NOTIFY transformationChanged) - Q_PROPERTY(QDateTime beginTime READ beginTime WRITE setBeginTime NOTIFY timeChanged) - Q_PROPERTY(QDateTime endTime READ endTime WRITE setEndTime NOTIFY timeChanged) -public: - explicit View(QObject *parent = nullptr); - ~View(); - - /** Map a geographic coordinate to a scene coordinate, ie. apply the mercator projection. */ - QPointF mapGeoToScene(OSM::Coordinate coord) const; - QRectF mapGeoToScene(OSM::BoundingBox box) const; - /** Map a scene coordinate to a geographic one, ie. apply the inverse mercator projection. */ - OSM::Coordinate mapSceneToGeo(QPointF p) const; - OSM::BoundingBox mapSceneToGeo(const QRectF &box) const; - - /** Screen-space sizes, ie the size of the on-screen area used for displaying. */ - int screenWidth() const; - int screenHeight() const; - void setScreenSize(QSize size); - - /** The transformation to apply to scene coordinate to get to the view on screen. */ - QTransform sceneToScreenTransform() const; - - /** The (floor) level to display. - * @see MapLevel. - */ - int level() const; - void setLevel(int level); - - /** OSM-compatible zoom level, ie. the 2^level-th subdivision of the scene space. */ - double zoomLevel() const; - /** Set the zoom level to @p zoom, and adjusting it around center position @p center. */ - Q_INVOKABLE void setZoomLevel(double zoom, QPointF screenCenter); - - /** The sub-rect of the scene bounding box currently displayed. - * Specified in scene coordinates. - */ - QRectF viewport() const; - void setViewport(const QRectF &viewport); - - /** The bounding box of the scene. - * The viewport cannot exceed this area. - */ - QRectF sceneBoundingBox() const; - void setSceneBoundingBox(OSM::BoundingBox bbox); - void setSceneBoundingBox(const QRectF &bbox); - - /** Converts a point in scene coordinates to screen coordinates. */ - QPointF mapSceneToScreen(QPointF scenePos) const; - /** Converts a rectangle in scene coordinates to screen coordinates. */ - QRectF mapSceneToScreen(const QRectF &sceneRect) const; - /** Converts a point in screen coordinates to scene coordinates. */ - QPointF mapScreenToScene(QPointF screenPos) const; - /** Converts a distance in screen coordinates to a distance in scene coordinates. */ - double mapScreenDistanceToSceneDistance(double distance) const; - - /** Returns how many units in scene coordinate represent the distance of @p meters in the current view transformation. */ - double mapMetersToScene(double meters) const; - /** Returns how many pixels on screen represent the distance of @p meters with the current view transformation. */ - Q_INVOKABLE double mapMetersToScreen(double meters) const; - /** Returns how many meters are represented by @p pixels with the current view transformation. */ - Q_INVOKABLE double mapScreenToMeters(int pixels) const; - - void panScreenSpace(QPoint offset); - /** Increase zoom level by one/scale up by 2x around the screen position @p center. */ - Q_INVOKABLE void zoomIn(QPointF screenCenter); - /** Decrease zoom level by one/scale down by 2x around the screen position @p center. */ - Q_INVOKABLE void zoomOut(QPointF screenCenter); - - /** Position of the viewport in pan coordinates. */ - double panX() const; - double panY() const; - /** Size of the pan-able area in screen coordinates. */ - double panWidth() const; - double panHeight() const; - - /** Move the viewport to the pan coordinates @p x and @p y. */ - Q_INVOKABLE void panTopLeft(double x, double y); - - /** Device tranformation for manual high DPI scaling. */ - QTransform deviceTransform() const; - void setDeviceTransform(const QTransform &t); - - /** Center the view on the given geo-coordinate. */ - Q_INVOKABLE void centerOnGeoCoordinate(QPointF geoCoord); - - /** Time range that is displayed. - * This matters for example when opening hours are considered for styling. - */ - QDateTime beginTime() const; - void setBeginTime(const QDateTime &beginTime); - QDateTime endTime() const; - void setEndTime(const QDateTime &endTime); - -Q_SIGNALS: - void transformationChanged(); - void floorLevelChanged(); - void timeChanged(); - -private: - /** Ensure we stay within the bounding box with the viewport, call after viewport modification. */ - void constrainViewToScene(); - - QRectF m_bbox; - QRectF m_viewport; - QSize m_screenSize; - QTransform m_deviceTransform = {}; - int m_level = 0; - - QDateTime m_beginTime; - QDateTime m_endTime; -}; - -} - -#endif // KOSMINDOORMAP_VIEW_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/scene/scenegraphitem.cpp new/kosmindoormap-22.12.1/src/map/scene/scenegraphitem.cpp --- old/kosmindoormap-22.12.0/src/map/scene/scenegraphitem.cpp 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/scene/scenegraphitem.cpp 2023-01-03 01:06:39.000000000 +0100 @@ -5,6 +5,7 @@ */ #include "scenegraphitem.h" +#include "view.h" #include <QDebug> @@ -28,9 +29,29 @@ return (pen.style() != Qt::NoPen ? StrokePhase : NoPhase) | (casingPen.style() != Qt::NoPen ? CasingPhase : NoPhase); } -QRectF PolylineItem::boundingRect() const +QRectF PolylineItem::boundingRect([[maybe_unused]] const View *view) const { - return path.boundingRect(); // TODO do we need to cache this? + auto r = path.boundingRect(); // TODO do we need to cache this? + double w = 0.0; + switch (penWidthUnit) { + case Unit::Pixel: + w += view->mapScreenDistanceToSceneDistance(pen.widthF()); + break; + case Unit::Meter: + w += view->mapMetersToScene(pen.widthF()); + break; + } + switch (casingPenWidthUnit) { + case Unit::Pixel: + w += view->mapScreenDistanceToSceneDistance(casingPen.widthF()); + break; + case Unit::Meter: + w += view->mapMetersToScene(casingPen.widthF()); + break; + } + w /= 2.0; + r.adjust(-w, -w, w, w); + return r; } @@ -39,12 +60,12 @@ return (pen.style() == Qt::NoPen ? NoPhase : StrokePhase) | (brush.style() == Qt::NoBrush ? NoPhase : FillPhase); } -QRectF PolygonItem::boundingRect() const +QRectF PolygonItem::boundingRect([[maybe_unused]] const View *view) const { return polygon.boundingRect(); // TODO do we need to cache this? } -QRectF MultiPolygonItem::boundingRect() const +QRectF MultiPolygonItem::boundingRect([[maybe_unused]] const View *view) const { return path.boundingRect(); // TODO do we need to cache this? } @@ -55,7 +76,7 @@ return LabelPhase; } -QRectF LabelItem::boundingRect() const +QRectF LabelItem::boundingRect([[maybe_unused]] const View *view) const { QRectF bbox; if (!text.text().isEmpty()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/scene/scenegraphitem.h new/kosmindoormap-22.12.1/src/map/scene/scenegraphitem.h --- old/kosmindoormap-22.12.0/src/map/scene/scenegraphitem.h 2022-12-01 18:18:32.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/scene/scenegraphitem.h 2023-01-03 01:06:39.000000000 +0100 @@ -26,6 +26,7 @@ namespace KOSMIndoorMap { class SceneGraphItemPayload; +class View; /** Unit for geometry sizes. */ enum class Unit : uint8_t { @@ -73,7 +74,7 @@ /** Bounding box of this item in scene coordinates. * Performance trumps precision here, so estimating this slightly larger rather than computing it expensively makes sense. */ - virtual QRectF boundingRect() const = 0; + virtual QRectF boundingRect(const View *view) const = 0; /** Is this item drawn in scene coordinates (as oposed to HUD coordinates)? */ bool inSceneSpace() const; @@ -89,7 +90,7 @@ { public: uint8_t renderPhases() const override; - QRectF boundingRect() const override; + QRectF boundingRect(const View *view) const override; QPolygonF path; QPen pen; @@ -115,7 +116,7 @@ class PolygonItem : public PolygonBaseItem { public: - QRectF boundingRect() const override; + QRectF boundingRect(const View *view) const override; QPolygonF polygon; }; @@ -125,7 +126,7 @@ class MultiPolygonItem : public PolygonBaseItem { public: - QRectF boundingRect() const override; + QRectF boundingRect(const View *view) const override; QPainterPath path; }; @@ -135,7 +136,7 @@ { public: uint8_t renderPhases() const override; - QRectF boundingRect() const override; + QRectF boundingRect(const View *view) const override; QPointF pos; QColor color; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/scene/view.cpp new/kosmindoormap-22.12.1/src/map/scene/view.cpp --- old/kosmindoormap-22.12.0/src/map/scene/view.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/scene/view.cpp 2023-01-03 01:06:39.000000000 +0100 @@ -0,0 +1,345 @@ +/* + SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "view.h" + +#include <osm/geomath.h> + +#include <cmath> + +using namespace KOSMIndoorMap; + +static constexpr const double SceneWorldSize = 256.0; // size of the scene when containing the full world +static constexpr const double LatitudeLimit = 85.05112879806592; // invtan(sinh(pi)) + radToDeg +static constexpr const auto MaxZoomFactor = 21; // 2^MaxZoomFactor subdivisions of the scene space + +View::View(QObject *parent) + : QObject(parent) +{ + setBeginTime(QDateTime::currentDateTime()); +} + +View::~View() = default; + +QPointF View::mapGeoToScene(OSM::Coordinate coord) const +{ + const auto lat = qBound(-LatitudeLimit, coord.latF(), LatitudeLimit); + return QPointF( + (coord.lonF() + 180.0) / 360.0 * SceneWorldSize, + SceneWorldSize / (2.0 * M_PI) * (M_PI - std::log(std::tan((M_PI / 4.0) + ((OSM::degToRad(lat) / 2.0))))) + ); +} + +QRectF View::mapGeoToScene(OSM::BoundingBox box) const +{ + const auto p1 = mapGeoToScene(box.min); + const auto p2 = mapGeoToScene(box.max); + return QRectF(QPointF(p1.x(), p2.y()), QPointF(p2.x(), p1.y())); +} + +OSM::Coordinate View::mapSceneToGeo(QPointF p) const +{ + return OSM::Coordinate( + OSM::radToDeg(std::atan(std::sinh(M_PI * (1 - 2 * (p.y() / SceneWorldSize))))), + (p.x() / SceneWorldSize) * 360.0 - 180.0 + ); +} + +OSM::BoundingBox View::mapSceneToGeo(const QRectF &box) const +{ + const auto c1 = mapSceneToGeo(box.bottomLeft()); + const auto c2 = mapSceneToGeo(box.topRight()); + return OSM::BoundingBox(c1, c2); +} + +int View::screenHeight() const +{ + return m_screenSize.height(); +} + +int View::screenWidth() const +{ + return m_screenSize.width(); +} + +void View::setScreenSize(QSize size) +{ + if (size.width() <= 0.0 || size.height() <= 0.0 || size == m_screenSize) { + return; + } + + const auto dx = (double)size.width() / (double)screenWidth(); + const auto dy = (double)size.height() / (double)screenHeight(); + m_screenSize = size; + + m_viewport.setWidth(m_viewport.width() * dx); + m_viewport.setHeight(m_viewport.height() * dy); + constrainViewToScene(); + Q_EMIT transformationChanged(); +} + +int View::level() const +{ + return m_level; +} + +void View::setLevel(int level) +{ + if (m_level == level) { + return; + } + + m_level = level; + Q_EMIT floorLevelChanged(); +} + +double View::zoomLevel() const +{ + const auto dx = m_viewport.width() / (screenWidth() / SceneWorldSize) / 360.0; + return - std::log2(dx); +} + +void View::setZoomLevel(double zoom, QPointF screenCenter) +{ + auto z = std::pow(2.0, - std::min(zoom, (double)MaxZoomFactor)); + const auto dx = ((screenWidth() / SceneWorldSize) * 360.0 * z) - m_viewport.width(); + const auto dy = ((screenHeight() / SceneWorldSize) * 360.0 * z) - m_viewport.height(); + + const auto centerScene = mapScreenToScene(screenCenter); + if (!m_viewport.contains(centerScene)) { + return; // invalid input + } + + const auto xr = (centerScene.x() - m_viewport.x()) / m_viewport.width(); + const auto yr = (centerScene.y() - m_viewport.y()) / m_viewport.height(); + + m_viewport.adjust(-xr * dx, -yr * dy, (1-xr) * dx, (1-yr) * dy); + constrainViewToScene(); + Q_EMIT transformationChanged(); +} + +QRectF View::viewport() const +{ + return m_viewport; +} + +void View::setViewport(const QRectF &viewport) +{ + m_viewport = viewport; + constrainViewToScene(); +} + +QRectF View::sceneBoundingBox() const +{ + return m_bbox; +} + +void View::setSceneBoundingBox(OSM::BoundingBox bbox) +{ + setSceneBoundingBox(mapGeoToScene(bbox)); +} + +void View::setSceneBoundingBox(const QRectF &bbox) +{ + if (m_bbox == bbox) { + return; + } + m_bbox = bbox; + + // scale to fit horizontally + m_viewport = bbox; + const auto screenAspectRatio = (double)screenWidth() / (double)screenHeight(); + m_viewport.setHeight(m_viewport.width() / screenAspectRatio); + + // if necessary, scale to fit vertically + if (m_viewport.height() > m_bbox.height()) { + const auto dy = (double)m_bbox.height() / (double)m_viewport.height(); + m_viewport.setHeight(m_viewport.height() * dy); + m_viewport.setWidth(m_viewport.width() * dy); + } + + Q_EMIT transformationChanged(); +} + + +QPointF View::mapSceneToScreen(QPointF scenePos) const +{ + return sceneToScreenTransform().map(scenePos); +} + +QRectF View::mapSceneToScreen(const QRectF &sceneRect) const +{ + return QRectF(mapSceneToScreen(sceneRect.topLeft()), mapSceneToScreen(sceneRect.bottomRight())); +} + +QPointF View::mapScreenToScene(QPointF screenPos) const +{ + // TODO this can be implemented more efficiently + return sceneToScreenTransform().inverted().map(screenPos); +} + +double View::mapScreenDistanceToSceneDistance(double distance) const +{ + const auto p1 = mapScreenToScene(m_viewport.center()); + const auto p2 = mapScreenToScene(m_viewport.center() + QPointF(1.0, 0)); + // ### does not consider rotations, needs to take the actual distance between p1 and p2 for that + return std::abs(p2.x() - p1.x()) * distance; +} + +void View::panScreenSpace(QPoint offset) +{ + auto dx = offset.x() * (m_viewport.width() / screenWidth()); + auto dy = offset.y() * (m_viewport.height() / screenHeight()); + m_viewport.adjust(dx, dy, dx, dy); + constrainViewToScene(); +} + +QTransform View::sceneToScreenTransform() const +{ + QTransform t; + t.scale(screenWidth() / (m_viewport.width()), screenHeight() / (m_viewport.height())); + t.translate(-m_viewport.x(), -m_viewport.y()); + return t; +} + +void View::zoomIn(QPointF screenCenter) +{ + setZoomLevel(zoomLevel() + 1, screenCenter); +} + +void View::zoomOut(QPointF screenCenter) +{ + setZoomLevel(zoomLevel() - 1, screenCenter); +} + +void View::constrainViewToScene() +{ + // ensure we don't scale smaller than the bounding box + const auto s = std::min(m_viewport.width() / m_bbox.width(), m_viewport.height() / m_bbox.height()); + if (s > 1.0) { + m_viewport.setWidth(m_viewport.width() / s); + m_viewport.setHeight(m_viewport.height() / s); + } + + // ensure we don't pan outside of the bounding box + if (m_bbox.left() < m_viewport.left() && m_bbox.right() < m_viewport.right()) { + const auto dx = std::min(m_viewport.left() - m_bbox.left(), m_viewport.right() - m_bbox.right()); + m_viewport.adjust(-dx, 0, -dx, 0); + } else if (m_bbox.right() > m_viewport.right() && m_bbox.left() > m_viewport.left()) { + const auto dx = std::min(m_bbox.right() - m_viewport.right(), m_bbox.left() - m_viewport.left()); + m_viewport.adjust(dx, 0, dx, 0); + } + + if (m_bbox.top() < m_viewport.top() && m_bbox.bottom() < m_viewport.bottom()) { + const auto dy = std::min(m_viewport.top() - m_bbox.top(), m_viewport.bottom() - m_bbox.bottom()); + m_viewport.adjust(0, -dy, 0, -dy); + } else if (m_bbox.bottom() > m_viewport.bottom() && m_bbox.top() > m_viewport.top()) { + const auto dy = std::min(m_bbox.bottom() - m_viewport.bottom(), m_bbox.top() - m_viewport.top()); + m_viewport.adjust(0, dy, 0, dy); + } +} + +double View::mapMetersToScene(double meters) const +{ + // ### this fails for distances above 180° due to OSM::distance wrapping around + // doesn't matter for our use-case though, we are looking at much much smaller areas + const auto d = OSM::distance(mapSceneToGeo(QPointF(m_viewport.left(), m_viewport.center().y())), mapSceneToGeo(QPointF(m_viewport.right(), m_viewport.center().y()))); + const auto scale = m_viewport.width() / d; + return meters * scale; +} + +double View::mapMetersToScreen(double meters) const +{ + const auto d = OSM::distance(mapSceneToGeo(QPointF(m_viewport.left(), m_viewport.center().y())), mapSceneToGeo(QPointF(m_viewport.right(), m_viewport.center().y()))); + const auto r = meters / d; + return r * m_screenSize.width(); +} + +double View::mapScreenToMeters(int pixels) const +{ + const auto d = OSM::distance(mapSceneToGeo(QPointF(m_viewport.left(), m_viewport.center().y())), mapSceneToGeo(QPointF(m_viewport.right(), m_viewport.center().y()))); + const auto r = (double)pixels / (double)m_screenSize.width(); + return d * r; +} + +double View::panX() const +{ + const auto r = (m_viewport.left() - m_bbox.left()) / m_bbox.width(); + return panWidth() * r; +} + +double View::panY() const +{ + const auto r = (m_viewport.top() - m_bbox.top()) / m_bbox.height(); + return panHeight() * r; +} + +double View::panWidth() const +{ + const auto r = m_bbox.width() / m_viewport.width(); + return screenWidth() * r; +} + +double View::panHeight() const +{ + const auto r = m_bbox.height() / m_viewport.height(); + return screenHeight() * r; +} + +void View::panTopLeft(double x, double y) +{ + m_viewport.moveLeft(m_bbox.x() + m_bbox.width() * (x / panWidth())); + m_viewport.moveTop(m_bbox.y() + m_bbox.height() * (y / panHeight())); + constrainViewToScene(); +} + +QTransform View::deviceTransform() const +{ + return m_deviceTransform; +} + +void View::setDeviceTransform(const QTransform &t) +{ + m_deviceTransform = t; +} + +void View::centerOnGeoCoordinate(QPointF geoCoord) +{ + const auto sceneCenter = mapGeoToScene(OSM::Coordinate(geoCoord.y(), geoCoord.x())); + m_viewport.moveCenter(sceneCenter); + constrainViewToScene(); + Q_EMIT transformationChanged(); +} + +QDateTime View::beginTime() const +{ + return m_beginTime; +} + +void View::setBeginTime(const QDateTime &beginTime) +{ + const auto alignedTime = QDateTime(beginTime.date(), {beginTime.time().hour(), beginTime.time().minute()}); + if (m_beginTime == alignedTime) { + return; + } + m_beginTime = alignedTime; + Q_EMIT timeChanged(); +} + +QDateTime View::endTime() const +{ + return m_endTime; +} + +void View::setEndTime(const QDateTime& endTime) +{ + const auto alignedTime = QDateTime(endTime.date(), {endTime.time().hour(), endTime.time().minute()}); + if (m_endTime == alignedTime) { + return; + } + m_endTime = alignedTime; + Q_EMIT timeChanged(); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kosmindoormap-22.12.0/src/map/scene/view.h new/kosmindoormap-22.12.1/src/map/scene/view.h --- old/kosmindoormap-22.12.0/src/map/scene/view.h 1970-01-01 01:00:00.000000000 +0100 +++ new/kosmindoormap-22.12.1/src/map/scene/view.h 2023-01-03 01:06:39.000000000 +0100 @@ -0,0 +1,161 @@ +/* + SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KOSMINDOORMAP_VIEW_H +#define KOSMINDOORMAP_VIEW_H + +#include "kosmindoormap_export.h" + +#include <KOSM/Datatypes> + +#include <QDateTime> +#include <QObject> +#include <QRectF> +#include <QSize> +#include <QTransform> + +namespace KOSMIndoorMap { + +/** View transformations and transformation manipulation. + * There are three different coordinate systems involved here: + * - The geographic world coordinates of the OSM input data. + * This uses OSM::Coordinate. + * - The scene coordinates which have a the Web Mercator projection applied (see https://en.wikipedia.org/wiki/Mercator_projection). + * This uses QPointF ranging from 0x0 to 256x256 + * - The screen coordinates (ie. visible pixels on screen). + * This uses QPoint. + * Further, there's also three slight variations of those in use here: + * - "HUD" coordinates: elements that follow the scene coordinates for their positioning, + * but the screen coordinates regarding scaling and rotation. This is used for map labels. + * - Geographic distances. This is needed to display things in a fixed width in meters in the scene, + * or to compute the map scale. Note that this only works due to the relatively high zoom levels, + * so that earth curvature or map projection effects are negligible. + * - "pan space": same transform as screen space, but with the origin at the origin of the scene bounding box + * This is useful for implementing scene-wide panning and showing scroll bars. + */ +class KOSMINDOORMAP_EXPORT View : public QObject +{ + Q_OBJECT + Q_PROPERTY(double panX READ panX NOTIFY transformationChanged) + Q_PROPERTY(double panY READ panY NOTIFY transformationChanged) + Q_PROPERTY(double panWidth READ panWidth NOTIFY transformationChanged) + Q_PROPERTY(double panHeight READ panHeight NOTIFY transformationChanged) + Q_PROPERTY(int floorLevel READ level WRITE setLevel NOTIFY floorLevelChanged) + Q_PROPERTY(double zoomLevel READ zoomLevel NOTIFY transformationChanged) + Q_PROPERTY(QDateTime beginTime READ beginTime WRITE setBeginTime NOTIFY timeChanged) + Q_PROPERTY(QDateTime endTime READ endTime WRITE setEndTime NOTIFY timeChanged) +public: + explicit View(QObject *parent = nullptr); + ~View(); + + /** Map a geographic coordinate to a scene coordinate, ie. apply the mercator projection. */ + QPointF mapGeoToScene(OSM::Coordinate coord) const; + QRectF mapGeoToScene(OSM::BoundingBox box) const; + /** Map a scene coordinate to a geographic one, ie. apply the inverse mercator projection. */ + OSM::Coordinate mapSceneToGeo(QPointF p) const; + OSM::BoundingBox mapSceneToGeo(const QRectF &box) const; + + /** Screen-space sizes, ie the size of the on-screen area used for displaying. */ + int screenWidth() const; + int screenHeight() const; + void setScreenSize(QSize size); + + /** The transformation to apply to scene coordinate to get to the view on screen. */ + QTransform sceneToScreenTransform() const; + + /** The (floor) level to display. + * @see MapLevel. + */ + int level() const; + void setLevel(int level); + + /** OSM-compatible zoom level, ie. the 2^level-th subdivision of the scene space. */ + double zoomLevel() const; + /** Set the zoom level to @p zoom, and adjusting it around center position @p center. */ + Q_INVOKABLE void setZoomLevel(double zoom, QPointF screenCenter); + + /** The sub-rect of the scene bounding box currently displayed. + * Specified in scene coordinates. + */ + QRectF viewport() const; + void setViewport(const QRectF &viewport); + + /** The bounding box of the scene. + * The viewport cannot exceed this area. + */ + QRectF sceneBoundingBox() const; + void setSceneBoundingBox(OSM::BoundingBox bbox); + void setSceneBoundingBox(const QRectF &bbox); + + /** Converts a point in scene coordinates to screen coordinates. */ + QPointF mapSceneToScreen(QPointF scenePos) const; + /** Converts a rectangle in scene coordinates to screen coordinates. */ + QRectF mapSceneToScreen(const QRectF &sceneRect) const; + /** Converts a point in screen coordinates to scene coordinates. */ + QPointF mapScreenToScene(QPointF screenPos) const; + /** Converts a distance in screen coordinates to a distance in scene coordinates. */ + double mapScreenDistanceToSceneDistance(double distance) const; + + /** Returns how many units in scene coordinate represent the distance of @p meters in the current view transformation. */ + double mapMetersToScene(double meters) const; + /** Returns how many pixels on screen represent the distance of @p meters with the current view transformation. */ + Q_INVOKABLE double mapMetersToScreen(double meters) const; + /** Returns how many meters are represented by @p pixels with the current view transformation. */ + Q_INVOKABLE double mapScreenToMeters(int pixels) const; + + void panScreenSpace(QPoint offset); + /** Increase zoom level by one/scale up by 2x around the screen position @p center. */ + Q_INVOKABLE void zoomIn(QPointF screenCenter); + /** Decrease zoom level by one/scale down by 2x around the screen position @p center. */ + Q_INVOKABLE void zoomOut(QPointF screenCenter); + + /** Position of the viewport in pan coordinates. */ + double panX() const; + double panY() const; + /** Size of the pan-able area in screen coordinates. */ + double panWidth() const; + double panHeight() const; + + /** Move the viewport to the pan coordinates @p x and @p y. */ + Q_INVOKABLE void panTopLeft(double x, double y); + + /** Device tranformation for manual high DPI scaling. */ + QTransform deviceTransform() const; + void setDeviceTransform(const QTransform &t); + + /** Center the view on the given geo-coordinate. */ + Q_INVOKABLE void centerOnGeoCoordinate(QPointF geoCoord); + + /** Time range that is displayed. + * This matters for example when opening hours are considered for styling. + */ + QDateTime beginTime() const; + void setBeginTime(const QDateTime &beginTime); + QDateTime endTime() const; + void setEndTime(const QDateTime &endTime); + +Q_SIGNALS: + void transformationChanged(); + void floorLevelChanged(); + void timeChanged(); + +private: + /** Ensure we stay within the bounding box with the viewport, call after viewport modification. */ + void constrainViewToScene(); + + QRectF m_bbox; + QRectF m_viewport; + QSize m_screenSize; + QTransform m_deviceTransform = {}; + int m_level = 0; + + QDateTime m_beginTime; + QDateTime m_endTime; +}; + +} + +#endif // KOSMINDOORMAP_VIEW_H
