Lens Magnifier and Flip Animation


Flip Animations
Fig 1 – Using ProjectionPlane for flip animations

Magnifiers are an interesting tool for some UIs. They function as a small area blowup of a larger Canvas. Here is a Magnifier example that Matt Serbinski put up on codeplex.

Flip Animations
Fig 2 – Sample Magnify Tool

I decided to look at approaches to making magnifier tools for Map controls. There are a couple of ways available for magnifying a Map Control in Silverlight. The easiest is to simply increase the LocalOffsetZ value of the canvas or UIControl. The effect is to bring the Canvas closer in the viewport by moving it in the positive z direction of the PlaneProjection axis. This does magnify the current zoom level, but adds no new detail. As the LocalOffsetZ increases the features eventually become blurry.

In cases where the layers are MapTileLayer.TileSources, a better approach is to actually move the magnifier through the tile source pyramid. This provides additional detail at each zoom level. To make use of this approach a Lens needs to replicate a set of the main map tile layer sources. Here is an example of a KeyMap populated with three tile sources, OSM, TopOSM, and TopOSMContours, matching the layers of the Main Map Canvas.

  <Canvas>
    <m:Map x:Name="KeyMap"
        Canvas.Left="-100" Canvas.Top="-50"
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch"
        AnimationLevel="Full"
        ScaleVisibility="Collapsed"
        LogoVisibility="Collapsed"
        NavigationVisibility="Collapsed"
        >

      <m:Map.Mode>
        <local:EmptyMapMode/>
      </m:Map.Mode>

      <m:Map.Children>
        <m:MapTileLayer Opacity="1" x:Name="OSM" Visibility="Visible">
          <m:MapTileLayer.TileSources>
            <local:OpenStreetMapTileSource></local:OpenStreetMapTileSource>
          </m:MapTileLayer.TileSources>
        </m:MapTileLayer>

        <m:MapTileLayer x:Name="TopOSMColor" Visibility="Collapsed">
          <m:MapTileLayer.TileSources>
            <local:TopOSMColorTileSource></local:TopOSMColorTileSource>
          </m:MapTileLayer.TileSources>
        </m:MapTileLayer>

        <m:MapTileLayer x:Name="TopOSMContours" Visibility="Collapsed">
          <m:MapTileLayer.TileSources>
            <local:TopOSMContoursTileSource></local:TopOSMContoursTileSource>
          </m:MapTileLayer.TileSources>
        </m:MapTileLayer>
      </m:Map.Children>
    </m:Map>

    <Button x:Name="Flip" Content="Key" Width="40" Height="20"
        Canvas.Left="80" Canvas.Top="170" />
    <Ellipse Stroke="Black" StrokeThickness="5" Width="200" Height="200"/>
  </Canvas>

In this example I have created two additional UserControls a LensPanel and a KeyPanel both like the one above with three tile source layers. These are then added to my MainPage.xaml:

<Canvas Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
    HorizontalAlignment="Left" VerticalAlignment="Top" >
  <local:DragDropPanel x:Name="KeyDragPanel">
    <Grid x:Name="KeyMapGrid" CacheMode="BitmapCache">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200" />
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
        <RowDefinition Height="200"/>
      </Grid.RowDefinitions>
      <Grid.Clip>
        <EllipseGeometry Center="100,100" RadiusX="100" RadiusY="100" />
      </Grid.Clip>
      <Grid.Projection>
        <PlaneProjection x:Name="FlipAnimate" RotationY="0" />
      </Grid.Projection>
      <local:Lens x:Name="LensPanel" Visibility="Visible"></local:Lens>
      <local:Key x:Name="KeyPanel" Visibility="Collapsed"></local:Key>
    </Grid>
  </local:DragDropPanel>
</Canvas>

As you can see from the above Canvas, these additional panels sit inside a 200 x 200 Grid element and make use of an EllipseGeometry Clip mask to limit the view to a circular lens area. In addition this Grid is wrapped in a DragDropPanel so that it can be moved with mouse events against the backdrop of the MainMap. At initialization a LayoutUpdated event handler is added to the KeyDragPanel:
    KeyDragPanel.LayoutUpdated += KeyChange;

