Detta är en serie korta noteringar som jag vill dela med mig efter att ha utvecklat Windows Phone appen TrafikTitt. Mer info om appen och ett index på noteringarna finns här.

En stor del av min tid, när jag utvecklade TrafikTitt, gick åt att få in alla kameror ifrån trafiken.nu. De hade tyvärr inte något enkelt sätt att exportera kamerapositionerna, utan det fick göras manuellt.

Jag valde att skapa en xmlfil, inkludera den i installationen och vid uppstarten av applikationen, initiera domänstrukturen med informationen.

Jag använda mig av xml av flera skäl:

  • Det är lätt lägga till information genom att endast använda sig av vilken texteditor som helst.
  • I framtiden kan det vara möjligt att uppdatera kamerapositionerna genom att anropa en webbtjänst som hämtar en uppdaterad xmlfil.

Min xmlfil har följande utseende (naturligtvis är denna förkortad):

<?xml version="1.0" encoding="utf-8" ?>
<TraficCameras>
  <Region Name="stockholm" Id="1">
    <Route Name="275 Akallavägen" Id="1">
      <TraficCamera>
        <Id>249</Id>
        <Name>Norrviksvägen</Name>
        <Url>http://www.trafiken.nu/cameraimages/249.jpg</Url>
        <Latitude>59.356073</Latitude>
        <Longitude>18.033202</Longitude>
        <CropX></CropX>
        <CropY></CropY>
      </TraficCamera>
    </Route>
    <Route Name="Kista" Id="2">
      <TraficCamera>
        <Id>251</Id>
        <Name>Husbykorset</Name>
        <Url>http://www.trafiken.nu/cameraimages/251.jpg</Url>
        <Latitude>59.414445</Latitude>
        <Longitude> 17.930409</Longitude>
        <CropX></CropX>
        <CropY></CropY>
      </TraficCamera>
    </Route>
</Region>
</TraficCameras>

Jag har en hierarkisk struktur bestående av Region, Route och TraficCamera. (Som den uppmärksamme läsaren ser har jag redan förberett för att ha möjlighet att registrera flera regioner såsom Malmö och Göteborg.) Route är ett begrepp som trafiken.nu använder sig av för att kunna samla ihop ett antal kameror efter en färdväg.

Elementet TraficCamera är den intressanta delen i denna fil. Den innehåller:

  • Name – namnet på kameran
  • Url – länken till kamerabilden på trafiken.nu
  • Latitude och Longitude – positonen på kameran
  • CropX och CropY – vilket inte använts just nu och egentligen skall tas bort

Hur man läser in xml och gör om den till sin domänmodell

Första steget är att läsa in filen. Här är min metod som gör det:

private XElement loadXmlFile()
{
    StreamResourceInfo xml =
    Application.GetResourceStream(new
Uri("/TrafikTitt;component/CameraPlaces.xml",
UriKind.Relative));

    return XElement.Load(xml.Stream);
}

Url:n ser lite konstig ut. Det viktiga för att få det att funka är att man verkligen ser till att xmlfilen har egenskapen Build Action satt till Resource. Då bakas den in i xap-filen, som är den som använts i telefonen.

Nästa steg är att ta denna xml och föra över den till den interna domänmodellen som jag har i programmet.

Den här koden kan ge lite skrämselhicka när man först ser den.

Dictionary<string, Region> regions = 
    (from region in xml.Elements("Region")
        select new Region()
        {
            Name = region.Attribute("Name").Value,
            Id = Convert.ToInt32(region.Attribute("Id").Value),
            Routes = (from route in region.Elements("Route")
                    select new Route()
                    {
                        Name = route.Attribute("Name").Value,
                        Id = Convert.ToInt32(route.Attribute("Id").Value),
                        Cameras = (from cam in route.Elements("TraficCamera")
                                    where cam.Element("Longitude").Value != "0"
                                    select new TraficCamera()
                                    {
                                        Id =
Convert.ToInt32(cam.Element("Id").Value),
                                        Name = cam.Element("Name").Value,
                                        Url = new Uri(cam.Element("Url").Value),
                                        Longitude =
                                            Convert.ToDouble(
                                            cam.Element("Longitude").Value,
                                            new CultureInfo("en-US")),
                                        Latitude =
                                            Convert.ToDouble(
                                            cam.Element("Latitude").Value,
                                            new CultureInfo("en-US")),
                                        IsDisplayed = false,
                                    }).ToList<TraficCamera>()
                    }).ToList<Route>()
        }).ToDictionary(k => k.Name);

Men om vi benar ut det bit för bit så blir det inte så komplext. Jag skapar en kollektion av Region. Varje Region har en egenskap som heter Routes, vilken är en kollektion av Route och varje Route har en kollektion av TraficCamera som finns i egenskapen Cameras.

Denna linqfråga använder sig av linq to xml för att traversera genom xmlfilen och skapa domänen.

Observera att jag använder mig av CulturInfo när jag skall läsa in ett tal som innehåller decimaler. Detta för att jag skall vara säker på att inläsningen skall funka oavsett vilket format som man valt i inställningarna på telefonen.