Wednesday, February 2, 2011

How to preserve the map center as the browser resizes

By default, when a Silverlight map (or its parent) is resized the map scale (or resolution) is preserved but not the map center. Instead, the map is “pinned” to the upper left hand corner.

In most situations this is desirable but occasionally it is not. The following code snippet will ensure that the current map center (and scale) is preserved as the browser is resized.

using System;
using System.Windows;
using System.Windows.Controls;
using ESRI.ArcGIS.Client.Geometry;

namespace SilverlightApplication2 {
    public partial class MainPage : UserControl {
        public MainPage() {
            InitializeComponent();

            this.SizeChanged += (s, e) => {
                if (e.PreviousSize.Height == 0 ||
e.PreviousSize.Width == 0) { return; }
                Point p = new Point() {
                    X = (this.MyMap.ActualWidth / 2d) -
(e.NewSize.Width - e.PreviousSize.Width),
                    Y = (this.MyMap.ActualHeight / 2d) -
(e.NewSize.Height - e.PreviousSize.Height)
                };
                MapPoint m = this.MyMap.ScreenToMap(p);
                TimeSpan t = this.MyMap.PanDuration;
                this.MyMap.PanDuration = TimeSpan.Zero;
                this.MyMap.PanTo(m);
                this.MyMap.PanDuration = t;
            };
        }
    }
}

Thursday, December 23, 2010

Remove map whitespace with an alternative “full extent”

This post describes a technique for calculating an adjusted “full extent” for a layer so that content can uniformly fill a map.

The following XAML will display simple map using ESRI’s ArcGIS API for Silverlight. The map contains the world topo map service from ArcGIS Online.

<UserControl
   x:Class="ESRI.PrototypeLab.DynamicDesign.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   xmlns:esri="http://schemas.esri.com/arcgis/client/2009" 
   mc:Ignorable="d"
   d:DesignHeight="600"
   d:DesignWidth="800"
   >
    <Grid Background="Gray">
        <esri:Map x:Name="Map">
            <esri:Map.Layers>
                <esri:ArcGISTiledMapServiceLayer
ID="topo"
Visible="True"
Url="http://services.arcgisonline.com/
ArcGIS/rest/services/World_Topo_Map/MapServer"
/>
            </esri:Map.Layers>
        </esri:Map>
    </Grid>
</UserControl>

The code behind below will zoom to the full extent of topo map service.

using System.Windows.Controls;

namespace ESRI.PrototypeLab.DynamicDesign {
    public partial class MainPage : UserControl {
        public MainPage() {
            InitializeComponent();

            // Zoom to full extent
            this.Map.Layers[0].Initialized += (s, e) => {
                this.Map.ZoomTo(this.Map.Layers[0].FullExtent);
            };
        }
    }
}

Depending on the aspect ratio of the browser, this will result in a map with “white space” (or whatever color you use for the background) at either the top/bottom or left/right as shown below.

Too much "white space"Too much "white space"

To address this I extended the Layer class with a method to return an adjusted full extent for the parent map.

using System.Windows;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;

namespace ESRI.PrototypeLab.DynamicDesign {
    public static class LayerExtension {
        public static Envelope AdjustedFullExtent(this Layer layer,
FrameworkElement parent) {
            Envelope extent = layer.FullExtent;
            double ratioMap = parent.ActualHeight / parent.ActualWidth;
            double ratioLay = extent.Height / extent.Width;
            if (ratioMap < ratioLay) {
                return new Envelope() {
                    XMin = extent.XMin,
                    YMin = extent.GetCenter().Y - 0.5d * ratioMap *
extent.Width,
                    XMax = extent.XMax,
                    YMax = extent.GetCenter().Y + 0.5d * ratioMap *
extent.Width
                };
            }
            return new Envelope() {
                XMin = extent.GetCenter().X - 0.5d *
extent.Height / ratioMap,
                YMin = extent.YMin,
                XMax = extent.GetCenter().X + 0.5d *
extent.Height / ratioMap,
                YMax = extent.YMax
            };
        }
    }
}

Let’s modify the code behind to use the new method, AdjustedFullExtent, defined above.

using System.Windows.Controls;