Here is the KeyChange event handler:

  private void KeyChange(object sender, EventArgs e)
  {
    if (KeyDragPanel.dragOn)
    {
      loc = MainMap.ViewportPointToLocation(KeyDragPanel.currentP);
      LensPanel.LensMap.View = new MapViewSpecification(loc, MainMap.View.ZoomLevel + lensZoom);
    }
  }

It takes the MainMap current mouse position as a Lat,Lon Location and synchronizes the LensMap.View to the that same Location. However, instead of simply synchronizing ZoomLevels the Lens ZoomLevel is increased by a lensZoom factor, which is initially set to 2. The lens magnification can be adjusted by changing this lensZoom factor with +/- buttons or MouseWheel events. Now the Lens is viewing the same location as the MainMap, but at a deeper more detailed level in the tile pyramid. With this relatively simple addition to a normal Map client we have a functional magnifier lens, which a user can drag around the map, showing tile source layers at a higher zoomlevel out of the source pyramid.

If you notice what happens when a lensZoom factor is subtracted rather than added, you have in essence a basic Key map. The focal length of the magnifier is reversed. Just to make this a little more interesting I have added both a lens and a key UserControl, but instead of making these two separate controls I decided to spice up the sample by making the lens into a flip animation. On one side is the magnifying lens and on the other side is the key map.


Flip Animations
Fig 3 – Key Map on the reverse side of a Lens Magnifier

Flip animations are made possible by targeting a ProjectionPlane Rotation from a Storyboard animation:

  <UserControl.Resources>
    <Storyboard x:Name="stb" Completed="stb_Completed" >
      <DoubleAnimation Storyboard.TargetName="FlipAnimate"
               Storyboard.TargetProperty="RotationY"
               Duration="0:0:1" From="0" To="90" />
    </Storyboard>
    <Storyboard x:Name="stbfinish" >
      <DoubleAnimation Storyboard.TargetName="FlipAnimate"
               Storyboard.TargetProperty="RotationY"
               Duration="0:0:1" From="-90" To="0" />
    </Storyboard>
  </UserControl.Resources>

Actually two Storyboards, one for the front side first 90 degrees, and a second for the backside last 90 degrees. As the front Storyboard completes it triggers a switch from the front user control to the back. In this case from the LensPanel to the KeyPanel. Here is the Completed event handler:

  private void stb_Completed(object sender, EventArgs e)
  {
    lensZoom = 2;
    if (LensPanel.Visibility == Visibility.Visible)
    {
        LensPanel.Visibility = Visibility.Collapsed;
        KeyPanel.Visibility = Visibility.Visible;
    }
    else
    {
        LensPanel.Visibility = Visibility.Visible;
        KeyPanel.Visibility = Visibility.Collapsed;
    }
    stbfinish.Begin();
  }

Rather than using a Clear and Add for switching User Controls between Lens and Key, this event handler just toggles Visibility. Keeping both User Controls in the MainPage and switching with Visibility doesn’t add much to downloads while making the switch faster. After toggling Visibility the Completed event handler then finishes the flip through the last 90 degrees by triggering the stbfinish Storyboard Begin() method. The switch happens at 90 degrees because that is the angle of RotationX when the view is edge on, and the panel disappears momentarily.

Here is the Flip Animation sequence:

  1. start the initial storyboard stb.Begin();
  2. at the end of 90 degrees the Completed event handler is triggered
  3. after toggling controls the Completed event triggers the final soryboard stbfinish.Begin()
  4. finally the RotationX is completed and the flip animation is done.

Summary

Using PlaneProjection for a flip animation of the Lens and KeyMap tools is an interesting UI enhancement. Although this works very well for tile pyramid sources, it does have one drawback: It is limited to tile source layers. WMS rectangle sources require a new GetMap request with each change, which means that magnification of WMS layers will not occur until the lens stops moving. WMS GetMap requests are just not fast enough to keep up with a Lens drag. In the case of a set of WMS layers it is probably better to work out a LocalOffsetZ magnifier.

The performance and user experience improvements are so great with tile pyramid sources that I assume going forward these will be the services of choice. Tile pyramids are not hard to create and in the case of GeoServer geowebcache builds the pyramids for you. With the cost of disk space and servers falling all the time there really doesn’t seem to be much of a reason to continue using the slower WMS GetMap request for relatively static layers.

