Thursday, 24 July 2008

DeepZoom Composer Tag "Generator" MkIII



A comment on a previous post says Because WebClient has a dependency on a web server running, the version I wrote won't be added as a project template into Deep Zoom Composer, which sounds like a shame and got me wondering how to get around the 'WebClient' problem.

The 'WebClient' problem is actually related to security and the sandbox. Kirupa's Tag Sample needs to load Metadata.xml into Silverlight/c#, and it uses WebClient to trigger an asynchronous download...

but you can't do that if you Silverlight control is hosted in a web page off your local disk!

It turned out to be pretty easy to get around - firstly here's a screenshot: the page has simply been double-clicked off the hard-drive, yet it is still able to load *some metadata* to enable tagging & description display.


Ah-ha - so you notice I said "some metadata" and not Metadata.xml. Since it is so hard to 'load' Xml off the filesystem (thanks to the Silverlight sandbox), and since my Tag Generator is parsing and re-writing Metadata.xml anyway, we just write another (JSON) file - Metadata.xml.js then go around the sandbox and load as JSON via Javascript Interop.

It took a little experimenting to get right, but the basic changes to make it work are:

1) Tag Generator emits JSON


2) Tag Generator saves "Metadata.xml.js"


3) Html page references "Metadata.xml.js"


4) Declare 'matching' C#/Silverlight class (to JSON), and a method to Deserialize to it


5) THE MAGIC Javascript Eval turns our string into JSON, replacing the LoadXMLData() at the start of the post


6) Update xmlClient_DownloadStringCompleted to walk the JSON object array instead of Xml, and the rest of the code 'just works'!

You'll want to add a couple of assembly references to your Silverlight (System.Json, System.ServiceModel.Web) and namespaces (System.IO, System.Text, System.Json, System.Runtime.Serializatin.Json) to get everything working. This post on Consum(ing) a JSON object in Silverlight set me on the right track, and the Silverlight and JavaScript Interop Basics filled in the gaps.

The Javascript Interop is capable of much more: HtmlPage.Window.Invoke for going to Javascript, and the [Scriptable*] attributes to expose managed code for Javascript to use; but a simple Eval was all I need for this little experiment.

p.s. I did wonder whether it's possible to "include" an .XML file in the HTML for DOM access... I vaguely remember IE supporting 'XML data islands' or something along those lines. However JSON definitely seems like the 'modern' way to approach the problem IMO, and without getting into x-browser compatibility stuff...

Monday, 21 July 2008

OpenSearch [in your browser]

Scott Hanselman blogged today about adding OpenSearch to a website; he made it sound so easy that I had to update Searcharoo.net and SilverlightEarth.com.

You can now access both "search" functions direct from your browser, then you can search:

Searcharoo for 'new york'


SilverligthEarth for 'new york'


and of course SilverligthEarth (2.0 beta) for 'new york'



In order to get it to 'look right' I had to rustle up a couple of favicons... I think they look okay for a few minutes work :-)

Sunday, 20 July 2008

DeepZoom Composer Tag "Generator"

If you've read the previous two posts, they're about using the new Tag input of Deep Zoom Composer, you'll know that the tags you type in DZC can be used to filter your Deep Zoom Collection like this.


HOWEVER, you probably already add metadata to your photos (using a program like iTag), right?


