2008-10-31 338 views
17

我正在编写一个使用Canvas定位元素的地图应用程序。对于每个元素,我都必须以编程方式将元素的纬/长转换为画布的坐标,然后设置Canvas.Top和Canvas.Left属性。更改WPF中Canvas的坐标系

如果我有一个360x180画布,我可以将画布上的坐标转换为X轴上的-180到180而不是0到360,Y轴上的90到90而不是0到180?

缩放要求:

  • 画布可以是任意大小,所以如果它是360x180或5000x100应该仍然工作。 Lat/Long区域可能并不总是(-90,-180)x(90,180),它可以是任何东西(即(5,-175)x(89,-174))。
  • 诸如PathGeometry之类的元素是基于点的,而不是基于Canvas.Top/Left的元素需要工作。

回答

0

我敢肯定,你不能那样做,但它有一个从lat/long转换为Canvas坐标的方法非常简单。

Point ToCanvas(double lat, double lon) { 
    double x = ((lon * myCanvas.ActualWidth)/360.0) - 180.0; 
    double y = ((lat * myCanvas.ActualHeight)/180.0) - 90.0; 
    return new Point(x,y); 
} 

(或者类似的规定)

+0

我有一个功能,我只是希望不需要始终将画布点和画布之间进行转换。 – Dylan 2008-10-31 16:48:39

+0

这样做确实具有使画布更加美观和可伸缩的优点。 – MojoFilter 2008-10-31 17:13:57

0

我想另一个办法是延长帆布和覆盖措施/安排,使之表现,你想要的方式。

+0

这更符合我的想法,但我不知道如何实现它。 – Dylan 2008-10-31 16:51:53

+0

这不是太简单。这个想法是你重写ArrangeOverride()和MeasureOverride()。 – MojoFilter 2008-10-31 17:11:31

1

我能够通过创建自己的自定义帆布和重写ArrangeOverride功能,像这样得到它:

public class CustomCanvas : Canvas 
    { 
     protected override Size ArrangeOverride(Size arrangeSize) 
     { 
      foreach (UIElement child in InternalChildren) 
      { 
       double left = Canvas.GetLeft(child); 
       double top = Canvas.GetTop(child); 
       Point canvasPoint = ToCanvas(top, left); 
       child.Arrange(new Rect(canvasPoint, child.DesiredSize)); 
      } 
      return arrangeSize; 
     } 
     Point ToCanvas(double lat, double lon) 
     { 
      double x = this.Width/360; 
      x *= (lon - -180); 
      double y = this.Height/180; 
      y *= -(lat + -90); 
      return new Point(x, y); 
     } 
    } 

这对我的描述问题的作品,但它可能不会再需要我有工作,这是一个PathGeometry。它不起作用,因为这些点不是定义为Top和Left,而是定义为实际点。

6

这是一个全XAML解决方案。那么,主要是XAML,因为你必须在代码中使用IValueConverter。所以:创建一个新的WPF项目并向它添加一个类。该类是MultiplyConverter:

namespace YourProject 
{ 
    public class MultiplyConverter : System.Windows.Data.IValueConverter 
    { 
     public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      return AsDouble(value)* AsDouble(parameter); 
     } 
     double AsDouble(object value) 
     { 
      var valueText = value as string; 
      if (valueText != null) 
       return double.Parse(valueText); 
      else 
       return (double)value; 
     } 

     public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new System.NotSupportedException(); 
     } 
    } 
} 

然后使用此XAML为您的窗口。现在,您应该可以在XAML预览窗口中看到结果。

编辑:您可以通过将Canvas放入另一个Canvas中来解决Background问题。有点奇怪,但它的作品。另外,我添加了一个ScaleTransform,它可以翻转Y轴,这样正Y就会上升,负值会下降。仔细注意哪些名字去的地方:

<Canvas Name="canvas" Background="Moccasin"> 
    <Canvas Name="innerCanvas"> 
     <Canvas.RenderTransform> 
      <TransformGroup> 
       <TranslateTransform x:Name="translate"> 
        <TranslateTransform.X> 
         <Binding ElementName="canvas" Path="ActualWidth" 
           Converter="{StaticResource multiplyConverter}" ConverterParameter="0.5" /> 
        </TranslateTransform.X> 
        <TranslateTransform.Y> 
         <Binding ElementName="canvas" Path="ActualHeight" 
           Converter="{StaticResource multiplyConverter}" ConverterParameter="0.5" /> 
        </TranslateTransform.Y> 
       </TranslateTransform> 
       <ScaleTransform ScaleX="1" ScaleY="-1" CenterX="{Binding ElementName=translate,Path=X}" 
         CenterY="{Binding ElementName=translate,Path=Y}" /> 
      </TransformGroup> 
     </Canvas.RenderTransform> 
     <Rectangle Canvas.Top="-50" Canvas.Left="-50" Height="100" Width="200" Fill="Blue" /> 
     <Rectangle Canvas.Top="0" Canvas.Left="0" Height="200" Width="100" Fill="Green" /> 
     <Rectangle Canvas.Top="-25" Canvas.Left="-25" Height="50" Width="50" Fill="HotPink" /> 
    </Canvas> 
</Canvas> 

至于你需要改变你的范围内的新要求,更复杂的ValueConverter可能会做的伎俩。

0

您可以使用transform在坐标系之间进行转换,也可以使用TranslateTranform的TransformGroup将(0,0)移动到画布的中心,并使用ScaleTransform将坐标移至正确的范围。

通过数据绑定和一个或两个值转换器,您可以根据画布大小自动更新变换。

这样做的好处是,它可以用于任何元素(包括PathGeometry),可能的缺点是它可以缩放一切,而不仅仅是点 - 因此它会改变地图上图标和文本的大小。

0

另一种可能的解决方案:

嵌入自定义画布(拉伸到帆布)在另一个画布(背景画布),并设置拉伸到帆布,以便它是透明的,不会夹到界限。用一个使y翻转的矩阵(M22 = -1)转换绘制到画布,并翻译/缩放父画布内的画布以查看您正在查看的世界的范围。

实际上,如果您在绘制画布上绘制-115,42,则您绘制的项目将“关闭”画布,但无论如何都会显示,因为画布未裁剪到边界。然后,您将绘制到画布转换为在背景画布上的正确位置显示该点。

这是我很快就会尝试自己的事情。希望能帮助到你。

0

这是an answer其中描述了一个Canvas扩展方法,允许您应用笛卡尔坐标系。即:

canvas.SetCoordinateSystem(-10, 10, -10, 10) 

将设置的canvas的坐标系,使x去从-10到10 y去从-10到10