Minimal Template - scale line problem

3097
6
04-18-2012 03:52 AM
MaciejGolebiewski
New Contributor
Hi all!
I'm using OpenStreetMap in my app based on Minimal Template the problem is it's not viewing the scale line (and the zoom stack, but that's less important): http://samantha.mazury.pl/Map2/ as you can see a scale line added into MainPage.xaml work perfect, but the one in the right globe-wing just doesn't (as you probably know it should look like this: http://help.arcgis.com/en/webapi/silverlight/samples/TemplateGalleryWeb/Samples/Customized_Templates...)
I think code used by scale line is inside Navigator.cs and in my opinion it's that part:
#region ScaleBar Helper Functions

        private void refreshScalebar()
        {
            Visibility viz = Visibility.Collapsed;
            bool canDisplay = true;
            if (ScaleBar != null)
            {
                viz = ScaleBar.Visibility;
                if (Map == null || double.IsNaN(Map.Resolution) ||
                    MapUnit == ScaleBarUnit.DecimalDegrees && Math.Abs(Map.Extent.GetCenter().Y) >= 90)
                {
                    viz = Visibility.Collapsed;
                    canDisplay = false;
                }
                if (ScaleBar != null) ScaleBar.Visibility = viz;
            }
            if (!canDisplay) return;

            ScaleBarUnit outUnit = ScaleBarUnit.Undefined;
            double outResolution;

            #region KiloMeters/Meters
            double roundedKiloMeters = getBestEstimateOfValue(Map.Resolution, ScaleBarUnit.Kilometers, out outUnit, out outResolution);
            double widthMeters = roundedKiloMeters / outResolution;
            bool inMeters = outUnit == ScaleBarUnit.Meters;

            if (PaddingLeftForScaleBarTextMeters != null)
                PaddingLeftForScaleBarTextMeters.Width = widthMeters;
            if (PaddingLeftTopNotch != null)
                PaddingLeftTopNotch.Width = widthMeters;
            if (ScaleBarTextForMeters != null)
            {
                ScaleBarTextForMeters.Text = string.Format("{0}{1}", roundedKiloMeters, (inMeters ? "m" : "km"));
                ScaleBarTextForMeters.Width = widthMeters;
            }
            #endregion

            #region Miles
            double roundedMiles = getBestEstimateOfValue(Map.Resolution, ScaleBarUnit.Miles, out outUnit, out outResolution);
            double widthMiles = roundedMiles / outResolution;
            bool inFeet = outUnit == ScaleBarUnit.Feet;

            if (PaddingLeftForScaleBarTextMiles != null)
                PaddingLeftForScaleBarTextMiles.Width = widthMiles;
            if (PaddingLeftBottomNotch != null)
                PaddingLeftBottomNotch.Width = widthMiles;
            if (ScaleBarTextForMiles != null)
            {
                ScaleBarTextForMiles.Text = string.Format("{0}{1}", roundedMiles, inFeet ? "ft" : "mi");
                ScaleBarTextForMiles.Width = widthMiles;
            }
            #endregion

            double widthOfNotches = 4; // 2 for left notch, 2 for right notch
            double scaleBarBlockWidth = (widthMiles > widthMeters) ? widthMiles : widthMeters;
            scaleBarBlockWidth += widthOfNotches;

            if (!double.IsNaN(scaleBarBlockWidth) && ScaleBarBlock != null)
                ScaleBarBlock.Width = scaleBarBlockWidth;

        }

        private double getBestEstimateOfValue(double resolution, ScaleBarUnit displayUnit, out ScaleBarUnit unit, out double outResolution)
        {
            unit = displayUnit;
            double rounded = 0;
            double originalRes = resolution;
            while (rounded < 0.5)
            {
                resolution = originalRes;
                if (MapUnit == ScaleBarUnit.DecimalDegrees)
                {
                    resolution = getResolutionForGeographic(Map.Extent, resolution);
                    resolution = resolution * (int)ScaleBarUnit.Meters / (int)unit;
                }
                else if (MapUnit != ScaleBarUnit.Undefined)
                {
                    resolution = resolution * (int)MapUnit / (int)unit;
                }

                double val = TargetWidth * resolution;
                val = roundToSignificant(val, resolution);
                double noFrac = Math.Round(val); // to get rid of the fraction
                if (val < 0.5)
                {
                    ScaleBarUnit newUnit = ScaleBarUnit.Undefined;
                    // Automatically switch unit to a lower one
                    if (unit == ScaleBarUnit.Kilometers)
                        newUnit = ScaleBarUnit.Meters;
                    else if (unit == ScaleBarUnit.Miles)
                        newUnit = ScaleBarUnit.Feet;
                    if (newUnit == ScaleBarUnit.Undefined) { break; } //no lower unit
                    unit = newUnit;
                }
                else if (noFrac > 1)
                {
                    rounded = noFrac;
                    var len = noFrac.ToString().Length;
                    if (len <= 2)
                    {
                        // single/double digits ... make it a multiple of 5 ..or 1,2,3,4
                        if (noFrac > 5)
                        {
                            rounded -= noFrac % 5;
                        }
                        while (rounded > 1 && (rounded / resolution) > TargetWidth)
                        {
                            // exceeded maxWidth .. decrement by 1 or by 5
                            double decr = noFrac > 5 ? 5 : 1;
                            rounded = rounded - decr;
                        }
                    }
                    else if (len > 2)
                    {
                        rounded = Math.Round(noFrac / Math.Pow(10, len - 1)) * Math.Pow(10, len - 1);
                        if ((rounded / resolution) > TargetWidth)
                        {
                            // exceeded maxWidth .. use the lower bound instead
                            rounded = Math.Floor(noFrac / Math.Pow(10, len - 1)) * Math.Pow(10, len - 1);
                        }
                    }
                }
                else
                { // anything between 0.5 and 1
                    rounded = Math.Floor(val);
                    if (rounded == 0)
                    {
                        //val >= 0.5 but < 1 so round up
                        rounded = (val == 0.5) ? 0.5 : 1;
                        if ((rounded / resolution) > TargetWidth)
                        {
                            // exceeded maxWidth .. re-try by switching to lower unit 
                            rounded = 0;
                            ScaleBarUnit newUnit = ScaleBarUnit.Undefined;
                            // Automatically switch unit to a lower one
                            if (unit == ScaleBarUnit.Kilometers)
                                newUnit = ScaleBarUnit.Meters;
                            else if (unit == ScaleBarUnit.Miles)
                                newUnit = ScaleBarUnit.Feet;
                            if (newUnit == ScaleBarUnit.Undefined) { break; } //no lower unit
                            unit = newUnit;
                        }
                    }
                }
            }
            outResolution = resolution;
            return rounded;
        }

        double roundToSignificant(double value, double resolution)
        {
            var round = Math.Floor(-Math.Log(resolution));
            if (round > 0)
            {
                round = Math.Pow(10, round);
                return Math.Round(value * round) / round;
            }
            else { return Math.Round(value); }
        }


        /// <summary>
        /// Calculates horizontal scale at center of extent
        /// for geographic / Plate Carrée projection.
        /// Horizontal scale is 0 at the poles.
        /// </summary>
        double toRadians = 0.017453292519943295769236907684886;
        double earthRadius = 6378137; //Earth radius in meters (defaults to WGS84 / GRS80)
        double degreeDist;
        private double getResolutionForGeographic(ESRI.ArcGIS.Client.Geometry.Envelope extent, double resolution)
        {
            degreeDist = earthRadius * toRadians;
            MapPoint center = extent.GetCenter();
            double y = center.Y;
            if (Math.Abs(y) > 90) { return 0; }
            return Math.Cos(y * toRadians) * resolution * degreeDist;
        }
        #endregion

I think there is a problem with a map units (OSM is in meters), but I'm really new to .NET and C#, I tried to figure it out by myself but no success.
Please help me... 🙂
0 Kudos
6 Replies
DominiqueBroux
Esri Frequent Contributor
Try by setting the MapUnit of the Navigator control:

<
localtoolkit:Navigator x:Name="nav" Map="{Binding ElementName=Map}" MapUnit="Meters" HorizontalAlignment="Left" VerticalAlignment="Top" Loaded="nav_Loaded" />
0 Kudos
MaciejGolebiewski
New Contributor
OK - it works, but there is a problem still - the values seems to be multiplied by ~2, see the link: http://samantha.mazury.pl/Map2/
So there still is something to sort out in an algorithm calculating rounded values...
I don't have time right now to play with it, so if you have some ideas please post them! 🙂
0 Kudos
DominiqueBroux
Esri Frequent Contributor
OK - it works, but there is a problem still - the values seems to be multiplied by ~2

The scale bar inside the custom navigator is not taking care of the Web Mercator distorsion.

I would recommend to replace it by the toolkit ScaleLine control, which is taking care of that.

I did it in the KML Viewer sample. No difficulty but you might perhaps same a little time by looking at that sample.
0 Kudos
MaciejGolebiewski
New Contributor
I've been fighting with it for long time but no luck, I think the client can't recognize the OSM resolution properly.
I divided the calculated value by 1.7 and it kinda works - the error doesn't exceed the scale line wraps actually, so I will leave it as it is now...

Thanks for all the replies anyway! 🙂

Cheers!
0 Kudos
dotMorten_esri
Esri Notable Contributor
Just note that the scaleline shows the scale at the center of the screen. In a webmercator projection there can be a LOT of difference in the scale between Equator and near the poles. You should see the scaleline change values as you pan away from Equator.
0 Kudos
MaciejGolebiewski
New Contributor
I added some feature layers to my client with defined coordinates (Web Mercator Auxiliary Sphere) and since then both scaleline and zoomstack works perfect!
I was such a moron that I didn't do that at first...:rolleyes:
So if you encounter problems with scaleline and zoomstack in Minimal Template with OSM basemap - add some layer (even an empty one) with defined coordinate system! 😉

One again thanks for the replies 🙂

Cheers
0 Kudos