public async Task <Plan> CreateAsync(PlanForm planForm) { await EventBus.TriggerAsync(new EventSearchPlace(planForm)); //update SearchedPlaces DB await _planFormRepository.InsertAsync(planForm); var plan = await _planProvider.GenerateAsync(planForm); await _planRepository.InsertAsync(plan); plan.Photo = await _placePhotoManager.GetPhotos(plan.PlanForm.PlaceId); //foreach(var element in plan.Elements) //{ // await _planElementRepository.InsertAsync(element); // if(element.EndingRoute != null) // { // await _planRouteRepository.InsertAsync(element.EndingRoute); // foreach(var step in element.EndingRoute.Steps) // { // await _planRouteStepRepository.InsertAsync(step); // } // } //} return(plan); }
// Co sprawdzić // -> różnica dat -> 2 tygodnie? // -> Timespan must be? public async Task CheckFormValidAsync(PlanForm planForm) { if (!planForm.IsDatesCorrect()) { throw new UserFriendlyException("Trip start or end data are incorrect!"); } //if (!(await _planDataProvider.IsPlaceIdValid(planForm.PlaceId))) //{ // throw new UserFriendlyException("PlaceId is incorrect!"); //} }
public async Task <Plan> GenerateAsync(PlanForm planForm) { // 1. Create decision array var DecisionArray = new DecisionArray(); // 2. Create plan object. Validate desitnation and accomodation. var destinationInfo = await _googlePlaceDetailsApiClient.GetAsync(_googlePlaceDetailsInputFactory.CreateAllUseful(planForm.PlaceId)); var plan = new Plan(destinationInfo.Result.name, destinationInfo.Result.geometry.location.lat, destinationInfo.Result.geometry.location.lng, (decimal?)destinationInfo.Result.rating, (decimal?)destinationInfo.Result.user_ratings_total, destinationInfo.Result.formatted_address); if (planForm.HasAccomodationBooked) { var accomodationInfo = await _googlePlaceDetailsApiClient.GetAsync(_googlePlaceDetailsInputFactory.CreateAllUseful(planForm.AccomodationId)); plan.PlanAccomodation = new PlanAccomodation(accomodationInfo.Result.geometry.location.lat, accomodationInfo.Result.geometry.location.lng, planForm.AccomodationId, accomodationInfo.Result.name, accomodationInfo.Result.formatted_address, (decimal?)accomodationInfo.Result.rating, (decimal?)accomodationInfo.Result.user_ratings_total); var distance = CalculateDistance(plan.Latitude, plan.Longitude, plan.PlanAccomodation.Lat, plan.PlanAccomodation.Lng); if (distance > MaximumDistanceToAccomodation) //more than 15km { throw new UserFriendlyException($"Odległość między celem podróży a miejscem zakwaterowania nie może być większa nić {(int)(MaximumDistanceToAccomodation/1000)} km"); } } plan.PlanForm = planForm; plan.Assumptions = new PlanAssumptions(planForm); // 3. Generate weight vector based on user preferences DecisionArray.WeightVector = _weightVectorProvider.Generate(planForm); plan.PlanFormWeightVector = PlanFormWeightVector.Create(DecisionArray.WeightVector); // 4. Get plan candidates var candidates = await _planElementCandidateFactory.GetCandidates(plan, DecisionArray.WeightVector); //5. Create decision row with values based on candidates int init = 1; foreach (var candidate in candidates) { DecisionArray.DecisionRows.Add(_decisionRowFactory.Create(candidate, init, plan.StartLocation)); ++init; } // 6. SCORE FUNCTION -> SAW Normalization (3 types - chosen first) and then calculate Score var minVector = DecisionArray.GetMinVector(); var maxVector = DecisionArray.GetMaxVector(); foreach (var decisionRow in DecisionArray.DecisionRows) { decisionRow.NormalizedScore = _sawMethod.CalculateNormalizedScore(SawNormalizationMethod.LinearFirstType, DecisionArray.WeightVector, decisionRow.DecisionValues, minVector, maxVector); } // 7. Clasification DecisionArray.DecisionRows = DecisionArray.DecisionRows.OrderByDescending(x => x.NormalizedScore).ToList(); int newPos = 1; foreach (var row in DecisionArray.DecisionRows) { row.ScorePosition = newPos; ++newPos; } // 8. Create Plan based on decision rows and optimize routes with travel salesman problem plan.Elements = await _planElementsProvider.GenerateAsync(DecisionArray, plan); return(plan); }
//0. Price //1. Rating //2. Distance //3. Popularity //4.Entertainment //5. Relax //6. Activity //7. Culture //8. Sightseeing //9. Partying //10. Shopping public WeightVector Generate(PlanForm planForm) { var weightVector = new WeightVector(); //-----------Dajemy na typy elementów planu 0.5m--------------- var allPreferedStep = Math.Round(0.3m / (planForm.PreferedPlanElements.Count()), 2);//max 0.3/14 decimal totalSecondCategory = 0.5m; //PreferedPlanElements moze mieć po maks. 2 elementy nalezace do typów od 4 do 10 -> licz.el * allPreferedStep weightVector.AddValue(WeightVectorLabel.Entertainment, allPreferedStep * planForm.PreferedPlanElements.Count(x => x == PlanElementType.Entertainment)); weightVector.AddValue(WeightVectorLabel.Sightseeing, allPreferedStep * planForm.PreferedPlanElements.Count(x => x == PlanElementType.Sightseeing)); weightVector.AddValue(WeightVectorLabel.Activity, allPreferedStep * planForm.PreferedPlanElements.Count(x => x == PlanElementType.Activity)); weightVector.AddValue(WeightVectorLabel.Culture, allPreferedStep * planForm.PreferedPlanElements.Count(x => x == PlanElementType.Culture)); weightVector.AddValue(WeightVectorLabel.Relax, allPreferedStep * planForm.PreferedPlanElements.Count(x => x == PlanElementType.Relax)); weightVector.AddValue(WeightVectorLabel.Partying, allPreferedStep * planForm.PreferedPlanElements.Count(x => x == PlanElementType.Partying)); weightVector.AddValue(WeightVectorLabel.Shopping, allPreferedStep * planForm.PreferedPlanElements.Count(x => x == PlanElementType.Shopping)); var allSortedStep = Math.Round((totalSecondCategory - weightVector.GetTotalSum()) / 9, 2); var remainingRest = (totalSecondCategory - weightVector.GetTotalSum()) - 9 * allSortedStep; //SortedPlanElements - punkty za miejsca : 3,2,2,1,1,0,0 for (int i = 0; i < planForm.SortedPlanElements.Count; i++) { decimal bonus = 0; switch (i) { case 0: bonus = allSortedStep * 3 + remainingRest; break; case 1: case 2: bonus = allSortedStep * 2; break; case 3: case 4: bonus = allSortedStep * 1; break; default: break; } if (planForm.SortedPlanElements[i] == PlanElementType.Entertainment) { weightVector.AddValue(WeightVectorLabel.Entertainment, bonus); } else if (planForm.SortedPlanElements[i] == PlanElementType.Sightseeing) { weightVector.AddValue(WeightVectorLabel.Sightseeing, bonus); } else if (planForm.SortedPlanElements[i] == PlanElementType.Activity) { weightVector.AddValue(WeightVectorLabel.Activity, bonus); } else if (planForm.SortedPlanElements[i] == PlanElementType.Culture) { weightVector.AddValue(WeightVectorLabel.Culture, bonus); } else if (planForm.SortedPlanElements[i] == PlanElementType.Relax) { weightVector.AddValue(WeightVectorLabel.Relax, bonus); } else if (planForm.SortedPlanElements[i] == PlanElementType.Partying) { weightVector.AddValue(WeightVectorLabel.Partying, bonus); } else if (planForm.SortedPlanElements[i] == PlanElementType.Shopping) { weightVector.AddValue(WeightVectorLabel.Shopping, bonus); } } //-----------Dajemy na te 0.5--------------- decimal totalFirstCategory = 0.5m; weightVector.SetPriority(WeightVectorLabel.Rating, 0.2m); totalFirstCategory -= 0.2m; var partsToDivide = 1; //Price if (planForm.PricePreference == PricePreference.Cheapest) { weightVector.AddValue(WeightVectorLabel.Price, 0.1m); totalFirstCategory -= 0.1m; partsToDivide += 2; } else if (planForm.PricePreference == PricePreference.MediumPrices) { weightVector.AddValue(WeightVectorLabel.Price, 0.05m); totalFirstCategory -= 0.05m; partsToDivide += 1; } //Popularity if (planForm.AtractionPopularityPreference == AtractionPopularityPreference.MostPopular) { weightVector.AddValue(WeightVectorLabel.Popularity, 0.1m); totalFirstCategory -= 0.1m; partsToDivide += 2; } else if (planForm.AtractionPopularityPreference == AtractionPopularityPreference.MixedPopular) { weightVector.AddValue(WeightVectorLabel.Popularity, 0.05m); totalFirstCategory -= 0.05m; partsToDivide += 1; } else if (planForm.AtractionPopularityPreference == AtractionPopularityPreference.NotWellKnown) { weightVector.AddValue(WeightVectorLabel.Popularity, 0m); totalFirstCategory -= 0.00m; } //Distance - im blizej tym bardziej wazne if (planForm.DistanceTypePreference == DistanceTypePreference.OnlyClosest) { weightVector.AddValue(WeightVectorLabel.Distance, 0.1m); totalFirstCategory -= 0.1m; partsToDivide += 2; } else if (planForm.DistanceTypePreference == DistanceTypePreference.MediumDistances) { weightVector.AddValue(WeightVectorLabel.Distance, 0.05m); totalFirstCategory -= 0.05m; partsToDivide += 1; } else if (planForm.DistanceTypePreference == DistanceTypePreference.LongDistances) { weightVector.AddValue(WeightVectorLabel.Distance, 0m); totalFirstCategory -= 0.00m; } if (totalFirstCategory > 0) { var totalFirstCategoryStep = Math.Round(totalFirstCategory / partsToDivide, 2); if (planForm.PricePreference == PricePreference.Cheapest) { weightVector.AddValue(WeightVectorLabel.Price, 2 * totalFirstCategoryStep); } else if (planForm.PricePreference == PricePreference.MediumPrices) { weightVector.AddValue(WeightVectorLabel.Price, 1 * totalFirstCategoryStep); } if (planForm.AtractionPopularityPreference == AtractionPopularityPreference.MostPopular) { weightVector.AddValue(WeightVectorLabel.Popularity, 2 * totalFirstCategoryStep); } else if (planForm.AtractionPopularityPreference == AtractionPopularityPreference.MixedPopular) { weightVector.AddValue(WeightVectorLabel.Popularity, 1 * totalFirstCategoryStep); } if (planForm.DistanceTypePreference == DistanceTypePreference.OnlyClosest) { weightVector.AddValue(WeightVectorLabel.Distance, 2 * totalFirstCategoryStep); } else if (planForm.DistanceTypePreference == DistanceTypePreference.MediumDistances) { weightVector.AddValue(WeightVectorLabel.Distance, 1 * totalFirstCategoryStep); } weightVector.AddValue(WeightVectorLabel.Rating, 1.0m - weightVector.GetTotalSum()); } var test = weightVector.Total; return(weightVector); }
public PlanAssumptions(PlanForm planForm) { SleepDuration = new TimeSpan(planForm.AverageSleep, 0, 0); var numberOfPartyActivity = planForm.PreferedPlanElements.Count(x => x == Enums.PlanElementType.Partying) + (planForm.SortedPlanElements.IndexOf(Enums.PlanElementType.Partying) < 3 ? 1 : 0); switch (numberOfPartyActivity) { case 1: SleepingTime = new TimeSpan(0, 0, 0); break; case 2: SleepingTime = new TimeSpan(1, 0, 0); break; case 3: SleepingTime = new TimeSpan(2, 0, 0); break; default: SleepingTime = new TimeSpan(23, 0, 0); break; } LunchTime = new TimeSpan(14, 0, 0); DinnerTime = new TimeSpan(19, 0, 0); if (planForm.AtractionDurationPreference == Enums.PlanFormEnums.AtractionDurationPreference.Fast) { EatingDuration = new TimeSpan(1, 0, 0); PlanElementDuration = new TimeSpan(1, 30, 0); } else if (planForm.AtractionDurationPreference == Enums.PlanFormEnums.AtractionDurationPreference.Medium) { EatingDuration = new TimeSpan(1, 30, 0); PlanElementDuration = new TimeSpan(2, 30, 0); } else if (planForm.AtractionDurationPreference == Enums.PlanFormEnums.AtractionDurationPreference.Slow) { EatingDuration = new TimeSpan(2, 0, 0); PlanElementDuration = new TimeSpan(3, 30, 0); } //radius search var hasVehicleTransport = planForm.PreferedTravelModes.Contains(Enums.GoogleTravelMode.Driving) || planForm.PreferedTravelModes.Contains(Enums.GoogleTravelMode.Transit); if (hasVehicleTransport) { RadiusSearch = 15000; } else { RadiusSearch = 6000; } //numbersOfElements var subTimeSpan = planForm.EndDateTime.Subtract(planForm.StartDateTime); var subHours = subTimeSpan.Days * 24 + subTimeSpan.Hours; var numberOfDays = (planForm.EndDateTime.DayOfYear - planForm.StartDateTime.DayOfYear + 1); subHours -= (numberOfDays - 1) * planForm.AverageSleep; // odejmujemy czas na spanie var eatingHours = EatingDuration.Multiply(NumberOfMealsPerDay * numberOfDays).Days *24 + EatingDuration.Multiply(NumberOfMealsPerDay * numberOfDays).Hours; subHours -= eatingHours; // odejmujemy czas na jedzenie decimal hoursPerPlanElement = PlanElementDuration.Hours; // ((decimal)PlanElementDuration.Minutes / 60); if (PlanElementDuration.Minutes > 0) { hoursPerPlanElement += 0.5m; } //Assume that moving is about 10% of plan subHours -= (int)(0.1m * (decimal)subHours); var assumedNumberOfElements = (int)((decimal)subHours / hoursPerPlanElement); AssumedNumberOfElement = assumedNumberOfElements; }