Изменить происхождение холста - PullRequest
0 голосов
/ 20 августа 2010

Мне нужно создать холст, содержащий объект с положением в привязке по широте и долготе.

Мой холст поддерживает перетаскивание и масштабирование элемента, но до сих пор я всегда использовал стандартную координату (от 0,0)

Теперь у меня есть GPS-координаты, и все объекты перекрывают друг друга, потому что GPS-координаты находятся, например, между 45 ° 11'00'N и 45 ° 11'60N ... так что в основном это 1px расстояния, если я не могу решить преобразование ... также холст начинается с 0,0, и у меня всегда есть 45 пикселей пробела

Я могу получить минимальное левое и верхнее значения и пересчитать размер, но как центрировать все объекты на холсте?

Ответы [ 2 ]

0 голосов
/ 20 августа 2010

Вам также нужно иметь значения минимальной и максимальной широт / длин, которые вы хотите, чтобы холст отображал (которые можно вычислить по уровню масштабирования или жестко их кодировать).Затем вы можете определить относительные координаты объекта, например: RelX = (ObjectLong-MinLong) / (MaxLong-MinLong) и RelY = (ObjectLat-MinLat) / (MaxLat-MinLat). Затем вы размещаете дочерние элементы холста на основе ихотносительные координаты в методе MeasureOverride ().

0 голосов
/ 20 августа 2010

В какой-то момент собирался сделать сообщение в блоге об этом и, возможно, все еще буду.Но, короче говоря, я решил эту проблему, просто написав свою собственную панель, которая понимает, как расположить детей в соответствии с проекцией меркатора.

Некоторые вещи, которые необходимо знать перед его использованием:

  • вам нужно установить MaxLatitude и MaxLongitude на любой диапазон, поддерживаемый вашим фоновым изображением.
  • Я не думаю, что он поддерживает обновления в обоих направлениях.Я планировал исправить это до моего сообщения в блоге.

Вы используете это так:

<ListBox ItemsSource="{Binding YourItems}">
    <ListBox.Template>
        <ControlTemplate>
            <Border BorderBrush="Black" BorderThickness="1" Background="#CEE3FF">
                <Grid>
                    <Grid.Resources>
                        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}">Transparent</SolidColorBrush>
                        <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}">Transparent</SolidColorBrush>
                    </Grid.Resources>
                    <Image x:Name="mapImage" Source="YourMap.png"/>
                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" Width="{Binding ActualWidth, ElementName=mapImage}" Height="{Binding ActualHeight, ElementName=mapImage}" />
                </Grid>
            </Border>
        </ControlTemplate>
    </ListBox.Template>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- make sure you set these values in line with YourMap.png -->
            <controls:MercatorProjectionPanel MaxLatitude="81" MinLatitude="-74"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="controls:MercatorProjectionPanel.Longitude" Value="{Binding Location.Longitude}"/>
            <Setter Property="controls:MercatorProjectionPanel.Latitude" Value="{Binding Location.Latitude}"/>
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock>Here's your item</TextBlock>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Вот код:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

