public async Task Query(string text)
{
try
{
if (Controller == null)
return;
IsLoadingSearchResults = true;
text = text.Trim();
if (_searchCancellationTokenSource != null)
{
if (_currentSearchString != null && _currentSearchString == text)
return;
_searchCancellationTokenSource.Cancel();
}
_searchCancellationTokenSource = new CancellationTokenSource();
var cancellationToken = _searchCancellationTokenSource.Token;
Envelope boundingBox = Controller.Extent;
if (string.IsNullOrWhiteSpace(text)) return;
if (_currentSearchString != null && _currentSearchString != text)
{
if (!cancellationToken.IsCancellationRequested)
_searchCancellationTokenSource.Cancel();
}
_searchResultLayer.Graphics.Clear();
var geo = new OnlineLocatorTask(new Uri("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer", UriKind.Absolute), "");
boundingBox = boundingBox.Expand(1.2);
_currentSearchString = text;
SearchResultStatus = string.Format("Searching for '{0}'...", text.Trim());
var result = await geo.FindAsync(new OnlineLocatorFindParameters(text)
{
MaxLocations = 25,
OutSpatialReference = WebMapVM.SpatialReference,
SearchExtent = boundingBox,
Location = (MapPoint)GeometryEngine.NormalizeCentralMeridian(boundingBox.GetCenter()),
Distance = GetDistance(boundingBox),
OutFields = new List<string>() { "PlaceName", "Type", "City", "Country" }
}, cancellationToken);
// if no results, try again with larger and larger extent
var retries = 3;
while (result.Count == 0 && --retries > 0)
{
if (cancellationToken.IsCancellationRequested)
return;
boundingBox = boundingBox.Expand(2);
result = await geo.FindAsync(new OnlineLocatorFindParameters(text)
{
MaxLocations = 25,
OutSpatialReference = WebMapVM.SpatialReference,
SearchExtent = boundingBox,
Location = (MapPoint)GeometryEngine.NormalizeCentralMeridian(boundingBox.GetCenter()),
Distance = GetDistance(boundingBox),
OutFields = new List<string>() { "PlaceName", "Type", "City", "Country"}
}, cancellationToken);
}
if (cancellationToken.IsCancellationRequested)
return;
if (result.Count == 0)
{
// atfer trying to expand the bounding box several times and finding no results,
// let us try finding results without the spatial bound.
result = await geo.FindAsync(new OnlineLocatorFindParameters(text)
{
MaxLocations = 25,
OutSpatialReference = WebMapVM.SpatialReference,
OutFields = new List<string>() { "PlaceName", "Type", "City", "Country"}
}, cancellationToken);
if (result.Any())
{
// since the results are not bound by any spatial filter, let us show well known administrative
// places e.g. countries and cities, and filter out other results e.g. restaurents and business names.
var typesToInclude = new List<string>()
{ "", "city", "community", "continent", "country", "county", "district", "locality", "municipality", "national capital",
"neighborhood", "other populated place", "state capital", "state or province", "territory", "village"};
for (var i = result.Count - 1; i >= 0; --i)
{
// get the result type
var resultType = ((string)result[i].Feature.Attributes["Type"]).Trim().ToLower();
// if the result type exists in the inclusion list above, keep it in the list of results
if (typesToInclude.Contains(resultType))
continue;
// otherwise, remove it from the list of results
result.RemoveAt(i);
}
}
}
if (result.Count == 0)
{
SearchResultStatus = string.Format("No results for '{0}' found", text);
if (Locations != null)
Locations.Clear();
//await new Windows.UI.Popups.MessageDialog(string.Format("No results for '{0}' found", text)).ShowAsync();
}
else
{
SearchResultStatus = string.Format("Found {0} results for '{1}'", result.Count.ToString(), text);
Envelope extent = null;
var color = (App.Current.Resources["AppAccentBrush"] as SolidColorBrush).Color;
var color2 = (App.Current.Resources["AppAccentForegroundBrush"] as SolidColorBrush).Color;
SimpleMarkerSymbol symbol = new SimpleMarkerSymbol()
{
Color = Colors.Black,
Outline = new SimpleLineSymbol() { Color = Colors.Black, Width = 2 },
Size = 16,
Style = SimpleMarkerStyle.Square
};
// set the picture marker symbol used in the search result composite symbol.
var imageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Icons/SearchResult.png"));
var imageSource = await imageFile.OpenReadAsync();
var pictureMarkerSymbol = new PictureMarkerSymbol();
await pictureMarkerSymbol.SetSourceAsync(imageSource);
// apply an x and y offsets so that the tip of of the pin points to the correct location.
pictureMarkerSymbol.XOffset = SearchResultPinXOffset;
pictureMarkerSymbol.YOffset = SearchResultPinYOffset;
int ID = 1;
foreach (var r in result)
{
if (extent == null)
extent = r.Extent;
else if (r.Extent != null)
extent = extent.Union(r.Extent);
var textSymbol = new TextSymbol()
{
Text = ID.ToString(),
Font = new SymbolFont() { FontFamily = "Verdena", FontSize = 10, FontWeight = SymbolFontWeight.Bold },
Color = color2,
BorderLineColor = color2,
HorizontalTextAlignment = Esri.ArcGISRuntime.Symbology.HorizontalTextAlignment.Center,
VerticalTextAlignment = Esri.ArcGISRuntime.Symbology.VerticalTextAlignment.Bottom,
XOffset = SearchResultPinXOffset,
YOffset = SearchResultPinYOffset
}; //Looks like Top and Bottom are switched - potential CR
// a compsite symbol for both the PictureMarkerSymbol and the TextSymbol could be used, but we
// wanted to higlight the pin without the text; therefore, we will add them separately.
// add the PictureMarkerSymbol to _searchResultLayer
Graphic pin = new Graphic()
{
Geometry = r.Extent.GetCenter(),
Symbol = pictureMarkerSymbol,
};
pin.Attributes["ID"] = ID;
pin.Attributes["Name"] = r.Name;
_searchResultLayer.Graphics.Add(pin);
// add the text to _searchResultLayer
Graphic pinText = new Graphic()
{
Geometry = r.Extent.GetCenter(),
Symbol = textSymbol,
};
pinText.Attributes["ID"] = ID;
pinText.Attributes["Name"] = r.Name;
_searchResultLayer.Graphics.Add(pinText);
ID++;
}
SetResult(result);
base.RaisePropertyChanged("IsClearGraphicsVisible");
if (extent != null)
{
var _ = SetViewAsync(extent, 50);
}
}
}
finally
{
IsLoadingSearchResults = false;
_searchCancellationTokenSource = null;
_currentSearchString = null;
}
}