KPublicTransport

coveragearea.cpp
1/*
2 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "coveragearea.h"
8#include "datatypes_p.h"
9#include "json_p.h"
10#include "location.h"
11#include "logging.h"
12#include "geo/geojson_p.h"
13
14#include <QFile>
15#include <QJsonDocument>
16#include <QJsonObject>
17#include <QPolygonF>
18
19using namespace KPublicTransport;
20
21namespace KPublicTransport {
22class CoverageAreaPrivate : public QSharedData {
23public:
24 void loadGeometry();
25 void recomputeBoundingBox();
26
27 CoverageArea::Type type = CoverageArea::Any;
28 QStringList regions;
29 QStringList uicCompanyCodes;
30 QStringList vdvOrganizationIds;
31 QString areaFile;
32 std::vector<QPolygonF> areas;
33 QRectF boundingBox;
34};
35}
36
37void CoverageAreaPrivate::loadGeometry()
38{
39 if (areaFile.isEmpty() || !areas.empty()) {
40 return;
41 }
42
43 QFile f(QLatin1String(":/org.kde.kpublictransport/networks/geometry/") + areaFile);
44 if (!f.open(QFile::ReadOnly)) {
45 qCWarning(Log) << "reading coverage area file failed:" << f.fileName() << f.errorString();
46 return;
47 }
48
49 const auto doc = QJsonDocument::fromJson(f.readAll());
50 areas = GeoJson::readOuterPolygons(doc.object());
51 recomputeBoundingBox();
52}
53
54void CoverageAreaPrivate::recomputeBoundingBox()
55{
56 for (const auto &area : areas) {
57 boundingBox |= area.boundingRect();
58 }
59}
60
61KPUBLICTRANSPORT_MAKE_GADGET(CoverageArea)
62KPUBLICTRANSPORT_MAKE_PROPERTY(CoverageArea, CoverageArea::Type, type, setType)
63KPUBLICTRANSPORT_MAKE_PROPERTY(CoverageArea, QStringList, regions, setRegions)
64KPUBLICTRANSPORT_MAKE_PROPERTY(CoverageArea, QStringList, uicCompanyCodes, setUicCompanyCodes)
65KPUBLICTRANSPORT_MAKE_PROPERTY(CoverageArea, QStringList, vdvOrganizationIds, setVdvOrganizationIds)
66
67bool CoverageArea::isEmpty() const
68{
69 return d->regions.isEmpty() && d->areas.empty();
70}
71
73{
74 if (d->regions.size() == 1 && d->regions.at(0) == QLatin1String("UN")) {
75 return true;
76 }
77
78 return d->boundingBox.topLeft() == QPointF(-180.0, -90.0) && d->boundingBox.bottomRight() == QPointF(180.0, 90.0);
79}
80
81static QStringView countryCode(QStringView isoCode)
82{
83 return isoCode.size() < 2 ? QStringView() : isoCode.left(2);
84}
85
87{
88 if (loc.hasCoordinate()) {
89 d->loadGeometry();
90 if (!d->areas.empty()) {
91 if (d->boundingBox.contains({loc.longitude(), loc.latitude()})) {
92 return std::any_of(d->areas.begin(), d->areas.end(), [&loc](const auto &area) {
93 return area.containsPoint({loc.longitude(), loc.latitude()}, Qt::WindingFill);
94 });
95 }
96 return false;
97 }
98 }
99
100 // TODO we could do a more precise check for ISO 3166-2 subdivision codes when available
101
102 if (loc.country().size() == 2 && !d->regions.empty()) {
103 if (d->regions.size() == 1 && d->regions.at(0) == QLatin1String("UN")) { // global coverage
104 return true;
105 }
106 return std::binary_search(d->regions.begin(), d->regions.end(), loc.country(), [](const auto &lhs, const auto &rhs) {
107 return countryCode(lhs) < countryCode(rhs);
108 });
109 }
110
111 // if we have nothing to check against, assume the backend can help (otherwise none would trigger)
112 return true;
113}
114
116{
117 return std::binary_search(d->regions.begin(), d->regions.end(), country);
118}
119
121{
122 CoverageArea ca;
123 ca.setRegions(Json::toStringList(obj.value(QLatin1String("region"))));
124 ca.setUicCompanyCodes(Json::toStringList(obj.value(QLatin1String("uicCompanyCodes"))));
125 std::sort(ca.d->regions.begin(), ca.d->regions.end());
126
127 ca.d->areaFile = obj.value(QLatin1String("areaFile")).toString();
128 if (ca.d->areaFile.isEmpty()) {
129 ca.d->areas = GeoJson::readOuterPolygons(obj.value(QLatin1String("area")).toObject());
130 ca.d->recomputeBoundingBox();
131 }
132 return ca;
133}
134
135#include "moc_coveragearea.cpp"
Describes the area a specific KPublicTransport::Backend can provide information for.
bool isGlobal() const
Returns true if this area covers the entire world.
bool hasNationWideCoverage(const QString &country) const
Checks whether this includes the entire country country.
QStringList regions
ISO 3166-1/2 codes of covered regions.
Type
Coverage quality as defined by the Transport API Repository format.
bool isEmpty() const
Checks whether this coverage area is empty.
bool coversLocation(const Location &loc) const
Checks whether loc is covered by this area.
static CoverageArea fromJson(const QJsonObject &obj)
Read a single coverage area information from a JSON object in Transport API Repository format.
Query operations and data types for accessing realtime public transport information from online servi...
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
iterator begin()
iterator end()
qsizetype size() const const
WindingFill
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:50:52 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.