mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-01-20 15:40:16 +01:00
Use Matrix for projection relative scale
This commit is contained in:
parent
2c9e478095
commit
ab155a26e7
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -27,11 +28,11 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
return new Point(
|
||||
return new Matrix(
|
||||
Math.Cos(Center.Latitude * Math.PI / 180d) / Math.Cos(latitude * Math.PI / 180d),
|
||||
1d);
|
||||
0d, 0d, 1d, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -25,7 +26,7 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
(var cosC, var _, var _) = GetPointValues(latitude, longitude);
|
||||
var k = 1d;
|
||||
|
|
@ -36,7 +37,7 @@ namespace MapControl
|
|||
k = c / Math.Sin(c); // p.195 (25-2)
|
||||
}
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -27,9 +28,9 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
return new Point(1d / Math.Cos(latitude * Math.PI / 180d), 1d);
|
||||
return new Matrix(1d / Math.Cos(latitude * Math.PI / 180d), 0d, 0d, 1d, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -25,12 +26,14 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
(var cosC, var _, var _) = GetPointValues(latitude, longitude);
|
||||
var h = 1d / (cosC * cosC); // p.165 (22-2)
|
||||
(var cosC, var x, var y) = GetPointValues(latitude, longitude);
|
||||
var k = 1d / cosC; // p.165 (22-3)
|
||||
var h = k * k; // p.165 (22-2)
|
||||
|
||||
return new Point(h, h); // TODO: rotate
|
||||
var scale = new Matrix(h, 0d, 0d, h, 0d, 0d);
|
||||
return scale;
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -177,37 +177,25 @@ namespace MapControl
|
|||
public ViewTransform ViewTransform { get; } = new ViewTransform();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map scale as horizontal and vertical scaling factors from meters to
|
||||
/// view coordinates at the specified geographic coordinates.
|
||||
/// Gets a transform Matrix from meters to view coordinates for scaling and rotating
|
||||
/// at the specified geographic coordinates.
|
||||
/// </summary>
|
||||
public Point GetMapScale(double latitude, double longitude)
|
||||
public Matrix GetMapToViewTransform(double latitude, double longitude)
|
||||
{
|
||||
var relativeScale = MapProjection.RelativeScale(latitude, longitude);
|
||||
var transform = MapProjection.RelativeScale(latitude, longitude);
|
||||
transform.Scale(ViewTransform.Scale, ViewTransform.Scale);
|
||||
transform.Rotate(ViewTransform.Rotation);
|
||||
|
||||
return new Point(ViewTransform.Scale * relativeScale.X, ViewTransform.Scale * relativeScale.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map scale as horizontal and vertical scaling factors from meters to
|
||||
/// view coordinates at the specified location.
|
||||
/// </summary>
|
||||
public Point GetMapScale(Location location)
|
||||
{
|
||||
return GetMapScale(location.Latitude, location.Longitude);
|
||||
return transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a transform Matrix from meters to view coordinates for scaling and rotating
|
||||
/// objects that are anchored at the specified Location.
|
||||
/// at the specified Location.
|
||||
/// </summary>
|
||||
public Matrix GetMapTransform(Location location)
|
||||
public Matrix GetMapToViewTransform(Location location)
|
||||
{
|
||||
var mapScale = GetMapScale(location.Latitude, location.Longitude);
|
||||
var transform = new Matrix(mapScale.X, 0d, 0d, mapScale.Y, 0d, 0d);
|
||||
|
||||
transform.Rotate(ViewTransform.Rotation);
|
||||
|
||||
return transform;
|
||||
return GetMapToViewTransform(location.Latitude, location.Longitude);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -75,12 +75,14 @@ namespace MapControl
|
|||
set => SetValue(FontSizeProperty, value);
|
||||
}
|
||||
|
||||
private double PixelPerDegree => Math.Max(1d, ParentMap.ViewTransform.Scale * MapProjection.Wgs84MeterPerDegree);
|
||||
|
||||
private double lineDistance;
|
||||
private string labelFormat;
|
||||
|
||||
private void SetLineDistance()
|
||||
{
|
||||
var minDistance = MinLineDistance / PixelPerLongitudeDegree(ParentMap.Center.Latitude, ParentMap.Center.Longitude);
|
||||
var minDistance = MinLineDistance / PixelPerDegree;
|
||||
var scale = minDistance < 1d / 60d ? 3600d : minDistance < 1d ? 60d : 1d;
|
||||
minDistance *= scale;
|
||||
|
||||
|
|
@ -98,14 +100,6 @@ namespace MapControl
|
|||
: lineDistance < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
|
||||
}
|
||||
|
||||
private double PixelPerLongitudeDegree(double latitude, double longitude)
|
||||
{
|
||||
var scale = ParentMap.GetMapScale(latitude, longitude);
|
||||
|
||||
return Math.Max(1d, // a reasonable lower limit
|
||||
scale.X * Math.Cos(latitude * Math.PI / 180d) * MapProjection.Wgs84MeterPerDegree);
|
||||
}
|
||||
|
||||
private string GetLabelText(double value, string hemispheres)
|
||||
{
|
||||
var hemisphere = hemispheres[0];
|
||||
|
|
@ -131,7 +125,7 @@ namespace MapControl
|
|||
{
|
||||
// Get rotation from second location with same latitude.
|
||||
//
|
||||
var pos = ParentMap.LocationToView(latitude, longitude + 10d / PixelPerLongitudeDegree(latitude, longitude));
|
||||
var pos = ParentMap.LocationToView(latitude, longitude + 10d / PixelPerDegree);
|
||||
|
||||
if (pos.HasValue)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ namespace MapControl
|
|||
{
|
||||
if (MapTransform != null && ParentMap != null && Location != null)
|
||||
{
|
||||
MapTransform.Matrix = ParentMap.GetMapTransform(Location);
|
||||
MapTransform.Matrix = ParentMap.GetMapToViewTransform(Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ namespace MapControl
|
|||
{
|
||||
if (ParentMap != null && Location != null && Data != null)
|
||||
{
|
||||
SetDataTransform(ParentMap.GetMapTransform(Location));
|
||||
SetDataTransform(ParentMap.GetMapToViewTransform(Location));
|
||||
}
|
||||
|
||||
MapPanel.SetLocation(this, Location);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -82,9 +83,10 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative map scale at the specified geographic coordinates.
|
||||
/// Gets the relative scale at the specified geographic coordinates.
|
||||
/// The returned Matrix represents the local distortion of the map projection.
|
||||
/// </summary>
|
||||
public virtual Point RelativeScale(double latitude, double longitude) => new Point(1d, 1d);
|
||||
public virtual Matrix RelativeScale(double latitude, double longitude) => new Matrix(1d, 0d, 0d, 1d, 0d, 0d);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms geographic coordinates to a Point in projected map coordinates.
|
||||
|
|
@ -101,7 +103,7 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Gets the relative map scale at the specified geographic Location.
|
||||
/// </summary>
|
||||
public Point RelativeScale(Location location) => RelativeScale(location.Latitude, location.Longitude);
|
||||
public Matrix RelativeScale(Location location) => RelativeScale(location.Latitude, location.Longitude);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Location in geographic coordinates to a Point in projected map coordinates.
|
||||
|
|
|
|||
|
|
@ -88,13 +88,13 @@ namespace MapControl
|
|||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
double scale;
|
||||
|
||||
if (ParentMap == null || (scale = ParentMap.GetMapScale(ParentMap.Center).X) <= 0d)
|
||||
if (ParentMap == null)
|
||||
{
|
||||
return new Size();
|
||||
}
|
||||
|
||||
var p = ParentMap.GetMapToViewTransform(ParentMap.Center).Transform(new Point(1d, 0d));
|
||||
var scale = Math.Sqrt(p.X * p.X + p.Y * p.Y);
|
||||
var length = MinWidth / scale;
|
||||
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,19 @@ namespace MapControl
|
|||
OffsetY += y;
|
||||
}
|
||||
|
||||
public void Scale(double scaleX, double scaleY)
|
||||
{
|
||||
// equivalent to Multiply(new Matrix(scaleX, 0, 0, scaleY, 0d, 0d));
|
||||
//
|
||||
SetMatrix(
|
||||
M11 * scaleX,
|
||||
M12 * scaleY,
|
||||
M21 * scaleX,
|
||||
M22 * scaleY,
|
||||
OffsetX * scaleX,
|
||||
OffsetY * scaleY);
|
||||
}
|
||||
|
||||
public void Rotate(double angle)
|
||||
{
|
||||
if (angle != 0d)
|
||||
|
|
@ -60,12 +73,12 @@ namespace MapControl
|
|||
// equivalent to Multiply(new Matrix(cos, sin, -sin, cos, 0d, 0d));
|
||||
//
|
||||
SetMatrix(
|
||||
cos * M11 - sin * M12,
|
||||
sin * M11 + cos * M12,
|
||||
cos * M21 - sin * M22,
|
||||
sin * M21 + cos * M22,
|
||||
cos * OffsetX - sin * OffsetY,
|
||||
sin * OffsetX + cos * OffsetY);
|
||||
M11 * cos - M12 * sin,
|
||||
M11 * sin + M12 * cos,
|
||||
M21 * cos - M22 * sin,
|
||||
M21 * sin + M22 * cos,
|
||||
OffsetX * cos - OffsetY * sin,
|
||||
OffsetX * sin + OffsetY * cos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -25,12 +26,13 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
(var cosC, var x, var y) = GetPointValues(latitude, longitude);
|
||||
var h = cosC; // p.149 (20-5)
|
||||
|
||||
return new Point(h, h);
|
||||
var scale = new Matrix(h, 0d, 0d, h, 0d, 0d);
|
||||
return scale;
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -26,7 +27,7 @@ namespace MapControl
|
|||
public double FalseNorthing { get; set; } = 2e6;
|
||||
public Hemisphere Hemisphere { get; set; }
|
||||
|
||||
public static double RelativeScale(Hemisphere hemisphere, double flattening, double scaleFactor, double latitude)
|
||||
public static double RelativeScale(Hemisphere hemisphere, double flattening, double latitude)
|
||||
{
|
||||
var sign = hemisphere == Hemisphere.North ? 1d : -1d;
|
||||
var phi = sign * latitude * Math.PI / 180d;
|
||||
|
|
@ -37,20 +38,18 @@ namespace MapControl
|
|||
var t = Math.Tan(Math.PI / 4d - phi / 2d)
|
||||
/ Math.Pow((1d - eSinPhi) / (1d + eSinPhi), e / 2d); // p.161 (15-9)
|
||||
|
||||
// r == ρ/a
|
||||
var r = 2d * scaleFactor * t
|
||||
/ Math.Sqrt(Math.Pow(1d + e, 1d + e) * Math.Pow(1d - e, 1d - e)); // p.161 (21-33)
|
||||
|
||||
// r == ρ/(a*k0), omit k0 for relative scale
|
||||
var r = 2d * t / Math.Sqrt(Math.Pow(1d + e, 1d + e) * Math.Pow(1d - e, 1d - e)); // p.161 (21-33)
|
||||
var m = Math.Cos(phi) / Math.Sqrt(1d - eSinPhi * eSinPhi); // p.160 (14-15)
|
||||
|
||||
return r / m; // p.161 (21-32)
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = RelativeScale(Hemisphere, Flattening, ScaleFactor, latitude);
|
||||
var k = RelativeScale(Hemisphere, Flattening, latitude);
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -25,12 +26,12 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
(var cosC, var _, var _) = GetPointValues(latitude, longitude);
|
||||
var k = 2d / (1d + cosC); // p.157 (21-4), k0 == 1
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -61,11 +61,6 @@ namespace MapControl
|
|||
Flattening = Wgs84Flattening;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
return new Point(ScaleFactor, ScaleFactor); // sufficiently precise
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
#if NETFRAMEWORK
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -26,11 +27,11 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = 1d / Math.Cos(latitude * Math.PI / 180d); // p.44 (7-3)
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
|
@ -28,11 +29,11 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = RelativeScale(latitude);
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
|
|
|
|||
|
|
@ -93,13 +93,6 @@ namespace MapControl.Projections
|
|||
|
||||
public MathTransform MapToLocationTransform { get; private set; }
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = CoordinateSystem?.Projection?.GetParameter("scale_factor")?.Value ?? 1d;
|
||||
|
||||
return new Point(k, k);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
if (LocationToMapTransform == null)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using ProjNet.CoordinateSystems;
|
||||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl.Projections
|
||||
|
|
@ -19,11 +17,11 @@ namespace MapControl.Projections
|
|||
CoordinateSystem = ProjectedCoordinateSystem.WebMercator;
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = 1d / Math.Cos(latitude * Math.PI / 180d); // p.44 (7-3)
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#if WPF
|
||||
using System.Windows;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl.Projections
|
||||
|
|
@ -23,11 +21,11 @@ namespace MapControl.Projections
|
|||
"AUTHORITY[\"EPSG\",\"32661\"]]";
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude);
|
||||
var k = PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, latitude);
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,11 +46,11 @@ namespace MapControl.Projections
|
|||
"AUTHORITY[\"EPSG\",\"32761\"]]";
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = PolarStereographicProjection.RelativeScale(Hemisphere.South, Wgs84Flattening, 0.994, latitude);
|
||||
var k = PolarStereographicProjection.RelativeScale(Hemisphere.South, Wgs84Flattening, latitude);
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#if WPF
|
||||
using System.Windows;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl.Projections
|
||||
|
|
@ -29,11 +27,11 @@ namespace MapControl.Projections
|
|||
"AUTHORITY[\"EPSG\",\"3395\"]]";
|
||||
}
|
||||
|
||||
public override Point RelativeScale(double latitude, double longitude)
|
||||
public override Matrix RelativeScale(double latitude, double longitude)
|
||||
{
|
||||
var k = MapControl.WorldMercatorProjection.RelativeScale(latitude);
|
||||
|
||||
return new Point(k, k);
|
||||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue