Deep Zoom albumid koos pildiinfoga

Sain mõni päev tagasi kirja ühelt inimeselt, kes oli näinud minu MIX esitlust ning saanud sellest innustust teha mõned Deep Zoom albumid ja nüüd soovis viia albumid järgmisele tasemele lisades piltide juurde ka selgitavat teksti. Ja tõsi ta on, siia ajaveebi jõuavad ka tellitud teemad… nii, et tellige aga… kui mul vähegi aega ja huvi on, siis genereerin postituse :)

Projekti ettevalmistamine

Kõigepealt loome Deep Zoom albumi kasutades rakendust Deep Zoom Composer ning paneme piltidele külge kirjeldused ehk tagid. Seda saab teha Compose sammul valides sammu ning sisestades kirjelduse tag kastikesse.

tagimine thumb Deep Zoom albumid koos pildiinfoga

Järgnevalt tuleb see album eksportida Silverlight projektina, kusjuures eksportimisel on oluline pildid eksportida kollektsiooni (Collection), mitte kompositsioonina (Composition). Teise variandiga ekrporditakse vaid üks pilt, esimesega aga kõik pildid eraldi ning seega jääb meil ligipääs igale pildile ning tema metadatale.

image thumb7 Deep Zoom albumid koos pildiinfoga

Avades selle Silverlight projekti näeme sellist pilti. Sellest projektipuust huvitavad meid sel korral failid Page.xaml, Page.xaml.cs ning Metadata.xml, kus asuvad need meie kirjutatud kirjeldused.

image thumb8 Deep Zoom albumid koos pildiinfoga Kõigepealt vaatame Page.xaml faili ning lisame sinna tekstikasti, kus piltide kirjeldusi näidata.

Lõpptulemus võiks olla näiteks selline ja siin element MultiScaleImage x:Name=”msi” on Deep Zoom album.

<UserControl x:Class="DeepZoomProject.Page"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Width="Auto" Height="Auto" >
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="0.95*"/>
            <RowDefinition Height="0.05*"/>
        </Grid.RowDefinitions>
        <Border Grid.Row="0" BorderBrush="#FF727272" 
                BorderThickness="1,1,1,1">
            <MultiScaleImage x:Name="msi"/>
        </Border>
        <TextBlock x:Name="taginfo" Text="Siia tuleb pildi info." 
                   Grid.Row="1" TextWrapping="Wrap" 
                   HorizontalAlignment="Stretch" 
                   FontWeight="Bold" 
                   FontFamily="Portable User Interface"/>
    </Grid>
</UserControl>

See, et sinna loodud tekstikasti näidataks iga pildi tag väärtust koosneb kolmest sammust:

  1. Kõigepealt on meil vaja teada, milline pilt fookuses on.
  2. Siis on vaja laadida Metadata.xml fail, sest seal on kirjas iga piltide metainfo.
  3. Kui me teame, milline pilt on fookuses ja Metadata.xml fail on ka laetud, siis otsime sealt LINQ abil õige tag väärtuse välja.

Milline pilt on fookuses?

Miskipärast on nii, et Silverlight koordinaadid ja DeepZoom rakenduse koordinaadid ei kattu ja seepärast on hetkel fookuses oleva pildi leidmine üks arvutamine ja teisendamine. Samas ratast me leiutama ei pea, sest veebist leiab mitmeid ja mitmeid erinevaid viise kuidas seda arvutada.

Üks arvutusviis on järgnev:

Võttes argumendiks Deep Zoom pildi (MultiScaleImage) ning hiire asukoha, käime läbi kõik alampildid (MultiScaleSubImage) ning kontrollime, kas hiire asukoht kattub mõne pildi (ViewPort) asukohaga. Enne kui me seda teha saame, peame teisendama näidatava deep zoom ruudu (ViewPort) koordinaadid vastavateks koordinaatideks DeepZoom pildi peal ning need koordinaadid omakorda teisendame koordinaatideks Silverlight rakenduse suhtes. Need viimased koordinaadid on samas mõõtkavas, kui hiire koordinaadid ja seega saame kontrollida, kas hiire koordinaadid asuvad ühe Deep Zoom pildi peal.  Koordinaatide võrdluseks loome ühe ristküliku, mille koordinaadid vastavad pildi koordinaatidele.

using System.Xml.Linq;
using System.IO;
using System.Text;
using System.Xml;
 
//Lisada viide järgmistele teekidele:
 

System.XML;

System.XML.Linq;

