using Autodesk.Revit.UI; using System; using System.Collections.Generic; using System.Linq; using System.Text; using Autodesk.Revit.DB; using Autodesk.Revit.UI.Selection; using System.Threading.Tasks; using KDCS.Utils; using System.Collections; using System.Windows.Forms; using Autodesk.Revit.DB.Plumbing; using Autodesk.Revit.DB.Mechanical; using System.Xml.Linq; using Autodesk.Revit.UI.Events; using System.IO; using KMBIM.Revit.Tools; using KMBIM.Revit.Tools.Cmd.ColorSort; using System.Resources; using KMBIM.Revit.Tools.Cmd.PipeMatchInterval; namespace KMBIM { /// /// Made By Lee Juho, DCS /// 2021.08 /// /// 1. 위치 조정할 배관 선택 /// 2. 기준 배관 선택 /// 3. 기준 배관의 LocationCurve의 벡터를 구해서 90도 눕힌 다음 가상의 선을 만들고 그 선에 위치 조정할 배관들 커넥터의 좌표를 투영 /// 4. 투영된 점들간의 간격 구하기 = 배관 중심과 중심 간의 간격 /// 5. LocationCurve의 시작점과 각 배관들의 거리에 따라 리스트 정렬, /// 6. 3에서 구한 LocationCurve의 시작점 -> 기준 배관 벡터 & 움직일 배관 -> 기준 배관이 동일한지 구분해서 배관을 두 개의 그룹으로 쪼개기 /// 7. WinForm 띄워서 단열재 두께 입력받기 /// 8. 6에서 쪼갠 리스트를 백터로 향하는 방향으로 따로 for문 루프 돌리기 /// 9. 원래 위치에 비해 배관이 더 멀어져야 하는지, 가까워져야 하는지에 따라 위치 조정 /// [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] public class PipeMatchInterval : IExternalCommand { UIApplication uiapp; UIDocument uidoc; static public Autodesk.Revit.DB.Document doc = null; Autodesk.Revit.ApplicationServices.Application app = null; public static List movePipeList = new List(); public static Pipe criteriaPipe = null; public static List mpiList = new List(); public static double criPipeInsul = 0; public static double userPipeInsulCri = 0; public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { try { // Verify active document if (null == commandData.Application.ActiveUIDocument.Document) { message = "현재 활성화된 뷰가 없습니다."; return Result.Failed; } uiapp = commandData.Application; uidoc = uiapp.ActiveUIDocument; app = uiapp.Application; doc = uidoc.Document; IList rList = uidoc.Selection.PickObjects(ObjectType.Element, new PipeSelectionFilter(), "위치 바꿀 배관 선택: "); movePipeList.Clear(); //바꿀 배관 foreach (Reference refer in rList) { if (refer == null) return Result.Cancelled; Pipe movePipe = doc.GetElement(refer.ElementId) as Pipe; movePipeList.Add(movePipe); } //기준 배관 Reference r = uidoc.Selection.PickObject(ObjectType.Element, new PipeSelectionFilter(), "기준 배관 선택: "); if (r == null) return Result.Failed; criteriaPipe = doc.GetElement(r.ElementId) as Pipe; List criPipeCon = Util.GetElementConnectors(criteriaPipe);//기준 파이프 커넥터들 //기준 배관 외경 Parameter criPipeODiaParam = criteriaPipe.get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER); double criPipeODia = Unit.FeetToMM(criPipeODiaParam.AsDouble()); //기준 배관 단열재 두께 Parameter criPipeInsulThickParam = criteriaPipe.get_Parameter(BuiltInParameter.RBS_REFERENCE_INSULATION_THICKNESS); criPipeInsul = Unit.FeetToMM(criPipeInsulThickParam.AsDouble()); LocationCurve criPipeLC = criteriaPipe.Location as LocationCurve;//기준 배관 직선 구하기 Line criPipeLine = criPipeLC.Curve as Line; XYZ stPt = criPipeLine.GetEndPoint(0); XYZ endPt = criPipeLine.GetEndPoint(1); XYZ midPt = Util.Midpoint(stPt, endPt); //간격 띄우기 값으로 가로로 나열되어 있는지, 세로(Z축 방향)로 나열되어 있는지 파악 double criPipeOffset = criteriaPipe.get_Parameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble(); double movePipeOffset = movePipeList[0].get_Parameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble(); Line guideLine = null; XYZ guideLinePt1 = null; if (Math.Abs(criPipeOffset - movePipeOffset) >= Unit.MMToFeet(10))//Z값이 다른 경우. 세로로 가이드라인 작성 { //Z방향으로 벡터 생성 XYZ criPipeZRot1 = new XYZ(midPt.X, midPt.Y, midPt.Z + 50); XYZ criPipeZRot2 = new XYZ(midPt.X, midPt.Y, midPt.Z - 50); XYZ criPipeZRotVec1 = (criPipeZRot2 - criPipeZRot1).Normalize(); XYZ criPipeZRotVec2 = (criPipeZRot1 - criPipeZRot2).Normalize(); guideLinePt1 = Util.Polar(midPt, criPipeZRotVec1, 100); XYZ guideLinePt2 = Util.Polar(midPt, criPipeZRotVec2, 100); guideLine = Line.CreateBound(guideLinePt1, guideLinePt2); } else//Z값이 같은 경우. 가로로 가이드 라인 작성 { //기준 배관 백터의 양쪽 횡방향 백터 구하기 XYZ criPipeRot1 = Util.RotateVector(criPipeLine.Direction, Util.DTR(90)); XYZ criPipeRot2 = Util.RotateVector(criPipeLine.Direction, Util.DTR(270)); guideLinePt1 = Util.Polar(midPt, criPipeRot1, 100); XYZ guideLinePt2 = Util.Polar(midPt, criPipeRot2, 100); guideLine = Line.CreateBound(guideLinePt1, guideLinePt2);//가상의 가이드 라인 생성 } mpiList.Clear(); //거리를 구하기 위해 가상의 선을 만들어서 투영시키기 foreach (Pipe p in movePipeList) { //커넥터의 좌표를 가상의 가이드 라인에 투영 List cList = Util.GetElementConnectors(p); IntersectionResult interRes = guideLine.Project(cList.First().Origin); MovePipeInfo mpi = new MovePipeInfo(); mpi.PipeToMove = p; mpi.InterPt = interRes.XYZPoint;//투영된 점 //mpi.InterPt = Util.PointZ0(interRes.XYZPoint);//투영된 점 //움직일 배관 간격 띄우기 값 Parameter mvPipeOffsetParam = p.get_Parameter(BuiltInParameter.RBS_OFFSET_PARAM); double mvPipeOffset = Unit.FeetToMM(mvPipeOffsetParam.AsDouble()); mpi.Offset = mvPipeOffset; //움직일 배관 호칭경 Parameter mvPipeNorDiaParam = p.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM); double mvPipeNorDia = Unit.FeetToMM(mvPipeNorDiaParam.AsDouble()); mpi.NormalDiameter = mvPipeNorDia; //움직일 배관 외경 Parameter mvPipeODiaParam = p.get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER); double mvPipeODia = Unit.FeetToMM(mvPipeODiaParam.AsDouble()); mpi.OuterDia = mvPipeODia; mpi.OuterDiaRound = Math.Round(mpi.OuterDia, 1); //움직일 배관 단열재 두께 Parameter mvPipeInsulThickParam = p.get_Parameter(BuiltInParameter.RBS_REFERENCE_INSULATION_THICKNESS); double mvPipeInsulThick = Unit.FeetToMM(mvPipeInsulThickParam.AsDouble()); mpi.InsultionThick = mvPipeInsulThick; Util.Pyosi(doc, mpi.InterPt, 1); //배관 중심과 중심 간의 간격 double distForSort = Unit.FeetToMM(mpi.InterPt.DistanceTo(guideLinePt1)); mpi.DistanceFromCri = distForSort;//나열을 위한 클래스에 집어넣기 mpiList.Add(mpi); } //if(Math.Abs(criPipeOffset - movePipeOffset) >= Unit.MMToFeet(10)) //{ // //위에서 아래로 나열 // //mpiList.Sort((a, b) => b.Offset.CompareTo(a.Offset)); // mpiList.OrderByDescending(num => num.Offset); //} //else ///{ //거리순으로 나열. 오름차순 mpiList.Sort((a, b) => a.DistanceFromCri.CompareTo(b.DistanceFromCri));//클래스 자체를 Sort //} //실제 배관 사이의 거리 구하기 GetPipesDistanceAndDivide(midPt, guideLinePt1, criPipeODia); SetOffset so = new SetOffset(); so.ShowDialog(); so.doc = doc; double offsetValFromUser = so.offsetValue;//간격 띄우기 값 if (so.isConfirmed == false) return Result.Cancelled;//폼에서 취소 누름 using (Transaction tr = new Transaction(doc)) { tr.Start("Start"); List infoTrue = new List(); List infoFalse = new List(); //기준 배관을 중심으로 나누기 DividePipesBasedOnCriteriaPipe(infoTrue, infoFalse); //기준 배관과 멀어져 가며 하나씩 옮기기 for (int i = infoTrue.Count() - 1; i >= 0; i--) { double offsetVal = 0; double offsetValForNextPipe = 0; if(userPipeInsulCri != criPipeInsul) { infoTrue[infoTrue.Count() - 1].Distance = infoTrue[infoTrue.Count() - 1].Distance - userPipeInsulCri; } if (offsetValFromUser <= infoTrue[i].Distance)//거리를 줄여야 할 경우 { //유저가 단열재 두께 값을 따로 지정한 경우 //따로 지정한 값이 원래의 단열재 두께보다 두꺼운 경우 if (infoTrue[i].UserInsultionThick > infoTrue[i].InsultionThick) { offsetVal = (infoTrue[i].Distance - offsetValFromUser) + infoTrue[i].UserInsultionThick; offsetValForNextPipe = offsetVal + infoTrue[i].UserInsultionThick;//단열재 두께가 추가되면 옆 배관까지 영향을 주기 때문에 따로 처리 } //따로 지정한 값이 원래의 단열재 두꼐보다 얇은 경우 else if (infoTrue[i].UserInsultionThick < infoTrue[i].InsultionThick) { offsetVal = (infoTrue[i].Distance - offsetValFromUser) - infoTrue[i].UserInsultionThick; offsetValForNextPipe = offsetVal - infoTrue[i].UserInsultionThick; } //값 수정 안된 경우 else if (infoTrue[i].UserInsultionThick == infoTrue[i].InsultionThick) { offsetVal = infoTrue[i].Distance - offsetValFromUser; offsetValForNextPipe = offsetVal; } XYZ toCriVec = (midPt - infoTrue[i].InterPt).Normalize() * Unit.MMToFeet(offsetVal); ElementTransformUtils.MoveElement(doc, infoTrue[i].PipeToMove.Id, toCriVec); if (i > 0 && infoTrue.Count() > 1)//하나의 배관을 바꾸면 다른 배관들도 다 영향을 받기 때문에 인덱스가 넘어가기 전에 값 조정 { infoTrue[i - 1].Distance = infoTrue[i - 1].Distance + offsetValForNextPipe; } } else if (offsetValFromUser > infoTrue[i].Distance)//거리를 늘려야 하는 경우 { //유저가 단열재 두께 값을 따로 지정한 경우 if (infoTrue[i].UserInsultionThick > infoTrue[i].InsultionThick) { offsetVal = (offsetValFromUser - infoTrue[i].Distance) + infoTrue[i].UserInsultionThick; offsetValForNextPipe = offsetVal + infoTrue[i].UserInsultionThick; } else if (infoTrue[i].UserInsultionThick < infoTrue[i].InsultionThick) { offsetVal = (offsetValFromUser - infoTrue[i].Distance) - infoTrue[i].UserInsultionThick; offsetValForNextPipe = offsetVal - infoTrue[i].UserInsultionThick; } else if (infoTrue[i].UserInsultionThick == infoTrue[i].InsultionThick) { offsetVal = offsetValFromUser - infoTrue[i].Distance; offsetValForNextPipe = offsetVal; } XYZ toCriVec = (infoTrue[i].InterPt - midPt).Normalize() * Unit.MMToFeet(offsetVal); ElementTransformUtils.MoveElement(doc, infoTrue[i].PipeToMove.Id, toCriVec); if (i > 0 && infoTrue.Count() > 1) { infoTrue[i - 1].Distance = infoTrue[i - 1].Distance - offsetValForNextPipe; } } } for (int i = 0; i < infoFalse.Count(); i++) { double offsetVal = 0; double offsetValForNextPipe = 0; if (userPipeInsulCri != criPipeInsul) { infoFalse[0].Distance = infoFalse[0].Distance - userPipeInsulCri; } if (offsetValFromUser <= infoFalse[i].Distance)//거리를 줄여야 할 경우 { //유저가 단열재 두께 값을 따로 지정한 경우 //따로 지정한 값이 원래의 단열재 두께보다 두꺼운 경우 if (infoFalse[i].UserInsultionThick > infoFalse[i].InsultionThick) { offsetVal = (infoFalse[i].Distance - offsetValFromUser) + infoFalse[i].UserInsultionThick; offsetValForNextPipe = offsetVal + infoFalse[i].UserInsultionThick; } //따로 지정한 값이 원래의 단열재 두께보다 얇은 경우 else if (infoFalse[i].UserInsultionThick < infoFalse[i].InsultionThick) { offsetVal = (infoFalse[i].Distance - offsetValFromUser) - infoFalse[i].UserInsultionThick; offsetValForNextPipe = offsetVal - infoFalse[i].UserInsultionThick; } //값 수정 안된 경우 else if (infoFalse[i].UserInsultionThick == infoFalse[i].InsultionThick) { offsetVal = infoFalse[i].Distance - offsetValFromUser; offsetValForNextPipe = offsetVal; } XYZ toCriVec = (midPt - infoFalse[i].InterPt).Normalize() * Unit.MMToFeet(offsetVal); ElementTransformUtils.MoveElement(doc, infoFalse[i].PipeToMove.Id, toCriVec); if (i < infoFalse.Count() - 1 && infoFalse.Count() > 1) { infoFalse[i + 1].Distance = infoFalse[i + 1].Distance + offsetValForNextPipe; } } else if (offsetValFromUser > infoFalse[i].Distance)//거리를 늘려야 하는 경우 { //유저가 단열재 두께 값을 따로 지정한 경우 if (infoFalse[i].UserInsultionThick > infoFalse[i].InsultionThick) { offsetVal = (offsetValFromUser - infoFalse[i].Distance) + infoFalse[i].UserInsultionThick; offsetValForNextPipe = offsetVal + infoFalse[i].UserInsultionThick; } else if (infoFalse[i].UserInsultionThick < infoFalse[i].InsultionThick) { offsetVal = (offsetValFromUser - infoFalse[i].Distance) - infoFalse[i].UserInsultionThick; offsetValForNextPipe = offsetVal - infoFalse[i].UserInsultionThick; } else if (infoFalse[i].UserInsultionThick == infoFalse[i].InsultionThick) { offsetVal = offsetValFromUser - infoFalse[i].Distance; offsetValForNextPipe = offsetVal; } XYZ toCriVec = (infoFalse[i].InterPt - midPt).Normalize() * Unit.MMToFeet(offsetVal); ElementTransformUtils.MoveElement(doc, infoFalse[i].PipeToMove.Id, toCriVec); if (i < infoFalse.Count() - 1 && infoFalse.Count() > 1) { infoFalse[i + 1].Distance = infoFalse[i + 1].Distance - offsetValForNextPipe; } } } tr.Commit(); } } catch (Exception e) { MessageBox.Show(e.Message); } return Result.Succeeded; } //Method-------------------------------------------------------------------------------------------------------------- public class PipeSelectionFilter : ISelectionFilter { public bool AllowElement(Element element) { if (element.Category == null) return false; if (element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeCurves) { return true; } return false; } public bool AllowReference(Reference refer, XYZ point) { return false; } } public double GetMinValue(List doubleList) { double minLength = doubleList[0]; for (int i = 0; i < doubleList.Count(); i++) { //최솟값 구하기 if (minLength > doubleList[i]) { minLength = doubleList[i]; } } return minLength; } public double RTD(double radian) { return radian * (180.0 / Math.PI); } /// /// 배관과 다른 배관 간의 간격을 구한다. /// 기준 배관으로 향하는 벡터를 기준으로 방향이 같은 것 / 다른 것을 나누어서 따로 거리를 구한다.(후에 나눈 배관들을 따로 처리 하기 위함) /// 거리를 구할 배관들의 외경, 단열재 두께, 기준 배관의 LocationCurve가 필요 /// /// /// /// public void GetPipesDistanceAndDivide(XYZ criteriaPipeMidPt, XYZ guideLinePt1, double criPipeODia) { for (int i = 0; i < mpiList.Count(); i++) { XYZ SortVec = (mpiList[i].InterPt - guideLinePt1).Normalize(); XYZ toMidPtVec = (criteriaPipeMidPt - mpiList[i].InterPt).Normalize(); if (toMidPtVec.IsAlmostEqualTo(SortVec))//정렬 방향 & 움직일 배관에서 기준 배관으로의 백터가 동일한 경우 { if (i != mpiList.Count() - 1) { double dist = Unit.FeetToMM(mpiList[i].InterPt.DistanceTo(mpiList[i + 1].InterPt));//배관 중심과 중심 사이의 간격 if (mpiList[i + 1].PipeToMove == criteriaPipe)//다음 배관이 기준 배관인 경우 { double realDist = dist - (criPipeODia / 2 + mpiList[i].OuterDia / 2 + mpiList[i + 1].InsultionThick + mpiList[i].InsultionThick); mpiList[i].Distance = realDist; } else//다음 배관이 기준 배관이 아닌 경우 { double realDist = dist - (mpiList[i + 1].OuterDia / 2 + mpiList[i].OuterDia / 2 + mpiList[i + 1].InsultionThick + mpiList[i].InsultionThick); mpiList[i].Distance = realDist; } //구별 시 기준 배관 배제 if (mpiList[i].PipeToMove.Id != criteriaPipe.Id) { mpiList[i].JudgePipe = 1; } else if (mpiList[i].PipeToMove.Id == criteriaPipe.Id) { mpiList[i].JudgePipe = 2; } } } else//반대 방향인 경우 { double dist = 0; if (i > 0) { dist = Unit.FeetToMM(mpiList[i].InterPt.DistanceTo(mpiList[i - 1].InterPt));//배관 중심과 중심 사이의 간격 } if (i != 0) { if (mpiList[i - 1].PipeToMove == criteriaPipe)//다음 배관이 기준 배관인 경우 { double realDist = dist - (criPipeODia / 2 + mpiList[i].OuterDia / 2 + mpiList[i - 1].InsultionThick + mpiList[i].InsultionThick); mpiList[i].Distance = realDist; } else//다음 배관이 기준 배관이 아닌 경우 { double realDist = dist - (mpiList[i - 1].OuterDia / 2 + mpiList[i].OuterDia / 2 + mpiList[i - 1].InsultionThick + mpiList[i].InsultionThick); mpiList[i].Distance = realDist; } } //구별 시 기준 배관 배제 if (mpiList[i].PipeToMove.Id != criteriaPipe.Id) { mpiList[i].JudgePipe = 0; } else if (mpiList[i].PipeToMove.Id == criteriaPipe.Id) { mpiList[i].JudgePipe = 2; } } } } /// /// 기준 배관으로 다른 배관들을 다른 리스트에 담아 분류하기 /// /// 리스트1 /// 리스트2 public void DividePipesBasedOnCriteriaPipe(List infoTrue, List infoFalse) { for (int i = 0; i < mpiList.Count(); i++) { if (mpiList[i].JudgePipe == 1)//정렬 방향 & 움직일 배관에서 기준 배관으로의 백터가 동일한 경우 { infoTrue.Add(mpiList[i]); } else if (mpiList[i].JudgePipe == 0) { infoFalse.Add(mpiList[i]); } else if (mpiList[i].JudgePipe == 2) continue;//기준 배관 패스 } } /// /// 배관 두 개 사이의 간격 구하기(중점에서 중점간 거리가 아닌 실제 겉 표면에서 부터의 겉 표면 까지의 거리) /// /// 첫번째 배관 /// 두번째 배관 /// 배관 사이의 거리 public double GetPipesDistanceMM(Pipe pipe1, Pipe pipe2) { //pipe1 배관 외경 Parameter Pipe1ODiaParam = criteriaPipe.get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER); double Pipe1ODia = Unit.FeetToMM(Pipe1ODiaParam.AsDouble()); //pipe1 단열재 두께 Parameter pipe1InsulThickParam = criteriaPipe.get_Parameter(BuiltInParameter.RBS_REFERENCE_INSULATION_THICKNESS); double pipe1InsultionThick = Unit.FeetToMM(pipe1InsulThickParam.AsDouble()); LocationCurve Pipe1LC = criteriaPipe.Location as LocationCurve;//pipe1 직선 구하기 Line Pipe1Line = Pipe1LC.Curve as Line; XYZ stPt = Pipe1Line.GetEndPoint(0); XYZ endPt = Pipe1Line.GetEndPoint(1); XYZ midPt = Util.Midpoint(stPt, endPt); //pipe1 백터의 양쪽 횡방향 백터 구하기 XYZ Pipe1Rot1 = Util.RotateVector(Pipe1Line.Direction, Util.DTR(90)); XYZ Pipe1Rot2 = Util.RotateVector(Pipe1Line.Direction, Util.DTR(270)); XYZ guideLinePt1 = Util.Polar(midPt, Pipe1Rot1, 100); XYZ guideLinePt2 = Util.Polar(midPt, Pipe1Rot2, 100); Line guideLine = Line.CreateBound(guideLinePt1, guideLinePt2);//가상의 가이드 라인 생성 //---------------------------------------------------------------------------------------- //커넥터의 좌표를 가상의 가이드 라인에 투영 List cList = Util.GetElementConnectors(pipe2); IntersectionResult interRes = guideLine.Project(cList.First().Origin); XYZ interPt = interRes.XYZPoint; //pipe2 배관 외경 Parameter pipe2ODiaParam = pipe2.get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER); double pipe2ODia = Unit.FeetToMM(pipe2ODiaParam.AsDouble()); //pipe2 단열재 두께 Parameter pipe2InsulThickParam = pipe2.get_Parameter(BuiltInParameter.RBS_REFERENCE_INSULATION_THICKNESS); double pipe2InsulThick = Unit.FeetToMM(pipe2InsulThickParam.AsDouble()); //배관 중심과 중심 간의 간격 double distBetweenPipesCenter = Unit.FeetToMM(interPt.DistanceTo(guideLinePt1)); double dist = distBetweenPipesCenter - (Pipe1ODia / 2 + pipe2ODia / 2 + pipe1InsultionThick + pipe2InsulThick); return dist; } /// /// 요소에 색상 지정. 가시성/그래픽에 투영/표면 재지정 없음으로 설정하면 안 보임 /// /// 색상 넣을 요소 Id /// 바꿀 색상 public void SetColor(Document doc, Element e, System.Drawing.Color SelectedColor) { FilteredElementCollector fec = new FilteredElementCollector(doc).OfClass(typeof(FillPatternElement)); ElementId fillEId = null; foreach (FillPatternElement fpe in fec) { if (fpe.GetFillPattern().IsSolidFill)//솔리드 채움으로 되어 있는 요소에만 색상 지정 가능 { fillEId = fpe.Id; break; } else MessageBox.Show("솔리드 채움으로 바꾸십시오.", "오류"); } System.Drawing.Color col = SelectedColor; Autodesk.Revit.DB.Color adeskColor = new Autodesk.Revit.DB.Color(col.R, col.G, col.B); OverrideGraphicSettings ogs = new OverrideGraphicSettings(); ogs.SetProjectionLineColor(adeskColor); ogs.SetSurfaceForegroundPatternColor(adeskColor); ogs.SetCutForegroundPatternColor(adeskColor); if (e.Id != null) { ogs.SetSurfaceForegroundPatternId(fillEId); ogs.SetCutForegroundPatternId(fillEId); ogs.SetProjectionLinePatternId(fillEId); } doc.ActiveView.SetElementOverrides(e.Id, ogs); } } }