Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Controls/Maps/src/HandlerImpl/Map.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ void IMap.UserLocationUpdated(Location location)

void IMap.HideInfoWindow(IMapPin pin) => Handler?.Invoke(nameof(IMap.HideInfoWindow), pin);

void IMap.MoveToRegion(MapSpan region) => MoveToRegion(region);

void IMap.MoveToRegion(MapSpan region, bool animated) => MoveToRegion(region, animated);

MapSpan? IMap.VisibleRegion
{
get
Expand All @@ -61,7 +65,7 @@ protected override void OnHandlerChanged()
{
base.OnHandlerChanged();
//The user specified on the ctor a MapSpan we now need the handler to move to that region
Handler?.Invoke(nameof(IMap.MoveToRegion), _lastMoveToRegion);
Handler?.Invoke(nameof(IMap.MoveToRegion), new MoveToRegionRequest(_lastMoveToRegion, false));
}

}
Expand Down
12 changes: 10 additions & 2 deletions src/Controls/Maps/src/Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,23 @@ public IEnumerator<IMapPin> GetEnumerator()
/// </summary>
/// <param name="mapSpan">A <see cref="MapSpan"/> object containing details on what region should be shown.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="mapSpan"/> is <see langword="null"/>.</exception>
public void MoveToRegion(MapSpan mapSpan)
public void MoveToRegion(MapSpan mapSpan) => MoveToRegion(mapSpan, true);

/// <summary>
/// Adjusts the viewport of the map control to view the specified region, with control over animation.
/// </summary>
/// <param name="mapSpan">A <see cref="MapSpan"/> object containing details on what region should be shown.</param>
/// <param name="animated">Whether the transition should be animated.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="mapSpan"/> is <see langword="null"/>.</exception>
public void MoveToRegion(MapSpan mapSpan, bool animated)
{
if (mapSpan is null)
{
throw new ArgumentNullException(nameof(mapSpan));
}

_lastMoveToRegion = mapSpan;
Handler?.Invoke(nameof(IMap.MoveToRegion), _lastMoveToRegion);
Handler?.Invoke(nameof(IMap.MoveToRegion), new MoveToRegionRequest(_lastMoveToRegion, animated));
}

IEnumerator IEnumerable.GetEnumerator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.LastUserLocation.get -> Microsoft.Maui.Devices.Sensors.Location?
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.MoveToRegion(Microsoft.Maui.Maps.MapSpan! mapSpan, bool animated) -> void
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.Map.UserLocationChanged -> System.EventHandler<Microsoft.Maui.Controls.Maps.UserLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.LastUserLocation.get -> Microsoft.Maui.Devices.Sensors.Location?
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.MoveToRegion(Microsoft.Maui.Maps.MapSpan! mapSpan, bool animated) -> void
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.Map.UserLocationChanged -> System.EventHandler<Microsoft.Maui.Controls.Maps.UserLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.LastUserLocation.get -> Microsoft.Maui.Devices.Sensors.Location?
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.MoveToRegion(Microsoft.Maui.Maps.MapSpan! mapSpan, bool animated) -> void
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.Map.UserLocationChanged -> System.EventHandler<Microsoft.Maui.Controls.Maps.UserLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.LastUserLocation.get -> Microsoft.Maui.Devices.Sensors.Location?
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.MoveToRegion(Microsoft.Maui.Maps.MapSpan! mapSpan, bool animated) -> void
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.Map.UserLocationChanged -> System.EventHandler<Microsoft.Maui.Controls.Maps.UserLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.LastUserLocation.get -> Microsoft.Maui.Devices.Sensors.Location?
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.MoveToRegion(Microsoft.Maui.Maps.MapSpan! mapSpan, bool animated) -> void
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.Map.UserLocationChanged -> System.EventHandler<Microsoft.Maui.Controls.Maps.UserLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.LastUserLocation.get -> Microsoft.Maui.Devices.Sensors.Location?
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.MoveToRegion(Microsoft.Maui.Maps.MapSpan! mapSpan, bool animated) -> void
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.Map.UserLocationChanged -> System.EventHandler<Microsoft.Maui.Controls.Maps.UserLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.LastUserLocation.get -> Microsoft.Maui.Devices.Sensors.Location?
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.MoveToRegion(Microsoft.Maui.Maps.MapSpan! mapSpan, bool animated) -> void
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.Map.UserLocationChanged -> System.EventHandler<Microsoft.Maui.Controls.Maps.UserLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:maps="http://schemas.microsoft.com/dotnet/2021/maui/maps"
x:Class="Maui.Controls.Sample.Pages.MapsGalleries.CameraZoomGallery"
Title="Camera &amp; Zoom">
<Grid RowDefinitions="Auto, Auto, *">
<HorizontalStackLayout Grid.Row="0" Spacing="5" Padding="5">
<Button Text="Fit All Pins" Clicked="OnFitAllPins" />
<Button Text="Animated" Clicked="OnMoveAnimated" />
<Button Text="Instant" Clicked="OnMoveInstant" />
</HorizontalStackLayout>
<Label x:Name="InfoLabel" Grid.Row="1" Padding="5" Text="Tap buttons to test camera/zoom features" />
<maps:Map x:Name="map" Grid.Row="2" />
</Grid>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Controls.Xaml;
using Microsoft.Maui.Devices.Sensors;
using Microsoft.Maui.Maps;

