Commit 5d508893 authored by Volker Krause's avatar Volker Krause
Browse files

Compute OTP bounding polygons as convex hull of all stop positions

This further improves the result beyond what just outlier filtering gives
us.
parent ef470b25
......@@ -91,6 +91,7 @@ target_sources(KPublicTransport PRIVATE
gbfs/gbfsstore.cpp
gbfs/gbfsvehicletypes.cpp
geo/convexhull.cpp
geo/geojson.cpp
geo/polylinedecoder.cpp
......
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "convexhull_p.h"
#include <QDebug>
#include <QLineF>
#include <QPolygonF>
using namespace KPublicTransport;
static double orientation(QPointF p, QPointF q, QPointF r)
{
return (q.y() - p.y()) * (r.x() - q.x()) - (r.y() - q.y()) * (q.x() - p.x());
}
// see https://en.wikipedia.org/wiki/Gift_wrapping_algorithm
QPolygonF ConvexHull::compute(const std::vector<QPointF> &points)
{
QPolygonF hull;
if (points.size() < 3) {
return hull;
}
// find left-most point
const auto it = std::min_element(points.begin(), points.end(), [](auto lhs, auto rhs) {
return lhs.x() < rhs.x();
});
hull.push_back(*it);
auto p = std::distance(points.begin(), it);
while (true) {
auto q = (p + 1) % points.size();
for (std::size_t r = 0; r < points.size(); ++r) {
if (q == r) {
continue;
}
auto o = orientation(points[p], points[q], points[r]);
if (o < 0.0) {
q = r;
} else if (o == 0.0) {
if (QLineF(points[p], points[r]).length() > QLineF(points[p], points[q]).length()) {
q = r;
}
}
}
p = q;
hull.push_back(points[p]);
if (hull.isClosed()) {
break;
}
}
return hull;
}
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KPUBLICTRANSPORT_CONVEXHULL_H
#define KPUBLICTRANSPORT_CONVEXHULL_H
#include <vector>
class QPointF;
class QPolygonF;
namespace KPublicTransport {
/** Convex hull algorithm.
* @internal
*/
namespace ConvexHull
{
/** Compute the convex hull of @p points. */
QPolygonF compute(const std::vector<QPointF> &points);
}
}
#endif // KPUBLICTRANSPORT_CONVEXHULL_H
......@@ -8,6 +8,7 @@ add_custom_target(run-endpoint-probe COMMAND endpointprobe ${CMAKE_SOURCE_DIR}/s
add_executable(otpprobe
otpprobe.cpp
../lib/geo/geojson.cpp
../lib/geo/convexhull.cpp
)
target_link_libraries(otpprobe Qt5::Gui Qt5::Network)
add_custom_target(run-otp-probe COMMAND otpprobe ${CMAKE_SOURCE_DIR}/src/lib/networks)
......
......@@ -4,6 +4,7 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "../lib/geo/convexhull_p.h"
#include "../lib/geo/geojson_p.h"
#include <QCoreApplication>
......@@ -124,8 +125,10 @@ void OtpProbeJob::stopsFetchDone(QNetworkReply *reply)
qWarning() << reply->errorString() << reply->url();
}
std::vector<QPointF> points;
std::vector<double> lats, lons;
const auto stops = QJsonDocument::fromJson(reply->readAll()).array();
points.reserve(stops.size());
lats.reserve(stops.size());
lons.reserve(stops.size());
for (const auto &stopV : stops) {
......@@ -137,6 +140,7 @@ void OtpProbeJob::stopsFetchDone(QNetworkReply *reply)
continue;
}
points.push_back(QPointF(lon, lat));
lats.push_back(lat);
lons.push_back(lon);
}
......@@ -146,9 +150,10 @@ void OtpProbeJob::stopsFetchDone(QNetworkReply *reply)
double latMin, latMax, lonMin, lonMax;
filterOutliers(lats, latMin, latMax);
filterOutliers(lons, lonMin, lonMax);
// TODO even better would be computing the concave hull of all points after outlier filtering
QRectF box(QPointF(lonMin, latMin), QPointF(lonMax, latMax));
m_boundingPolygon = m_boundingPolygon.intersected(box);
points.erase(std::remove_if(points.begin(), points.end(), [&box](auto p) { return !box.contains(p); }), points.end());
m_boundingPolygon = KPublicTransport::ConvexHull::compute(points);
} else {
qDebug() << "didn't get stop data:" << reply->url();
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment