2010-08-20 103 views
0

我必须创建一个包含具有与经度和纬度绑定的位置的对象的画布。更改画布的原点

我的画布支撑元件的拖动和缩放但是到现在为止我已经使用总是标准坐标(从0,0)

现在我有GPS COORDS和所有对象彼此重叠,因为GPS坐标为,例如45°之间11'00'N和45°11'60N ...所以基本上是1px的距离,如果我不能解决这个转换...帆布从0,0开始,我总是有45像素的空白空间

我可以检索最小左值和最大值并重新计算大小,但如何将所有对象放在画布中?

回答

0

是不是会在某个时候发表一篇关于这方面的博客文章,可能还会这样。但是 - 简而言之 - 我通过编写自己的小组来解决这个问题,该小组明白如何根据墨卡托投影定位儿童。

有些东西在使用它之前就知道:

  • 需要设置MaxLatitudeMaxLongitude到什么范围,你的背景图像支持。
  • 我不认为它支持双向更新。我打算在我的博客文章之前解决这个问题。

你使用这样的:

<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)); 
    } 
} 

如果我得到周围的博客文章,我会更新这个答案。

+0

我想根据我的模型测试你的代码,我也创建了MinLongitude和MaxLongitude,现在我正在寻找一种方法从儿童中获取这些值(因此面板不会比必要的大),但我无法在“渲染”工作流程中找到一个入口点,可以访问所有孩子并设置最大和最小经度和纬度 – 2010-08-20 13:51:37

0

您还需要显示您希望画布显示的纬度/经度最小值和最大值(可以从缩放级别计算出来,或者可以对它们进行硬编码)。然后你可以确定一个对象的相对坐标,例如:RelX =(ObjectLong-MinLong)/(MaxLong-MinLong)和RelY =(ObjectLat-MinLat)/(MaxLat-MinLat) 然后你根据它们的相对坐标在MeasureOverride()方法中。