At the same time Silverlight vector performance has improved significantly over javascript. This means that dynamic geospatial sources might be better sourced as vector features either directly from the database or decoupled with WFS services. Unlike WMS services vector magnification can happen indefintely in the client.

Web service architectures will likely evolve toward static tile pyramids and dynamic vector layers. However, there will be older important WMS services for some time to come. It would be nice to see some of the federal web services add tile pyramid endpoints in the coming year. Maybe this is a good opportunity for using stimulus funding at USGS, MRLC, NASA, JPL, Census, NOAA ….. Cloud resources such as Amazon AWS and Microsoft Azure make high capacity public tile pyramids feasible even for smaller agencies.

Stacking Maps


PlaneProjection stacking
Fig 1 – Stacking Maps in PlaneProjection

Stacking maps is a pretty common experience. It goes back a long way with the now antique, but intriguing, use of clear mylars to portray various feature overlays. I don’t think I was the only kid fascinated with Encyclopedia Britannica’s plastic overlay illustrations. Even as a young engineer we used mylar overlays occasionally. It’s the same thing today but computers make virtual overlays, or layers, relatively easy. Stacking layers is a useful paradigm when using a single coordinated set of features that have a common origin, resolution, coordinate system, and order. However, now that we’ve gotten used to pulling maps from disparate parts of the internet, layer stacking is not always so simple. We are now stacking layers and then moving up the ontology stream to stack additional sets of layers, from other related maps.

Pulling maps from various WMS or WFS sources comes with a set of problems. Even after dealing with matching coordinate systems, resolution differences, and date currency issues, there is still order to take into account. This means choosing what should be at the bottom and what sits on top. The issues that inevitably come up are problems with masking. This is especially a problem when using multiple opaque WMS map sources. The user should have ultimate say as to what layer fits where in his view of his stack, but this flexibility is not always easy to provide.

For example last week I discovered this beautiful map source, “TopOSM”. You can see a Colorado Relief Map here in OpenLayers and here is a Silverlight view of the same tile service.

PlaneProjection stacking
Fig 2 – Stacking TopOSM

When I started playing with this tile source, which was put together by Lars Ahlzen, I was unable to consistently get the tiles for the transportation feature layer. I didn’t solve that problem, but began thinking ,”why use another transportation feature layer when OpenStreetMap is readily available, also very attractive, and doubtless more up to date.” This presented the masking problem because tiles from Open Street Map are opaque and the base relief layer of TopOSM is also opaque. In WMS this is typically handled by setting a TRANSPARENT parameter true. The WMS server then produces a png image with the background invisible or transparent.

However, in a tile source this has to be done when creating the tiles. The decision is usually made based on the percentage of cover in a given layer. For example aerial or satellite imagery has 100% coverage and so TRANSPARENT = true is meaningless. Road layers, though, are relatively sparse and would generally benefit from transparency. In the case of TopOSM the base relief layer includes terrain shading with 100% coverage. Contours on the TopOSMContour layer are generally sparse and published as a transparent=true tile source. The OSM maps contain large polygonal color areas and some shading so the decision was evidently made to produce OSM tiles without transparent background. Unfortunately I have a conflict. How to use two opaque map layers.

First I went searching for an OSM tile source that had a transparent background. There seems to have been one, at least at one time, as indicated here by this WorldWind Wiki link. I wasn’t able to get this tile source to respond so perhaps it is now defunct. Of course you could get a copy of OSM and publish your own set of transparent=true tiles, but that is a lot of work for a weekend. I also ran across some ‘on the fly’ java transform approaches that turn a color range into a transparent png as the tiles are read. However, this approach would be a performance bottleneck, defeating the whole purpose of a tile service.

One workaround is to adjust opacity in the client view. Even though this can result in a dim or blurry layer, it lets multiple opaque layers coexists.


PlaneProjection stacking
Fig 3 – Stacking Maps with opacity masking lower layer

PlaneProjection stacking
Fig 4 – Stacking Maps with opacity adjusted to show lower layer