Rather than RE-type into Composer, the attached project TagUpdater will 'post-parse' your Metadata.xml (after you've finished laying out your photos in DeepZoom Componser) and extract your Title, Description, Keywords (Tags) and Rating, which it will then save back to the XML.

Simply type this:
TagUpdater.exe "C:\...\ClientBin\GeneratedImages\Metadata.xml"

See this:


And it does this:


Then deep zoom away (use this post as a guide).

Note that TagUpdater currently extracts more metadata fields than Silverlight DeepZoom Tag Cloud example displays, but it's trivial to add more (such as the Title and Rating). Further, TagUpdater could be tweaked to extract camera data, GPS location and any other information to be saved in Metadata.xml for use in Silverlight.

DOWNLOAD TagUpdater.zip (24Kb)

Note: I borrowed the jpeg parser from Searcharoo, parts of which were originally written by Asim Goheer and Omar Shahine. It could be updated to use WIC.

Saturday, 19 July 2008

DeepZoom Composer Tag "Cloud" Mk II



Adding tags within the latest Deep Zoom Composer is a neat way to navigate DeepZoom 'Collections'.

But if we are going to introduce a new file (Metadata.xml) why not really leverage it?

To that end, I've added a Description element to the Metadata.xml file and typed my own descriptions for each image in my collection. There are two new TextBlocks in the Silverlight control to show the data, and whenever your mouse is over an image, its Tag and Description are displayed. Try it out (beta 2) (or RC0).



The code to match extract the description is a simple extension of the Tag sample.


Then we need to add some code to the MouseMove event to see what SubImage we're over:


which uses SubImageHitTest() from ProjectSilverlight to determine the index of the image to show Tag/Description for.



Finally, you'll notice the 'browse by tag' is now a very basic "cloud". To accomplish that I replaced the ListBox with WrapPanel for Silverlight 2.0 off CodeProject, replaced the items with Buttons and added this ControlTemplate:


The updated files are Page.xaml.cs and Page.xaml.

Wednesday, 9 July 2008

DeepZoom Composer Tag "Cloud"



One of the Deep Zoom Composer team recently posted a Filtering by Tag Sample that takes advantage of the new "Tag" input in the latest Deep Zoom Composer release.

Kirupa says specifically "A major limitation in the current version is that if you have multiple tags separated by commas, the application doesn’t break them up and allow you to filter by them.That means, your images can only have one tag associated with them. Don’t worry – I will fix that shortly"...

Turns out you can enable multiple tags with relatively few changes to the example code (and a bit of inspiration from Kosta's Tag Cloud example).

The result looks like this (click to view larger image; notice the tags are scaled according to frequency), or try it here (beta2) (or RC0. The Metadata.xml shows how the source data looks.



The only changes to Kirupa's sample were in the xmlClient_DownloadStringCompleted handler, which is called when the Metadata.xml has been retrieved. To try out the multiple-tag-"cloud" you should follow his instructions to get it running, then update xmlClient_DownloadStringCompleted with this code. Don't forget to seperate your tags with commas or semicolons!

The major changes to enable multiple tags were:

1. split and trim the tag string/s


2. use the uniqueTags Dictionary to store the 'count' of tag occurances


3. loop through all the tags to build a unique list, rather than assume just one is attached to the image


4. 'hardcode' the relative font sizes


There are many areas where this code to be improved - the tag handling is case sensitive (so "monastery" and "Monastery" would be listed seperately), the font size could be calculated (logarithmically?) rather than hardcoded , the tag-list could be more 'cloud like' rather than being hosted in a ListBox and much more. They may be addressed in future posts... or attempted by the reader.

Tuesday, 24 June 2008

Silverlight(DeepZoom)Earth (beta 3)

SilverlightEarth.com 2.0 beta (DeepZoom) now includes a new 'row' of functionality: latitude/longitude display, DeepZoom "zoom" factor display and a geocoding search:



It's not the most "full featured" search function - if zoom factor is less than '40' it will zoom to that level, otherwise it will preserve any higher 'power' of zoom. It might occasionally take you somewhere unexpected, as disambiguation isn't supported (yet).

You can also trigger a search from the querystring, so you can 'bookmark' a search, like this:

silverlightearth.com/2b2/?q=Golden Gate Bridge

or with zoom specified as well (note it's "DeepZoom Zoom Factor" and not your usual VE 1..18 range)

silverlightearth.com/2b2/?q=Seattle Space Needle&z=8000

"Reset" zooms back to the starting viewpoint. It seems I've broken the overlay with these changes - fix to come...

OT: this is one case where it's actually easier not to use DeepZoom - check out how much faster the Silverlight 1.0 version loads
silverlightearth.com/?q=Seattle Space Needle&z=16

UPDATE (25-Jun): Accepts both "Zoom Factor" AND "Tile Level"... if z < 18, z^2 is used as the "Zoom Factor", eg.
silverlightearth.com/?q=Sydney&z=16

Map selection is also supported from the URL, for example
silverlightearth.com/?q=Sydney&z=15&m=earthgt
The map 'codes' are backward compatible with Silverlight Earth 1.0.

Mapm=
Virtual Earth (road)earth
Virtual Earth (shade) earth2
Virtual Earth (satellite)earths
Virtual Earth (hybrid)earthh
Google (road) earthgr
Google (satellite) earthgs
Google (topo)earthgt
Yahoo (satellite)earthy
Blue Marbleearthbm
Moonmoon
Marsmars
The "q"uery can also be a simple latitude/longitude point, like this silverlightearth.com/2b2/?q=51.500151,-0.126236&z=12&m=earthg (or in SilverlightEarth 1.0).

Saturday, 14 June 2008

Silverlight(DeepZoom)Earth (beta 2)

After reading these posts on using new Silverlight 2.0 beta 2 Deep Zoom functionality to utilise external tile pyramids (such as Virtual Earth) I decided to give it a go...

The sample code was pretty complete, I just added the Geoquery MapSources, and away it goes...

silverlightearth.com/2b2 (requires Silverlight 2.0 beta 2)


(screenshot a bit stylized to show the different map possibilities)

Okay, there's no searching/geocoding or other map niceties; and the DeepZoom shading (which works great for most things) is a little screwy on text-layers - you'll see what that means if you try it out. But it does work nicely, and puts my (still not finished) Silverlight 1.0 code to shame!

Monday, 9 June 2008

Latest Searcharoo.net - images and geocoding

Searcharoo.net has been updated to version 6; which includes Jpeg image parsing, Gps latitude/longitude metadata and integration with Google Earth and Maps.



There's still a final round of testing to do before the article goes onto CodeProject, but it seems to work pretty well so far. Now, back to DeepZoom Publisher...

UPDATE 11-Jun-08: Image and Location Search(aroo) article now on CodeProject.

Wednesday, 28 May 2008

Thursday, 15 May 2008

Whither Geoquery?

If you were reading this blog earlier in the year, you may have seen these posts on Geoquery for SQL Server 2008.

Since my last post (in March) a few things have happened to stall development:
1) Running. I was injured over summer (that's Dec-Feb down here) and couldn't train - all that extra free time went into developing Geoquery. Now I'm back in training.
2) Holidays. Spent a few weeks in Thailand and Bhutan.
3) Job. It's busier now.
4) I was using the demo version Visual Studio 2008. It's expired. The Express versions just don't cut it for "real" work.
5) I can't help thinking Microsoft is going to release something similar themselves. It was great fun building it up to v0.72, but now real hard work is required to get the functionality "to the next level". That's a lot of effort for "nothing", less fun and more sweat.

