ArcGISLocalDynamicMapServiceLayer PanBuffer?

3560
14
08-02-2012 03:42 AM
JamesMcElroy
New Contributor
The PanBuffer property in the Beta allowed for smooth panning around a dynamic map layer (ie. no or little white space while waiting for refresh).  While you can still find this in the online documentation here, it appears to have been removed for the release version.

So my question is this, how can I get my map object to pan in a smoother fashion without having constant white space while it's waiting to load and refresh?

Thanks,

James
0 Kudos
14 Replies
IanBates
New Contributor II

Hi

It is possible (but a little difficult to implement ) we can workaround the Accelerated Mode restriction - however we cannot use the sample unless we can solve how to make it TimeExtent Aware / supporting temporal data in the mpk. At present the sample ignores TimeExtent on the map - can you suggest how to modify the "BufferedArcGISDynamicMapServiceLayer" sample to respect the current time extent on the host map?

Thanks

Ian

0 Kudos
MichaelBranscomb
Esri Frequent Contributor

Hi,

I have spent some time investigating how we can get the custom layer to respect the time extent of the map. Unfortunately the map determines which layers can honour the map's time extent via an internal interface, therefore it is not directly possible to have the layer respect the map's time extent. However - I am just pursuing another avenue - injecting the time parameter into the request URL.

Cheers

Mike

0 Kudos
MichaelBranscomb
Esri Frequent Contributor

Hi,

Hopefully some good news for you - by taking a different approach with the buffered dynamic layer (with some support from a couple of the devs on the team here) we have managed to get the buffered dynamic layer with the accelerated display and time aware support you were looking for. The code for updated custom layer below - not necessarily the most elegant code but I am aware you are under time pressure therefore I wanted to get it to you as soon as I had confirmed basic functioning. you will likely need to make some tweaks to this to make it work in your app. I will send your contact in tech support (Pete) a zipped up project which demonstrates how you might tie it in with the TimeSlider control from the toolkit and he can forward to you.

Cheers

Mike

using System;

using System.Collections.Generic;

using System.IO;

using System.Reflection;

using System.Windows.Media;

using ESRI.ArcGIS.Client;

using ESRI.ArcGIS.Client.Geometry;

using System.Globalization;

namespace CustomDynamicLayer

{

    /// <summary>

    /// This layer is a DynamicLayer that displays an ArcGIS dynamic map service, while maintaining a buffer of imagery outside the visible extent in order to

    /// reduce the number of requests made to the server.

    /// When the map is panned but the newly visible map extent is still within the extent of previously-requested imagery*, no new request is made to the

    /// dynamic map service.

    ///

    /// *NB: the extent that is checked when suppressing new requests is smaller than the extent that is then requested, this is to trigger new requests before "blank" areas

    /// of the map are exposed.

    ///

    /// Diagram showing visible extent, the extent that gets requested, and the smaller extent inside which no new request is made.

    ///

    /// +----------------------------------------------+

    /// | extent requested from server                 |

    /// |     +----------------------------------+     |

    /// |     |requests suppressed               |     |

    /// |     |      +--------------------+      |     |

    /// |     |      | visible extent     |      |     |

    /// |     |      |                    |      |     |

    /// |     |      |                    |      |     |

    /// |     |      |                    |      |     |

    /// |     |      |                    |      |     |

    /// |     |      |                    |      |     |

    /// |     |      +--------------------+      |     |

    /// |     |                                  |     |

    /// |     +----------------------------------+     |

    /// |                                              |

    /// +----------------------------------------------+

    ///

    /// After panning to the east, there's still no new request made because the visible extent is still inside the 'requests suppressed' extent:

    ///

    /// +----------------------------------------------+

    /// |cached image requested from server            |

    /// |     +----------------------------------+     |

    /// |     | requests suppressed              |     |

    /// |     |            +--------------------++     |

    /// |     |            | visible extent     ||     |

    /// |     |            |                    ||     |

    /// |     |            |                    ||     |

    /// |     |            |                    ||     |

    /// |     |            |                    ||     |

    /// |     |            |                    ||     |

    /// |     |            +--------------------++     |

    /// |     |                                  |     |

    /// |     +----------------------------------+     |

    /// |                                              |

    /// +----------------------------------------------+

    ///

    /// When visible extent moves outside "requests suppressed", then a new expanded extent is requested from the server.

    ///

    /// </summary>

  internal class BufferedArcGISDynamicMapServiceLayerByUrl : DynamicMapServiceLayer

    {

        /// <summary>

        /// Contained layer we use to hit the server.