namespace ESRI.PrototypeLab.DynamicDesign {
    public partial class MainPage : UserControl {
        public MainPage() {
            InitializeComponent();

            // Zoom to full extent
            this.Map.Layers[0].Initialized += (s, e) => {
                this.Map.ZoomTo(
this.Map.Layers[0].AdjustedFullExtent(this.Map));
            };
        }
    }
}

Now, when the application starts, the layer will fill the entire map regardless of the browsers aspect ratio.

No more "white space" No more "white space"

Tuesday, December 7, 2010

How to add “ScrollIntoView” to an ItemsControl

The ListBox control (in Silverlight or WPF) has a handy method called ScrollIntoView that forces a listbox scroll so that the parsed item is in view.  This post will describe how to add this capability to an ItemsControl.

Firstly, to enable vertical scrolling in an ItemsControl apply the following template.

<ItemsControl x:Name="myItemsControl">

    <ItemsControl.Template>
        <ControlTemplate>
            <ScrollViewer Padding="{TemplateBinding Padding}">
                <ItemsPresenter />
            </ScrollViewer>
        </ControlTemplate>
    </ItemsControl.Template>
</ItemsControl>

Next, add the following class that extends the ItemsControl class with an overloaded “Scroll Into View” method.

using System.Windows;
using System.Windows.Controls;

namespace myNamespace {
    public static class Extensions {
        public static void ScrollIntoView(
this ItemsControl control,
object item) {
            FrameworkElement framework =
control.ItemContainerGenerator.ContainerFromItem(item)
as FrameworkElement;
            if (framework == null) { return; }
            framework.BringIntoView();
        }
        public static void ScrollIntoView(this ItemsControl control) {
            int count = control.Items.Count;
            if (count == 0) { return; }
            object item = control.Items[count - 1];
            control.ScrollIntoView(item);
        }
    }
}

With this extension you can force the ItemsControl to scroll to a specific item, or alternatively, using the overloaded method, simply scroll to the last item in the collection.  For example.

this.myItemsControl.ScrollIntoView();

Tuesday, November 23, 2010

Mosaic Image Finder for ArcMap

Mosaic Image Finder for ArcMap

The Mosaic Image Finder is a new add-in for ArcMap to explore imagery within a mosaic.  The add-in supports mosaics stored locally in a geodatabase (or SDE connection) and mosaics published to ArcGIS Server as image services.

Mosaic Image Finder for ArcMap  Mosaic Image Finder for ArcMap

When a mosaic layer is dropped on to the Mosaic Image Finder window, thumbnails of mosaic images that overlap the current map extent are displayed in the three dimensional viewer as shown above. The images can be sorted vertically based on any numeric field.

The aim of this add-in was to provide an intuitive user interface for sorting and searching imagery in a mosaic. This add-in is particularly useful when mosaics contain good attribution and images are plentiful at an area of interest. When an image is selected, as indicated above with a red outline, it can be added to ArcMap as a new layer.

The add-in (with source code) can be downloaded from the ArcGIS Resource Center here. The download page contains installation instructions.

The Mosaic Image Finder window is created using WPF 3D (.NET). One of the difficulties when developing this application was creating a threading model for background image requests. If images were downloaded in the UI thread then ArcMap would totally lock-up. Even though background threads can use ArcObjects, it is not recommended to parse ArcObject references between threads, doing so will cause cross-thread issues such as a severe performance hit.  When possible I created proxy classes to assist with the exchange of simple data types like IEnvelope. However to parse a Layer I was forced to persist the layer to file and have it opened in the background thread, not the best but unavoidable.

Monday, November 22, 2010

3D Video Capture with hacked Xbox 360 Kinect

image

Last week Oliver Kreylos released this video on youtube showing a hacked Xbox 360 Kinect being used to generate 3D video. Amazing! It is no surprise that there has been almost 1.4 million views during the past week.

There are, of course, a tremendous number of possible applications of this technology. Many of which are impractical (like most augmented reality applications) but others may have promise. Interior mapping and video change detection are two fields that may benefit from variants of this technology.

In more recent videos Kreylos discusses options to achieve a true 360° 3D video. One option is to use two Kinect devices, with polarized filters, facing opposite each other. The filters would prevent one device from accidentally processing infrared beams from the opposing device. Software would then automagically stitch the two 3D video feeds together.