/// <summary>
/// Implements a panel that lays out children according to the mercator projection.
/// </summary>
public class MercatorProjectionPanel : Panel
{
    /// <summary>
    /// Identifies the <see cref="MinLatitude"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty MinLatitudeProperty = DependencyProperty.Register(
        "MinLatitude",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(DefaultMinLatitude, OnLatitudeRangeChanged));

    /// <summary>
    /// Identifies the <see cref="MaxLatitude"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty MaxLatitudeProperty = DependencyProperty.Register(
        "MaxLatitude",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(DefaultMaxLatitude, OnLatitudeRangeChanged));

    /// <summary>
    /// Identifies the <c>Longitude</c> attached dependency property.
    /// </summary>
    public static readonly DependencyProperty LongitudeProperty = DependencyProperty.RegisterAttached(
        "Longitude",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(double.NaN, OnGeographicalCoordinateChanged));

    /// <summary>
    /// Identifies the <c>Latitude</c> attached dependency property.
    /// </summary>
    public static readonly DependencyProperty LatitudeProperty = DependencyProperty.RegisterAttached(
        "Latitude",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(double.NaN, OnGeographicalCoordinateChanged));

    /// <summary>
    /// Identifies the <c>Left</c> attached dependency property.
    /// </summary>
    public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached(
        "Left",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(double.NaN, OnCoordinateChanged));

    /// <summary>
    /// Identifies the <c>Top</c> attached dependency property.
    /// </summary>
    public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached(
        "Top",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(double.NaN, OnCoordinateChanged));

    private static readonly DependencyProperty XRatioProperty = DependencyProperty.RegisterAttached(
        "XRatio",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(double.NaN));

    private static readonly DependencyProperty YRatioProperty = DependencyProperty.RegisterAttached(
        "YRatio",
        typeof(double),
        typeof(MercatorProjectionPanel),
        new FrameworkPropertyMetadata(double.NaN));

    private const double DefaultMinLatitude = -80;
    private const double DefaultMaxLatitude = 80;
    private const double DegreesPerRadian = 57.2957;

    private double minY = CalculateYRelative(DefaultMinLatitude);
    private double maxY = CalculateYRelative(DefaultMaxLatitude);

    /// <summary>
    /// Initializes a new instance of the MercatorProjectionPanel class.
    /// </summary>
    public MercatorProjectionPanel()
    {
        SizeChanged += delegate
        {
            InvalidateArrange();
        };
    }

    /// <summary>
    /// Gets or sets the minimum latitude displayed by this mercator projection panel.
    /// </summary>
    public double MinLatitude
    {
        get { return (double)GetValue(MinLatitudeProperty); }
        set { SetValue(MinLatitudeProperty, value); }
    }

    /// <summary>
    /// Gets or sets the maximum latitude displayed by this mercator projection panel.
    /// </summary>
    public double MaxLatitude
    {
        get { return (double)GetValue(MaxLatitudeProperty); }
        set { SetValue(MaxLatitudeProperty, value); }
    }

    /// <summary>
    /// Gets the longitude for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <returns>
    /// The longitude.
    /// </returns>
    public static double GetLongitude(DependencyObject dependencyObject)
    {
        return (double)dependencyObject.GetValue(LongitudeProperty);
    }

    /// <summary>
    /// Sets the longitude for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <param name="longitude">
    /// The longitude.
    /// </param>
    public static void SetLongitude(DependencyObject dependencyObject, double longitude)
    {
        dependencyObject.SetValue(LongitudeProperty, longitude);
    }

    /// <summary>
    /// Gets the latitude for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <returns>
    /// The latitude.
    /// </returns>
    public static double GetLatitude(DependencyObject dependencyObject)
    {
        return (double)dependencyObject.GetValue(LatitudeProperty);
    }

    /// <summary>
    /// Sets the latitude for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <param name="latitude">
    /// The latitude.
    /// </param>
    public static void SetLatitude(DependencyObject dependencyObject, double latitude)
    {
        dependencyObject.SetValue(LatitudeProperty, latitude);
    }

    /// <summary>
    /// Gets the left offset for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <returns>
    /// The left offset.
    /// </returns>
    public static double GetLeft(DependencyObject dependencyObject)
    {
        return (double)dependencyObject.GetValue(LeftProperty);
    }

    /// <summary>
    /// Sets the left offset for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <param name="left">
    /// The left offset.
    /// </param>
    public static void SetLeft(DependencyObject dependencyObject, double left)
    {
        dependencyObject.SetValue(LeftProperty, left);
    }

    /// <summary>
    /// Gets the top offset for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <returns>
    /// The top offset.
    /// </returns>
    public static double GetTop(DependencyObject dependencyObject)
    {
        return (double)dependencyObject.GetValue(TopProperty);
    }

    /// <summary>
    /// Sets the top offset for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <param name="top">
    /// The top offset.
    /// </param>
    public static void SetTop(DependencyObject dependencyObject, double top)
    {
        dependencyObject.SetValue(TopProperty, top);
    }

    /// <summary>
    /// Gets the horizontal alignment for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <returns>
    /// The horizontal alignment.
    /// </returns>
    public static HorizontalAlignment GetHorizontalAlignment(DependencyObject dependencyObject)
    {
        return (HorizontalAlignment)dependencyObject.GetValue(HorizontalAlignmentProperty);
    }

    /// <summary>
    /// Sets the horizontal alignment for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <param name="horizontalAlignment">
    /// The horizontal alignment.
    /// </param>
    public static void SetHorizontalAlignment(DependencyObject dependencyObject, HorizontalAlignment horizontalAlignment)
    {
        dependencyObject.SetValue(HorizontalAlignmentProperty, horizontalAlignment);
    }

    /// <summary>
    /// Gets the vertical alignment for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <returns>
    /// The vertical alignment.
    /// </returns>
    public static VerticalAlignment GetVerticalAlignment(DependencyObject dependencyObject)
    {
        return (VerticalAlignment)dependencyObject.GetValue(VerticalAlignmentProperty);
    }

    /// <summary>
    /// Sets the vertical alignment for a specified dependency object.
    /// </summary>
    /// <param name="dependencyObject">
    /// The dependency object.
    /// </param>
    /// <param name="verticalAlignment">
    /// The vertical alignment.
    /// </param>
    public static void SetVerticalAlignment(DependencyObject dependencyObject, VerticalAlignment verticalAlignment)
    {
        dependencyObject.SetValue(VerticalAlignmentProperty, verticalAlignment);
    }

    /// <summary>
    /// Measures all child controls, imposing no restrictions on their size.
    /// </summary>
    /// <param name="availableSize">
    /// The available size.
    /// </param>
    /// <returns>
    /// The measured size.
    /// </returns>
    protected override Size MeasureOverride(Size availableSize)
    {
        availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);

        foreach (UIElement child in this.InternalChildren)
        {
            if (child != null)
            {
                child.Measure(availableSize);
            }
        }

        return new Size();
    }

    /// <summary>
    /// Arranges all child controls.
    /// </summary>
    /// <param name="finalSize">
    /// The final size.
    /// </param>
    /// <returns>
    /// The size of the content.
    /// </returns>
    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (FrameworkElement child in this.InternalChildren)
        {
            if (child == null)
            {
                continue;
            }

            var xRatio = GetXRatio(child);
            var yRatio = GetYRatio(child);
            var x = xRatio * ActualWidth;
            var y = yRatio * ActualHeight;

            switch (child.HorizontalAlignment)
            {
                case HorizontalAlignment.Center:
                    x -= child.DesiredSize.Width / 2;
                    break;
                case HorizontalAlignment.Right:
                    x -= child.DesiredSize.Width;
                    break;
            }

            switch (child.VerticalAlignment)
            {
                case VerticalAlignment.Center:
                    y -= child.DesiredSize.Height / 2;
                    break;
                case VerticalAlignment.Bottom:
                    y -= child.DesiredSize.Height;
                    break;
            }

            child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
        }

        return finalSize;
    }

    private static double GetXRatio(DependencyObject dependencyObject)
    {
        return (double)dependencyObject.GetValue(XRatioProperty);
    }

    private static void SetXRatio(DependencyObject dependencyObject, double xRatio)
    {
        dependencyObject.SetValue(XRatioProperty, xRatio);
    }

    private static double GetYRatio(DependencyObject dependencyObject)
    {
        return (double)dependencyObject.GetValue(YRatioProperty);
    }

    private static void SetYRatio(DependencyObject dependencyObject, double yRatio)
    {
        dependencyObject.SetValue(YRatioProperty, yRatio);
    }

    private static double CalculateYRelative(double latitude)
    {
        return Math.Log(Math.Tan(((latitude / 360d) * Math.PI) + (Math.PI / 4)));
    }

    private static void OnLatitudeRangeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var reference = dependencyObject as MercatorProjectionPanel;

        if (reference != null)
        {
            reference.minY = CalculateYRelative(reference.MinLatitude);
            reference.maxY = CalculateYRelative(reference.MaxLatitude);
        }
    }

    private static void OnGeographicalCoordinateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var reference = dependencyObject as FrameworkElement;

        if (reference != null)
        {
            var parent = VisualTreeHelper.GetParent(reference) as MercatorProjectionPanel;

            if (parent != null)
            {
                SetLeft(reference, ConvertLongitudeToX(parent, GetLongitude(reference)));
                SetTop(reference, ConvertLatitudeToY(parent, GetLatitude(reference)));

                parent.InvalidateArrange();
            }
        }
    }

    private static void OnCoordinateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var reference = dependencyObject as FrameworkElement;

        if (reference != null)
        {
            var parent = VisualTreeHelper.GetParent(reference) as MercatorProjectionPanel;

            if (parent != null)
            {
                ////SetLongitude(reference, ConvertXToLongitude(parent, GetLeft(reference)));
                ////SetLatitude(reference, ConvertYToLatitude(parent, GetTop(reference)));

                ////parent.InvalidateArrange();

                var left = GetLeft(reference);
                var top = GetTop(reference);

                SetXRatio(reference, left / parent.ActualWidth);
                SetYRatio(reference, top / parent.ActualHeight);
            }
        }
    }

    private static double ConvertXToLongitude(MercatorProjectionPanel panel, double left)
    {
        return ((left / panel.ActualWidth) * 360) - 180;
    }

    private static double ConvertYToLatitude(MercatorProjectionPanel panel, double top)
    {
        var input = panel.maxY - ((top / panel.ActualHeight) * (panel.maxY - panel.minY));
        return Math.Atan(Math.Sinh(input)) * DegreesPerRadian;
    }

    private static double ConvertLongitudeToX(MercatorProjectionPanel panel, double longitude)
    {
        return ((longitude + 180) / 360) * panel.ActualWidth;
    }

    private static double ConvertLatitudeToY(MercatorProjectionPanel panel, double latitude)
    {
        return panel.ActualHeight - (panel.ActualHeight * (CalculateYRelative(latitude) - panel.minY) / (panel.maxY - panel.minY));
    }
}

ЕслиЯ когда-нибудь доберусь до поста в блоге, я обновлю этот ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...