Подтвердить что ты не робот

Измените систему координат холста в WPF

Я пишу приложение сопоставления, в котором используется элемент Canvas для позиционирования. Для каждого элемента я должен программно преобразовать элемент Lat/Long в координату canvas, а затем установить свойства Canvas.Top и Canvas.Left.

Если бы у меня был холст 360x180, можно ли преобразовать координаты на холсте от -180 до 180, а не от 0 до 360 на оси X и от 90 до -90, а не от 0 до 180 по оси Y?

Требования к масштабированию:

  • Холст может быть любого размера, поэтому он должен работать, если он 360x180 или 5000x100.
  • Область Lat/Long может быть не всегда (-90, -180) x (90,180), это может быть что угодно (т.е. (5, -175) x (89, -174)).
  • Элементы, такие как PathGeometry, которые являются базовой базой, а не Canvas.Top/Left, должны работать.
4b9b3361

Ответ 1

Здесь решение all-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.

РЕДАКТИРОВАТЬ. Вы можете исправить проблему 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, вероятно, сделает трюк.

Ответ 2

Мне удалось получить его, создав собственный собственный холст и переопределив функцию 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. Это не сработает, потому что точки не определяются как верхние и левые, а как фактические точки.

Ответ 3

Я уверен, что вы не можете этого сделать точно, но было бы довольно тривиально иметь метод, который переводится с 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);
}

(или что-то в этом роде)

Ответ 4

Я предполагаю, что другой вариант заключается в расширении холста и переопределении меры/аранжировки, чтобы он вел себя так, как вы хотите.

Ответ 5

Вы можете использовать преобразование для перевода между системами координат, возможно, TransformGroup с TranslateTranform, чтобы переместить (0,0) в центр холста и ScaleTransform, чтобы получить координаты в правильном диапазоне.

С привязкой данных и, возможно, конвертером значений или двумя, вы можете автоматически преобразовать трансляции в зависимости от размера холста.

Преимущество этого заключается в том, что он будет работать для любого элемента (включая PathGeometry), возможным недостатком является то, что он будет масштабировать все, а не только точки, поэтому он изменит размер значков и текста на карте.

Ответ 6

Другое возможное решение:

Вставить пользовательский холст (рисование-холст) в другой холст (фоновый холст) и установить холст рисования, чтобы он был прозрачным и не привязывался к границам. Преобразуйте рисовальный холст с матрицей, которая делает y flip (M22 = -1) и переводит/масштабирует холст внутри родительского холста, чтобы просмотреть расширение мира, на который вы смотрите.

В действительности, если вы нарисуете на -115, 42 в холсте рисования, элемент, который вы рисуете, "выключен" на холсте, но все равно появляется, потому что холст не обрезается до границ. Затем вы трансформируете рисование в холст, чтобы точка отображалась в нужном месте на фоне холста.

Это то, что я скоро буду пытаться. Надеюсь, что это поможет.

Ответ 7

Здесь ответ, в котором описывается метод расширения 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/