diff --git a/MapControl/Avalonia/MapPath.Avalonia.cs b/MapControl/Avalonia/MapPath.Avalonia.cs index 1d3b4ebf..55262031 100644 --- a/MapControl/Avalonia/MapPath.Avalonia.cs +++ b/MapControl/Avalonia/MapPath.Avalonia.cs @@ -4,10 +4,6 @@ using Avalonia.Controls.Shapes; using Avalonia.Media; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; namespace MapControl { @@ -43,57 +39,5 @@ namespace MapControl InvalidateVisual(); } - - #region Methods used only by derived classes MapPolyline, MapPolygon and MapMultiPolygon - - protected void DataCollectionPropertyChanged(IEnumerable oldValue, IEnumerable newValue) - { - if (oldValue is INotifyCollectionChanged oldCollection) - { - oldCollection.CollectionChanged -= DataCollectionChanged; - } - - if (newValue is INotifyCollectionChanged newCollection) - { - newCollection.CollectionChanged += DataCollectionChanged; - } - - UpdateData(); - } - - protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - UpdateData(); - } - - protected void SetPathFigures(PathFigures pathFigures) - { - ((PathGeometry)Data).Figures = pathFigures; - - InvalidateGeometry(); - } - - protected void AddPolylinePoints(PathFigures pathFigures, IEnumerable locations, double longitudeOffset, bool closed) - { - if (locations.Count() >= 2) - { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); - - var figure = new PathFigure - { - StartPoint = points.First(), - IsClosed = closed, - IsFilled = true - }; - - figure.Segments.Add(new PolyLineSegment(points.Skip(1))); - pathFigures.Add(figure); - } - } - - #endregion } } diff --git a/MapControl/Avalonia/MapPolypoint.Avalonia.cs b/MapControl/Avalonia/MapPolypoint.Avalonia.cs new file mode 100644 index 00000000..ca2dcc65 --- /dev/null +++ b/MapControl/Avalonia/MapPolypoint.Avalonia.cs @@ -0,0 +1,90 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2024 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using Avalonia.Media; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace MapControl +{ + /// + /// Base class of MapPolyline and MapPolygon. + /// + public class MapPolypoint : MapPath + { + public static readonly StyledProperty FillRuleProperty = + DependencyPropertyHelper.Register(nameof(FillRule), FillRule.EvenOdd, + (polypoint, oldValue, newValue) => ((PathGeometry)polypoint.Data).FillRule = newValue); + + public FillRule FillRule + { + get => GetValue(FillRuleProperty); + set => SetValue(FillRuleProperty, value); + } + + protected MapPolypoint() + { + Data = new PathGeometry(); + } + + protected void DataCollectionPropertyChanged(IEnumerable oldValue, IEnumerable newValue) + { + if (oldValue is INotifyCollectionChanged oldCollection) + { + oldCollection.CollectionChanged -= DataCollectionChanged; + } + + if (newValue is INotifyCollectionChanged newCollection) + { + newCollection.CollectionChanged += DataCollectionChanged; + } + + UpdateData(); + } + + protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + UpdateData(); + } + + protected void UpdateData(IEnumerable locations, bool closed) + { + var pathFigures = new PathFigures(); + + if (ParentMap != null && locations != null) + { + var longitudeOffset = GetLongitudeOffset(Location ?? locations.FirstOrDefault()); + + AddPolylinePoints(pathFigures, locations, longitudeOffset, closed); + } + + ((PathGeometry)Data).Figures = pathFigures; + + InvalidateGeometry(); + } + + protected void AddPolylinePoints(PathFigures pathFigures, IEnumerable locations, double longitudeOffset, bool closed) + { + if (locations.Count() >= 2) + { + var points = locations + .Select(location => LocationToView(location, longitudeOffset)) + .Where(point => point.HasValue) + .Select(point => point.Value); + + var figure = new PathFigure + { + StartPoint = points.First(), + IsClosed = closed, + IsFilled = true + }; + + figure.Segments.Add(new PolyLineSegment(points.Skip(1))); + pathFigures.Add(figure); + } + } + } +} diff --git a/MapControl/Shared/MapPath.cs b/MapControl/Shared/MapPath.cs index 499c063f..c16a7ef6 100644 --- a/MapControl/Shared/MapPath.cs +++ b/MapControl/Shared/MapPath.cs @@ -2,8 +2,6 @@ // Copyright © 2024 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System.Collections.Generic; -using System.Linq; #if WPF using System.Windows; using System.Windows.Media; @@ -84,8 +82,6 @@ namespace MapControl MapPanel.SetLocation(this, Location); } - #region Methods used only by derived classes MapPolyline, MapPolygon and MapMultiPolygon - protected Point? LocationToMap(Location location, double longitudeOffset) { if (longitudeOffset != 0d) @@ -140,34 +136,5 @@ namespace MapControl return longitudeOffset; } - - protected PathFigureCollection GetPathFigures(IEnumerable locations, bool closed) - { - var pathFigures = new PathFigureCollection(); - - if (parentMap != null && locations != null) - { - var longitudeOffset = GetLongitudeOffset(Location ?? locations.FirstOrDefault()); - - AddPolylinePoints(pathFigures, locations, longitudeOffset, closed); - } - - return pathFigures; - } - - protected void AddMultiPolygonPoints(PathFigureCollection pathFigures, IEnumerable> polygons) - { - if (parentMap != null && polygons != null) - { - var longitudeOffset = GetLongitudeOffset(Location); - - foreach (var polygon in polygons) - { - AddPolylinePoints(pathFigures, polygon, longitudeOffset, true); - } - } - } - - #endregion } } diff --git a/MapControl/Shared/MapPolygon.cs b/MapControl/Shared/MapPolygon.cs index 5998190f..4449ebc0 100644 --- a/MapControl/Shared/MapPolygon.cs +++ b/MapControl/Shared/MapPolygon.cs @@ -5,15 +5,11 @@ using System.Collections.Generic; #if WPF using System.Windows; -using System.Windows.Media; #elif UWP using Windows.UI.Xaml; -using Windows.UI.Xaml.Media; #elif WINUI using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Media; #elif AVALONIA -using Avalonia.Media; using DependencyProperty = Avalonia.AvaloniaProperty; #endif @@ -22,16 +18,12 @@ namespace MapControl /// /// A polygon defined by a collection of Locations. /// - public class MapPolygon : MapPath + public class MapPolygon : MapPolypoint { public static readonly DependencyProperty LocationsProperty = DependencyPropertyHelper.Register>(nameof(Locations), null, (polygon, oldValue, newValue) => polygon.DataCollectionPropertyChanged(oldValue, newValue)); - public static readonly DependencyProperty FillRuleProperty = - DependencyPropertyHelper.Register(nameof(FillRule), FillRule.EvenOdd, - (polygon, oldValue, newValue) => ((PathGeometry)polygon.Data).FillRule = newValue); - /// /// Gets or sets the Locations that define the polygon points. /// @@ -44,20 +36,9 @@ namespace MapControl set => SetValue(LocationsProperty, value); } - public FillRule FillRule - { - get => (FillRule)GetValue(FillRuleProperty); - set => SetValue(FillRuleProperty, value); - } - - public MapPolygon() - { - Data = new PathGeometry(); - } - protected override void UpdateData() { - SetPathFigures(GetPathFigures(Locations, true)); + UpdateData(Locations, true); } } } diff --git a/MapControl/Shared/MapPolyline.cs b/MapControl/Shared/MapPolyline.cs index ccbcc9e3..ffb0b5a4 100644 --- a/MapControl/Shared/MapPolyline.cs +++ b/MapControl/Shared/MapPolyline.cs @@ -5,15 +5,11 @@ using System.Collections.Generic; #if WPF using System.Windows; -using System.Windows.Media; #elif UWP using Windows.UI.Xaml; -using Windows.UI.Xaml.Media; #elif WINUI using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Media; #elif AVALONIA -using Avalonia.Media; using DependencyProperty = Avalonia.AvaloniaProperty; #endif @@ -22,16 +18,12 @@ namespace MapControl /// /// A polyline defined by a collection of Locations. /// - public class MapPolyline : MapPath + public class MapPolyline : MapPolypoint { public static readonly DependencyProperty LocationsProperty = DependencyPropertyHelper.Register>(nameof(Locations), null, (polyline, oldValue, newValue) => polyline.DataCollectionPropertyChanged(oldValue, newValue)); - public static readonly DependencyProperty FillRuleProperty = - DependencyPropertyHelper.Register(nameof(FillRule), FillRule.EvenOdd, - (polyline, oldValue, newValue) => ((PathGeometry)polyline.Data).FillRule = newValue); - /// /// Gets or sets the Locations that define the polyline points. /// @@ -44,20 +36,9 @@ namespace MapControl set => SetValue(LocationsProperty, value); } - public FillRule FillRule - { - get => (FillRule)GetValue(FillRuleProperty); - set => SetValue(FillRuleProperty, value); - } - - public MapPolyline() - { - Data = new PathGeometry(); - } - protected override void UpdateData() { - SetPathFigures(GetPathFigures(Locations, true)); + UpdateData(Locations, false); } } } diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index fd9a25a5..9b28d840 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -260,6 +260,9 @@ MapPath.WinUI.cs + + MapPolypoint.WinUI.cs + Matrix.WinUI.cs diff --git a/MapControl/WPF/MapMultiPolygon.WPF.cs b/MapControl/WPF/MapMultiPolygon.WPF.cs index f7ad7ee8..fd9db3d6 100644 --- a/MapControl/WPF/MapMultiPolygon.WPF.cs +++ b/MapControl/WPF/MapMultiPolygon.WPF.cs @@ -4,28 +4,23 @@ using System.Collections.Generic; using System.Windows; -using System.Windows.Media; namespace MapControl { /// /// A multi-polygon defined by a collection of collections of Locations. - /// Allows to draw polygons with holes. + /// Allows to draw filled polygons with holes. /// /// A PolygonCollection (with ObservableCollection of Location elements) may be used /// for the Polygons property if collection changes of the property itself and its /// elements are both supposed to trigger UI updates. /// - public class MapMultiPolygon : MapPath + public class MapMultiPolygon : MapPolypoint { public static readonly DependencyProperty PolygonsProperty = DependencyPropertyHelper.Register>>(nameof(Polygons), null, (polygon, oldValue, newValue) => polygon.DataCollectionPropertyChanged(oldValue, newValue)); - public static readonly DependencyProperty FillRuleProperty = - DependencyPropertyHelper.Register(nameof(FillRule), FillRule.EvenOdd, - (polygon, oldValue, newValue) => ((PathGeometry)polygon.Data).FillRule = newValue); - /// /// Gets or sets the Locations that define the multi-polygon points. /// @@ -35,22 +30,9 @@ namespace MapControl set => SetValue(PolygonsProperty, value); } - public FillRule FillRule - { - get => (FillRule)GetValue(FillRuleProperty); - set => SetValue(FillRuleProperty, value); - } - - public MapMultiPolygon() - { - Data = new PathGeometry(); - } - protected override void UpdateData() { - var figures = ((PathGeometry)Data).Figures; - figures.Clear(); - AddMultiPolygonPoints(figures, Polygons); + UpdateData(Polygons); } } } diff --git a/MapControl/WPF/MapPath.WPF.cs b/MapControl/WPF/MapPath.WPF.cs index 27a383c0..f3419a0a 100644 --- a/MapControl/WPF/MapPath.WPF.cs +++ b/MapControl/WPF/MapPath.WPF.cs @@ -2,18 +2,13 @@ // Copyright © 2024 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; using System.Windows; using System.Windows.Media; using System.Windows.Shapes; namespace MapControl { - public partial class MapPath : Shape, IWeakEventListener + public partial class MapPath : Shape { public MapPath() { @@ -60,56 +55,5 @@ namespace MapControl Data.Transform = new MatrixTransform(matrix); } } - - #region Methods used only by derived classes MapPolyline, MapPolygon and MapMultiPolygon - - protected void DataCollectionPropertyChanged(IEnumerable oldValue, IEnumerable newValue) - { - if (oldValue is INotifyCollectionChanged oldCollection) - { - CollectionChangedEventManager.RemoveListener(oldCollection, this); - } - - if (newValue is INotifyCollectionChanged newCollection) - { - CollectionChangedEventManager.AddListener(newCollection, this); - } - - UpdateData(); - } - - bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) - { - UpdateData(); - return true; - } - - protected void SetPathFigures(PathFigureCollection pathFigures) - { - ((PathGeometry)Data).Figures = pathFigures; - } - - protected void AddPolylinePoints(PathFigureCollection pathFigures, IEnumerable locations, double longitudeOffset, bool closed) - { - if (locations.Count() >= 2) - { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); - - var figure = new PathFigure - { - StartPoint = points.First(), - IsClosed = closed, - IsFilled = true - }; - - figure.Segments.Add(new PolyLineSegment(points.Skip(1), true)); - pathFigures.Add(figure); - } - } - - #endregion } } diff --git a/MapControl/WPF/MapPolypoint.WPF.cs b/MapControl/WPF/MapPolypoint.WPF.cs new file mode 100644 index 00000000..258cf3a4 --- /dev/null +++ b/MapControl/WPF/MapPolypoint.WPF.cs @@ -0,0 +1,108 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2024 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Windows; +using System.Windows.Media; + +namespace MapControl +{ + /// + /// Base class of MapPolyline, MapPolygon and MapMultiPolygon. + /// + public class MapPolypoint : MapPath, IWeakEventListener + { + public static readonly DependencyProperty FillRuleProperty = + DependencyPropertyHelper.Register(nameof(FillRule), FillRule.EvenOdd, + (polypoint, oldValue, newValue) => ((PathGeometry)polypoint.Data).FillRule = newValue); + + public FillRule FillRule + { + get => (FillRule)GetValue(FillRuleProperty); + set => SetValue(FillRuleProperty, value); + } + + protected MapPolypoint() + { + Data = new PathGeometry(); + } + + protected void DataCollectionPropertyChanged(IEnumerable oldValue, IEnumerable newValue) + { + if (oldValue is INotifyCollectionChanged oldCollection) + { + CollectionChangedEventManager.RemoveListener(oldCollection, this); + } + + if (newValue is INotifyCollectionChanged newCollection) + { + CollectionChangedEventManager.AddListener(newCollection, this); + } + + UpdateData(); + } + + bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) + { + UpdateData(); + return true; + } + + protected void UpdateData(IEnumerable locations, bool closed) + { + var pathFigures = new PathFigureCollection(); + + if (ParentMap != null && locations != null) + { + var longitudeOffset = GetLongitudeOffset(Location ?? locations.FirstOrDefault()); + + AddPolylinePoints(pathFigures, locations, longitudeOffset, closed); + } + + ((PathGeometry)Data).Figures = pathFigures; + } + + protected void UpdateData(IEnumerable> polygons) + { + var pathFigures = new PathFigureCollection(); + + if (ParentMap != null && polygons != null) + { + var longitudeOffset = GetLongitudeOffset(Location); + + foreach (var polygon in polygons) + { + AddPolylinePoints(pathFigures, polygon, longitudeOffset, true); + } + } + + ((PathGeometry)Data).Figures = pathFigures; + } + + protected void AddPolylinePoints(PathFigureCollection pathFigures, IEnumerable locations, double longitudeOffset, bool closed) + { + if (locations.Count() >= 2) + { + var points = locations + .Select(location => LocationToView(location, longitudeOffset)) + .Where(point => point.HasValue) + .Select(point => point.Value); + + var figure = new PathFigure + { + StartPoint = points.First(), + IsClosed = closed, + IsFilled = true + }; + + figure.Segments.Add(new PolyLineSegment(points.Skip(1), true)); + pathFigures.Add(figure); + } + } + } +} diff --git a/MapControl/WinUI/MapPath.WinUI.cs b/MapControl/WinUI/MapPath.WinUI.cs index 98a9135a..0e785b46 100644 --- a/MapControl/WinUI/MapPath.WinUI.cs +++ b/MapControl/WinUI/MapPath.WinUI.cs @@ -2,10 +2,6 @@ // Copyright © 2024 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; #if UWP using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Shapes; @@ -35,109 +31,5 @@ namespace MapControl Data.Transform = new MatrixTransform { Matrix = matrix }; } } - - #region Methods used only by derived classes MapPolyline and MapPolygon - - protected void DataCollectionPropertyChanged(IEnumerable oldValue, IEnumerable newValue) - { - if (oldValue is INotifyCollectionChanged oldCollection) - { - oldCollection.CollectionChanged -= DataCollectionChanged; - } - - if (newValue is INotifyCollectionChanged newCollection) - { - newCollection.CollectionChanged += DataCollectionChanged; - } - - UpdateData(); - } - - protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - UpdateData(); - } - - protected void SetPathFigures(PathFigureCollection pathFigures) - { - ((PathGeometry)Data).Figures = pathFigures; - } - - protected void AddPolylinePoints(PathFigureCollection pathFigures, IEnumerable locations, double longitudeOffset, bool closed) - { - if (locations.Count() >= 2) - { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); - - if (closed) - { - var segment = new PolyLineSegment(); - - foreach (var point in points.Skip(1)) - { - segment.Points.Add(point); - } - - var figure = new PathFigure - { - StartPoint = points.First(), - IsClosed = closed, - IsFilled = true - }; - - figure.Segments.Add(segment); - pathFigures.Add(figure); - } - else - { - var pointList = points.ToList(); - - if (closed) - { - pointList.Add(pointList[0]); - } - - var viewport = new Rect(0, 0, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height); - PathFigure figure = null; - PolyLineSegment segment = null; - - for (int i = 1; i < pointList.Count; i++) - { - var p1 = pointList[i - 1]; - var p2 = pointList[i]; - var inside = Intersections.GetIntersections(ref p1, ref p2, viewport); - - if (inside) - { - if (figure == null) - { - figure = new PathFigure - { - StartPoint = p1, - IsClosed = false, - IsFilled = true - }; - - segment = new PolyLineSegment(); - figure.Segments.Add(segment); - pathFigures.Add(figure); - } - - segment.Points.Add(p2); - } - - if (!inside || p2 != pointList[i]) - { - figure = null; - } - } - } - } - } - -#endregion } } diff --git a/MapControl/WinUI/MapPolypoint.WinUI.cs b/MapControl/WinUI/MapPolypoint.WinUI.cs new file mode 100644 index 00000000..7f58f542 --- /dev/null +++ b/MapControl/WinUI/MapPolypoint.WinUI.cs @@ -0,0 +1,148 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2024 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +#if UWP +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +#else +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; +#endif + +namespace MapControl +{ + /// + /// Base class of MapPolyline and MapPolygon. + /// + public class MapPolypoint : MapPath + { + public static readonly DependencyProperty FillRuleProperty = + DependencyPropertyHelper.Register(nameof(FillRule), FillRule.EvenOdd, + (polypoint, oldValue, newValue) => ((PathGeometry)polypoint.Data).FillRule = newValue); + + public FillRule FillRule + { + get => (FillRule)GetValue(FillRuleProperty); + set => SetValue(FillRuleProperty, value); + } + + protected MapPolypoint() + { + Data = new PathGeometry(); + } + + protected void DataCollectionPropertyChanged(IEnumerable oldValue, IEnumerable newValue) + { + if (oldValue is INotifyCollectionChanged oldCollection) + { + oldCollection.CollectionChanged -= DataCollectionChanged; + } + + if (newValue is INotifyCollectionChanged newCollection) + { + newCollection.CollectionChanged += DataCollectionChanged; + } + + UpdateData(); + } + + protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + UpdateData(); + } + + protected void UpdateData(IEnumerable locations, bool closed) + { + var pathFigures = new PathFigureCollection(); + + if (ParentMap != null && locations != null) + { + var longitudeOffset = GetLongitudeOffset(Location ?? locations.FirstOrDefault()); + + AddPolylinePoints(pathFigures, locations, longitudeOffset, closed); + } + + ((PathGeometry)Data).Figures = pathFigures; + } + + protected void AddPolylinePoints(PathFigureCollection pathFigures, IEnumerable locations, double longitudeOffset, bool closed) + { + if (locations.Count() >= 2) + { + var points = locations + .Select(location => LocationToView(location, longitudeOffset)) + .Where(point => point.HasValue) + .Select(point => point.Value); + + if (closed) + { + var segment = new PolyLineSegment(); + + foreach (var point in points.Skip(1)) + { + segment.Points.Add(point); + } + + var figure = new PathFigure + { + StartPoint = points.First(), + IsClosed = closed, + IsFilled = true + }; + + figure.Segments.Add(segment); + pathFigures.Add(figure); + } + else + { + var pointList = points.ToList(); + + if (closed) + { + pointList.Add(pointList[0]); + } + + var viewport = new Rect(0, 0, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height); + PathFigure figure = null; + PolyLineSegment segment = null; + + for (int i = 1; i < pointList.Count; i++) + { + var p1 = pointList[i - 1]; + var p2 = pointList[i]; + var inside = Intersections.GetIntersections(ref p1, ref p2, viewport); + + if (inside) + { + if (figure == null) + { + figure = new PathFigure + { + StartPoint = p1, + IsClosed = false, + IsFilled = true + }; + + segment = new PolyLineSegment(); + figure.Segments.Add(segment); + pathFigures.Add(figure); + } + + segment.Points.Add(p2); + } + + if (!inside || p2 != pointList[i]) + { + figure = null; + } + } + } + } + } + } +}