18

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

如果我有一个 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 的元素需要工作。
4

8 回答 8

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 预览窗口中看到结果。

编辑:您可以通过将画布放在另一个画布中来解决背景问题。有点奇怪,但它有效。此外,我添加了一个 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 可能会解决问题。

于 2008-10-31T19:59:57.200 回答
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。它不起作用,因为这些点没有被定义为顶部和左侧,而是作为实际点。

于 2008-10-31T19:54:04.323 回答
0

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

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

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

于 2008-11-02T09:52:00.170 回答
0

这是一个答案,它描述了Canvas一种允许您应用笛卡尔坐标系的扩展方法。IE:

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

将设置的坐标系从 -10 到 10 并canvas从-10 到 10。xy

于 2012-11-25T00:58:09.153 回答
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);
}

(或类似的规定)

于 2008-10-31T16:41:57.770 回答
0

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

于 2008-10-31T16:50:25.943 回答
0

另一种可能的解决方案:

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

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

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

于 2010-06-18T14:17:21.383 回答
0

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

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

于 2016-12-14T21:11:23.363 回答