File indexing completed on 2025-02-16 04:21:49

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KOSMINDOORMAP_VIEW_H
0008 #define KOSMINDOORMAP_VIEW_H
0009 
0010 #include "kosmindoormap_export.h"
0011 
0012 #include <KOSM/Datatypes>
0013 
0014 #include <QDateTime>
0015 #include <QObject>
0016 #include <QRectF>
0017 #include <QSize>
0018 #include <QTransform>
0019 
0020 namespace KOSMIndoorMap {
0021 
0022 /** View transformations and transformation manipulation.
0023  *  There are three different coordinate systems involved here:
0024  *  - The geographic world coordinates of the OSM input data.
0025  *    This uses OSM::Coordinate.
0026  *  - The scene coordinates which have a the Web Mercator projection applied (see https://en.wikipedia.org/wiki/Mercator_projection).
0027  *    This uses QPointF ranging from 0x0 to 256x256
0028  *  - The screen coordinates (ie. visible pixels on screen).
0029  *    This uses QPoint.
0030  *  Further, there's also three slight variations of those in use here:
0031  *  - "HUD" coordinates: elements that follow the scene coordinates for their positioning,
0032  *    but the screen coordinates regarding scaling and rotation. This is used for map labels.
0033  *  - Geographic distances. This is needed to display things in a fixed width in meters in the scene,
0034  *    or to compute the map scale. Note that this only works due to the relatively high zoom levels,
0035  *    so that earth curvature or map projection effects are negligible.
0036  *  - "pan space": same transform as screen space, but with the origin at the origin of the scene bounding box
0037  *    This is useful for implementing scene-wide panning and showing scroll bars.
0038  */
0039 class KOSMINDOORMAP_EXPORT View : public QObject
0040 {
0041     Q_OBJECT
0042     Q_PROPERTY(double panX READ panX NOTIFY transformationChanged)
0043     Q_PROPERTY(double panY READ panY NOTIFY transformationChanged)
0044     Q_PROPERTY(double panWidth READ panWidth NOTIFY transformationChanged)
0045     Q_PROPERTY(double panHeight READ panHeight NOTIFY transformationChanged)
0046     Q_PROPERTY(int floorLevel READ level WRITE setLevel NOTIFY floorLevelChanged)
0047     Q_PROPERTY(QRectF viewport READ viewport NOTIFY transformationChanged)
0048     Q_PROPERTY(double zoomLevel READ zoomLevel NOTIFY transformationChanged)
0049     Q_PROPERTY(QDateTime beginTime READ beginTime WRITE setBeginTime NOTIFY timeChanged)
0050     Q_PROPERTY(QDateTime endTime READ endTime WRITE setEndTime NOTIFY timeChanged)
0051 public:
0052     explicit View(QObject *parent = nullptr);
0053     ~View();
0054 
0055     /** Map a geographic coordinate to a scene coordinate, ie. apply the mercator projection. */
0056     [[nodiscard]] static QPointF mapGeoToScene(OSM::Coordinate coord);
0057     [[nodiscard]] static QRectF mapGeoToScene(OSM::BoundingBox box);
0058     /** Map a scene coordinate to a geographic one, ie. apply the inverse mercator projection. */
0059     [[nodiscard]] static OSM::Coordinate mapSceneToGeo(QPointF p);
0060     Q_INVOKABLE [[nodiscard]] static OSM::BoundingBox mapSceneToGeo(const QRectF &box);
0061 
0062     /** Screen-space sizes, ie the size of the on-screen area used for displaying. */
0063     int screenWidth() const;
0064     int screenHeight() const;
0065     void setScreenSize(QSize size);
0066 
0067     /** The transformation to apply to scene coordinate to get to the view on screen. */
0068     QTransform sceneToScreenTransform() const;
0069 
0070     /** The (floor) level to display.
0071      *  @see MapLevel.
0072      */
0073     int level() const;
0074     void setLevel(int level);
0075 
0076     /** OSM-compatible zoom level, ie. the 2^level-th subdivision of the scene space. */
0077     double zoomLevel() const;
0078     /** Set the zoom level to @p zoom, and adjusting it around center position @p center. */
0079     Q_INVOKABLE void setZoomLevel(double zoom, QPointF screenCenter);
0080 
0081     /** The sub-rect of the scene bounding box currently displayed.
0082      *  Specified in scene coordinates.
0083      */
0084     [[nodiscard]] QRectF viewport() const;
0085     void setViewport(const QRectF &viewport);
0086 
0087     /** Computes the viewport for the given @p zoom level and @p screenCenter.
0088      *  This does not apply the zoom change to the view itself though.
0089      */
0090     [[nodiscard]] QRectF viewportForZoom(double zoom, QPointF screenCenter) const;
0091 
0092     /** The bounding box of the scene.
0093      *  The viewport cannot exceed this area.
0094      */
0095     QRectF sceneBoundingBox() const;
0096     void setSceneBoundingBox(OSM::BoundingBox bbox);
0097     void setSceneBoundingBox(const QRectF &bbox);
0098 
0099     /** Converts a point in scene coordinates to screen coordinates. */
0100     QPointF mapSceneToScreen(QPointF scenePos) const;
0101     /** Converts a rectangle in scene coordinates to screen coordinates. */
0102     QRectF mapSceneToScreen(const QRectF &sceneRect) const;
0103     /** Converts a point in screen coordinates to scene coordinates. */
0104     QPointF mapScreenToScene(QPointF screenPos) const;
0105     /** Converts a distance in screen coordinates to a distance in scene coordinates. */
0106     double mapScreenDistanceToSceneDistance(double distance) const;
0107 
0108     /** Returns how many units in scene coordinate represent the distance of @p meters in the current view transformation. */
0109     double mapMetersToScene(double meters) const;
0110     /** Returns how many pixels on screen represent the distance of @p meters with the current view transformation. */
0111     Q_INVOKABLE double mapMetersToScreen(double meters) const;
0112     /** Returns how many meters are represented by @p pixels with the current view transformation. */
0113     Q_INVOKABLE double mapScreenToMeters(int pixels) const;
0114 
0115     void panScreenSpace(QPoint offset);
0116     /** Increase zoom level by one/scale up by 2x around the screen position @p center. */
0117     Q_INVOKABLE void zoomIn(QPointF screenCenter);
0118     /** Decrease zoom level by one/scale down by 2x around the screen position @p center. */
0119     Q_INVOKABLE void zoomOut(QPointF screenCenter);
0120 
0121     /** Position of the viewport in pan coordinates. */
0122     double panX() const;
0123     double panY() const;
0124     /** Size of the pan-able area in screen coordinates. */
0125     double panWidth() const;
0126     double panHeight() const;
0127 
0128     /** Move the viewport to the pan coordinates @p x and @p y. */
0129     Q_INVOKABLE void panTopLeft(double x, double y);
0130 
0131     /** Device tranformation for manual high DPI scaling. */
0132     QTransform deviceTransform() const;
0133     void setDeviceTransform(const QTransform &t);
0134 
0135     /** Center the view on the given geo-coordinate. */
0136     Q_INVOKABLE void centerOnGeoCoordinate(QPointF geoCoord);
0137 
0138     /** Time range that is displayed.
0139      *  This matters for example when opening hours are considered for styling.
0140      */
0141     QDateTime beginTime() const;
0142     void setBeginTime(const QDateTime &beginTime);
0143     QDateTime endTime() const;
0144     void setEndTime(const QDateTime &endTime);
0145 
0146 Q_SIGNALS:
0147     void transformationChanged();
0148     void floorLevelChanged();
0149     void timeChanged();
0150 
0151 private:
0152     /** Ensure we stay within the bounding box with the viewport, call after viewport modification. */
0153     void constrainViewToScene();
0154     [[nodiscard]] QRectF constrainedViewport(QRectF viewport) const;
0155 
0156     /** Needs to be called for any update to the viewport to updates internal caches
0157      *  and emits change signals.
0158      */
0159     void updateViewport();
0160 
0161     QRectF m_bbox;
0162     QRectF m_viewport;
0163     QSize m_screenSize;
0164     QTransform m_deviceTransform = {};
0165     int m_level = 0;
0166 
0167     // cached values
0168     double m_screenWidthInMeters = 1.0;
0169     QTransform m_sceneToScreenTransform;
0170     QTransform m_screenToSceneTransform;
0171 
0172     QDateTime m_beginTime;
0173     QDateTime m_endTime;
0174 };
0175 
0176 }
0177 
0178 #endif // KOSMINDOORMAP_VIEW_H