bottombackground
Dec30

Create and Use of Google Maps Webparts(Dutch)  

Categories: Moss 2007, Wss 3.0
Technorati Tags: ,

I have writen a article for SDN Magazine it is now public available so i published the copy here. It is dutch so if you still want to read it use a Bing translater.

 

Dit artikel beschrijft de mogelijkheden van een Google Maps webpart in een SharePoint Portal omgeving en hoe deze o.a. gebruikt wordt door het Spaarne Ziekenhuis. Het webpart heeft als doel het inzichtelijk maken van de huisartsen in de regio van het ziekenhuis.

Een dergelijke webpart is niet beschikbaar als standaard voor een MOSS omgeving en  is natuurlijk een leuke uitdaging om te bouwen. Rest de vraag wat heeft men allemaal nodig om dit webpart aantrekkelijk te maken en meerwaarde te geven? Vragen die hierbij naar boven kwamen waren onder andere: hoe kan men de locaties ophalen, wat voor iconen zijn intuïtief voor gebruiker en hoe wordt de plaats aangeven ...

De Vraag van de klant

Het Huisartsen-portaal wordt, onafhankelijk van het ziekenhuis, aangeboden in een eigen,  beveiligde omgeving (conform NEN-normen voor de zorg). Transport van data tussen de praktijken van huisartsen en zorginstellingen vindt plaats via een SSL-verbinding.

Het portaal (gebaseerd op het Microsoft Office SharePoint Server 2007 platform) is ontwikkeld en wordt beheerd door de afdeling ICT van het Spaarne Ziekenhuis.

Het Spaarne Ziekenhuis sponsort de ontwikkeling van dit portaal conform de wensen van de huisartsen, op basis van de bestaande technologie. Door gebruik te maken van al aanwezige techniek en kennis wordt het portaal kostenneutraal aangeboden aan de huisartsen. Het doel van het Spaarne Ziekenhuis ligt in de verbetering van de ketenzorg, waardoor zij haar interne processen verder kan optimaliseren.

Het Google Maps webpart biedt de mogelijkheid om te zien welke huisartsen in een bepaalde regio actief zijn. Daarmee toont het informatie van zorgverleners in de regio, zodat in geval van b.v. vakantie de huisarts de zorg voor een cliënt tijdelijk kan overdragen.

Waar te beginnen?

Het vertrekpunt om een Google Map webpart te bouwen is inzicht krijgen in de werking, en hiermee API, van Google Maps. Google heeft uitgebreide informatie op de volgende site: http://code.google.com/intl/nl-NL/apis/maps/documentation/

Fig. 1: Google Maps documentatie

Er zijn 3 versies van de Google API beschikbaar. Versie 2 is de meest recente productie versie. Versie 3 is een beta-versie en heeft met name voor applicaties voor de mobiele telefoon een aantal leuk features. Dit artikel is gebaseerd op het gebruik van versie 2.

De API's van Google Maps zijn goed gedocumenteerd. In deze documentatie staat ook dat er per website een API key aangevraagd moet worden. Deze aanvraag geldt per root van de site. Aanvragen en registreren van de site gaat via de signup: http://code.google.com/intl/nl-NL/apis/maps/signup.html.

Hiervoor dien je de beschikking te hebben over een Google account (zie figuur 2).

figuur 1

Fig.2: Aanmelden bij  Google Maps

Er zijn vier belangrijke punten waarop gelet moet worden bij het bouwen en deployment van het webpart.

Het eerste is dat het verwijderen of wijzigen van het webpart van/op de portal tot gevolg kan hebben dat er opnieuw een key aangevraagd moet worden. Omdat versie 3 nog in beta is, is daarvoor geen key nodig.

In deze business case wordt er de portal aangeboden via een SSL-verbinding en wordt de kaart opgevraagd in Internet Explorer 8. In deze situatie is een Premium Membership verplicht. Google heeft als doel de maps voor iedereen zichtbaar te maken.

Het derde punt is dat Google Maps voornamelijk werkt met JavaScript, wat voor het webpart inhoudt dat er HTML gerenderd moet worden.

Tenslotte worden plaatsbepalingen binnen een Google Map opgegeven (of uitgelezen) via de latitude- en longitude-coördinaten. Latitude beschrijft de locatie van een plaats op aarde ten Noorden of ten Zuiden van de evenaar, en longitude beschrijft de locatie van een plaats op aarde ten Oosten of ten Westen van een Noord-Zuid lijn (de nul meridiaan).

Tom Tom en andere navigatie apperatuur werken ook met deze gegevens om je naar de juiste plaats te brengen.

Vervolgens ben ik voor de zekerheid nog even op Codeplex gaan kijken of er niet al een kant en klare oplossing was die mij veel ontwikkeltijd zou kunnen schelen. Na het testen van verschillende mogelijkheden kwam ik elke keer met hetzelfde resultaat. Het resultaat was dat er een grijs vlak werd getoond met Google erin maar nergens een Map (dit komt waarschijnlijk omdat er wijzigingen zijn gedaan die niet getest zijn bij Google)?

Omdat de adressen van de huisartsen bekend zijn, kunnen deze coördinaten opgehaald en opgeslagen worden. De coördinaten worden bij het gebruikersprofiel van de huisarts opgeslagen (huisartsen zijn de gebruikers van het portaal).

 

TIP: een goede website om snel de coördinaten te vinden op basis van een adres is http://mygeoposition.com/

Fig. 3: MyGeoPosition levert de coordinaten voor een Google Msps locatie

Het webpart zelf

Het maken van het webpart kan eenvoudig via Visual Studio 2008. Hierbij is het aan te raden om met WSP-Builder te werken. Wanneer je WSP-Builder geïnstalleerd hebt, krijg je de mogelijkheid om projecten aan te maken van het type WSP.

Zo'n  project bestaat uit de volgende elementen:

  • een feature, bestaande uit het manifest (feature.xml) en een elements-file (elements.xml);
  • de webpart definitie (HiszMaps.webpart);
  • de webpart class (de logica).

Het vertrekpunt is het maken van een feature waarmee de webpart geïnstalleerd kan worden. Het voordeel van een feature is o.a. de herbruikbaarheid. Het manifest beschrijft de applicatie (in dit geval het webpart) en geeft aan welke elementen (files) er nodig zijn om het webpart goed te installeren.


Het manifest van de feature ziet als volgt uit.

<?xml version="1.0"encoding="utf-8"?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
  Id="D37F0226-5712-44b3-AF52-4E3CADB63A77"
  Version="0.0.0.1"
  Title="SpaarneZiekenhuis HiszMaps Webpart"
  Description="Toont artsen in een Google Map."
  Scope="Site"
  ImageUrl="SZFeatureImage.gif"
  ImageUrlAltText="SpaarneZiekenhuis Feature"
  Hidden="FALSE">
  <ElementManifests>
  <!-- Verwijzingen naar element-file en webpart-file beide
       in feature directory

    <ElementManifest Location="elements.xml" />
    <ElementFile Location="Webparts/HiszMaps.webpart" />
  </ElementManifests>
</Feature>

In dezelfde map als het feature-manifest dient een elements-file geplaatst te worden. Hierin staat aangegeven dat het een webpart is dat in de webpartlijst beschikbaar moet komen. Deze ziet er als volgt uit:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="WebParts" List="113"
    Url="_catalogs/wp" Path="Webparts">
    <File Url="HiszMaps.webpart" Type="GhostableInLibrary">
      <Property Name="Group" Value="SpaarneZiekenhuis" />
    </File>
  </Module>
</Elements>


En als laaste het webpart-file:

<?xmlversion="1.0"encoding="utf-8"?>
<webParts>
  <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
    <metaData>
      <type name="SpaarneZiekenhuis.Core.WebParts.HiszMaps,
        SpaarneZiekenhuis.Core, Version=1.0.0.0,
        Culture=neutral,PublicKeyToken=11a665981f0e5261" />
      <importErrorMessage>
        Cannot import HiszMaps Web Part.
      </importErrorMessage>
    </metaData>
    <data>
      <properties>
        <property name="Title" type="string">
           HiszMaps Web Part
        </property>
        <property name="Description" type="string">
          Toont artsen in een Google Map.
        </property>
      </properties>
    </data>
  </webPart>
</webParts>

Het webpart-file geeft aan wat de eigenschappen zijn van het webpart en welke assembly deze moet gebruiken (type). Bij de Type Name staat de verwijzing naar de code class,  in dit geval dus "SpaarneZiekenhuis.Core.Webparts.HiszMaps".

Het totaal plaatje van de feature ziet er nu zo uit:

Fig. 4: Naam van het webpart-file

De Webpart Class

De class Hiszmaps erft over van de System.Web.UI.WebControls.WebParts.WebPart. In de class zijn de volgende properties gedefineerd.

  • apiKey: key die Google nodig heeft om te kunnen valideren;
  • mapWidth: breedte van het webpart in totaal;
  • mapHeight: hoogte  van het webpart in totaal;
  • zoomLevel: dit is in hoeverre de map ingezoomd is tussen 1 en 19;
  • infoWindowHtml: dit is om de HTML van de getoonde ballon aan te kunnen passen;
  • Longitude: de longitude van de beginplek;
  • Latitude: de latitude van de beginplek;
  • MapType: de manier waarop Google de kaart laat zien (road, satelite of hybride).

Indien het mogelijk moet zijn om een van de eigenschapen van het webpart te kunnen wijzigen via het (web)interface, kun je de volgende code gebruiken:

[Personalizable(PersonalizationScope.Shared),
  WebBrowsable(true),
  Category("Hiszmaps"),
  WebDisplayName("google Api key"),
  WebDescription("google api key")]
Public string APIKey
{
  Get {return _apiKey; }
  Set {_apiKey = value; }
}

Uiteindelijk ziet het editor-window er dan zo uit:

Fig. 5: Editen van properties van het webpart

Om de Google Map op het webpart te tonen dient eerst een instantie van het GooglemapControl.GMapControl aangemaakt te worden. In de  CreateChildControls-functie word de class GMapControl aangeroepen en de properties van de GMapControl gevuld. Vervolgens wordt de control toegevoegd aan de controls-collection van de pagina.

Protected override void CreateChildControls()
{
// add map
  GooglemapControl.GMapControl gM = new
  GooglemapControl.GMapControl(MapWidth,MapHeight);
  gM.GoogleMapLicense = APIKey;
  gM.WidthMap = MapWidth;
  gM.HeightMap = MapHeight;
  Width = MapWidth;
  Height = MapHeight;
  gM.Enabled = true;
  gM.InfoWindowHtml = InfoWindowHtml;
  gM.Markers = GetHuisartsAddresses();
  gM.zoomLevel = ZoomLevel;
  gM.mapThemeType = mapThemeType;
  Controls.Add(gM);
  base.CreateChildControls();
}

De eigenschappen van het GMapControl zijn instelbaar, doordat deze via de webparteigenschappen doorgegeven worden. De werking wordt verderop uitgelegd dit artikel.

De volgende eigenschappen zijn van belang voor de GMapControl:

  • GoogleMapLicence: licentie die je aan kunt vragen bij Google (hier een Premium Member licentie);
  • WidthMap: breedte van het Google Map kaartje;
  • HeightMap: hoogte van het Google Map kaartje;
  • Enabled: mogelijkheid om in/uit te zoomen, te scrollen binnen het kaartje;
  • InfoWindowHtml: stukje tekst (in HTML) met informatie over het kaartje;
  • Markers: posities die in het kaartje moet worden aangezet;
  • ZoomLevel: standaard percentage waarop ingezoomd wordt;
  • MapThemeType: manier waarop het kaartje getoond moet worden; hier kan gekozen worden voor Hybrid, Satellite en Normal.

Om de huisartsenposten te laten zien in de kaart wordt bij gM.Markers de methode Gethuisartsaddresses() aangeroepen. Deze methode geeft een Arraylist terug van alle namen en posities van de huisartsen.

Vervolgens, om de HTML en bijbehorende JavaScript te genereren van het webpart wordt, de Render-functie aangeroepen.

Protected override void Render(HtmlTextWriter writer)
{
  Try
  {
    string str = "";
    writer.Write(str);
    RenderChildren(writer);
    ClientScriptManager csm = Page.ClientScript;
    csm.RegisterStartupScript(this.GetType(), "refreshMap",
      "<script>map.checkResize(); </script>");
  }
  catch (Exception ex)
  {
    //log to file splogger…
  }
}

Door een ClientScriptManager toe te voegen aan de  pagina wordt de uiteindelijke Javascript beschikbaar gemaakt voor de gebruiker.

Nu de webpart-class klaar is kunnen we verder met de GmapControl-class.

GMapControl-class

Bovenaan de class wordt de Control gegenereerd ,in dit geval dus de GmapControl.

De class erft over van de Webcontrol.