        /// </summary>

        public ArcGISDynamicMapServiceLayer _layer;

        /// <summary>

        /// Extent inside which the map can be panned without triggering a new request

        /// </summary>

        private Envelope _suppressRequestsExtent;

        /// <summary>

        /// Resolution of cached imagery

        /// </summary>

        private double _requestedResolution;

      

        private ImageResult _cachedResult;

        private string _cachedUrl;

        /// <summary>

        /// List of callbacks waiting to be called.

        /// </summary>

        List<OnUrlComplete> _callbacks = new List<OnUrlComplete>();

        private ImageParameters _adjustedProperties;

  public BufferedArcGISDynamicMapServiceLayerByUrl()

        {

            _layer = new ArcGISDynamicMapServiceLayer();

            _layer.Initialized += ContainedLayerInitialized;

            _layer.InitializationFailed += ContainedLayerInitializationFailed;

        }

        public override void Initialize()

        {

            _layer.Initialize();

        }

        private void ContainedLayerInitializationFailed(object sender, EventArgs e)

        {

            InitializationFailure = _layer.InitializationFailure;

            base.Initialize();

        }

        private void ContainedLayerInitialized(object sender, EventArgs e)

        {

            SpatialReference = _layer.SpatialReference;

            SupportsRotation = _layer.SupportsRotation;

            base.Initialize();

        }

        public string Url

        {

            get { return _layer.Url; }

            set { _layer.Url = value; }

        }

        double GetImageParametersResolution(ImageParameters properties)

        {

            return properties.Extent.Width / properties.Width;

        }

        public override void GetUrl(ImageParameters properties, OnUrlComplete onComplete)

        {

            // calculate if the requested extent can be dealt with without a new HTTP request

            bool requestedExtentIsContainedInExistingImagery = _suppressRequestsExtent.Contains(properties.Extent);

            bool resolutionMatchesExistingImagery = Math.Abs(GetImageParametersResolution(properties) - _requestedResolution) < 0.1;

            if (requestedExtentIsContainedInExistingImagery && resolutionMatchesExistingImagery)

            {

                _layer.GetUrl(_adjustedProperties, (url, res) =>

                {

  _cachedUrl = url + "&time="

  + MillisecondsSinceEpoch(Map.TimeExtent.Start)

  + ","

  + MillisecondsSinceEpoch(Map.TimeExtent.End)

  + "&imageSR=" + Map.SpatialReference.WKID.ToString()

  + "&bboxSR=" + Map.SpatialReference.WKID.ToString();

                    _cachedResult = res;

                    onComplete(_cachedUrl, _cachedResult);

                });

            }

            else

            {

                // A new image needs to be requested to cover the map

               

                // Add a buffer around the requested extent

                Envelope bufferedExtent = properties.Extent.Expanded(properties.Extent.Width/2,

                    properties.Extent.Height/2);

                _adjustedProperties = new ImageParameters(bufferedExtent, properties.Width*2, properties.Height*2)

                {

                    Dpi = properties.Dpi,            

                    Rotation = properties.Rotation,

                    WrapAround = properties.WrapAround

                };

                // Store new 'covered extent' and resolution now, otherwise if new getsource calls are made we might re-request new images that would have been covered by this one.

                _suppressRequestsExtent = properties.Extent.Expanded(properties.Extent.Width/3, properties.Extent.Height/3);

                _requestedResolution = GetImageParametersResolution(_adjustedProperties);

  _layer.GetUrl(_adjustedProperties, (url, res) =>

  {

  url += "&time="

  + MillisecondsSinceEpoch(Map.TimeExtent.Start)

  + ","

  + MillisecondsSinceEpoch(Map.TimeExtent.End)

  + "&imageSR=" + Map.SpatialReference.WKID.ToString()

  + "&bboxSR=" + Map.SpatialReference.WKID.ToString();

  onComplete(url, res);

  });

            }

        }

  private string MillisecondsSinceEpoch(DateTime myDateTime)

  {

  //return myDateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;

  DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

  return ((myDateTime).ToUniversalTime() - epoch).TotalMilliseconds.ToString(CultureInfo.InvariantCulture);

  }

    }

}

0 Kudos
IanBates
New Contributor II

Mike,

Thanks very much for helping us with this - and all the better that it works with Accelerated Display Mode on.

We will start investigating how we can use this in our app immediately.

Regards

Ian

0 Kudos
MichaelBranscomb
Esri Frequent Contributor

Hi,

I have attached the app to this post - it might take a while to get through via email.

Good luck.

Cheers

Mike

0 Kudos