void ElementGeometryClipper::visitWay(const Way& way) { ClipperLib::Path wayShape; PointLocation pointLocation = setPath(quadKeyBbox_, way, wayShape); // 1. all geometry inside current quadkey: no need to truncate. if (pointLocation == PointLocation::AllInside) { callback_(way, quadKey_); return; } // 2. all geometry outside : way should be skipped if (pointLocation == PointLocation::AllOutside) { return; } ClipperLib::PolyTree solution; clipper_.AddPath(wayShape, ClipperLib::ptSubject, false); clipper_.AddPath(createPathFromBoundingBox(), ClipperLib::ptClip, true); clipper_.Execute(ClipperLib::ctIntersection, solution); clipper_.Clear(); std::size_t count = static_cast<std::size_t>(solution.Total()); // 3. way intersects border only once: store a copy with clipped geometry if (count == 1) { Way clippedWay; setData(clippedWay, way, solution.GetFirst()->Contour); callback_(clippedWay, quadKey_); } // 4. in this case, result should be stored as relation (collection of ways) else { Relation relation; relation.id = way.id; relation.tags = way.tags; relation.elements.reserve(count); ClipperLib::PolyNode* polyNode = solution.GetFirst(); while (polyNode) { auto clippedWay = std::make_shared<Way>(); clippedWay->id = way.id; setCoordinates(*clippedWay, polyNode->Contour); relation.elements.push_back(clippedWay); polyNode = polyNode->GetNext(); } callback_(relation, quadKey_); } }
void ElementGeometryClipper::visitRelation(const Relation& relation) { struct RelationVisitor : public ElementVisitor { RelationVisitor(const BoundingBox& quadKeyBbox, ClipperLib::Clipper& clipper) : bbox_(quadKeyBbox), clipper_(clipper) { } void visitNode(const Node& node) { // TODO } void visitWay(const Way& way) { ClipperLib::Path wayShape; setPath(bbox_, way, wayShape); clipper_.AddPath(wayShape, ClipperLib::ptSubject, false); } void visitArea(const Area& area) { ClipperLib::Path areaShape; setPath(bbox_, area, areaShape); clipper_.AddPath(areaShape, ClipperLib::ptSubject, true); } void visitRelation(const Relation& relation) { for (const auto& element : relation.elements) { element->accept(*this); } } private: const BoundingBox& bbox_; ClipperLib::Clipper& clipper_; } visitor(quadKeyBbox_, clipper_); relation.accept(visitor); // Process results ClipperLib::PolyTree solution; clipper_.AddPath(createPathFromBoundingBox(), ClipperLib::ptClip, true); clipper_.Execute(ClipperLib::ctIntersection, solution); clipper_.Clear(); std::size_t count = static_cast<size_t>(solution.Total()); // Do not store one result as relation if (count == 1) { ClipperLib::PolyNode* node = solution.GetFirst(); if (node->IsOpen()) { Way way; setData(way, relation, node->Contour); callback_(way, quadKey_); } else { if (!node->IsHole()) { Area area; setData(area, relation, node->Contour); callback_(area, quadKey_); } } } else if (count > 1) { Relation newRelation; newRelation.id = relation.id; newRelation.tags = relation.tags; newRelation.elements.reserve(count); ClipperLib::PolyNode* polyNode = solution.GetFirst(); while (polyNode) { if (polyNode->IsOpen()) { auto way = std::make_shared<Way>(); setCoordinates(*way, polyNode->Contour); newRelation.elements.push_back(way); } else { auto area = std::make_shared<Area>(); setCoordinates(*area, polyNode->Contour); newRelation.elements.push_back(area); } polyNode = polyNode->GetNext(); } callback_(newRelation, quadKey_); } }