[DefaultProperty("Text")]
[ToolboxData
  ("<{0}:GMapControl runat=server></{0}:GMapControl>")]
//generates custom control

Public class GMapControl : WebControl

In deze control worden dezelfde eigenschappen opgenomen als in het webpart, zodat het kaartje met de juiste informatie wordt gegenereerd.

In deze class wordt in een functie het hele script voor de Google Map control opgebouwd. De JavaScript code wordt middels een StringBuilder opgebouwd.

TIP:  string.Format is een goede manier om code goed leesbaar te houden en fouten te voorkomen; let op dat als je de accolade wilt gebruiken in je script dat je ze dan dubbel neerzet, anders gaan ze verloren in het generen van je code

Een Google Map wordt middels een JavaScript- include op het scherm geplaatst. Het script begint met het toevoegen van deze reference-url:

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"<script
    src=""http://maps.google.com/maps?file=api&v=2&key={0}""
    type=""Text/javascript""></script>",
  GoogleMapLicense))

Om de map te tonen is het nodig om in de pagina de locatie aan te geven. Dit gebeurt middels een DIV-tag met een bepaalde Id. De Google Map wordt op de locatie van deze DIV-tag getoond.

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"<div
    id=""large-google-map""
    style=""width:{0}px;height:{1}px;""></div>",
  WidthMap, HeightMap));

Voor de posities van de adressen (markers) wordt een hidden textbox geplaatst.

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"<input name=""markers"" runat=""server"" 
    id=""markers"" type=""hidden"" value="""">
  <input name=""marks"" runat=""server"" id=""marks""
   type=""hidden"" value="""">));

Vervolgens wordt een blokje JavaScript gegenereerd waar het Array in een variabele wordt geplaatst. Hiervoor wordt door de profielen van de huisartsen gegaan en de informatie in het array geplaatst.

Script.Append(string.Format(CultureInfo.InvariantCulture,
  <script type=""Text/javascript"">
    //Global variables
    var gmarkers = [];
    var map;
    var data = ["));

//loop trough all the huisartsen for creation
//of the markers
foreach (HuisArtsProfile huisArts in Markers)
{
  Script.Append(string.Format(CultureInfo.InvariantCulture,
    @"{{account:""{0}"", naam:""{1}"", adres:""{2}"",
    postcode:""{3}"", plaats:""{4}"" , tel:""{5}"",
    email:""{6}"", latitude:""{7}"",
    longitude:""{8}""}},",
    huisArts.Account,
    huisArts.Name,
    huisArts.Address,
    huisArts.PostalCode,
    huisArts.City,
    huisArts.WorkPhone,
    huisArts.WorkEmail,
    huisArts.Latitude,
    huisArts.Longitude));
}
Script.Append("];");

De volgende stap is het opbouwen van de JavaScript functie GoogleMapLoad(). Dit is een standaard functie die GoogleMaps nodig heeft om de kaart te tonen op de pagina.

Belangrijk is dat de grootte van de nieuwe map wordt meegegeven; als deze namelijk vergeten wordt, is het niet mogelijk de map netjes te centreren. Dit resulteert in een grijze layer en de standaard lokatie (home) wordt niet op de juiste plaats aangegeven:

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"
  function GoogleMapLoad() {{
    if (GBrowserIsCompatible())
    {{
      var map = new GMap2(document.getElementById(""{3}""),
      {{ size: new   GSize({5}, {6}) }});
      var zoomLevel =  parseInt({4});
      var point = new GLatLng(52.325243,4.653537);
      map.setCenter(point,zoomLevel, {7});
      map.addControl(new GMapTypeControl());
      map.addControl(new GLargeMapControl());
      var geocoder = new GClientGeocoder();
      showMarker(map, geocoder, """",
        ""Spaarne Ziekenhuis"", ""Spaarnepoort  1"", """",
        ""Hoofddorp"", """", """",
        52.325243, 4.653537, spaarneicon);

      for(var i = 0; i < data.length; i++)
      {{
        var account = data[i].account;
        var name = data[i].naam;
        //etc..
        showMarker(map, geocoder, account, name, address,
          postalcode, city, phone, email, latitude,
          longitude, icon);
 
      }}
    }};
  }};",
  GoogleMapLicense, //0
  longtitude,//1
  latitude,   //2
  "large-google-map", //3
  zoomLevel, // 4
  WidthMap.ToString(), //5
  HeightMap.ToString(),//6
  mapThemeType));//7

