Zoom/center the page map so that all locations are in view
This commit is contained in:
@@ -58,6 +58,7 @@ add_library(neochat STATIC
|
|||||||
delegatesizehelper.cpp
|
delegatesizehelper.cpp
|
||||||
models/livelocationsmodel.cpp
|
models/livelocationsmodel.cpp
|
||||||
models/locationsmodel.cpp
|
models/locationsmodel.cpp
|
||||||
|
locationhelper.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_qt_declare_logging_category(neochat
|
ecm_qt_declare_logging_category(neochat
|
||||||
|
|||||||
44
src/locationhelper.cpp
Normal file
44
src/locationhelper.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "locationhelper.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
QRectF LocationHelper::unite(const QRectF &r1, const QRectF &r2)
|
||||||
|
{
|
||||||
|
// this looks weird but is actually intentional as we need to handle point-like "rects" as well
|
||||||
|
if ((!r1.isEmpty() || r1.isNull()) && (!r2.isEmpty() || r2.isNull())) {
|
||||||
|
return r1 | r2;
|
||||||
|
}
|
||||||
|
return (!r1.isEmpty() || r1.isNull()) ? r1 : r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF LocationHelper::center(const QRectF &r)
|
||||||
|
{
|
||||||
|
return r.center();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline double degToRad(double deg)
|
||||||
|
{
|
||||||
|
return deg / 180.0 * M_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QPointF mercatorProject(double lat, double lon, double zoom)
|
||||||
|
{
|
||||||
|
const auto x = (256.0 / (2.0 * M_PI)) * std::pow(2.0, zoom) * (degToRad(lon) + M_PI);
|
||||||
|
const auto y = (256.0 / (2.0 * M_PI)) * std::pow(2.0, zoom) * (M_PI - std::log(std::tan(M_PI / 4.0 + degToRad(lat) / 2.0)));
|
||||||
|
return QPointF(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
float LocationHelper::zoomToFit(const QRectF &r, float mapWidth, float mapHeight)
|
||||||
|
{
|
||||||
|
const auto p1 = mercatorProject(r.bottomLeft().y(), r.bottomLeft().x(), 1.0);
|
||||||
|
const auto p2 = mercatorProject(r.topRight().y(), r.topRight().x(), 1.0);
|
||||||
|
|
||||||
|
const auto zx = std::log2((mapWidth / (p2.x() - p1.x())));
|
||||||
|
const auto zy = std::log2((mapHeight / (p2.y() - p1.y())));
|
||||||
|
const auto z = std::min(zx, zy);
|
||||||
|
|
||||||
|
return std::clamp(z, 5.0, 18.0);
|
||||||
|
}
|
||||||
22
src/locationhelper.h
Normal file
22
src/locationhelper.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "linkpreviewer.h"
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QRectF>
|
||||||
|
|
||||||
|
/** Location related helper functions for QML. */
|
||||||
|
class LocationHelper
|
||||||
|
{
|
||||||
|
Q_GADGET
|
||||||
|
public:
|
||||||
|
/** Unite two rectanlges. */
|
||||||
|
Q_INVOKABLE static QRectF unite(const QRectF &r1, const QRectF &r2);
|
||||||
|
/** Returns the center of @p r. */
|
||||||
|
Q_INVOKABLE static QPointF center(const QRectF &r);
|
||||||
|
|
||||||
|
/** Returns the highest zoom level to fit @r into a map of size @p mapWidth x @p mapHeight. */
|
||||||
|
Q_INVOKABLE static float zoomToFit(const QRectF &r, float mapWidth, float mapHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(LocationHelper)
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
#include "delegatesizehelper.h"
|
#include "delegatesizehelper.h"
|
||||||
#include "filetypesingleton.h"
|
#include "filetypesingleton.h"
|
||||||
#include "linkpreviewer.h"
|
#include "linkpreviewer.h"
|
||||||
|
#include "locationhelper.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "login.h"
|
#include "login.h"
|
||||||
#include "matriximageprovider.h"
|
#include "matriximageprovider.h"
|
||||||
@@ -288,6 +289,9 @@ int main(int argc, char *argv[])
|
|||||||
return engine->toScriptValue(KAboutData::applicationData());
|
return engine->toScriptValue(KAboutData::applicationData());
|
||||||
});
|
});
|
||||||
qmlRegisterSingletonType(QUrl("qrc:/OsmLocationPlugin.qml"), "org.kde.neochat", 1, 0, "OsmLocationPlugin");
|
qmlRegisterSingletonType(QUrl("qrc:/OsmLocationPlugin.qml"), "org.kde.neochat", 1, 0, "OsmLocationPlugin");
|
||||||
|
qmlRegisterSingletonType("org.kde.neochat", 1, 0, "LocationHelper", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
|
||||||
|
return engine->toScriptValue(LocationHelper());
|
||||||
|
});
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
qRegisterMetaTypeStreamOperators<Emoji>();
|
qRegisterMetaTypeStreamOperators<Emoji>();
|
||||||
|
|||||||
@@ -23,8 +23,15 @@ Kirigami.Page {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
plugin: OsmLocationPlugin.plugin
|
plugin: OsmLocationPlugin.plugin
|
||||||
|
|
||||||
|
center: {
|
||||||
|
let c = LocationHelper.center(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox));
|
||||||
|
return QtPositioning.coordinate(c.y, c.x);
|
||||||
|
}
|
||||||
|
zoomLevel: LocationHelper.zoomToFit(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox), map.width, map.height)
|
||||||
|
|
||||||
MapItemView {
|
MapItemView {
|
||||||
model: LocationsModel {
|
model: LocationsModel {
|
||||||
|
id: locationsModel
|
||||||
room: locationsPage.room
|
room: locationsPage.room
|
||||||
}
|
}
|
||||||
delegate: LocationMapItem {
|
delegate: LocationMapItem {
|
||||||
@@ -34,6 +41,7 @@ Kirigami.Page {
|
|||||||
|
|
||||||
MapItemView {
|
MapItemView {
|
||||||
model: LiveLocationsModel {
|
model: LiveLocationsModel {
|
||||||
|
id: liveLocationsModel
|
||||||
room: locationsPage.room
|
room: locationsPage.room
|
||||||
}
|
}
|
||||||
delegate: LocationMapItem {}
|
delegate: LocationMapItem {}
|
||||||
|
|||||||
Reference in New Issue
Block a user