This actually works pretty well. I can stack two opaque layers from different map sources along with one additional transparent layer, the contours. I now have the beautiful relief and USGS topo contours, along with a very nice and up to date set of road features from OSM. I can change opacity of the two opaque tile sources to suit my need. If I want just the bare earth relief, I can set TopOSM opacity to one, but if I want to know where on earth I am, it helps to see the OSM transportation features. A nice compromise is opacity 0.5 for the top opaque map layer. I settled on using OSM as base and TopOSM as the opacity adjusted top map layer. With just two opaque sources this works OK, but what if you have several. Things get a bit misty with opacity on opacity. As we look forward to hyperspectral stacks with hundreds or thousands of layers in giant stacks, opacity won’t do the job.

Since Silverlight 3 introduced PlaneProjection properties, why not try something different. Here is a cool link that explains as well as illustrates what is going on with PlaneProjection. You can see from this post that it is relatively simple to manipulate plane projection parameters by binding slider values to axis of Rotation, GlobalOffset, and LocalOffset. So the question is can I do this with maps?

Here is an example of a MapTileLayer, and yes, it does include a Projection Property so now we can hook up all kinds of tools for manipulating maps out of plane.

<m:MapTileLayer Opacity="1" x:Name="OSM"
                Visibility="Visible">
    <m:MapTileLayer.Projection>
        <PlaneProjection x:Name="OSMProjection"
                CenterOfRotationX="0.5"
                CenterOfRotationY="0.5"
                CenterOfRotationZ="0.5"
                LocalOffsetX="0"
                LocalOffsetY="0"
                LocalOffsetZ="0"
        />
    </m:MapTileLayer.Projection>

    <m:MapTileLayer.TileSources>
        <local:OpenStreetMapTileSource/>
    </m:MapTileLayer.TileSources>
</m:MapTileLayer>

PlaneProjection stacking
Fig 5 – Stacking Maps with PlaneProjection RotationX but all in the same LocalOffset

PlaneProjection stacking
Fig 6 – Stacking Maps with with PlaneProjection RotationX and LocalOffset changes

Here is a link that lets you try out various parameters of a Map Stack: http://www.web-demographics.com:8089/MapStack/

It is interesting to play around with plane projection and move layers here and there, but as it turns out this is not all that useful for map portrayal. I guess the academics call this a “cartography” issue. The point is how usefully is information presented to a map user or the map consumer. The opacity approach, in my opinion, is by far the better way to use multiple opaque map sources in a cartographically meaningful manner, as long as there are just a few. However, It is still very interesting technically to play with PlaneProjection and see how well it is implemented, even with deeply nested controls.

Another use for PlaneProjection often seen now is the “Flip” animation. Basically it uses an animation tied to a layout control that flips the layout over using PlaneProjection Rotation. If the first layout is replaced by a new one, it reproduces the effect of turning pages. This is similar to the experience of paper pages in a book and could be applied to maps using an Atlas model. But it is not especially helpful since the point of using maps is usually not moving from paper to paper, but from space to space, and in our world all space happens to be contiguous. The physicists among us may beg to differ but cartography in Geospatial systems is all about solid earth surfaces.

However, aside from GIS, here is a small example of using an animated illustration along with a map. In this sample there is an animation with a “before” and “after” view that can be flipped with a simple “next” button. The novelty is a bit interesting …. if you have adequate hardware. However, this is a deeply nested, multi animated layout, and the flip sequence is not all that smooth especially on older hardware. You can see that the interior runoff animations do continue running even while the wrapping ProjectionPlane is rotating, which says a lot for the power of Microsoft’s implementation. Linear Algebra seems less abstract when you see it doing something right before your eyes.


PlaneProjection stacking
Fig 7 – Use of animated PlaneProjection for flip effects

Performance is a consideration and Silverlight 3 adds GPU acceleration. Here is a link describing the use of GPU acceleration in Silverlight. It seemed to help with the map plane projection example above, but GPU BitCaching is not suited for animation so it does nothing to help performance in my flip example.

Summary

Silverlight 3 PlaneProjection has some very nice features and warrants some more experimentation. However, it doesn’t add very much to the current state of slippy map tiled cartography. Working with many layers is another matter, and PlaneProjection could play a big part in UI development for hyperspectral sources.

Silverlight 3 PlaneProjection does add interest to ancillary map information. I’m not saying Metadata will be a great joy if we can virtually flip pages and pages of the stuff, but there are uses for out of plane media placed in a geographic context. If nothing else, it allows access to more information with less visual clutter. Maybe the Flickr photo pattern has been overdone, but some business workflow processes could actually benefit from media embedded usefully in a map surface.