Hier komt ook de referentie terug op basis van het Id naar de DIV-tag, die we eerder hebben toegevoegd.

In deze code word de Showmarker-functie aangeroepen. Dit is een standaard functie van GoogleMaps waar de marker (positieaanduiding) wordt geplaatst. Deze functie wordt voor 2 doeleinden aangeroepen: 1 keer voor het plaatsen van de hoofdvestiging, het Spaarne Ziekenhuis (spaarneIcon), en 1 keer in de array (dus gebeurt bij elke loop) voor het bepalen van de posities van de huisartsenposten, met het huisartsenicoon.

Het ShowMarker()-script voegt de latitude en longitude samen in een nieuwe variable GlatLng. Deze wordt gebruikt om de CreateMarker functie aan te roepen; als deze terugkomt, kan hij toegevoegd worden aan de Map via Map.AddOverlay.

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"function ShowMarker(map,geocoder,account,name,
    address,postalcode,city,phone,email,
    latitude,longitude,icon)

  {{"));

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @" var loc = new GLatLng(latitude, longitude);
  var marker = createMarker(loc,account,name,address,
    postalcode,city,phone,email,icon);
  map.addOverlay(marker);
}}
GoogleMapLoad()
</script>"));

GoogleMapLoad() dient aangeroepen te worden op het moment dat de pagina wordt geladen. Dit wordt aan het einde van het script toegevoegd.

In de CreateMarker-functie wordt de icoon op de juiste positie gegenereerd. Aan de marker kan een event toegevoegd worden. Door een listener toe te voegen aan zo'n event is bekend welke functie moet worden aangeroepen om te reageren op mouseovers van een marker.

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"function CreateMarker(loc,account,name,address,
    postalcode,city,phone,email,icon)
  {{
    var marker = new GMarker(loc,icon);
    GEvent.addListener(marker, ""mouseover"", function()
{{"));

Aan deze mouseover wordt een functie toegevoegd die een Popup-window toont met de informatie die aan het webpart wordt meegeven in de eigenschap "InfoWindowHtml".

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @" var html = ""<b><a
    href='#'>""+name+""</a></b>""+""<br/>""+
    address+""<br/>""
    +postalcode+""<br/>""+city+""<br/>""+phone\n
    marker.openInfoWindowHtml(html);"));
}
Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"}});
return marker;
}}"));

Rest nog de definities van de iconen. De iconen worden opgeslagen in de 12 hive, waardoor deze altijd aanspreekbaar zijn en security-problemen voorkomen worden, waar je ook bent binnen SharePoint:

Script.Append(string.Format(CultureInfo.InvariantCulture,
  @"// create marker icon
  var icon = new GIcon();
  icon.image = ""/_layouts/images/huisarts.png"";
  icon.iconSize = new GSize(30, 30);
  icon.iconAnchor = new GPoint(30, 30);
  icon.infoWindowAnchor = new GPoint(30, 30);

Tenslotte wordt de opgebouwde string teruggegeven aan de HTMLwriter.

output.Write(Script.ToString())

Na het compileren en installeren van het webpart is dit het eindresultaat:

Fig. 6: eindreslutaat

Conclusie

Het is vrij éénvoudig Google Maps te integreren in een SharePoint webpart, en de vraag daarna ziet men dan ook steeds meer toenemen.

De functies die beschreven zijn, maken deel uit van de basisfuncties van Google. Van hieruit is het mogelijk andere functies toe te voegen zoals een routeberekening. Zoals altijd is het van belang dat de JavaScript klopt, anders wordt er niets weergegeven. Dit kan ook resulteren in StringBuilder fouten.

Ik heb ook de tegenhanger van Google Maps gemaakt, namelijk Bing Maps. De structuur is nagenoeg gelijk, en voordeel is dat er hier geen key  benodigd is. Persoonlijk vind ik dat de map ook mooier weergeven wordt. Binnenkort toon ik een voorbeeld van de Bing-map op mijn website.

 
Bookmark this post with:                                  
 
 

Links to this post

Comments

Leave a comment





CAPTCHA Image Validation






© Copyright 2010 KbWorks B.V.    Paul Keijzers op linkedin KbWorksLinkedin Paul Keijzers op Twitter @KbWorksTwitter KbWorks op Promote your networkPromote Your network