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

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

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

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

  • Размер холста может быть любым, поэтому он все равно будет работать, если он 360x180 или 5000x100.
  • Область широты / долготы не всегда может быть (-90, -180) x (90,180), это может быть что угодно (например, (5, -175) x (89, -174)).
  • Такие элементы, как PathGeometry, которые являются точечной базой, а не Canvas.Top/Left, должны работать.
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
0
22 562
8

Ответы 8

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

(Или что-то вдоль этих линий)

У меня есть эта функция, я просто надеялся, что мне не нужно постоянно конвертировать точки на холст и обратно.

Dylan 31.10.2008 19:48

Такой способ дает дополнительное преимущество - делает ваш холст красивым и масштабируемым.

MojoFilter 31.10.2008 20:13

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

Это больше похоже на то, о чем я думал, но я не уверен, как это реализовать.

Dylan 31.10.2008 19:51

Это не совсем так просто. Идея состоит в том, что вы переопределяете ArrangeOverride () и MeasureOverride ().

MojoFilter 31.10.2008 20:11

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

Вот решение, полностью использующее 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, вероятно, поможет.

Это довольно близко к тому, что мне нужно, но я не думаю, что это достаточно хорошо масштабируется. Если система координат была Lat = (2, -74) Lon = (- 180, -175) или если размер холста изменился (h = 50, w = 100), это не сработает. Кроме того, свойство Background невозможно использовать.

Dylan 01.11.2008 00:17

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

Ryan Lundy 01.11.2008 08:27

... и это не упрощается тем фактом, что в окне предварительного просмотра XAML часто не отображаются элементы для масштабирования. Если что-то, что должно быть правильным, не отображается в окне предварительного просмотра, вам, возможно, придется запустить приложение, чтобы убедиться, что на самом деле это не так.

Ryan Lundy 01.11.2008 08:30

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

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

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

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

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

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

Я скоро попробую это сделать. Надеюсь, это поможет.

Примечание: я пробовал это, но обнаружил одну странность. Если вы увеличиваете масштаб до тех пор, пока метр не составит примерно несколько десятков пикселей, и вы НЕ приблизитесь к фактической точке 0,0 на холсте для рисования, перемещение холста для рисования (через преобразование ИЛИ через Canvas.SetTop / SetLeft) кажется быть квантованным. Даже если вы измените либо верхнее / левое положение, либо M11 / M22 на небольшие значения, визуальный рисунок не перемещается, за исключением «квантованных» значений (не уверен, что это за значения). Мне интересно, выполняет ли он вычисления в двойном режиме, но передает их некоторой подпрограмме нижнего уровня, которая преобразует в числа с плавающей запятой, таким образом «квантуя» большие числа.

FTLPhysicsGuy 23.06.2010 23:31

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

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

установит систему координат canvas так, чтобы x шел от -10 до 10, а y - от -10 до 10.

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

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

Другие вопросы по теме