namespace Maui.Controls.Sample.Pages.MapsGalleries
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CameraZoomGallery
{
readonly Microsoft.Maui.Devices.Sensors.Location[] _cities =
{
new(47.6062, -122.3321), // Seattle
new(37.7749, -122.4194), // San Francisco
new(34.0522, -118.2437), // Los Angeles
new(40.7128, -74.0060), // New York
new(41.8781, -87.6298), // Chicago
};

public CameraZoomGallery()
{
InitializeComponent();

foreach (var city in _cities)
{
map.Pins.Add(new Pin
{
Label = $"City ({city.Latitude:F2}, {city.Longitude:F2})",
Location = city,
Type = PinType.Place,
});
}
}

void OnFitAllPins(object? sender, EventArgs e)
{
var span = MapSpan.FromLocations(_cities);
map.MoveToRegion(span, true);
InfoLabel.Text = $"Best fit: center={span.Center.Latitude:F2},{span.Center.Longitude:F2} span={span.LatitudeDegrees:F2}x{span.LongitudeDegrees:F2}";
}

void OnMoveAnimated(object? sender, EventArgs e)
{
// Move to Seattle with animation
var seattle = new MapSpan(new Microsoft.Maui.Devices.Sensors.Location(47.6062, -122.3321), 0.1, 0.1);
map.MoveToRegion(seattle, true);
InfoLabel.Text = "Animated move to Seattle";
}

void OnMoveInstant(object? sender, EventArgs e)
{
// Move to New York instantly
var newYork = new MapSpan(new Microsoft.Maui.Devices.Sensors.Location(40.7128, -74.0060), 0.1, 0.1);
map.MoveToRegion(newYork, false);
InfoLabel.Text = "Instant move to New York";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public MapsGallery()
GalleryBuilder.NavButton("Map Long Click", () => new MapLongClickGallery(), Navigation),
GalleryBuilder.NavButton("Info Window", () => new InfoWindowGallery(), Navigation),
GalleryBuilder.NavButton("User Location", () => new UserLocationGallery(), Navigation),
GalleryBuilder.NavButton("Camera & Zoom", () => new CameraZoomGallery(), Navigation),
}
}
};
Expand Down
60 changes: 60 additions & 0 deletions src/Controls/tests/Core.UnitTests/MapSpanTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Devices.Sensors;
using Microsoft.Maui.Maps;
Expand Down Expand Up @@ -42,5 +44,63 @@ public void RangeClamping()
Assert.True(span.LatitudeDegrees > 0);
Assert.True(span.LongitudeDegrees > 0);
}

[Fact]
public void FromLocationsThrowsOnNull()
{
Assert.Throws<ArgumentNullException>(() => MapSpan.FromLocations(null!));
}

[Fact]
public void FromLocationsThrowsOnEmpty()
{
Assert.Throws<ArgumentException>(() => MapSpan.FromLocations(Array.Empty<Location>()));
}

[Fact]
public void FromLocationsSingleUsesOneKmRadius()
{
var loc = new Location(47.6, -122.3);
var span = MapSpan.FromLocations(new[] { loc });

Assert.Equal(47.6, span.Center.Latitude);
Assert.Equal(-122.3, span.Center.Longitude);
Assert.True(span.Radius.Kilometers > 0.9 && span.Radius.Kilometers < 1.1);
}

[Fact]
public void FromLocationsMultipleComputesBoundingBox()
{
var locations = new[]
{
new Location(40.0, -74.0), // New York area
new Location(42.0, -72.0), // Boston area
};

var span = MapSpan.FromLocations(locations);

Assert.Equal(41.0, span.Center.Latitude, 1);
Assert.Equal(-73.0, span.Center.Longitude, 1);
// With 10% padding: lat span = 2.0 * 1.1 = 2.2, lon span = 2.0 * 1.1 = 2.2
Assert.Equal(2.2, span.LatitudeDegrees, 1);
Assert.Equal(2.2, span.LongitudeDegrees, 1);
}