I saw today that Google Maps now has a Flash API (and demos), something Yahoo has had for a while. I might get back to playing with silverlightearth.com and deepzoompublisher.com in Silverlight 2.0.

There may yet be a way to tie them all together: converting silverlightearth.com to Silverlight 2.0 will result in a whole pile of C# and Xaml that would then plug right into Geoquery 2008. Will see how things develop... ;-)

DeepZoom "Publisher" with Xps (part 1)

Further to this post about turning a PDF document into a Silverlight 2.0 DeepZoom image, here is some code to parse an XPS document into individual PNG images which can then be turned into DeepZoom content. It's slightly updated from this MSDN forum post: How to convert xps documents to other formats, for example bmp.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Documents;
using System.Windows.Xps.Packaging;
using System.Windows.Media.Imaging;
using System.Windows.Media;
// class definition...
static public void SaveXpsToPng(string xpsFileName)
{
XpsDocument xpsDoc = new XpsDocument(xpsFileName, System.IO.FileAccess.Read);
FixedDocumentSequence docSeq = xpsDoc.GetFixedDocumentSequence();
Dictionary<string, string> docPageText = new Dictionary<string, string>();
string txtPage;

for (int pageNum = 0; pageNum < docSeq.DocumentPaginator.PageCount; pageNum++)
{
DocumentPage docPage = docSeq.DocumentPaginator.GetPage(pageNum);
txtPage = string.Empty;

foreach (System.Windows.UIElement uie in ((FixedPage)docPage.Visual).Children)
{
if (uie is System.Windows.Documents.Glyphs)
{
txtPage += ((System.Windows.Documents.Glyphs)uie).UnicodeString;
}
}

BitmapImage bitmap = new BitmapImage();
RenderTargetBitmap renderTarget =
new RenderTargetBitmap((int)(docPage.Size.Width * 300/96),
(int)(docPage.Size.Height * 300/96),
300,
300,
PixelFormats.Pbgra32);
renderTarget.Render(docPage.Visual);

BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
string filename = xpsFileName + ".Page" + pageNum;
FileStream pageOutStream = new FileStream(filename+".png", FileMode.Create, FileAccess.Write);
encoder.Save(pageOutStream);
pageOutStream.Close();
// oh, and save the text too
System.IO.File.WriteAllText(filename + ".txt", txtPage);
}
}

