Изменить систему координат холста в WPF - PullRequest
18 голосов
/ 31 октября 2008

Я пишу картографическое приложение, которое использует 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.

Ответы [ 8 ]

6 голосов
/ 31 октября 2008

Вот решение для всех 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 вверх, а отрицательный вниз. Обратите внимание, какие имена идут куда:

<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, вероятно, сработает.

1 голос
/ 31 октября 2008

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

0 голосов
/ 15 декабря 2016

У меня почти такая же проблема. так что я вышел в интернет. и этот парень использует матрицу для преобразования из «пикселя устройства» в то, что он называет «мировыми координатами», и под этим он подразумевает числа реального мира вместо «пикселей устройства».

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

0 голосов
/ 25 ноября 2012

Вот ответ , который описывает метод расширения Canvas, который позволяет применять декартову систему координат. I.e.:

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

установит систему координат canvas, так что x перейдет от -10 до 10, а y перейдет от -10 до 10.

0 голосов
/ 18 июня 2010

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

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

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

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

0 голосов
/ 02 ноября 2008

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

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

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

0 голосов
/ 31 октября 2008

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

0 голосов
/ 31 октября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...