[Fact]
public void FromLocationsEncompassesAllPoints()
{
var locations = new List<Location>
{
new Location(10, 20),
new Location(30, 40),
new Location(20, 30),
};

var span = MapSpan.FromLocations(locations);

// Center should be midpoint
Assert.Equal(20.0, span.Center.Latitude);
Assert.Equal(30.0, span.Center.Longitude);
}
}
}
55 changes: 55 additions & 0 deletions src/Controls/tests/Core.UnitTests/MapTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,45 @@ public void PolylineClickedEventSenderIsPolyline()
Assert.Same(polyline, sender);
}

[Fact]
public void MoveToRegionThrowsOnNull()
{
var map = new Map();
Assert.Throws<ArgumentNullException>(() => map.MoveToRegion(null!));
}

[Fact]
public void MoveToRegionAnimatedThrowsOnNull()
{
var map = new Map();
Assert.Throws<ArgumentNullException>(() => map.MoveToRegion(null!, true));
}

[Fact]
public void MoveToRegionAnimatedOverloadExists()
{
var map = new Map();
var span = new MapSpan(new Location(0, 0), 1, 1);

// Should not throw - verifies the overload exists and is callable
map.MoveToRegion(span, false);
map.MoveToRegion(span, true);
}

[Fact]
public void MoveToRegionRequestProperties()
{
var span = new MapSpan(new Location(10, 20), 1, 1);
var request = new MoveToRegionRequest(span, true);

Assert.Same(span, request.Region);
Assert.True(request.Animated);

var request2 = new MoveToRegionRequest(null, false);
Assert.Null(request2.Region);
Assert.False(request2.Animated);
}

[Fact]
public void ShowInfoWindowDoesNotThrowWhenPinHasNoParent()
{
Expand Down Expand Up @@ -1027,5 +1066,21 @@ public void IMap_LastUserLocation_ReturnsMapLastUserLocation()
}

#endregion

[Fact]
public void FromLocationsHandlesAntimeridianCrossing()
{
// Points near the antimeridian (179° and -179° should result in a small span, not 358°)
var locations = new[]
{
new Location(0, 179),
new Location(0, -179)
};

var span = MapSpan.FromLocations(locations);

// The span should be small (around 2-3 degrees), not 358 degrees
Assert.True(span.LongitudeDegrees < 10, $"Expected small longitude span for antimeridian crossing, got {span.LongitudeDegrees}");
}
}
}
8 changes: 8 additions & 0 deletions src/Core/maps/src/Core/IMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,13 @@ public interface IMap : IView
/// </summary>
/// <param name="pin">The pin whose info window should be hidden.</param>
void HideInfoWindow(IMapPin pin);

/// <summary>
/// Moves the map so that it displays the specified <see cref="MapSpan"/> region,
/// with control over whether the transition is animated.
/// </summary>
/// <param name="region">The region to display.</param>
/// <param name="animated">Whether the transition should be animated.</param>
void MoveToRegion(MapSpan region, bool animated);
}
}
9 changes: 7 additions & 2 deletions src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,14 @@ public static void MapIsClusteringEnabled(IMapHandler handler, IMap map)

public static void MapMoveToRegion(IMapHandler handler, IMap map, object? arg)
{
MapSpan? newRegion = arg as MapSpan;
if (newRegion != null)
if (arg is MoveToRegionRequest request && request.Region != null)
{
(handler as MapHandler)?.MoveToRegion(request.Region, request.Animated);
}
else if (arg is MapSpan newRegion)
{
(handler as MapHandler)?.MoveToRegion(newRegion, true);
}
}

public void UpdateMapElement(IMapElement element)
Expand Down
9 changes: 7 additions & 2 deletions src/Core/maps/src/Handlers/Map/MapHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,14 @@ public static void MapElements(IMapHandler handler, IMap map)

public static void MapMoveToRegion(IMapHandler handler, IMap map, object? arg)
{
MapSpan? newRegion = arg as MapSpan;
if (newRegion != null)
if (arg is MoveToRegionRequest request && request.Region != null)
{
(handler as MapHandler)?.MoveToRegion(request.Region, request.Animated);
}
else if (arg is MapSpan newRegion)
{
(handler as MapHandler)?.MoveToRegion(newRegion, true);
}
}

public void UpdateMapElement(IMapElement element)
Expand Down
Loading
Loading