Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 1015053
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 16, 20262026-05-16T10:20:39+00:00 2026-05-16T10:20:39+00:00

I have to create a canvas that contains object with position in binding with

  • 0

I have to create a canvas that contains object with position in binding with latitude and longitude.

My canvas support element’s drag and zooming but until now I have used always standard coordinate (from 0,0)

Now I have GPS coords and all objects overlap each other because GPS coord is for example between 45°11’00’N and 45°11’60N… so is basically 1px of distance if I can’t solve this conversion… also canvas starts from 0,0 and I have always 45 pixel of white space

I can retrive minimum left and top values and recalc the size but how can center all the objects in the canvas?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-16T10:20:39+00:00Added an answer on May 16, 2026 at 10:20 am

    Was gonna do a blog post on this at some point and may still do. But – in short – I solved this issue just by writing my own panel that understands how to position children according to the mercator projection.

    Some things to know before using it:

    • you need to set MaxLatitude and MaxLongitude to whatever range is supported in your background image.
    • I don’t think it supports updates in both directions yet. I was planning to fix this prior to my blog post.

    You use it like this:

    <ListBox ItemsSource="{Binding YourItems}">
        <ListBox.Template>
            <ControlTemplate>
                <Border BorderBrush="Black" BorderThickness="1" Background="#CEE3FF">
                    <Grid>
                        <Grid.Resources>
                            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}">Transparent</SolidColorBrush>
                            <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}">Transparent</SolidColorBrush>
                        </Grid.Resources>
                        <Image x:Name="mapImage" Source="YourMap.png"/>
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" Width="{Binding ActualWidth, ElementName=mapImage}" Height="{Binding ActualHeight, ElementName=mapImage}" />
                    </Grid>
                </Border>
            </ControlTemplate>
        </ListBox.Template>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <!-- make sure you set these values in line with YourMap.png -->
                <controls:MercatorProjectionPanel MaxLatitude="81" MinLatitude="-74"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="controls:MercatorProjectionPanel.Longitude" Value="{Binding Location.Longitude}"/>
                <Setter Property="controls:MercatorProjectionPanel.Latitude" Value="{Binding Location.Latitude}"/>
            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock>Here's your item</TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    Here’s the code:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    
    /// <summary>
    /// Implements a panel that lays out children according to the mercator projection.
    /// </summary>
    public class MercatorProjectionPanel : Panel
    {
        /// <summary>
        /// Identifies the <see cref="MinLatitude"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty MinLatitudeProperty = DependencyProperty.Register(
            "MinLatitude",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(DefaultMinLatitude, OnLatitudeRangeChanged));
    
        /// <summary>
        /// Identifies the <see cref="MaxLatitude"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty MaxLatitudeProperty = DependencyProperty.Register(
            "MaxLatitude",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(DefaultMaxLatitude, OnLatitudeRangeChanged));
    
        /// <summary>
        /// Identifies the <c>Longitude</c> attached dependency property.
        /// </summary>
        public static readonly DependencyProperty LongitudeProperty = DependencyProperty.RegisterAttached(
            "Longitude",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(double.NaN, OnGeographicalCoordinateChanged));
    
        /// <summary>
        /// Identifies the <c>Latitude</c> attached dependency property.
        /// </summary>
        public static readonly DependencyProperty LatitudeProperty = DependencyProperty.RegisterAttached(
            "Latitude",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(double.NaN, OnGeographicalCoordinateChanged));
    
        /// <summary>
        /// Identifies the <c>Left</c> attached dependency property.
        /// </summary>
        public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached(
            "Left",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(double.NaN, OnCoordinateChanged));
    
        /// <summary>
        /// Identifies the <c>Top</c> attached dependency property.
        /// </summary>
        public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached(
            "Top",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(double.NaN, OnCoordinateChanged));
    
        private static readonly DependencyProperty XRatioProperty = DependencyProperty.RegisterAttached(
            "XRatio",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(double.NaN));
    
        private static readonly DependencyProperty YRatioProperty = DependencyProperty.RegisterAttached(
            "YRatio",
            typeof(double),
            typeof(MercatorProjectionPanel),
            new FrameworkPropertyMetadata(double.NaN));
    
        private const double DefaultMinLatitude = -80;
        private const double DefaultMaxLatitude = 80;
        private const double DegreesPerRadian = 57.2957;
    
        private double minY = CalculateYRelative(DefaultMinLatitude);
        private double maxY = CalculateYRelative(DefaultMaxLatitude);
    
        /// <summary>
        /// Initializes a new instance of the MercatorProjectionPanel class.
        /// </summary>
        public MercatorProjectionPanel()
        {
            SizeChanged += delegate
            {
                InvalidateArrange();
            };
        }
    
        /// <summary>
        /// Gets or sets the minimum latitude displayed by this mercator projection panel.
        /// </summary>
        public double MinLatitude
        {
            get { return (double)GetValue(MinLatitudeProperty); }
            set { SetValue(MinLatitudeProperty, value); }
        }
    
        /// <summary>
        /// Gets or sets the maximum latitude displayed by this mercator projection panel.
        /// </summary>
        public double MaxLatitude
        {
            get { return (double)GetValue(MaxLatitudeProperty); }
            set { SetValue(MaxLatitudeProperty, value); }
        }
    
        /// <summary>
        /// Gets the longitude for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <returns>
        /// The longitude.
        /// </returns>
        public static double GetLongitude(DependencyObject dependencyObject)
        {
            return (double)dependencyObject.GetValue(LongitudeProperty);
        }
    
        /// <summary>
        /// Sets the longitude for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <param name="longitude">
        /// The longitude.
        /// </param>
        public static void SetLongitude(DependencyObject dependencyObject, double longitude)
        {
            dependencyObject.SetValue(LongitudeProperty, longitude);
        }
    
        /// <summary>
        /// Gets the latitude for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <returns>
        /// The latitude.
        /// </returns>
        public static double GetLatitude(DependencyObject dependencyObject)
        {
            return (double)dependencyObject.GetValue(LatitudeProperty);
        }
    
        /// <summary>
        /// Sets the latitude for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <param name="latitude">
        /// The latitude.
        /// </param>
        public static void SetLatitude(DependencyObject dependencyObject, double latitude)
        {
            dependencyObject.SetValue(LatitudeProperty, latitude);
        }
    
        /// <summary>
        /// Gets the left offset for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <returns>
        /// The left offset.
        /// </returns>
        public static double GetLeft(DependencyObject dependencyObject)
        {
            return (double)dependencyObject.GetValue(LeftProperty);
        }
    
        /// <summary>
        /// Sets the left offset for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <param name="left">
        /// The left offset.
        /// </param>
        public static void SetLeft(DependencyObject dependencyObject, double left)
        {
            dependencyObject.SetValue(LeftProperty, left);
        }
    
        /// <summary>
        /// Gets the top offset for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <returns>
        /// The top offset.
        /// </returns>
        public static double GetTop(DependencyObject dependencyObject)
        {
            return (double)dependencyObject.GetValue(TopProperty);
        }
    
        /// <summary>
        /// Sets the top offset for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <param name="top">
        /// The top offset.
        /// </param>
        public static void SetTop(DependencyObject dependencyObject, double top)
        {
            dependencyObject.SetValue(TopProperty, top);
        }
    
        /// <summary>
        /// Gets the horizontal alignment for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <returns>
        /// The horizontal alignment.
        /// </returns>
        public static HorizontalAlignment GetHorizontalAlignment(DependencyObject dependencyObject)
        {
            return (HorizontalAlignment)dependencyObject.GetValue(HorizontalAlignmentProperty);
        }
    
        /// <summary>
        /// Sets the horizontal alignment for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <param name="horizontalAlignment">
        /// The horizontal alignment.
        /// </param>
        public static void SetHorizontalAlignment(DependencyObject dependencyObject, HorizontalAlignment horizontalAlignment)
        {
            dependencyObject.SetValue(HorizontalAlignmentProperty, horizontalAlignment);
        }
    
        /// <summary>
        /// Gets the vertical alignment for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <returns>
        /// The vertical alignment.
        /// </returns>
        public static VerticalAlignment GetVerticalAlignment(DependencyObject dependencyObject)
        {
            return (VerticalAlignment)dependencyObject.GetValue(VerticalAlignmentProperty);
        }
    
        /// <summary>
        /// Sets the vertical alignment for a specified dependency object.
        /// </summary>
        /// <param name="dependencyObject">
        /// The dependency object.
        /// </param>
        /// <param name="verticalAlignment">
        /// The vertical alignment.
        /// </param>
        public static void SetVerticalAlignment(DependencyObject dependencyObject, VerticalAlignment verticalAlignment)
        {
            dependencyObject.SetValue(VerticalAlignmentProperty, verticalAlignment);
        }
    
        /// <summary>
        /// Measures all child controls, imposing no restrictions on their size.
        /// </summary>
        /// <param name="availableSize">
        /// The available size.
        /// </param>
        /// <returns>
        /// The measured size.
        /// </returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
    
            foreach (UIElement child in this.InternalChildren)
            {
                if (child != null)
                {
                    child.Measure(availableSize);
                }
            }
    
            return new Size();
        }
    
        /// <summary>
        /// Arranges all child controls.
        /// </summary>
        /// <param name="finalSize">
        /// The final size.
        /// </param>
        /// <returns>
        /// The size of the content.
        /// </returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (FrameworkElement child in this.InternalChildren)
            {
                if (child == null)
                {
                    continue;
                }
    
                var xRatio = GetXRatio(child);
                var yRatio = GetYRatio(child);
                var x = xRatio * ActualWidth;
                var y = yRatio * ActualHeight;
    
                switch (child.HorizontalAlignment)
                {
                    case HorizontalAlignment.Center:
                        x -= child.DesiredSize.Width / 2;
                        break;
                    case HorizontalAlignment.Right:
                        x -= child.DesiredSize.Width;
                        break;
                }
    
                switch (child.VerticalAlignment)
                {
                    case VerticalAlignment.Center:
                        y -= child.DesiredSize.Height / 2;
                        break;
                    case VerticalAlignment.Bottom:
                        y -= child.DesiredSize.Height;
                        break;
                }
    
                child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
            }
    
            return finalSize;
        }
    
        private static double GetXRatio(DependencyObject dependencyObject)
        {
            return (double)dependencyObject.GetValue(XRatioProperty);
        }
    
        private static void SetXRatio(DependencyObject dependencyObject, double xRatio)
        {
            dependencyObject.SetValue(XRatioProperty, xRatio);
        }
    
        private static double GetYRatio(DependencyObject dependencyObject)
        {
            return (double)dependencyObject.GetValue(YRatioProperty);
        }
    
        private static void SetYRatio(DependencyObject dependencyObject, double yRatio)
        {
            dependencyObject.SetValue(YRatioProperty, yRatio);
        }
    
        private static double CalculateYRelative(double latitude)
        {
            return Math.Log(Math.Tan(((latitude / 360d) * Math.PI) + (Math.PI / 4)));
        }
    
        private static void OnLatitudeRangeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var reference = dependencyObject as MercatorProjectionPanel;
    
            if (reference != null)
            {
                reference.minY = CalculateYRelative(reference.MinLatitude);
                reference.maxY = CalculateYRelative(reference.MaxLatitude);
            }
        }
    
        private static void OnGeographicalCoordinateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var reference = dependencyObject as FrameworkElement;
    
            if (reference != null)
            {
                var parent = VisualTreeHelper.GetParent(reference) as MercatorProjectionPanel;
    
                if (parent != null)
                {
                    SetLeft(reference, ConvertLongitudeToX(parent, GetLongitude(reference)));
                    SetTop(reference, ConvertLatitudeToY(parent, GetLatitude(reference)));
    
                    parent.InvalidateArrange();
                }
            }
        }
    
        private static void OnCoordinateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var reference = dependencyObject as FrameworkElement;
    
            if (reference != null)
            {
                var parent = VisualTreeHelper.GetParent(reference) as MercatorProjectionPanel;
    
                if (parent != null)
                {
                    ////SetLongitude(reference, ConvertXToLongitude(parent, GetLeft(reference)));
                    ////SetLatitude(reference, ConvertYToLatitude(parent, GetTop(reference)));
    
                    ////parent.InvalidateArrange();
    
                    var left = GetLeft(reference);
                    var top = GetTop(reference);
    
                    SetXRatio(reference, left / parent.ActualWidth);
                    SetYRatio(reference, top / parent.ActualHeight);
                }
            }
        }
    
        private static double ConvertXToLongitude(MercatorProjectionPanel panel, double left)
        {
            return ((left / panel.ActualWidth) * 360) - 180;
        }
    
        private static double ConvertYToLatitude(MercatorProjectionPanel panel, double top)
        {
            var input = panel.maxY - ((top / panel.ActualHeight) * (panel.maxY - panel.minY));
            return Math.Atan(Math.Sinh(input)) * DegreesPerRadian;
        }
    
        private static double ConvertLongitudeToX(MercatorProjectionPanel panel, double longitude)
        {
            return ((longitude + 180) / 360) * panel.ActualWidth;
        }
    
        private static double ConvertLatitudeToY(MercatorProjectionPanel panel, double latitude)
        {
            return panel.ActualHeight - (panel.ActualHeight * (CalculateYRelative(latitude) - panel.minY) / (panel.maxY - panel.minY));
        }
    }
    

    If I ever get around to the blog post, I’ll update this answer.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.