I'm writing a mapping app that uses a Canvas for positioning elements. For each element I have to programatically convert element's Lat/Long to the canvas' coordinate, then set the Canvas.Top and Canvas.Left properties.
If I had a 360x180 Canvas, can I convert the coordinates on the canvas to go from -180 to 180 rather than 0 to 360 on the X axis and 90 to -90 rather than 0 to 180 on the Y axis?
Scaling requirements:
Here's an all-XAML solution. Well, mostly XAML, because you have to have the IValueConverter in code. So: Create a new WPF project and add a class to it. The class is 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();
}
}
}
Then use this XAML for your Window. Now you should see the results right in your XAML preview window.
EDIT: You can fix the Background problem by putting your Canvas inside another Canvas. Kind of weird, but it works. In addition, I've added a ScaleTransform which flips the Y-axis so that positive Y is up and negative is down. Note carefully which Names go where:
<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>
As for your new requirements that you need varying ranges, a more complex ValueConverter would probably do the trick.
I was able to get it to by creating my own custom canvas and overriding the ArrangeOverride function like so:
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);
}
}
Which works for my described problem, but it probably would not work for another need I have, which is a PathGeometry. It wouldn't work because the points are not defined as Top and Left, but as actual points.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With