-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from SyncfusionExamples/Maps_AISample
Maps AI sample added
Showing
56 changed files
with
1,660 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.8.34309.116 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MAUIMaps", "MAUIMaps\MAUIMaps.csproj", "{4F6A4296-B0BC-45E4-BA18-E658172E0C3A}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{4F6A4296-B0BC-45E4-BA18-E658172E0C3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{4F6A4296-B0BC-45E4-BA18-E658172E0C3A}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{4F6A4296-B0BC-45E4-BA18-E658172E0C3A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU | ||
{4F6A4296-B0BC-45E4-BA18-E658172E0C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{4F6A4296-B0BC-45E4-BA18-E658172E0C3A}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{4F6A4296-B0BC-45E4-BA18-E658172E0C3A}.Release|Any CPU.Deploy.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {56271685-1BDE-449C-B414-AFE0958FFBC8} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version = "1.0" encoding = "UTF-8" ?> | ||
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
xmlns:local="clr-namespace:MAUIMaps" | ||
x:Class="MAUIMaps.App"> | ||
<Application.Resources> | ||
<ResourceDictionary> | ||
<ResourceDictionary.MergedDictionaries> | ||
<ResourceDictionary Source="Resources/Styles/Colors.xaml" /> | ||
<ResourceDictionary Source="Resources/Styles/Styles.xaml" /> | ||
</ResourceDictionary.MergedDictionaries> | ||
</ResourceDictionary> | ||
</Application.Resources> | ||
</Application> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
namespace MAUIMaps | ||
{ | ||
public partial class App : Application | ||
{ | ||
public App() | ||
{ | ||
InitializeComponent(); | ||
|
||
MainPage = new MainPage(); | ||
} | ||
} | ||
} |
301 changes: 301 additions & 0 deletions
301
MAUIMaps/MAUIMaps/Behavior/MapsGettingStartedBehavior.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,301 @@ | ||
namespace MAUIMaps | ||
{ | ||
using Newtonsoft.Json.Linq; | ||
using Syncfusion.Maui.Core; | ||
using Syncfusion.Maui.Inputs; | ||
using Syncfusion.Maui.Maps; | ||
using System.Collections.ObjectModel; | ||
using System.Text.RegularExpressions; | ||
public class MapsGettingStartedBehavior : Behavior<ContentPage> | ||
{ | ||
/// <summary> | ||
/// Holds the map tile layer. | ||
/// </summary> | ||
private MapTileLayer? mapTileLayer; | ||
|
||
/// <summary> | ||
/// Holds the map zoompanbehaviour instance. | ||
/// </summary> | ||
private MapZoomPanBehavior? zoomPanBehavior; | ||
|
||
/// <summary> | ||
/// Holds the autocomplete instance. | ||
/// </summary> | ||
private SfAutocomplete? autoComplete; | ||
|
||
/// <summary> | ||
/// Holds the button instance. | ||
/// </summary> | ||
private Button? button; | ||
|
||
/// <summary> | ||
/// Holds the busy indicator instance. | ||
/// </summary> | ||
private SfBusyIndicator? busyIndicator; | ||
|
||
/// <summary> | ||
/// Holds the azure ai helper instance. | ||
/// </summary> | ||
private AzureOpenAIService azureAIHelper = new AzureOpenAIService(); | ||
|
||
/// <summary> | ||
/// Holds the maps data helper instance. | ||
/// </summary> | ||
private MapsDataHelper dataHelper = new MapsDataHelper(); | ||
|
||
/// <summary> | ||
/// Holds the custom marker view model instance. | ||
/// </summary> | ||
private ObservableCollection<CustomMarker>? customMarkers = new ObservableCollection<CustomMarker>(); | ||
|
||
/// <summary> | ||
/// Begins when the behavior attached to the view. | ||
/// </summary> | ||
/// <param name="bindable"></param> | ||
protected override void OnAttachedTo(ContentPage bindable) | ||
{ | ||
base.OnAttachedTo(bindable); | ||
this.mapTileLayer = bindable.Content.FindByName<MapTileLayer>("layer"); | ||
this.zoomPanBehavior = bindable.Content.FindByName<MapZoomPanBehavior>("zoomPanBehavior"); | ||
this.autoComplete = bindable.Content.FindByName<SfAutocomplete>("autoComplete"); | ||
this.button = bindable.Content.FindByName<Button>("button"); | ||
this.busyIndicator = bindable.Content.FindByName<SfBusyIndicator>("busyIndicator"); | ||
if (this.autoComplete != null) | ||
{ | ||
if (this.azureAIHelper.Client != null) | ||
{ | ||
this.autoComplete.NoResultsFoundText = string.Empty; | ||
} | ||
else | ||
{ | ||
this.autoComplete.ItemsSource = new ObservableCollection<string>() | ||
{ | ||
"Hospitals in New York", "Hotels in Denver" | ||
}; | ||
} | ||
} | ||
|
||
if (this.button != null) | ||
{ | ||
this.button.Clicked += SearchButtonClicked; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Method to button click changed. | ||
/// </summary> | ||
/// <param name="sender"></param> | ||
/// <param name="e"></param> | ||
private async void SearchButtonClicked(object? sender, EventArgs e) | ||
{ | ||
if (this.autoComplete == null) | ||
{ | ||
return; | ||
} | ||
|
||
await GetRecommendationAsync(this.autoComplete.Text); | ||
if (this.busyIndicator != null) | ||
{ | ||
this.busyIndicator.IsVisible = false; | ||
this.busyIndicator.IsRunning = false; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Method to contain AI respose and updates. | ||
/// </summary> | ||
/// <param name="userQuery"></param> | ||
/// <returns></returns> | ||
private async Task GetRecommendationAsync(string userQuery) | ||
{ | ||
if (this.autoComplete == null || this.mapTileLayer == null || this.zoomPanBehavior == null) | ||
{ | ||
return; | ||
} | ||
if (string.IsNullOrWhiteSpace(this.autoComplete.Text)) | ||
{ | ||
return; | ||
} | ||
if (this.busyIndicator != null) | ||
{ | ||
this.busyIndicator.IsVisible = true; | ||
this.busyIndicator.IsRunning = true; | ||
} | ||
|
||
string prompt = $"Given location name: {userQuery}" + | ||
$"\nSome conditions need to follow:" + | ||
$"\nCheck the location name is just a state, city, capital or region, then retrieve the following fields: location name, detail, latitude, longitude, and set address value as null" + | ||
$"\nOtherwise, retrieve minimum 5 to 6 entries with following fields: location's name, details, latitude, longitude, address." + | ||
$"\nThe return format should be the following JSON format: markercollections[Name, Details, Latitude, Longitude, Address]" + | ||
$"\nRemove ```json and remove ``` if it is there in the code." + | ||
$"\nProvide JSON format details only, No need any explanation."; | ||
|
||
var returnMessage = await azureAIHelper.GetResultsFromAI(prompt); | ||
try | ||
{ | ||
var jsonObj = new JObject(); | ||
if (returnMessage == string.Empty) | ||
{ | ||
if (this.autoComplete.Text == "Hospitals in New York") | ||
{ | ||
jsonObj = JObject.Parse(dataHelper.hospitalsData); | ||
this.zoomPanBehavior.ZoomLevel = 10; | ||
} | ||
else if (this.autoComplete.Text == "Hotels in Denver") | ||
{ | ||
jsonObj = JObject.Parse(dataHelper.hotelsData); | ||
this.zoomPanBehavior.ZoomLevel = 12; | ||
} | ||
else | ||
{ | ||
return; | ||
} | ||
} | ||
else | ||
{ | ||
jsonObj = JObject.Parse(returnMessage); | ||
} | ||
|
||
this.customMarkers?.Clear(); | ||
foreach (var marker in jsonObj["markercollections"]!) | ||
{ | ||
CustomMarker customMarker = new CustomMarker(); | ||
customMarker.Name = marker["Name"]?.ToString(); | ||
customMarker.Details = marker["Details"]?.ToString(); | ||
customMarker.Address = marker["Address"]?.ToString(); | ||
customMarker.Latitude = StringToDoubleConverter(marker["Latitude"]?.ToString()); | ||
customMarker.Longitude = StringToDoubleConverter(marker["Longitude"]?.ToString()); | ||
if (this.azureAIHelper.Client != null) | ||
{ | ||
customMarker.Image = await azureAIHelper.GetImageFromAI(customMarker.Name); | ||
customMarker.ImageName = string.Empty; | ||
} | ||
else | ||
{ | ||
customMarker.Image = null; | ||
customMarker.ImageName = this.UpdateOfflineImage(customMarker.Name); | ||
} | ||
|
||
this.customMarkers?.Add(customMarker); | ||
} | ||
|
||
this.mapTileLayer.Markers = this.customMarkers!; | ||
this.mapTileLayer.EnableCenterAnimation = true; | ||
if (this.customMarkers != null && this.customMarkers.Count > 0) | ||
{ | ||
var firstMarker = this.customMarkers[0]; | ||
this.mapTileLayer.Center = new MapLatLng | ||
{ | ||
Latitude = firstMarker.Latitude, | ||
Longitude = firstMarker.Longitude, | ||
}; | ||
|
||
if (this.azureAIHelper.Client != null) | ||
{ | ||
this.zoomPanBehavior.ZoomLevel = 10; | ||
} | ||
} | ||
} | ||
catch | ||
{ | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Remove the unwanted string value for latitude and longitude, and change its to double. | ||
/// </summary> | ||
/// <param name="input"></param> | ||
/// <returns></returns> | ||
private double StringToDoubleConverter(string? input) | ||
{ | ||
if (input == null) | ||
{ | ||
return 0; | ||
} | ||
|
||
string cleanedInput = Regex.Replace(input, @"\s*[°NSEW]\s*", string.Empty); | ||
return double.Parse(cleanedInput); | ||
} | ||
|
||
/// <summary> | ||
/// Return the image name based on offline datas. | ||
/// </summary> | ||
/// <param name="name"></param> | ||
/// <returns></returns> | ||
private string UpdateOfflineImage(string? name) | ||
{ | ||
string imageName = string.Empty; | ||
if (name == "NewYork-Presbyterian Hospital" || name == "Hospital for Special Surgery" || name == "Kings County Hospital Center") | ||
{ | ||
imageName = "hospital1.jpg"; | ||
} | ||
else if (name == "Mount Sinai Hospital" || name == "Memorial Sloan Kettering Cancer Center") | ||
{ | ||
imageName = "hospital2.jpg"; | ||
} | ||
else if (name == "NYU Langone Health" || name == "New York Eye and Ear Infirmary of Mount Sinai") | ||
{ | ||
imageName = "hospital3.jpg"; | ||
} | ||
else if (name == "Lenox Hill Hospital" || name == "St. Luke's Roosevelt Hospital Center") | ||
{ | ||
imageName = "hospital4.jpg"; | ||
} | ||
else if (name == "Bellevue Hospital Center" || name == "Bronx-Lebanon Hospital Center") | ||
{ | ||
imageName = "hospital5.jpg"; | ||
} | ||
else if (name == "Mount Sinai West" || name == "Jamaica Hospital Medical Center") | ||
{ | ||
imageName = "hospital6.jpg"; | ||
} | ||
else if (name == "Mount Sinai Beth Israel" || name == "Flushing Hospital Medical Center") | ||
{ | ||
imageName = "hospital7.jpg"; | ||
} | ||
else if (name == "The Brown Palace Hotel and Spa" || name == "Le Méridien Denver Downtown" || name == "The Maven Hotel at Dairy Block") | ||
{ | ||
imageName = "hotel1.jpg"; | ||
} | ||
else if (name == "Hyatt Regency Denver at Colorado Convention Center" || name == "JW Marriott Denver Cherry Creek") | ||
{ | ||
imageName = "hotel2.jpg"; | ||
} | ||
else if (name == "The Ritz-Carlton, Denver" || name == "Halcyon, a hotel in Cherry Creek") | ||
{ | ||
imageName = "hotel3.jpg"; | ||
} | ||
else if (name == "Kimpton Hotel Born" || name == "The Art Hotel Denver, Curio Collection by Hilton") | ||
{ | ||
imageName = "hotel4.jpg"; | ||
} | ||
else if (name == "Grand Hyatt Denver" || name == "Kimpton Hotel Monaco Denver") | ||
{ | ||
imageName = "hotel5.jpg"; | ||
} | ||
else if (name == "The Oxford Hotel" || name == "Four Seasons Hotel Denver") | ||
{ | ||
imageName = "hotel6.jpg"; | ||
} | ||
else if (name == "Hotel Teatro" || name == "The Crawford Hotel") | ||
{ | ||
imageName = "hotel7.jpg"; | ||
} | ||
|
||
return imageName; | ||
} | ||
|
||
/// <summary> | ||
/// Begins when the behavior attached to the view. | ||
/// </summary> | ||
/// <param name="bindable"></param> | ||
protected override void OnDetachingFrom(ContentPage bindable) | ||
{ | ||
base.OnDetachingFrom(bindable); | ||
if (this.button != null) | ||
{ | ||
this.button.Clicked -= SearchButtonClicked; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.