System.Linq;

 
//Põhineb: //http://jimlynn.wordpress.com/2008/10/28/silverlight-deep-zoom-collections-and-hit-testing/
bool GetImageTag(MultiScaleImage aMsi, Point p){
    bool gotHit = false;
    for (int i = 0; i < aMsi.SubImages.Count; i++)
    {
        MultiScaleSubImage subimage = aMsi.SubImages[i];
        
        // DeepZoom pildid on jagatud ruutudeks 
        // järgnev on hetkel kontrollitava ruudu vasaku ülemise nurga koordinaat.         // Seda hetkel näidatava pildi suhtes, 
        // mitte Silverlight rakenduse suhtes, 
        // ega ka mitte terve Deep Zoom pildi suhtes.         Point topLeft = subimage.ViewportOrigin;  
        //Võttes arvesse zoom taset,         //arvutame, kus on selle ruudu vasak ülemine nurk DeepZoom pildi suhtes.
        topLeft.X = -(topLeft.X / subimage.ViewportWidth); 
        topLeft.Y = -(topLeft.Y / subimage.ViewportWidth); 
        // Tekitatava ruudu kõrgus
        double width = 1 / subimage.ViewportWidth;
	
        // Tekitatava ruudu laius
	double height = width / subimage.AspectRatio;

	// Tekitatava ruudu ülejäänud kaks koordinaati
	Point bottomright = new Point(topLeft.X + width, topLeft.Y + height);

        // Meil on olemas ruudu koordinaadid Deep Zoom pildi suhtes,
        // teisendame need Silverlight koordinaatideks	topLeft = aMsi.LogicalToElementPoint(topLeft);
        bottomright = aMsi.LogicalToElementPoint(bottomright);
        // Kontrollime, kas hiire koordinaadid (p) on meie loodud ruudu sees
        Rect r = new Rect(topLeft, bottomright);
        if (r.Contains(p))
        {
        gotHit = true;
        // Otsime varem laetud Metadata.xml failist (muutuja metadata)
// õige pildi tag väärtuse ning seame selle tekstikasti väärtuseks.
        List<string> tags = (from image in metadata.Descendants("Image")
                            where int.Parse(image.Element("ZOrder").Value) == i + 1
                            select image.Element("Tag").Value).ToList();
        this.taginfo.Text = tags.First().ToString();
       }
    }
return gotHit;
}

Laeme Metadata.xml-i

Selleks, et me saaks XML failist infot otsida, tuleb see fail kõigepealt rakendusse laadida:

bool metadataLoaded = false;
XDocument metadata = new XDocument();

private void LoadMetadataXML()
{
    WebClient webclient = new WebClient();    webclient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(MetadataLoaded);
    webclient.DownloadStringAsync(new Uri("GeneratedImages/Metadata.xml", UriKind.RelativeOrAbsolute));
}

 

Kui fail on laetud, loeme tema sees oleva XML-i muutujasse metadata.

private void MetadataLoaded(object sender, DownloadStringCompletedEventArgs e)
{
    if (e.Error == null)
    {
	string xmlData = e.Result;

	MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(xmlData));
	XmlReader reader = XmlReader.Create(ms);
	metadata = XDocument.Load(reader);

       	metadataLoaded = true;
    }
}

XML-i laeme kohe rakenduse laadimisel:

void Page_Loaded(object sender, RoutedEventArgs e)
{
    LoadMetadataXML();
}

Nüüd ei olegi muud üle jäänud, kui hiire liigutamisel see meie GetImageTag meetod välja kutsuda:

this.MouseMove += delegate(object sender, MouseEventArgs e)
{
	GetImageTag(msi, e.GetPosition(this.msi));
	if (mouseButtonPressed)
       	{
		mouseIsDragging = true;
	}
	this.lastMousePos = e.GetPosition(this.msi);
};

Ja ongi valmis, nüüd on meil teada ka lisainfo piltide kohta. Muidugi võiks seda infot kuvada kuidagi palju peenemalt, aga ma mõtlen, et kood võiks siin näidetes võimalikult lihtsaks jääda :)

Salavõti Deep Zoom rakendusesse

Hard Rock Cafe memrobilia veebileht on üks väga lahe näide Deep Zoom rakendusest. Äge on veel see, et sinna on sisse programmeeritud salavõti! Minge vajutage V tähte ja vaadake, mis juhtub ;) . Äge ju… aga kui keeruline seda teha on? Proovime.

Kõigepealt on meil vaja üht DeepZoom veebilehte ning kuna me tahame selle käitumist ise muuta ekpordime selle Silverlight rakendusena.

Lisame Page.xaml.cs faili sündmusekuulari klahvivajutustele:

void msi_Loaded(object sender, RoutedEventArgs e)
{
     this.KeyUp += new KeyEventHandler(Page_KeyUp);
}

Ja kogu “maagia” seisneb suurenduse ja selle asukoha (vasaku ülemise nurga) paika panekus. Otsustame ka, et salavõtmeks on meil V klahv.

void Page_KeyUp(object sender, KeyEventArgs e)
{
	if (e.Key == Key.V)
	{
            //zoomi asukoht
	    this.msi.ViewportOrigin = new Point(0.63691, 0.408636);
            //zoomi tase, mida väiksem number, seda rohkem pildi sees me oleme
	    this.msi.ViewportWidth = 0.0001;
	}
}
Ja ongi kõik, tulemust saab uurida siit.
Siin on väga hea blogipostitus, mis selgitab, mida muutujad ViewportOrigin ja ViewportWidth täpselt tähendavad.

Seadragon (Deep Zoom) ja iPhone

Nohik minu sees on alati väga rõõmus, kui mõned minu lemmiktehnoloogiad sõpradeks saavad. Ja täna on jälle üks selline päev.

Nimelt Seadragon, mis on see sama tehnoloogia, millel põhineb Silverlight Deep Zoom on nüüd iPhone peal.

Seadragon võimaldab vaadata hästi suuri pilte (mõtle gigabaitides) nii, et see ei võta miljon aastat aega, aga täpselt nii detailselt ja hea kvaliteediga kui vaja.

Hetkel on Seadragon Mobile rakenduses võimalik sirvida kogu Photosynth veebi ja siis on veel hulk erinevaid äärmiselt kõrgekvaliteedilisi albumeid.

Mõned pildid:

Seadragon on iPhone by you.

photo.jpg by you.

photo.jpg by you.