Измените систему координат холста в 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, должны работать.
Ответы
Ответ 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/