Commit 14fd9ac7 authored by Volker Krause's avatar Volker Krause
Browse files

Basic simplification of coverage polygons

This uses the Douglas Peucker algorithm with a 10km threshold, which
already yields useful results. This however still needs better handling
of multi-polygons, and polygon offsetting by the threshold.
parent dfc727b3
Pipeline #58776 passed with stage
in 24 seconds
......@@ -4,6 +4,7 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "geo/geojson.cpp"
#include "backends/accessibilitycloudparser.cpp"
#include <QFile>
......
......@@ -7,21 +7,17 @@
#ifndef KPUBLICTRANSPORT_GEOJSON_P_H
#define KPUBLICTRANSPORT_GEOJSON_P_H
#include "kpublictransport_export.h"
class QJsonObject;
class QPointF;
class QPolygonF;
namespace KPublicTransport {
/** GeoJSON utilities.
* @internal only exported for unit tests
*/
/** GeoJSON utilities. */
namespace GeoJson
{
/** Coordinate of a point geometry object. */
KPUBLICTRANSPORT_EXPORT QPointF readPoint(const QJsonObject &obj);
QPointF readPoint(const QJsonObject &obj);
/** Reads a line string object. */
QPolygonF readLineString(const QJsonObject &obj);
......
......@@ -13,6 +13,10 @@ add_executable(gbfsprobe gbfsprobe.cpp)
target_link_libraries(gbfsprobe Qt5::Network KPublicTransport)
add_custom_target(run-gbfs-probe COMMAND gbfsprobe ${CMAKE_SOURCE_DIR}/src/lib/gbfs/gbfs-feeds.json)
add_executable(transport-api-sync transport-api-sync.cpp)
target_link_libraries(transport-api-sync Qt5::Core)
add_executable(transport-api-sync
transport-api-sync.cpp
polygonsimplifier.cpp
../lib/geo/geojson.cpp
)
target_link_libraries(transport-api-sync Qt5::Gui KOSM)
add_custom_target(run-transport-api-sync COMMAND transport-api-sync --config-path ${CMAKE_SOURCE_DIR}/src/lib/networks --transport-apis ${CMAKE_CURRENT_BINARY_DIR}/transport-apis)
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "polygonsimplifier.h"
#include <osm/geomath.h>
#include <QDebug>
#include <QPolygonF>
static QPolygonF douglasPeucker(const QPolygonF::const_iterator &begin, const QPolygonF::const_iterator &end, double threshold)
{
QPolygonF result;
if (std::distance(begin, end) < 3) {
std::copy(begin, end, std::back_inserter(result));
return result;
}
double maxDistance = 0.0;
auto maxDistIt = std::next(begin);
for (auto it = maxDistIt; it != end; ++it) {
const auto d = OSM::distance(OSM::Coordinate((*begin).y(), (*begin).x()), OSM::Coordinate((*std::prev(end)).y(), (*std::prev(end)).x()), OSM::Coordinate((*it).y(), (*it).x()));
if (d > maxDistance) {
maxDistance = d;
maxDistIt = it;
}
}
if (maxDistance >= threshold) {
result += douglasPeucker(begin, std::next(maxDistIt), threshold);
result.pop_back();
result += douglasPeucker(maxDistIt, end, threshold);
return result;
}
return QPolygonF(QVector<QPointF>({*begin, (*std::prev(end))}));
}
QPolygonF PolygonSimplifier::douglasPeucker(const QPolygonF &poly, double distance)
{
auto result = douglasPeucker(poly.begin(), poly.end(), distance);
qDebug() << "got" << poly.size() << "dropped" << poly.size() - result.size();
return result;
}
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef POLYGONSIMPLIFIER_H
#define POLYGONSIMPLIFIER_H
class QPolygonF;
/** Methods to simplify polygon geometry. */
namespace PolygonSimplifier
{
/** Douglas Peucker algorithm.
* @param distance Threshold in meters.
* @see https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
*/
QPolygonF douglasPeucker(const QPolygonF &poly, double distance);
}
#endif // POLYGONSIMPLIFIER_H
......@@ -4,6 +4,9 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "polygonsimplifier.h"
#include "../lib/geo/geojson_p.h"
#include <QCommandLineOption>
#include <QCommandLineParser>
#include <QCoreApplication>
......@@ -12,6 +15,7 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QPolygonF>
#include <QProcess>
#include <QRegularExpression>
......@@ -115,7 +119,12 @@ static void preProcessCoverage(QJsonObject &obj)
}
// reduce resolution of the area geometry
// TODO
using namespace KPublicTransport;
auto poly = GeoJson::readOuterPolygon(obj.take(QLatin1String("area")).toObject());
poly = PolygonSimplifier::douglasPeucker(poly, 10'000.0);
if (!poly.empty()) {
obj.insert(QLatin1String("area"), GeoJson::writePolygon(poly));
}
}
static void preProcessConfig(QJsonObject &top)
......
Markdown is supported
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