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

Reply via email to