【问题标题】:Change the coordinate system of a Canvas in WPF在 WPF 中更改画布的坐标系
【发布时间】:2010-09-20 05:27:25
【问题描述】:

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

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

扩展要求:

  • 画布可以是任意大小,因此在 360x180 或 5000x100 的情况下仍然可以使用。
  • 纬度/经度区域可能并不总是 (-90,-180)x(90,180),它可以是任何值(即 (5,-175)x(89,-174))。
  • PathGeometry 等基于点而不是 Canvas.Top/Left 的元素需要工作。

【问题讨论】:

    标签: c# wpf xaml wpf-controls


    【解决方案1】:

    我很确定你不能完全做到这一点,但是有一个从 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);
    }
    

    (或类似的东西)

    【讨论】:

    • 我有那个功能,我只是希望不需要一直在画布上转换点。
    • 这样做确实具有使您的画布美观且可扩展的额外好处。
    【解决方案2】:

    我想另一种选择是扩展画布并覆盖度量/安排以使其按照您想要的方式运行。

    【讨论】:

    • 这更符合我的想法,但我不确定如何实现它。
    • 这真的不是太简单。这个想法是你覆盖 ArrangeOverride() 和 MeasureOverride()。
    【解决方案3】:

    我可以通过创建自己的自定义画布并像这样覆盖 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。它不起作用,因为这些点不是定义为顶部和左侧,而是定义为实际点。

    【讨论】:

      【解决方案4】:

      这是一个全 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 来解决背景问题。有点奇怪,但它有效。此外,我添加了一个 ScaleTransform 翻转 Y 轴,使正 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 可能会解决问题。

      【讨论】:

      • 这非常接近我的需要,但我认为这还不够好。如果坐标系是 Lat=(2, -74) Lon=(-180, -175) 或者 Canvas 的大小发生变化 (h=50,w=100),它将不起作用。此外,Background 属性不可用。
      • 各种变换是你想要的,但是找到它们的正确组合和顺序有点痛苦。
      • ...XAML 预览窗口通常不会按比例显示内容,这并没有使事情变得更容易。如果在预览窗口中应该是正确的东西消失了,您可能需要运行应用程序才能看到它并没有真正关闭。
      【解决方案5】:

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

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

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

      【讨论】:

        【解决方案6】:

        另一种可能的解决方案:

        在另一个画布(背景画布)中嵌入自定义画布(绘图画布),并将绘图画布设置为透明且不剪裁到边界。使用使 y 翻转 (M22 = -1) 并在父画布内平移/缩放画布以查看您正在查看的世界的范围的矩阵转换绘图画布。

        实际上,如果您在 draw-to 画布中的 -115、42 处绘制,则您正在绘制的项目“不在”画布上,但无论如何都会显示出来,因为画布没有剪裁到边界。然后,您转换绘制到的画布,使该点显示在背景画布上的正确位置。

        这是我很快就会尝试的事情。希望对您有所帮助。

        【讨论】:

        • 注意:我试过这个,但发现了一个奇怪的地方。如果您按比例放大直到一米大约为几十个像素,并且您不在绘制画布的实际 0,0 点附近,则移动绘制画布(通过变换或通过 Canvas.SetTop/SetLeft)似乎被量化。即使您将顶部/左侧或 M11/M22 更改为小的值,视觉绘图也不会移动,除非是“量化”值(不确定这些值是什么)。我想知道它是否以双精度进行计算,但将它们传递给一些转换为浮点数的较低级别的例程,从而“量化”大数字。
        【解决方案7】:

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

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

        将设置canvas 的坐标系,使x 从-10 变为10,y 从-10 变为10。

        【讨论】:

          【解决方案8】:

          我几乎有同样的问题。所以我上网了。这家伙使用矩阵将“设备像素”转换为他所谓的“世界坐标”,他指的是真实世界的数字而不是“设备像素”,请参阅链接

          http://csharphelper.com/blog/2014/09/use-transformations-draw-graph-wpf-c/

          【讨论】:

            猜你喜欢
            • 2017-12-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-13
            • 2023-03-28
            • 2020-06-09
            • 1970-01-01
            相关资源
            最近更新 更多