Commit 89a0e901 authored by Dennis Nienhüser's avatar Dennis Nienhüser

Use the clipper library as an alternative clipping algorithm

- clipper library seems more robust (less clipping glitches, no
  endless loops) at this point, but might switch back later
- clipper library (version 6.4, Boost license) C++ code inside
- VectorClipper::clipTo() has an internal switch to select between
  BaseClipper and clipper library
parent 0e3df329
......@@ -23,6 +23,7 @@ include_directories(
)
set( ${TARGET}_SRC
clipper/clipper.cpp
main.cpp
BaseClipper.cpp
BaseFilter.cpp
......
......@@ -17,6 +17,8 @@
#include "OsmPlacemarkData.h"
#include "OsmObjectManager.h"
#include "clipper/clipper.hpp"
#include <QDebug>
#include <QPolygonF>
#include <QPair>
......@@ -30,6 +32,32 @@ VectorClipper::VectorClipper(GeoDataDocument* document) :
}
GeoDataDocument *VectorClipper::clipTo(const GeoDataLatLonBox &tileBoundary)
{
bool const useBaseClipper = false;
if (useBaseClipper) {
return clipToBaseClipper(tileBoundary);
}
GeoDataDocument* tile = new GeoDataDocument();
auto const clip = clipPath(tileBoundary);
foreach (GeoDataPlacemark* placemark, placemarks()) {
if(placemark && placemark->geometry() && tileBoundary.intersects(placemark->geometry()->latLonAltBox())) {
if( placemark->geometry()->nodeType() == GeoDataTypes::GeoDataPolygonType) {
clipPolygon(placemark, clip, tile);
} else if (placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLineStringType) {
clipString<GeoDataLineString>(placemark, clip, tile);
} else if (placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLinearRingType) {
clipString<GeoDataLinearRing>(placemark, clip, tile);
} else {
tile->append(new GeoDataPlacemark(*placemark));
}
}
}
return tile;
}
GeoDataDocument *VectorClipper::clipToBaseClipper(const GeoDataLatLonBox &tileBoundary)
{
GeoDataDocument* tile = new GeoDataDocument();
BaseClipper clipper;
......@@ -183,6 +211,89 @@ GeoDataDocument *VectorClipper::clipTo(unsigned int zoomLevel, unsigned int tile
return tile;
}
ClipperLib::Path VectorClipper::clipPath(const GeoDataLatLonBox &box) const
{
using namespace ClipperLib;
Path path;
int const steps = 20;
double x = box.west() * m_scale;
double const horizontalStep = (box.east() * m_scale - x) / (steps-1);
double y = box.north() * m_scale;
double const verticalStep = (box.south() * m_scale - y) / (steps-1);
for (int i=0; i<steps; ++i) {
path << IntPoint(qRound(x), qRound(y));
x += horizontalStep;
}
for (int i=0; i<steps; ++i) {
path << IntPoint(qRound(x), qRound(y));
y += verticalStep;
}
for (int i=0; i<steps; ++i) {
path << IntPoint(qRound(x), qRound(y));
x -= horizontalStep;
}
for (int i=0; i<steps; ++i) {
path << IntPoint(qRound(x), qRound(y));
y -= verticalStep;
}
return path;
}
void VectorClipper::clipPolygon(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, GeoDataDocument *document)
{
const GeoDataPolygon* polygon = static_cast<const GeoDataPolygon*>(placemark->geometry());
using namespace ClipperLib;
Path path;
foreach(auto const & node, polygon->outerBoundary()) {
path << IntPoint(node.longitude() * m_scale, node.latitude() * m_scale);
}
Clipper clipper;
clipper.AddPath(tileBoundary, ptClip, true);
clipper.AddPath(path, ptSubject, true);
Paths paths;
clipper.Execute(ctIntersection, paths);
foreach(const auto &path, paths) {
GeoDataLinearRing outerRing;
foreach(const auto &point, path) {
outerRing << GeoDataCoordinates(double(point.X) / m_scale, double(point.Y) / m_scale);
}
GeoDataPlacemark* newPlacemark = new GeoDataPlacemark;
GeoDataPolygon* newPolygon = new GeoDataPolygon;
newPolygon->setOuterBoundary(outerRing);
newPlacemark->setGeometry(newPolygon);
int index = -1;
OsmObjectManager::initializeOsmData(newPlacemark);
copyTags(*placemark, *newPlacemark);
copyTags(placemark->osmData().memberReference(index), newPlacemark->osmData().memberReference(index));
auto const & innerBoundaries = polygon->innerBoundaries();
for (index = 0; index < innerBoundaries.size(); ++index) {
clipper.Clear();
clipper.AddPath(path, ptClip, true);
Path innerPath;
foreach(auto const & node, innerBoundaries.at(index)) {
innerPath << IntPoint(node.longitude() * m_scale, node.latitude() * m_scale);
}
clipper.AddPath(innerPath, ptSubject, true);
Paths innerPaths;
clipper.Execute(ctIntersection, innerPaths);
foreach(auto const &innerPath, innerPaths) {
GeoDataLinearRing innerRing;
foreach(const auto &point, innerPath) {
innerRing << GeoDataCoordinates(double(point.X) / m_scale, double(point.Y) / m_scale);
}
newPolygon->appendInnerBoundary(innerRing);
OsmObjectManager::initializeOsmData(newPlacemark);
copyTags(placemark->osmData().memberReference(index), newPlacemark->osmData().memberReference(newPolygon->innerBoundaries().size()-1));
}
}
document->append(newPlacemark);
}
}
void VectorClipper::copyTags(const GeoDataPlacemark &source, GeoDataPlacemark &target) const
{
copyTags(source.osmData(), target.osmData());
......
......@@ -15,9 +15,14 @@
#include "OsmPlacemarkData.h"
#include <GeoDataLatLonBox.h>
#include "GeoDataPlacemark.h"
#include "clipper/clipper.hpp"
namespace Marble {
class GeoDataLinearRing;
class VectorClipper : public BaseFilter
{
public:
......@@ -27,8 +32,50 @@ public:
GeoDataDocument* clipTo(unsigned int zoomLevel, unsigned int tileX, unsigned int tileY);
private:
GeoDataDocument* clipToBaseClipper(const GeoDataLatLonBox &box);
ClipperLib::Path clipPath(const GeoDataLatLonBox &box) const;
template<class T>
void clipString(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, GeoDataDocument* document)
{
const T* ring = static_cast<const T*>(placemark->geometry());
using namespace ClipperLib;
Path path;
foreach(auto const & node, *ring) {
path << IntPoint(node.longitude() * m_scale, node.latitude() * m_scale);
}
Clipper clipper;
bool const isClosed = ring->isClosed();
clipper.AddPath(tileBoundary, ptClip, true);
clipper.AddPath(path, ptSubject, isClosed);
PolyTree tree;
clipper.Execute(ctIntersection, tree);
Paths paths;
if (isClosed) {
ClosedPathsFromPolyTree(tree, paths);
} else {
OpenPathsFromPolyTree(tree, paths);
}
foreach(const auto &path, paths) {
T* ring = new T;
foreach(const auto &point, path) {
*ring << GeoDataCoordinates(double(point.X) / m_scale, double(point.Y) / m_scale);
}
GeoDataPlacemark* newPlacemark = new GeoDataPlacemark();
newPlacemark->setGeometry(ring);
copyTags(*placemark, *newPlacemark);
document->append(newPlacemark);
}
}
void clipPolygon(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, GeoDataDocument* document);
void copyTags(const GeoDataPlacemark &source, GeoDataPlacemark &target) const;
void copyTags(const OsmPlacemarkData &originalPlacemarkData, OsmPlacemarkData& targetOsmData) const;
static qint64 const m_scale = 10000000;
};
}
......
Boost Software License - Version 1.0 - August 17th, 2003
http://www.boost.org/LICENSE_1_0.txt
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
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