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::visitArea(const Area& area) { ClipperLib::Path areaShape; PointLocation pointLocation = setPath(quadKeyBbox_, area, areaShape); // 1. all geometry inside current quadkey: no need to truncate. if (pointLocation == PointLocation::AllInside) { callback_(area, quadKey_); return; } // 2. all geometry outside: skip if (pointLocation == PointLocation::AllOutside) { return; } ClipperLib::Paths solution; clipper_.AddPath(areaShape, ClipperLib::ptSubject, true); clipper_.AddPath(createPathFromBoundingBox(), ClipperLib::ptClip, true); clipper_.Execute(ClipperLib::ctIntersection, solution); clipper_.Clear(); // 3. way intersects border only once: store a copy with clipped geometry if (solution.size() == 1) { Area clippedArea; setData(clippedArea, area, solution[0]); callback_(clippedArea, quadKey_); } // 4. in this case, result should be stored as relation (collection of areas) else { Relation relation; relation.id = area.id; relation.tags = area.tags; relation.elements.reserve(solution.size()); for (auto it = solution.begin(); it != solution.end(); ++it) { auto clippedArea = std::make_shared<Area> (); clippedArea->id = area.id; setCoordinates(*clippedArea, *it); relation.elements.push_back(clippedArea); } callback_(relation, quadKey_); } }
ElementGeometryClipper::ElementGeometryClipper(const utymap::QuadKey &quadKey, const utymap::BoundingBox &quadKeyBbox, Callback callback) : callback_(callback), quadKey_(quadKey), quadKeyBbox_(quadKeyBbox), clipper_() { addClip(clipper_, createPathFromBoundingBox(quadKeyBbox_)); }
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_); } }