diff options
Diffstat (limited to 'Xamarin.Forms.Maps')
-rw-r--r-- | Xamarin.Forms.Maps/Distance.cs | 78 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/Geocoder.cs | 27 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/Map.cs | 107 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/MapSpan.cs | 116 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/MapType.cs | 9 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/Pin.cs | 99 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/PinType.cs | 10 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/Position.cs | 47 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/Properties/AssemblyInfo.cs | 29 | ||||
-rw-r--r-- | Xamarin.Forms.Maps/Xamarin.Forms.Maps.csproj | 73 |
10 files changed, 595 insertions, 0 deletions
diff --git a/Xamarin.Forms.Maps/Distance.cs b/Xamarin.Forms.Maps/Distance.cs new file mode 100644 index 00000000..7125f086 --- /dev/null +++ b/Xamarin.Forms.Maps/Distance.cs @@ -0,0 +1,78 @@ +using System.Diagnostics; + +namespace Xamarin.Forms.Maps +{ + public struct Distance + { + const double MetersPerMile = 1609.344; + const double MetersPerKilometer = 1000.0; + + public Distance(double meters) + { + Meters = meters; + } + + public double Meters { get; } + + public double Miles => Meters / MetersPerMile; + + public double Kilometers => Meters / MetersPerKilometer; + + public static Distance FromMiles(double miles) + { + if (miles < 0) + { + Debug.WriteLine("Negative values for distance not supported"); + miles = 0; + } + return new Distance(miles * MetersPerMile); + } + + public static Distance FromMeters(double meters) + { + if (meters < 0) + { + Debug.WriteLine("Negative values for distance not supported"); + meters = 0; + } + return new Distance(meters); + } + + public static Distance FromKilometers(double kilometers) + { + if (kilometers < 0) + { + Debug.WriteLine("Negative values for distance not supported"); + kilometers = 0; + } + return new Distance(kilometers * MetersPerKilometer); + } + + public bool Equals(Distance other) + { + return Meters.Equals(other.Meters); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + return obj is Distance && Equals((Distance)obj); + } + + public override int GetHashCode() + { + return Meters.GetHashCode(); + } + + public static bool operator ==(Distance left, Distance right) + { + return left.Equals(right); + } + + public static bool operator !=(Distance left, Distance right) + { + return !left.Equals(right); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/Geocoder.cs b/Xamarin.Forms.Maps/Geocoder.cs new file mode 100644 index 00000000..e5d6d886 --- /dev/null +++ b/Xamarin.Forms.Maps/Geocoder.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Xamarin.Forms.Maps +{ + public class Geocoder + { + internal static Func<string, Task<IEnumerable<Position>>> GetPositionsForAddressAsyncFunc; + + internal static Func<Position, Task<IEnumerable<string>>> GetAddressesForPositionFuncAsync; + + public Task<IEnumerable<string>> GetAddressesForPositionAsync(Position position) + { + if (GetAddressesForPositionFuncAsync == null) + throw new InvalidOperationException("You MUST call Xamarin.FormsMaps.Init (); prior to using it."); + return GetAddressesForPositionFuncAsync(position); + } + + public Task<IEnumerable<Position>> GetPositionsForAddressAsync(string address) + { + if (GetPositionsForAddressAsyncFunc == null) + throw new InvalidOperationException("You MUST call Xamarin.FormsMaps.Init (); prior to using it."); + return GetPositionsForAddressAsyncFunc(address); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/Map.cs b/Xamarin.Forms.Maps/Map.cs new file mode 100644 index 00000000..a4587a3e --- /dev/null +++ b/Xamarin.Forms.Maps/Map.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; + +namespace Xamarin.Forms.Maps +{ + public class Map : View, IEnumerable<Pin> + { + public static readonly BindableProperty MapTypeProperty = BindableProperty.Create("MapType", typeof(MapType), typeof(Map), default(MapType)); + + public static readonly BindableProperty IsShowingUserProperty = BindableProperty.Create("IsShowingUser", typeof(bool), typeof(Map), default(bool)); + + public static readonly BindableProperty HasScrollEnabledProperty = BindableProperty.Create("HasScrollEnabled", typeof(bool), typeof(Map), true); + + public static readonly BindableProperty HasZoomEnabledProperty = BindableProperty.Create("HasZoomEnabled", typeof(bool), typeof(Map), true); + + readonly ObservableCollection<Pin> _pins = new ObservableCollection<Pin>(); + MapSpan _visibleRegion; + + public Map(MapSpan region) + { + LastMoveToRegion = region; + + VerticalOptions = HorizontalOptions = LayoutOptions.FillAndExpand; + + _pins.CollectionChanged += PinsOnCollectionChanged; + } + + // center on Rome by default + public Map() : this(new MapSpan(new Position(41.890202, 12.492049), 0.1, 0.1)) + { + } + + public bool HasScrollEnabled + { + get { return (bool)GetValue(HasScrollEnabledProperty); } + set { SetValue(HasScrollEnabledProperty, value); } + } + + public bool HasZoomEnabled + { + get { return (bool)GetValue(HasZoomEnabledProperty); } + set { SetValue(HasZoomEnabledProperty, value); } + } + + public bool IsShowingUser + { + get { return (bool)GetValue(IsShowingUserProperty); } + set { SetValue(IsShowingUserProperty, value); } + } + + public MapType MapType + { + get { return (MapType)GetValue(MapTypeProperty); } + set { SetValue(MapTypeProperty, value); } + } + + public IList<Pin> Pins + { + get { return _pins; } + } + + public MapSpan VisibleRegion + { + get { return _visibleRegion; } + internal set + { + if (_visibleRegion == value) + return; + if (value == null) + throw new ArgumentNullException(nameof(value)); + OnPropertyChanging(); + _visibleRegion = value; + OnPropertyChanged(); + } + } + + internal MapSpan LastMoveToRegion { get; private set; } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator<Pin> GetEnumerator() + { + return _pins.GetEnumerator(); + } + + public void MoveToRegion(MapSpan mapSpan) + { + if (mapSpan == null) + throw new ArgumentNullException(nameof(mapSpan)); + LastMoveToRegion = mapSpan; + MessagingCenter.Send(this, "MapMoveToRegion", mapSpan); + } + + void PinsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.NewItems != null && e.NewItems.Cast<Pin>().Any(pin => pin.Label == null)) + throw new ArgumentException("Pin must have a Label to be added to a map"); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/MapSpan.cs b/Xamarin.Forms.Maps/MapSpan.cs new file mode 100644 index 00000000..883e630f --- /dev/null +++ b/Xamarin.Forms.Maps/MapSpan.cs @@ -0,0 +1,116 @@ +using System; + +namespace Xamarin.Forms.Maps +{ + public sealed class MapSpan + { + const double EarthRadiusKm = 6371; + const double EarthCircumferenceKm = EarthRadiusKm * 2 * Math.PI; + const double MinimumRangeDegrees = 0.001 / EarthCircumferenceKm * 360; // 1 meter + + public MapSpan(Position center, double latitudeDegrees, double longitudeDegrees) + { + Center = center; + LatitudeDegrees = Math.Min(Math.Max(latitudeDegrees, MinimumRangeDegrees), 90.0); + LongitudeDegrees = Math.Min(Math.Max(longitudeDegrees, MinimumRangeDegrees), 180.0); + } + + public Position Center { get; } + + public double LatitudeDegrees { get; } + + public double LongitudeDegrees { get; } + + public Distance Radius + { + get + { + double latKm = LatitudeDegreesToKm(LatitudeDegrees); + double longKm = LongitudeDegreesToKm(Center, LongitudeDegrees); + return new Distance(1000 * Math.Min(latKm, longKm) / 2); + } + } + + public MapSpan ClampLatitude(double north, double south) + { + north = Math.Min(Math.Max(north, 0), 90); + south = Math.Max(Math.Min(south, 0), -90); + double lat = Math.Max(Math.Min(Center.Latitude, north), south); + double maxDLat = Math.Min(north - lat, -south + lat) * 2; + return new MapSpan(new Position(lat, Center.Longitude), Math.Min(LatitudeDegrees, maxDLat), LongitudeDegrees); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + return obj is MapSpan && Equals((MapSpan)obj); + } + + public static MapSpan FromCenterAndRadius(Position center, Distance radius) + { + return new MapSpan(center, 2 * DistanceToLatitudeDegrees(radius), 2 * DistanceToLongitudeDegrees(center, radius)); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = Center.GetHashCode(); + hashCode = (hashCode * 397) ^ LongitudeDegrees.GetHashCode(); + hashCode = (hashCode * 397) ^ LatitudeDegrees.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(MapSpan left, MapSpan right) + { + return Equals(left, right); + } + + public static bool operator !=(MapSpan left, MapSpan right) + { + return !Equals(left, right); + } + + public MapSpan WithZoom(double zoomFactor) + { + double maxDLat = Math.Min(90 - Center.Latitude, 90 + Center.Latitude) * 2; + return new MapSpan(Center, Math.Min(LatitudeDegrees / zoomFactor, maxDLat), LongitudeDegrees / zoomFactor); + } + + static double DistanceToLatitudeDegrees(Distance distance) + { + return distance.Kilometers / EarthCircumferenceKm * 360; + } + + static double DistanceToLongitudeDegrees(Position position, Distance distance) + { + double latCircumference = LatitudeCircumferenceKm(position); + return distance.Kilometers / latCircumference * 360; + } + + bool Equals(MapSpan other) + { + return Center.Equals(other.Center) && LongitudeDegrees.Equals(other.LongitudeDegrees) && LatitudeDegrees.Equals(other.LatitudeDegrees); + } + + static double LatitudeCircumferenceKm(Position position) + { + return EarthCircumferenceKm * Math.Cos(position.Latitude * Math.PI / 180.0); + } + + static double LatitudeDegreesToKm(double latitudeDegrees) + { + return EarthCircumferenceKm * latitudeDegrees / 360; + } + + static double LongitudeDegreesToKm(Position position, double longitudeDegrees) + { + double latCircumference = LatitudeCircumferenceKm(position); + return latCircumference * longitudeDegrees / 360; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/MapType.cs b/Xamarin.Forms.Maps/MapType.cs new file mode 100644 index 00000000..365f5afb --- /dev/null +++ b/Xamarin.Forms.Maps/MapType.cs @@ -0,0 +1,9 @@ +namespace Xamarin.Forms.Maps +{ + public enum MapType + { + Street, + Satellite, + Hybrid + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/Pin.cs b/Xamarin.Forms.Maps/Pin.cs new file mode 100644 index 00000000..6c0fb556 --- /dev/null +++ b/Xamarin.Forms.Maps/Pin.cs @@ -0,0 +1,99 @@ +using System; + +namespace Xamarin.Forms.Maps +{ + public sealed class Pin : BindableObject + { + public static readonly BindableProperty TypeProperty = BindableProperty.Create("Type", typeof(PinType), typeof(Pin), default(PinType)); + + public static readonly BindableProperty PositionProperty = BindableProperty.Create("Position", typeof(Position), typeof(Pin), default(Position)); + + public static readonly BindableProperty AddressProperty = BindableProperty.Create("Address", typeof(string), typeof(Pin), default(string)); + + // introduced to store the unique id for Android markers + + string _label; + + public string Address + { + get { return (string)GetValue(AddressProperty); } + set { SetValue(AddressProperty, value); } + } + + public string Label + { + get { return _label; } + set + { + if (_label == value) + return; + _label = value; + OnPropertyChanged(); + } + } + + public Position Position + { + get { return (Position)GetValue(PositionProperty); } + set { SetValue(PositionProperty, value); } + } + + public PinType Type + { + get { return (PinType)GetValue(TypeProperty); } + set { SetValue(TypeProperty, value); } + } + + internal object Id { get; set; } + + public event EventHandler Clicked; + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != GetType()) + return false; + return Equals((Pin)obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = Label?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ Position.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)Type; + hashCode = (hashCode * 397) ^ (Address?.GetHashCode() ?? 0); + return hashCode; + } + } + + public static bool operator ==(Pin left, Pin right) + { + return Equals(left, right); + } + + public static bool operator !=(Pin left, Pin right) + { + return !Equals(left, right); + } + + internal bool SendTap() + { + EventHandler handler = Clicked; + if (handler == null) + return false; + + handler(this, EventArgs.Empty); + return true; + } + + bool Equals(Pin other) + { + return string.Equals(Label, other.Label) && Equals(Position, other.Position) && Type == other.Type && string.Equals(Address, other.Address); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/PinType.cs b/Xamarin.Forms.Maps/PinType.cs new file mode 100644 index 00000000..a0b06fcf --- /dev/null +++ b/Xamarin.Forms.Maps/PinType.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Forms.Maps +{ + public enum PinType + { + Generic, + Place, + SavedPin, + SearchResult + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/Position.cs b/Xamarin.Forms.Maps/Position.cs new file mode 100644 index 00000000..17a86523 --- /dev/null +++ b/Xamarin.Forms.Maps/Position.cs @@ -0,0 +1,47 @@ +using System; + +namespace Xamarin.Forms.Maps +{ + public struct Position + { + public Position(double latitude, double longitude) + { + Latitude = Math.Min(Math.Max(latitude, -90.0), 90.0); + Longitude = Math.Min(Math.Max(longitude, -180.0), 180.0); + } + + public double Latitude { get; } + + public double Longitude { get; } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (obj.GetType() != GetType()) + return false; + var other = (Position)obj; + return Latitude == other.Latitude && Longitude == other.Longitude; + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = Latitude.GetHashCode(); + hashCode = (hashCode * 397) ^ Longitude.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(Position left, Position right) + { + return Equals(left, right); + } + + public static bool operator !=(Position left, Position right) + { + return !Equals(left, right); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/Properties/AssemblyInfo.cs b/Xamarin.Forms.Maps/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..09deb1af --- /dev/null +++ b/Xamarin.Forms.Maps/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using Xamarin.Forms; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("Xamarin.Forms.Maps")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS.Classic")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.Android")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.WP8")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.UWP")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.WinRT.Phone")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.WinRT.Tablet")] +[assembly: InternalsVisibleTo("iOSUnitTests")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Core.UnitTests")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Core.Android.UnitTests")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Core.WP8.UnitTests")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Xaml.UnitTests")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Xaml")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.Design")] +[assembly: Preserve]
\ No newline at end of file diff --git a/Xamarin.Forms.Maps/Xamarin.Forms.Maps.csproj b/Xamarin.Forms.Maps/Xamarin.Forms.Maps.csproj new file mode 100644 index 00000000..69667a33 --- /dev/null +++ b/Xamarin.Forms.Maps/Xamarin.Forms.Maps.csproj @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{7D13BAC2-C6A4-416A-B07E-C169B199E52B}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Xamarin.Forms.Maps</RootNamespace> + <AssemblyName>Xamarin.Forms.Maps</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <TargetFrameworkProfile>Profile259</TargetFrameworkProfile> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|AnyCPU'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\Turkey\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemGroup> + <!-- A reference to the entire .NET Framework is automatically included --> + <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj"> + <Project>{57b8b73d-c3b5-4c42-869e-7b2f17d354ac}</Project> + <Name>Xamarin.Forms.Core</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs"> + <Link>Properties\GlobalAssemblyInfo.cs</Link> + </Compile> + <Compile Include="Map.cs" /> + <Compile Include="Pin.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Distance.cs" /> + <Compile Include="MapSpan.cs" /> + <Compile Include="MapType.cs" /> + <Compile Include="PinType.cs" /> + <Compile Include="Position.cs" /> + <Compile Include="Geocoder.cs" /> + </ItemGroup> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file |