2010-08-20 93 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()方法中。