The output files look like this. Two things to note:
1) the PNGs are between 1 and 3Mb in size (around 300dpi I think)
2) there is a .txt file for each image (look carefully in the code above).



Here is one of the images and its associated .txt file



Why output a text file you ask? For indexing and searching! It's debateable whether it makes sense to use DeepZoom as a mechanism for published 'documents' when you can use PDF or the Silverlight 2 XPS Viewer, however for graphically heavy content (say... magazines, photo books) a searchable, indexable DeepZoom collection could actually be a better user experience (particularly for browsing).

Anyway, this is only half the solution - we've got images and text OUT of the Xps document with that code, but we haven't yet processed them IN to DeepZoom via the DeepZoom Composer (Seadragon/Mermaid)... stay tuned...

Tuesday, 29 April 2008

IMG - big company, worst website ever

IMG eCommerce is a part of IMG, "the world's premier and most diversified sports, entertainment and media company"... too bad all that money can't do any better than produce a product like clubsONLINE, the worse website ever.

Think I'm exaggerating? Here are some screenshots from their 'online payment wizard'...

Let's start with a pretty simple form... it's not just the layout that is screwed up... how about allowing the 'Enter' key to submit the form (maybe a visual indicator on the button too); and what does that red diamond mean?


Ever heard of radiobuttons; even a DDLB would do. Is capitalization really important?


Hmmm, the "wizard"-type UI normally guides us through the steps. How about a hint on which button you'd like pressed?


Saving the best until "last" - you've GOT to be kidding. No, seriously, two buttons [Submit Payment] and [Payment]? Is one of these really meant to be 'cancel' or 'back'?


I 'transact' quite a bit on the web - and this is the first time since about 1994 that I can recall a payment process quite so unwieldy!

Wednesday, 2 April 2008

Loose Xaml (WPF and data binding)

Inspired by a no-longer-working (?) demo of Avalon Xaml (from 2004/05) by Joe Marini, I've put together this Loose Xaml sample blog reader with an RSS.XML feed from this blog.

It works in a browser, if you download the files (Xaml & rss) and run locally, or just check the screenshot:


The code is only 43 lines of markup (with breaks for readability!) - no C# or VB in sight.

I think exercises like this serve two purposes:

Firstly, helps to understand the databinding concepts in WPF
  <Canvas.Resources>
<XmlDataProvider x:Key="NewsData" Source="rss.xml" XPath="rss/channel" />
</Canvas.Resources>
<Canvas.DataContext>
<Binding Source="NewsData" XPath="rss/channel" />
</Canvas.DataContext>
also
Text="{Binding Source={StaticResource NewsData},XPath=description}"
and
    <StackPanel Margin="5" Width="500"
