Commit e41efa70 authored by Volker Krause's avatar Volker Krause
Browse files

Grow coverage polygons by the simplification threshold

This ensures that the result actually contains the original polygon in
all cases and we aren't creating gaps along the border.
parent 1604fe5c
......@@ -41,7 +41,9 @@ endif()
find_package(ZLIB REQUIRED)
find_package(OsmTools)
set_package_properties(OsmTools PROPERTIES TYPE OPTIONAL PURPOSE "Needed only for regenereating line metadata tables (ie. you most likely don't need this)")
set_package_properties(OsmTools PROPERTIES TYPE OPTIONAL PURPOSE "Needed only for regenereating line metadata tables (you don't need this for distribution)")
find_package(PolyClipping)
set_package_properties(PolyClipping PROPERTIES TYPE OPTIONAL PURPOSE "Needed only for processing coverage area data from the Transport API Repository (you don't need this for distribution)")
if (NOT WIN32)
find_package(Protobuf)
......
# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
find_package(PkgConfig QUIET)
pkg_check_modules(PolyClipping polyclipping QUIET)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PolyClipping
FOUND_VAR
PolyClipping_FOUND
REQUIRED_VARS
PolyClipping_LIBRARIES
PolyClipping_INCLUDE_DIRS
VERSION_VAR
PolyClipping_VERSION
)
if (PolyClipping_FOUND AND NOT TARGET PolyClipping::PolyClipping)
add_library(PolyClipping::PolyClipping UNKNOWN IMPORTED)
set_target_properties(PolyClipping::PolyClipping PROPERTIES
IMPORTED_LOCATION "${PolyClipping_LINK_LIBRARIES}"
INTERFACE_COMPILE_OPTIONS "${PolyClipping_CFLAGS}"
INTERFACE_INCLUDE_DIRECTORIES "${PolyClipping_INCLUDE_DIRS}"
)
endif()
mark_as_advanced(PolyClipping_LIBRARIES PolyClipping_INCLUDE_DIRS PolyClipping_VERSION)
include(FeatureSummary)
set_package_properties(PolyClipping PROPERTIES
DESCRIPTION "A library for clipping and offsetting lines and polygons"
URL "http://www.angusj.com/delphi/clipper.php"
)
......@@ -16,10 +16,12 @@ 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
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)
if (TARGET PolyClipping::PolyClipping)
add_executable(transport-api-sync
transport-api-sync.cpp
polygonsimplifier.cpp
../lib/geo/geojson.cpp
)
target_link_libraries(transport-api-sync Qt5::Gui KOSM PolyClipping::PolyClipping)
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)
endif()
......@@ -11,6 +11,8 @@
#include <QDebug>
#include <QPolygonF>
#include <clipper.hpp>
static QPolygonF douglasPeucker(const QPolygonF::const_iterator &begin, const QPolygonF::const_iterator &end, double threshold)
{
QPolygonF result;
......@@ -54,3 +56,44 @@ QPolygonF PolygonSimplifier::douglasPeucker(const QPolygonF &poly, double thresh
return result;
}
static ClipperLib::Path qPolygonToClipperPath(const QPolygonF &poly)
{
ClipperLib::Path result;
result.reserve(poly.size());
std::transform(poly.begin(), poly.end(), std::back_inserter(result), [](auto p) {
return ClipperLib::IntPoint(p.x() * 10'000'000.0, p.y() * 10'000'000);
});
return result;
}
static QPolygonF clipperPathToQPolygon(const ClipperLib::Path &path)
{
QPolygonF result;
result.reserve(path.size() + 1);
std::transform(path.begin(), path.end(), std::back_inserter(result), [](auto p) {
return QPointF(p.X / 10'000'000.0, p.Y / 10'000'000.0);
});
if (!result.empty() && !result.isClosed()) {
result.push_back(result.front());
}
return result;
}
QPolygonF PolygonSimplifier::offset(const QPolygonF &poly, double distance)
{
if (poly.empty()) {
return {};
}
// convert meter distance to 100 nano-degree, the unit used by the polygon offset algorithm
const auto bbox = poly.boundingRect();
const auto bboxWidth = OSM::distance(bbox.center().y(), bbox.left(), bbox.center().y(), bbox.right());
const auto delta = bbox.width() / bboxWidth * distance * 10'000'000.0;
ClipperLib::ClipperOffset co;
co.AddPath(qPolygonToClipperPath(poly), ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
ClipperLib::Paths result;
co.Execute(result, delta);
return clipperPathToQPolygon(result[0]);
}
......@@ -17,6 +17,9 @@ namespace PolygonSimplifier
* @see https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
*/
QPolygonF douglasPeucker(const QPolygonF &poly, double distance);
/** Offset ("grow") a polygon by @p distance in meters. */
QPolygonF offset(const QPolygonF &poly, double distance);
}
#endif // POLYGONSIMPLIFIER_H
......@@ -122,7 +122,12 @@ static void preProcessCoverage(QJsonObject &obj)
using namespace KPublicTransport;
auto polys = GeoJson::readOuterPolygons(obj.take(QLatin1String("area")).toObject());
for (auto &poly : polys) {
const auto originalPolySize = poly.size();
poly = PolygonSimplifier::douglasPeucker(poly, 10'000.0);
if (originalPolySize > poly.size()) {
// only apply offsetting if we actually simplified the polygon
poly = PolygonSimplifier::offset(poly, 10'000.0);
}
}
if (!polys.empty()) {
obj.insert(QLatin1String("area"), GeoJson::writePolygons(polys));
......
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