DataContext="{Binding ElementName=NewsList,Path=SelectedItem}">
<TextBlock DockPanel.Dock="Top" Text="Article Description:"/>
<TextBlock Margin="10" TextWrapping="Wrap" Name="SelectedItemDetails"
Text="{Binding XPath=description}" />
</StackPanel>
Secondly, helps structure your thinking around a UI that sits atop 'data services'
Quite a lot can be accomplished with Xaml markup - I think it would be interesting to see just how complex an 'application' could be created in a single Xaml file, using only databinding (and whatever server-side services you needed). WCF/REST could play a role (Syndication services... where's that link?).

The sandbox that loose Xaml sits in does present some problems... you can only load files from the origin, so no referencing RSS feeds from other servers. A middle-man pass-through (or other side-step) could get around that, however. It would seem trivial to create a fairly neat Gmail (or other service) reader; basically anything that publishes RSS (or any Xml, really). What is not clear to me just yet is whether it would be do-able to write a complete mail client, for example, following the same principles...


p.s. It's interesting to see how the databinding syntax changed, from "*Bind(XPath=author)" to "{Binding XPath=description}". Notice also how
TextContent → Text
XmlDataSource → XmlDataProvider
ID → Name
FlowPanel → FlowDocument (?)
Text → TextBlock
I think it's kinda cool to see the tools evolve like that - same now with Silverlight...

UPDATE: If you are wanting to use a datasource from ASMX or SVC, this post on XmlNamespaceManager will be useful. Once you start adding namespaces, the SelectedItem method I've used will break, and you'll want to read this post explaining the intricacies of {Binding} to Xml and the selected item.

Tuesday, 25 March 2008

DeepZoom in Silverlight 1.0 (part deux)

One of the 'issues' I found with using DeepZoom output in Silverlight 1.0 was the irregular shape of DeepZoom Composer's (Mermaid) output -- the entire 'image' is not square, nor are the right/bottom edge tiles.

These two simple examples shows how 'Mermaid' thinks: it will fill up tiles from top-left, leaving the bottom-right tiles "just" big enough to encapsulate the source image.


That's fine for Silverlight 2.0 MultiScaleImage controls to work with, since it can 'cheat' during layout, using the data in info.bin/info.xml to figure out how many Image controls to dynamically create.


But for a basic tile-client (of the sort that can easily display map tiles) that expects the source data to be a perfect square, the basic 'Mermaid' output is "too efficient".

Enter the MakeSquare console application...

It needs a little bit of 'tuning' (the "base" zoom level is hardcoded), but it basically walks a DeepZoom Composer output directory and
  1. makes all tiles square
  2. fills in missing tiles to make each 'zoom level' square
like this basic example (currently working best in Firefox :-s )...

Wednesday, 19 March 2008

DeepZoom "Publisher"

Firstly, a quick note about hosting Silverlight 2.0 on 'locked down' hosting providers (ie. where you can't set the MIME type for the .xap extension to application/x-silverlight-app) and you follow the advice to change the extension to .zip... it doesn't seem to work when your Silverlight 2.0 application uses DeepZoom/MultiScaleImage! I can't explain why, but my DeepZoom test doesn't work (currently; request is in to set the MIME types) even when Tim's test page does (on my server).

Anyway, until my hosting provider fixes the server, here's what this page should look like:


which of course pans/zooms with a little help from Scott Hanselman and the Expression team blog


Pretty easy to do with the DeepZoom Composer, but even easier with a little program that takes an Adobe Acrobat/PDF file and generates everything for you!


You'll need the following:and you will get a complete MultiScaleImage 'set' (info.bin, info.xml and a whole piled of numbered directories). Check Scott H and the Expression team posts on wiring it up.


UPDATE (20-Mar): now that my hosting provider has added the .XAP extension, the demo works. To 'prove' that renaming to .ZIP fails when using MultiScaleImage, this is an identical page with .XAP renamed to .ZIP, both in the object tag and the file itself (obviously).

Thursday, 13 March 2008

DeepZoom in Silverlight 1.0

DeepZoom (ex-SeaDragon) is a very cool 'application' of the image-tiling concept used in online maps like google and live maps/virtual earth.

There are (well, seem to be) two 'modes' for DeepZoom: with or without a 'Collection'; where 'Collection' seems to mean each element of the image has it's own set of tiles and can be treated individually.

It is the 'Collection' mode that allows the Hard Rock Cafe Memorabilia to slide images around when you choose different categories (by artist, type, decade, genre, location). When a 'Deep Zoom Collection' is created, each element has it's own folder within which the tiles are grouped by zoom level. It's easy to conceptualise how this model will 'scale up' to PhotoSynth where each element exists not just in a 2D space but in a 3D space with corresponding 3D positioning (but with the same basic metadata that supports DeepZoom Collections now) residing in the info.bin/info.xml files. Of course building the 'composer' for PhotoSynth will be orders of magnitude more difficult :)

Anyway, back to DeepZoom... when you do NOT generate a collection,

DeepZoom Composer (Mermaid) generates the ENTIRE SCENE in a single set of zoom-level-grouped images... awfully similar to the way map tiles are cut, and this is where it gets interesting!

If you already have (coincidentally) a Silverlight 1.0 "Tile Client" and map browser running, it would be a simple matter to change your google topographic image location template (for example) from
var gridPos = quadkey.charAt(quadkey.length-1);
var t = new Tile(quadkey, WORLD.Zoom)
var nz = 17 - WORLD.Zoom;
imageUrl = 'http://mt'+gridPos+'.google.com/mt?n=404&v=w2p.63&x='
+t.Column+'&y='+t.Row+'&zoom='+nz+'';
to
var nz = 9 + WORLD.Zoom; // DeepZoom idiosyncrasy
imageUrl = '/ClientBin/DeepZoomMags/'+nz+'/'
+t.Column+'_'+t.Row+'.jpg';
and be able to navigate a DeepZoom image using Silverlight 1.0!

This example is NOT live on the web (for now), screenshots follow to prove it works:




Just to tie this back to Geoquery 2008, the same Tile Client code exists in C#/WPF so it would also be trivial to modify the configurable MapSources.xml to browse a DeepZoom (NON-Collection) image from a WPF application...

NOTE: there are known issues with this approach, including DeepZoom Composer not generating a perfect 'square' of source images, and trimming 'edge' images to the size they need to be rather than 'squares' (causing stretching on the right & bottom edges). Both would be simple to cater for in a purpose-built Silverlight 1.0 DeepZoom viewer.

Tuesday, 4 March 2008

Geoquery 2008 v0.72

Another minor release of Geoquery 2008 to finish off some of the 'known bugs' around line and polygon rendering... It can be downloaded here.

First up, Morten has helped fix a number of bugs in Geoquery, and this release is no exception. While trying out his OGC conformance test map I discovered some GEOMETRY idiosyncrasies I still hadn't fixed. If you read his post and download the schema/data, this sql works best in Geoquery.


Morten also pointed out the issue with holes... (notice how the GEOGRAPHY instance is rendered on both the Shape & Map tabs!)


And finally, after a month away from the code (some of it at the beach :) it was relatively quick to solve most of the problems around 'wrapping': with a bit more help from Ed Williams I was able to discard my previous efforts (using the SqlTypes) and solve the problem with some basic math - hoorah!




I expect it will be a bit of a wait for the next version of this code. I have a lot of ideas to incorporate, some of which require further architectural changes. I will also update the Examples to discuss the new features in CTP6. Until then - enjoy Geoquerying...

Monday, 3 March 2008

Back to "work" on Geoquery

After a great holiday at the beach, a rushed implementation of PayPal, some silverlight prototyping and fighting with IL at work, I've finally found some time to work on Geoquery again.

v0.8 is pretty ambitious, so it's likely to be an interim version that fixes a few more bugs, such as polygons that cross +/- 180 degrees Longitude.


and Geography displayed on the Shapes tab


and the dodgy example from my last post


Still a little more to do (what about when the polygon disappears above +/-85 degrees Latitude), but hopefully I'll be back working on street-level maps 'any time now'.



p.s. yeah, I went to the "2008 launch wave". What I don't get is when headlines that look like {data binding} became cool marketing?