SharePoint Benutzer und deren Berechtigungen mit PowerShell auslesen

Dieser Artikel beschreibt, wie mittels einfacher PowerShell-Kommandos die Benutzer und Berechtigungen von einer Site Collection eines SharePoint Servers ausgelesen werden können.

Durch das PowerShell SharePoint-AddIn werden eine Fülle von Befehlen bereitgestellt, mit denen man Informationen aus dem SharePoint 2010 auslesen, anlegen oder aber auch den SharePoint konfigurieren kann.

Das folgende PowerShell-Commandlet ist folgendermaßen aufgebaut:

  1. Hinzufügen des SharePoint Powershell-AddIn
  2. Abfrage aller Benutzer und deren Berechtigungen über den Befehl Get-SPUser
  3. Speichern der Informationen in einem Objekt für eine spätere Ausgabe

In diesem Fall möchten wir die folgenden Benutzer und deren Berechtigungsinformationen ausgeben:

  • Loginname
  • Benutzername
  • explizite Benutzerrolle
  • Benutzerrolle innerhalb der Gruppe
  • Gruppe(n)
param($Site = "<Name der Site Collection>")

if((Get-PSSnapin -Name "Microsoft.SharePoint.PowerShell" -EA 0) -eq $null)
{
  Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}


#Auslesen aller Benutzer mit Detailinformationen wie Gruppen, Rollen, etc. innerhalb der aktuellen Site Collection Url.
$users = Get-SPUser -Web $Site -ErrorAction Stop | select UserLogin, DisplayName, @{Label="ExplicitGivenRoles";expression={$_.Roles}}, @{Label="RolesGivenViaGroups";expression={$_.Groups | %{$_.Roles}}}, Groups

$users | foreach{
    $strGroups = ""
    $strGroupWithoutPraefix = ""
    $_.Groups | foreach{$strGroups += "$_, "}
    if($strGroups -ne "")
    {$strGroupWithoutPraefix = $strGroups.Substring(0,$strGroups.Length-2)}
    $object = New-Object PSObject
        $object | add-member noteproperty -name "UserLogin" -value $_.UserLogin
        $object | add-member noteproperty -name "DisplayName" -value $_.DisplayName
        $object | add-member noteproperty -name "ExplicitRoles" -value $_.ExplicitGivenRoles
        $object | add-member noteproperty -name "GroupRoles" -value $_.RolesGivenViaGroups
        $object | add-member noteproperty -name "SPGroup" -value $strGroupWithoutPraefix
        $object
}

Fazit

Das innerhalb dieses Artikels beschriebene Powershell-Commandlet ermöglicht es, mit wenigen Zeilen Code die Benutzer und Berechtigungen einer Site Collection im SharePoint Server auszulesen.
 
Zusätzlich zum aktuell verwendeten Commandlet Get-SPUser, existieren innerhalb des SharePoint SnapIns noch weitere Commandlets, welche Sie auf den Microsoft Technet noch näher erläutert finden.

SharePoint 2010 Deployment (Teil 5) – Deployment von SQL Server Reporting Services Reports

Bei SharePoint 2010 Deployment handelt es sich um eine Serie von Blogbeiträgen, die die verschiedenen Schritte für das Deployment von SiteCollections erläutert und ausführt. Dabei wird der Fokus auf das Wiederherstellen einer Backup-Datei aus einer SharePoint-Umgebung in eine andere (z.B. von einer Entwicklungsumgebung auf eine Test- oder Produktivumgebung) gelegt.

 

Die Serie “SharePoint 2010” besteht aus den folgenden Beiträgen:

  1. Erstellen einer SiteCollection
  2. Wiederherstellen einer Backup-Datei
  3. Erstellen von Secure Store Applikationen
  4. Importieren von Business Data Connectivity Models (BDCM)
  5. Deployment von SQL Server Reporting Services (SSRS) Reports

Im letzten Teil dieser Blogserie behandeln wir das Deployment von SSRS Reports in eine SharePoint-Bibliothek mit Hilfe von Powershell. Dafür erweitern wir die, im ersten Teil der Blogserie, erstellte Konfigurationsdatei sowie das Powerhell-Skript, um die Aktionen für das Deployment von Report-Dateien (*.rdl) und der Datenquelldateien (*.rds). Dieses Powershell-Skript kann entsprechend der Anforderungen individuell angepasst werden.

Für die Erweiterung, der im ersten Teil der Blogserie erstellten Powershell- und Konfigurationsdatei, sowie das nachträgliche Ausführen sind die folgenden Schritte auszuführen:

  1. Erweitern der Konfigurationsdatei
  2. Erweitern der Powershell-Datei
  3. Ausführen

In diesem Beispiel deployen wir einen SSRS Report, der eine Shared DataSource verwendet.

Voraussetzungen

Voraussetzung, für ein erfolgreiches Deployment von SSRS Reports, ist das Bestehen einer Reportbibliothek, sowie einer Datenverbindungsbibliothek auf ihrer SiteCollection.

Des Weiteren muss SSRS im “SharePoint Integrated Mode” konfiguriert werden. Hier finden Sie eine Anleitung dazu.

Erweitern der Konfigurationsdatei

Um ein unabhängiges Deployment zu verwenden, haben wir im ersten Teil dieser Blogserie Erstellen einer SiteCollection eine Konfigurationsdatei (Konfiguration.xml) erstellt. Diese müssen wir um die zu konfigurierenden Elemente für das Deployment von Reports erweitern.

<ReportSourcePath>Pfad zu den Report-Dateien (*.rdl, *.rds, *.rsd)</ReportSourcePath>
<TargetReportLibrary>Name der Reportbibliothek</TargetReportLibrary>
<TargetReportFolder>Name des Ordners in dem die Reports liegen</TargetReportFolder>
<TargetDataConnectionLibrary>Name der Datenverbindungsbibliothek</TargetDataConnectionLibrary>
<TargetDataConnectionFolder>Name des Ordners in dem die DataSources liegen</TargetDataConnectionFolder>
<TargetDataSetLibrary>Name der DataSets-Bibliothek</TargetDataSetLibrary>
<TargetDataSetFolder>Name des Ordners in dem die DataSets liegen</TargetDataSetFolder>
<SSRSWebservice>/ReportServer/ReportService2010.asmx?wsdl</SSRSWebservice>

Erweitern der Powershell-Datei

Im folgenden Schritt erweitern das im ersten Teil der Blogserie erstellte Powershell-Skript (Deployment.ps1) um die in der Konfigurationsdatei neu erstellten Elemente.

#Reporting Variables
$ReportSourcePath = $configurationElement.Config_Content.ReportSourcePath
$TargRepLibrary = $configurationElement.Config_Content.TargetReportLibrary
$TargetReportFolder = $configurationElement.Config_Content.TargetReportFolder
$TargDataConnLibrary = $configurationElement.Config_Content.TargetDataConnectionLibrary
$TargDataConnFolder = $configurationElement.Config_Content.TargetDataConnectionFolder
$TargDataSetLibrary = $configurationElement.Config_Content.TargetDataSetLibrary
$TargDataSetFolder = $configurationElement.Config_Content.TargetDataSetFolder

$SSRSWebservice = $configurationElement.Config_Content.SSRSWebservice
$WebserviceUrl = $Webapplication + $SSRSWebservice

#Create Proxy
$ssrsProxy = New-WebServiceProxy -Uri $WebserviceUrl -UseDefaultCredential

$force = $true;
$ErrorActionPreference="Stop"
$proxyNamespace = $ssrsProxy.GetType().Namespace

Als nächstes erstellen wir drei Funktionen:

  1. Install-DataSource
  2. Install-DataSets
  3. Install-Reports

Install-DataSources

function Install-DataSource([string] $dsFile)
{
$dataSourcePath = $newCollectionSite + "/" + $TargDataConnLibrary
$dataSourceFolder = $dataSourcePath + "/" + $TargDataConnFolder

if($force)
{
#Check if folder is existing, create if not found
try
{
$ssrsProxy.CreateFolder($TargDataConnFolder, $dataSourcePath, $null)
Write-Host "Created new folder: $TargDataConnFolder"
}
catch [System.Web.Services.Protocols.SoapException]
  {
if ($_.Exception.Detail.InnerText -match "[^rsItemAlreadyExists400]")
{
Write-Host "Folder: $TargDataConnFolder already exists."
}
else
{
$msg = "Error creating folder: $TargDataConnFolder. Msg: '{0}'" -f $_.Exception.Detail.InnerText
Write-Error $msg
}
}
}



try
{
#Load the data source Xml
$DSXml = Get-Content ($dsFile);
#Initialize a DataSourceDefinition object
$dsDefinition = New-Object ("$proxyNamespace.DataSourceDefinition");
#Initialize a DataSource object
$dSource = New-Object ("$proxyNamespace.DataSource")
$dSource.Item = $dsDefinition
#Read the settings from XML and populate related props
$dsDefinition.Extension = $DSXml.RptDataSource.ConnectionProperties.Extension
$dsDefinition.ConnectString = $DSXml.RptDataSource.ConnectionProperties.ConnectString
$dsDefinition.ImpersonateUserSpecified = $false
$dsDefinition.Prompt = $null
$dsDefinition.WindowsCredentials = $false
#$dsDefinition.CredentialRetrieval = [CredentialRetrievalEnum]::Integrated
$dSource.Name = $DSXml.RptDataSource.Name
$dsFileName = [String]::Concat($DSXml.RptDataSource.Name,".rsds")
$rsdsAbsoluteUrl = [string]::Concat($dataSourceFolder,$dsFileName)

#Call Proxy to upload report
$ssrsProxy.CreateDataSource($dsFileName,$dataSourceFolder,$force,$dsDefinition,$null)
if($warnings.Length -eq $null) { Write-Host "Upload Success." }
else { $warnings | % { Write-Warning "Warning: $_" }}
}
catch [System.IO.IOException]
{
$msg = "Error while reading rsds file : '{0}', Message: '{1}'" -f $dsFile, $_.Exception.Message
Write-Error $msg
}
catch [System.Web.Services.Protocols.SoapException]
{
$msg = "Error while uploading rsds file : '{0}', Message: '{1}'" -f $dsFile, $_.Exception.Detail.InnerText
Write-Error $msg
}
}

Install-DataSets

function Install-DataSets([string] $dSetFile)
{
$dataSetPath = $newCollectionSite + "/" + $TargDataSetLibrary
$dataSetFolder = $dataSetPath + "/" + $TargDataSetFolder

if($force)
{
#Check if folder is existing, create if not found
try
{
$ssrsProxy.CreateFolder($TargDataSetFolder, $dataSetPath, $null)
Write-Host "Created new folder: $TargDataSetFolder"
}
catch [System.Web.Services.Protocols.SoapException]
{
if ($_.Exception.Detail.InnerText -match "[^rsItemAlreadyExists400]")
{
Write-Host "Folder: $TargDataSetFolder already exists."
}
else
{
$msg = "Error creating folder: $TargDataSetFolder. Msg: '{0}'" -f $_.Exception.Detail.InnerText
Write-Error $msg
}
}
}



#Load the data source Xml
$DSXml = Get-Content ($dSetFile);
$file = Get-ChildItem($dSetFile);
$site = Get-SPSite $newCollectionSite;
$web = $site.RootWeb;
$rsdFolder = $web.GetFolder("$TargDataSetLibrary/$TargDataSetFolder");

$destUrl = $dataSetFolder+"/"+$file.Name

(Get-Content $dSetFile) | % {$_ -replace "</DataSourceReference>",".rsds</DataSourceReference>"} | Set-Content -path $dSetFile

$rsdFile = $web.GetFile($desturl)
$fileCheckedOut = "N"
if($rsdFile.Exists)
{
$rsdFile.CheckOut();
$fileCheckedOut = "Y"
}
$stream = [IO.File]::OpenRead($dSetFile)
$resultingfile = $rsdFolder.files.Add($desturl,$stream,$true)
$stream.close()

if($fileCheckedOut -eq "Y")
{
$rsdFile.CheckIn("Deployment Script")
}
$rsdFile.Update()

WRITE-HOST "Successfully Deployed Data Set:" $file.Name
}

Install-Reports

function Install-Reports([string]$rdlFile)
{
$reportPath = $newCollectionSite+"/"+$TargRepLibrary

if($force)
{
#Check if folder is existing, create if not found
try
{
$ssrsProxy.CreateFolder($TargetReportFolder, $reportPath, $null)
Write-Host "Created new folder: $TargRepLibrary"
}
catch [System.Web.Services.Protocols.SoapException]
{
if ($_.Exception.Detail.InnerText -match "[^rsItemAlreadyExists400]")
{
Write-Host "Folder: $TargRepLibrary already exists."
}
else
{
$msg = "Error creating folder: $TargRepLibrary. Msg: '{0}'" -f $_.Exception.Detail.InnerText
Write-Error $msg
}
}
}

#Add datasource and dataset extensions
(Get-Content $rdlFile) | % {$_ -replace "</DataSourceReference>",".rsds</DataSourceReference>"} | Set-Content -path $rdlFile
(Get-Content $rdlFile) | % {$_ -replace "</SharedDataSetReference>",".rsd</SharedDataSetReference"} | Set-Content -path $rdlFile

#Get the RDL Item
$rptFileInfo = Get-Item ($rdlFile);
$reportName = $rptFileInfo.Name;

try
{
#Get Report content in bytes
$byteArray = gc $rdlFile -encoding byte

$reportFolder = $newCollectionSite + "/" + $TargRepLibrary + "/" + $TargetReportFolder

#Call Proxy to upload report
$warnings = $null
$ssrsProxy.CreateCatalogItem("Report",$reportName,$reportFolder,$force,$byteArray,$null,[ref]$warnings)
if($warnings.Length -eq $null) { Write-Host "Upload Success." }
}
catch [System.IO.IOException]
{
$msg = "Error while reading rdl file : '{0}', Message: '{1}'" -f $rdlFile, $_.Exception.Message
Write-Error $msg
}
catch [System.Web.Services.Protocols.SoapException]
{
$msg = "Error while uploading rdl file : '{0}', Message: '{1}'" -f $rdlFile, $_.Exception.Detail.InnerText
Write-Error $msg
}

#Fix up the data source
$rptXml = Get-Content ($rdlFile);
#Extract the data source used by report
$localDSName = $rptXml.Report.DataSources.DataSource.Name
$dataSourceFolder = $newCollectionSite + "/" + $TargDataConnLibrary + "/" + $TargDataConnFolder

$rptAbsoluteUrl = [string]::Concat($reportFolder,"/",$reportName)
$rsdsAbsoluteUrl = [string]::Concat($dataSourceFolder,"/",$localDSName,".rsds")

$catalogItemDtSrcs = $ssrsProxy.GetItemDataSources($rptAbsoluteUrl)
$reference = New-Object ("$proxyNamespace.DataSourceReference")
$dsNew = New-Object ("$proxyNamespace.DataSource")
$reference.Reference = $rsdsAbsoluteUrl
$dsNew = $catalogItemDtSrcs[0]
$dsNew.Item = $reference
$ssrsProxy.SetItemDataSources($rptAbsoluteUrl,$catalogItemDtSrcs)
}

Im nächsten Schritt werden die Dateien (*.rdl, *.rsd, *.rds) in die entsprechenden Bibliotheken auf der Zielseite deployed.

$ReportDataSourcePath = $ReportSourcePath + "DataSource"
$ReportDataSetSourcePath = $ReportSourcePath + "DataSet"

#Deploy Data sources 
[Object[]] $dataSourcesToPublish = [System.IO.Directory]::GetFiles($ReportDataSourcePath, "*.rds");
$dataSourcesToPublish | % { Install-DataSource $_ };
  
#Deploy Data Sets 
[Object[]] $dataSetsToPublish = [System.IO.Directory]::GetFiles($ReportDataSetSourcePath, "*.rsd");
$dataSetsToPublish | % { Install-DataSet $_ };
  
#Deploy Reports
[Object[]] $reportsToPublish = [System.IO.Directory]::GetFiles($ReportSourcePath, "*.rdl");
$reportsToPublish | % { Install-Reports $_ };

Abschließend müssen die Dateien, falls noch nicht geschehen, als MajorVersion deployed. Ansonsten können andere Benutzer nicht auf die Reportdateien zugreifen und diese erfolgreich ausführen.

#Publish a Major Version of the files stored in the Report Library
$spAssignment = Start-SPAssignment
$list = (Get-SPWeb -identity $newCollectionSite -AssignmentCollection $spAssignment).Lists["$TargetReportLibrary"]
foreach($item in $list.Items)
{
   $item.File.Publish("Copy of version: $VersionLabel")
   $itemName = $item.Name
   Write-Host "Successfully Published Major Version for file: $itemName"
}

Ausführen

Öffnen Sie nun auf dem Server das Microsoft SharePoint 2010 Management Shell und navigieren zu dem Verzeichnis, in welchem Sie das Powershell-Skript Deployment.ps1 gespeichert haben.

Eine Anleitung, um diese Powershell-Skript auch mit dem vom Windows Server mitgelieferten Powershell-Tool ausführen zu können, finden Sie im zweiten Teil dieser Blogserie.

Den Code, der in den vorherigen Artikeln dieser Blogserie erstellt wurde, können Sie auskommentieren (<# Code #>). So wird nicht alles, sondern nur der Teil für das Deployment der SSRS Reports ausgeführt.

Führen Sie das Skript Deployment.ps1 aus. Anschließend sollte folgendes Fenster erscheinen.

image

Fazit

Zusammenfassend kann man sagen, dass sich mit Hilfe von Powershell in SharePoint 2010 einfach administrative und konfigurierbare Schritte ausführen lassen. Dieses bedeutet, insbesondere für Administratoren und Entwickler, eine erhebliche Zeitersparnis.

Das Deployen von SSRS Reports und deren dazugehörigen Datenquelldateien mit Hilfe von Powershell ist dahingehend sehr hilfreich, da die Pfade der SharePoint-Umgebung angepasst werden. Sollte man die Reports nicht mit diesem Skript deployen, bleiben die Pfade erhalten, die auf der SharePoint-Umgebung vorhanden waren von der die Backup-Datei erstellt wurde.

 

Mit den Dateien Konfiguration.xml und Deployment.ps1, die im Laufe der Blogserie erstellt und erweitert wurden, kann man ohne großen Aufwand seine entwickelte SiteCollection auf eine andere SharePoint-Umgebung übertragen. Man spart sich einen erheblichen Zeitaufwand, da Dateien (z.B. BDCM-Dateien) nicht noch der Umgebung angepasst werden müssen. Des Weiteren erspart man sich die gesamte Erstellung und Einstellung in der Central Administration der jeweiligen SharePoint-Umgebung.

Ich hoffe, dass ich Ihnen mit dieser BlogSerie das Deployment einer SiteCollection von der einen SharePoint-Umgebung zu einer anderen erleichtern konnte und wünsche Ihnen viel Spaß beim Testen des Deployments.

Die Dateien Deployment.ps1 und Konfiguration.xml zu der Blogserie SharePoint 2010 Deployment können Sie sich hier herunterladen.

Hier finden Sie eine Übersicht aller Blogbeiträge zu dieser Blogserie.

Teil 1                    Teil 2                    Teil 3                    Teil 4                    Teil 5

SharePoint 2010 Deployment (Teil 4) – Importieren von Business Data Connectivity Models

Bei SharePoint 2010 Deployment handelt es sich um eine Serie von Blogbeiträgen, die die verschiedenen Schritte für das Deployment von SiteCollections erläutert und ausführt. Dabei wird der Fokus auf das Wiederherstellen einer Backup-Datei aus einer SharePoint-Umgebung in eine andere (z.B. von einer Entwicklungsumgebung auf eine Test- oder Produktivumgebung) gelegt.

 

Die Serie “SharePoint 2010 Deployment” besteht aus den folgenden Beiträgen:

  1. Erstellen einer SiteCollection
  2. Wiederherstellen einer Backup-Datei
  3. Erstellen von SecureStore-Applikationen
  4. Importieren von Business Data Connectivity Models (BDCM)
  5. Deployment von SQL Server Reporting Service Reports

Im vierten Teil dieser Blogserie behandeln wir das Importieren von Business Data Connectivity Models in SharePoint 2010 mit Hilfe von Powershell. Dafür erweitern wir die, im ersten Teil der Blogserie, erstellte Konfigurationsdatei sowie das Powershell-Skript, um die Aktionen für das Importieren von BDCM-Dateien. Diese Powershell-Skript kann entsprechend der Anforderungen individuell angepasst werden.

Dieser Schritt muss nur in das Deployment eingebunden werden, wenn Sie Business Data Connectivity Models (BDCM) erstellt haben, die in die Zielumgebung deployed werden müssen.

Für das Erweitern der im ersten Teil dieser Blogserie erstellten Powershell-Datei sowie das nachträgliche Ausführen, sind die folgenden Schritte notwendig:

  1. Erweitern der Konfigurationsdatei
  2. Erweitern der Powershell-Datei
  3. Ausführen

Beim Importieren von BDCMs ist zu beachten, dass die Datenquellen, auf die das Model zugreift, vorhanden sind.

Was sind Business Data Connectivity Models (BDCM)

BDCMs werden verwendet, um in SharePoint 2010 externe Listen z.B. aus einer SQL Server Datenbank oder über einen Webservice, einzubinden. Mit Hilfe solcher externen Listen können die Daten einfach von den Benutzern bearbeitet werden. Die Aktionen, die in diesem Model zu Verfügung stehen, werden beim Erstellen eines Business Data Connectivity Model gewählt.

Eine Möglichkeit, eine BDCM-Datei zu erstellen, ist das Erstellen eines External Content Types der anschließend als BDCM-Datei exportiert wird.

Hier finden Sie eine Anleitung um einen External Content Type zu erstellen.

Erweitern der Konfigurationsdatei

Um ein unabhängiges Deployment zu verwenden, haben wir im ersten Teil diese Blogserie Erstellen einer SiteColleciton, eine Konfigurationsdatei (Konfiguration.xml) erstellt. Diese müssen wir nun um den Pfad für die BDCMs erweitern

<!-- Paths -->
<BDCMFilePath>Pfad zu den BDCM-Dateien</BDCMFilePath>

Erweitern der Powershell-Datei

Im nächsten Schritt erweitern wir, das im ersten Teil der Blogserie erstellte Powershell-Skript (Deployment.ps1) um das Element <BDCMFilePath> aus der Konfigurationsdatei

$BDCMFilePath = $configurationElement.Config_Content.BDCMFilePath

Im zweiten Schritt müssen wir dem Powershell-Skript nun dem Befehl zum Importieren der BDCMs hinzufügen.

Write-Host "Import Business Data Catalog Models Start"
#Get metadata informations from the existing Business Data Catalog.
$MetadataStore = Get-SPBusinessDataCatalogMetadataObject -BdcObjectType Catalog -ServiceContext $Webapplication

#Import Business Data Catalog Models
#Read content of the folder to get the list of the BDCM files.
$models = [IO.Directory]::GetFiles($BDCMFilePath, "*.bdcm")
foreach($model in $models)
{
  Import-SPBusinessDataCatalogModel -Identity $MetadataStore -Path "$model" -force -    ModelsIncluded -PermissionsIncluded -PropertiesIncluded -Confirm:$false
  Write-Output "BDCM '$model' imported successfully."
}
Write-Host "Import Business Data Catalog Models End"
Write-Host "----------------------------------------------------"

Ausführen

Öffnen Sie nun auf dem Server das Microsoft SharePoint 2010 Management Shell und navigieren zu dem Verzeichnis, in dem Sie das Powershell-Skript gespeichert haben.

Um das Powershell-Skript auch in der von Windows Server mitgelieferten Powershell-Konsole auszuführen, finden Sie im zweiten Teil dieser Blogserie eine Anleitung.

Führen Sie das Powershell-Skript Deployment.ps1 aus. Anschließend sollte folgendes Fenster erscheinen:

image

Fazit

Zusammenfassend kann man sagen, dass sich mit Hilfe von Powershell in SharePoint 2010 einfach administrative und konfigurierbare Schritte ausführen lassen. Dieses bedeutet, insbesondere für Administratoren und Entwickler, eine erhebliche Zeitersparnis.

Aufgrund des angepassten Skriptes können nun BDCM-Informationen ausgewertet werden, und diese in das Deployment integriert werden. Hierbei werden die Dateien passend ausgelesen und, mit Hilfe des Powershell-Skripts, in die neue Datenbank importiert.

Im fünften Teil dieser Blogserie SharePoint 2010 Deployment beschäftigen wir uns mit dem Thema Deplyoment von SQL Server Reporting Services Reports.

Hier finden Sie eine Übersicht aller Blogbeiträge zu dieser Blogserie

Teil 1                    Teil 2                    Teil 3                    Teil 4                    Teil 5

SharePoint 2010 Deployment (Teil 3) – Erstellen von Secure Store Applikationen

Bei SharePoint 2010 Deployment handelt es sich um eine Serie von Blogbeiträgen, die die verschiedenen Schritte für das Deployment von SiteCollections erläutert und ausführt. Dabei wird der Fokus auf das Wiederherstellen einer Backup-Datei aus einer SharePoint-Umgebung in eine andere (z.B. von einer Entwicklungsumgebung auf eine Test- oder Produktivumgebung) gelegt.

 

Die Serie “SharePoint 2010 Deployment” besteht aus den folgenden Beiträgen:

  1. Erstellen einer SiteCollection
  2. Wiederherstellen einer Backup-Datei
  3. Erstellen von Secure Store Applikationen
  4. Importieren von Business Data Connectivity Models
  5. Deployment von SQL Server Reporting Services Reports

Im dritten Teil dieser Blogserie behandeln wir das Erstellen von Secure Store Applikationen in SharePoint 2010 mit Hilfe von Powershell. Dafür erweitern wir die, im ersten Teil der Blogserie, erstellte Konfigurationsdatei sowie das Powershell-Skript, um die Aktionen für das Erstellen einer Secure Store Applikation. Dieses Powershell-Skript kann entsprechend der Anforderung individuell angepasst werden.

Für das Erweitern, der in dem ersten beiden Teilen der Blogserie erstellten Powershell-Datei, sowie das nachträgliche Ausführen sind die folgenden Schritte auszuführen:

  1. Erweitern der Konfigurationsdatei
  2. Erweitern der Powershell-Datei
  3. Ausführen

Was ist SharePoint 2010 Secure Store Service

Der Secure Store Service ist eine Dienstapplikation, welche als Manager für Berechtigungen z.B. auf SQL Server Datenbanken fungiert. Es ersetzt, das aus SharePoint 2007 bekannte, Single Sign-On.

Ein Secure Store Service kann aus einer oder mehreren Secure Store Applikationen bestehen. Wobei jede Applikation auf eine andere Datenquelle zugreift.

Bevor wir mit dem Erstellen von Secure Store Applikationen beginnen, sollte sichergestellt werden, dass bereits ein Key in den Secure Store Services generiert und eine Pass Phrase erstellt worden sind.

Klicken Sie hierzu in der Central Administration unter Application Management->Manage service applications auf den Secure Store Service. Wenn noch kein Master Key erstellt worden ist, klicken Sie auf Generate New Key, um einen neuen Key zu erstellen.

image

Geben Sie im nächsten Schritt einen Key für die Pass Phrase ein.

image

Weitere Informationen zur Konfiguration des Secure Store Service finden Sie hier.

Erweitern der Konfigurationsdatei

Um ein unabhängiges Deployment zu verwenden, haben wir in den vorherigen Teilen dieser Blogserie eine Konfigurationsdatei (Konfiguation.xml) erstellt. Diese muss nun um die Elemente für die Secure Store Applikationen erweitert werden.

<!-- Configuration for Secure Store Applications -->
<SecureStoreName>MySecureStore</SecureStoreName>
<TargetContactEmail>Email address</TargetContactEmail>
<CredentialUserName>Username</CredentialUserName>
<CredentialPassword>Password</CredentialPassword>

In diesem Fall erstellen wir nur eine  Secure Store Applikation. Die Konfigurationsdatei muss dann um die entsprechenden Elemente für die weiteren Secure Store Applikationen erweitert werden.

Erweitern der Powershell-Datei

In den kommenden Schritten muss die Powershell-Datei (Deployment.ps1), die wir bereits im ersten Teil dieser Blogserie erstellt haben, angepasst werden.

Zuerst müssen die Elemente aus der Konfigurationsdatei in die Powershell-Datei eingebunden werden.

#Secure Store Variables
$TargAppContact = $configurationElement.Config_Content.TargetContactEmail
$SecureStoreName = $configurationElement.Config_Content.SecureStoreName
$CredentialUserName = $configurationElement.Config_Content.CredentialUserName
$CredentialPassword = $configurationElement.Config_Content.CredentialPassword

In dem folgenden Beispiel erstellen wir lediglich eine neue Secure Store Applikation. Wenn mehrere Secure Store Applikationen erstellt werden sollen, muss das Powershell-Skript um die weiteren Secure Store Applikationen erweitert werden

Im folgenden Schritte überprüfen wir vorab, ob bereits eine Secure Store Applikation mit demselben Namen existiert. Falls dieses der Fall ist, soll die vorhandene Applikation gelöscht werden.

Write-Host "Create Secure Store Applications Start"
#----- Remove Secure Store if exist -----
$SecureStoreExist = Get-SPSecureStoreApplication -ServiceContext $Webapplication -Name "$SecureStoreName" -EA 0
if($SecureStoreExist -ne $null)
{
  Remove-SPSecureStoreApplication -Identity $SecureStoreExist -Confirm:$false
}

Anschließend erstellen wir Variablen, die später an den Befehl zum Erstellen der SecureStore-Applikation angebunden werden.

#----- Declare variables used for all Secure Store Applications -----
$usernameField = New-SPSecureStoreApplicationField -Name "Windows User Name" -Type WindowsUserName -Masked:$false
$passwordField = New-SPSecureStoreApplicationField -Name "Windows Password" -Type WindowsPassword -Masked:$true
$securityfields = $usernameField, $passwordField
#Secure Store Application Administrator Account (DomainAccountname)
$owner1 = New-SPClaimsPrincipal -Identity "<Domain><User>" -IdentityType WindowsSamAccountName

Im folgenden Code-Segment fügen wir den Befehl zum Erstellen der Secure Store Applikationen aus.

#----- Secure Store 'MySecureStore' ------
# The next row add a new Target Application for the Secure Store. The informations like "Name", "FriendlyName" and "ContactEmail" are parameterized.
$TargetApp = New-SPSecureStoreTargetApplication -Name "$SecureStoreName" - FriendlyName "MySecureStore" -ContactEmail "TargetAppContact" -ApplicationType Individual
#This row creates a new Secure Store Application on a server.
New-SPSecureStoreApplication -ServiceContext $Webapplication -TargetApplication $TargetApp -Fields $securityfields -Administrator $owner1

Sollte der ApplicationType Group verwendet werden, benötigt man zusätzlich noch ein oder mehrere Benutzer, die auf die Secure Store Applikation zugreifen dürfen. Hierzu muss zusätzlich noch der Parameter –CredentialsOwnerGroup in die Methode New-SPSecureStoreApplication hinzugefügt werden.

Bei der Verwendung des ApplicationType Group, muss das Code-Segment für das Erstellen der Secure Store Applikation folgendermaßen aussehen:

#Secure Store Application members (DomainAccountname)
$member1 = New-SPClaimsPrincipal -Identity "<Domain><User>" -IdentityType WindowsSamAccountName
$member2 = New-SPClaimsPrincipal -Identity "<Domain><User>" -IdentityType WindowsSamAccountName
…
$memberList = $member1, $member2,…

#----- SecureStore ------
# The next row add a new Target Application for the Secure Store. The informations like "Name", "FriendlyName" and "ContactEmail" are parameterized.
$TargetApp = New-SPSecureStoreTargetApplication –Name "$SecureStoreName" –FriendlyName "MySecureStore" -ContactEmail "$TargAppContact" –ApplicationType Group 
# This row creates a new Secure Store Application on a server. It uses the variables created in the rows before for the configuration
# of the SecureStore Application.
New-SPSecureStoreApplication –ServiceContext $Webapplication –TargetApplication $TargetApp –Fields $securityfields –Administrator $owner1 -CredentialsOwnerGroup $memberList

Abschließend müssen noch die Credentials für die Applikation gesetzt werden. Dafür muss der folgenden Code in der Powershell-Datei eingefügt werden:

# Set credentials for the SecureStoreApplication 
$ssoapp = Get-SPSecureStoreApplication -ServiceContext $Webapplication -Name "$SecureStoreName"
$username = ConvertTo-SecureString $CredentialUsername -AsPlainText -Force
$password = ConvertTo-SecureString $CredentialPassword -AsPlainText -Force
$credentialValues = $username, $password
# Set the credentials for the SecureStoreApplication if the type is "Individual"
Update-SPSecureStoreCredentialMapping -Identity $ssoapp -Values $credentialValues -Principal $owner1
# Set the credentials for the SecureStoreApplication if the type is "Group"
#Update-SPSecureStoreGroupCredentialMapping -Identity $ssoapp -Values $credentialValues
Write-Host "Create SecureStore Applications End"

Ausführen

Öffnen Sie nun auf dem Server das Microsoft SharePoint 2010 Management Shell und navigieren zu dem Verzeichnis, in dem Sie die Datei Deployment.ps1 gespeichert haben.

Sie können die Code-Blöcke für das Erstellen einer SiteCollection sowie das Wiederherstellen einer Backup-Datei auskommentieren (<# Code-Block #>).

Führen Sie das Powershell-Skript Deployment.ps1 aus. Anschließend sollte folgendes Fenster erscheinen.

image

Fazit

Zusammenfassend lässt sich sagen, dass man mit Hilfe von Powershell in SharePoint 2010 einfach administrative und konfigurierbare Schritte ausführen lassen. Dieses bedeutet, insbesondere für Administratoren und Entwickler, eine erhebliche Zeitersparnis.

Mit dem Erstellen von Secure Store Applikationen, werden die erstellten Dateien aus dem ersten Teil um die Secure Store Applikationen erweitert. Mit Hilfe des angepassten Skriptes können jetzt die benötigten Secure Store Informationen ausgewertet und in das Deployment integriert werden. Dabei werden die Secure Store Informationen passend ausgelesen und mit Hilfe des Powershell Skripts in die neue Datenbank importiert.

Im vierten Teil dieser Blogserie SharePoint 2010 Deployment beschäftigen wir uns mit dem Thema Importieren von Business Data Connectivity Models.

Hier finden Sie eine Übersicht aller Blogbeiträge zu dieser Blogserie.

Teil 1                    Teil 2                    Teil 3                    Teil 4                    Teil 5

SharePoint 2010 Deployment (Teil 2) – Wiederherstellen einer Backup-Datei

Bei SharePoint 2010 Deployment handelt es sich um eine Serie von Blogbeiträgen, die die verschiedenen Schritte für das Deployment von SiteCollections erläutert und ausführt. Dabei wird der Fokus auf das Wiederherstellen einer Backup-Datei aus einer SharePoint-Umgebung in eine andere (z.B. von einer Entwicklungsumgebung auf eine Test- oder Produktivumgebung) gelegt.

 

Die Serie “SharePoint 2010 Deployment” besteht aus den folgenden Beiträgen:

  1. Erstellen einer SiteCollection
  2. Wiederherstellen ein Backup-Datei
  3. Erstellen von SecureStore-Applikationen
  4. Importieren von Business Data Connectivity Models
  5. Deployment von SQL Server Reporting Services Reports

Im zweiten Teil dieser Blogserie behandeln wir das Wiederherstellen einer Backup-Datei in SharePoint 2010 mit Hilfe von Powershell. Dafür erweitern wir die, im ersten Teil der Blogserie, erstellte Konfigurationsdatei sowie das Powershell-Skript, um die Aktionen für das Wiederherstellen der Backup-Datei. Dieses Powershell-Skript kann entsprechend der Anforderungen individuell angepasst werden.

Für das Erweitern, der im ersten Teil der Blogserie erstellten Powershell-Datei, sowie das nachträgliche Ausführen sind die folgenden Schritte auszuführen:

  1. Erweitern der Konfigurationsdatei
  2. Erweitern der Powershell-Datei
  3. Ausführen

Erweitern der Konfigurationsdatei

Um ein unabhängiges Deployment zu verwenden, haben wir im ersten Teil dieser Blogserie Erstellen einer SiteCollection eine Konfigurationsdatei (Konfiguration.xml) erstellt. Diese müssen wir nun um den Pfad für die Backup-Datei erweitern.

<!--Paths-->
<BackupFilePath>Pfad zur Backup-Datei einfügen</BackupFilePath>

Erweitern der Powershell-Datei

Im nächsten Schritt erweitern wir, das im ersten Teil der Blogserie erstellte Powershell-Skript (Deployment.ps1) um das Element <BackupFilePath> aus der Konfigurationsdatei.

$BackupFilePath = $configurationElement.Config_Content.BackupFilePath

Im zweiten Schritt fügen wir dem Powershell-Skript dem Befehl zum Wiederherstellen der Backup-Datei in eine bestimmte SiteCollection hinzu.

#----- Restore the Site Collection -----
Write-Host "Restore Site Collection Start"
Restore-SPSite -Identity $newCollectionSite -Path "$BackupFilePath" -Force -Confirm:$false
Write-Host "Restore Site Collection End"

Beim Verwenden des Powershell-Befehls Restore-SPSite ist darauf zu achten, dass die Backup-Datei auf einem SharePoint-Server mit derselben oder einer höheren Versionsnummer wiederhergestellt wird. Andernfalls kann das Wiederherstellen der Backup-Datei nicht ausgeführt werden.

Ausführen

Öffnen Sie nun auf dem Server das Microsoft SharePoint 2010 Management Shell und navigieren zum dem Verzeichnis, in dem Sie die neu erstellten Dateien gespeichert haben.

Sie können das Skript auch mit dem von Windows Server mitgelieferten Powershell-Fenster ausführen. Hierzu müssen Sie jedoch noch die SharePoint spezifischen Methoden einbinden. Geben Sie dafür folgenden Befehl ein, bevor Sie das Deployment-Skript ausführen:

if((Get-PSSnapin -Name "Microsoft.SharePoint.PowerShell" - EA 0) -eq $null)
{
  Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

Um die SharePoint spezifischen Funktionen auch dauerhaft in der Windows Server mitgelieferten Powershell-Konsole nutzen zu können, finden Sie hier einen interessanten Blobeintrag von Kirk Evans.

Führen Sie das Powershell-Skript Deployment.ps1 aus. Anschließend sollte folgendes Fenster erscheinen:

image

Fazit

Zusammenfassend kann man sagen, dass sich mit Hilfe von Powershell in SharePoint 2010 einfach administrative und konfigurierbare Schritte ausführen lassen. Dieses bedeutet, insbesondere für Administratoren und Entwickler, eine erhebliche Zeitersparnis.

Ein Vorteil, welches das Wiederherstellen  einer Backup-Datei in eine SiteCollection gegenüber dem Copy-Befehlen für die einzelnen Listen, Bibliotheken, etc. hat, ist das die gesamten Security-Einstellungen mit übernommen werden. So kann man alle Einstellungen für die Berechtigungen auf Seiten, Listen, etc. bereits in der Entwicklungs- oder Testumgebung einstellen ohne sie auf dem produktiven SharePoint wiederholen zu müssen.

Im dritten Teil dieser Blogserie SharePoint 2010 Deployment beschäftigen wir uns mit dem Thema Erstellen von SecureStore-Applikationen.

Hier finden Sie eine Übersicht aller Blogbeiträge zu dieser Blogserie.

Teil 1                    Teil 2                    Teil 3                    Teil 4                    Teil 5

SharePoint 2010 Deployment (Teil 1) – Erstellen einer SiteCollection

Bei SharePoint 2010 Deployment handelt sich um eine Serie von Blogbeiträgen, die die verschiedenen Schritte für das Deployment von SiteCollection erläutert und ausführt. Dabei wird der Fokus auf das Einspielen einer Backup-Datei aus der Entwicklungsumgebung auf die Test- oder Produktivumgebung gelegt.

 

In diesem, sowie in den folgenden Beiträgen zu diesem Thema, werden die folgenden Punkte für das Deployment von SharePoint 2010 Seiten näher erläutert:

  1. Erstellen einer SiteCollection
  2. Wiederherstellen einer Backup-Datei
  3. Erstellen von SecureStore-Applikationen
  4. Importieren von Business Data Connectivity Models
  5. Deployen von SQL Server Reporting Services Reports

Im ersten Teil dieser Blogserie behandeln wir das Erstellen einer SiteCollection in SharePoint 2010 mit Hilfe von Powershell. Hierfür nutzen wir eine konfigurierbare Powershell-Datei, welche nach Belieben angepasst werden kann.

Für das Erstellen der Powershell-Datei sowie das nachträgliche Ausführen sind die folgenden Schritte notwendig:

  1. Erstellen einer Konfigurationsdatei
  2. Erstellen einer Powershell-Datei
  3. Einbinden der Konfigurationsdatei in die Powershell-Datei
  4. Ausführen

Konfigurationsdatei

Um ein unabhängiges Deployment zu erstellen, verwenden wir eine XML-Datei, die zur Konfiguration in dieser Blogserie verwendet wird.

Erstellen Sie hierfür eine neue Datei mit dem Namen Konfiguration.xml und fügen Sie den folgenden Teil in die Konfigurationsdatei.

   1: <?xml version="1.0" encoding="utf-8" ?>

   2: <Config_Content>

   3:   <!--Servername where the application will be installed/imported.-->

   4:   <Webapplication>http://<Servername></Webapplication>

   5:   

   6:   <!--Configuration for create and import SiteCollection-(Backup)-->

   7:   <!--Destination Url of the Site collection (Cannot be empty!!!)-->

   8:   <Url>/sites/mySite</Url>

   9:   <!--Title of the Site Collection (Cannot be empty!!!)-->

  10:   <Title>MySite</Title>

  11:   <!--Description of the Site Collection-->

  12:   <Description>This is a testpage for the blog.</Description>

  13:   <!--LanguageID are used for this Site Collection and their subsites (1033:English; 1031:German)-->

  14:   <LCID>1033</LCID>

  15:   <!--TemplateId from Microsoft (here: TeamSite Template)-->

  16:   <WebTemplate>STS#0</WebTemplate>

  17:   <!--Loginname of the Site Collection owner (Cannot be empty!!!)-->

  18:   <FirstOwnerLogin>Farm-Administrator</FirstOwnerLogin>

  19:   <!—Email address of the Site Collection owner (Cannot be empty!!!)-->

  20:   <FirstOwnerEmail>Thomas.Nissen@mySite.de</FirstOwnerEmail>

  21:   <!--Loginname of the Second Site Collection Administrator -->

  22:   <SecondaryOwnerLogin>Administrator</SecondaryOwnerLogin>

  23:   <!--Email address of the Second Site Collection Administrator -->

  24:   <SecondaryOwnerEmail>test.test@test.ag</SecondaryOwnerEmail>

  25: </Config_Content>

Eine Übersicht der Webtemplates können Sie mit dem Befehl

   1: Get-SPWebTemplate | Sort-Object „Name”

in der Microsoft SharePoint 2010 Management Shell abfragen.

Powershell-Datei

Im zweiten Schritt erstellen wir ein neues Powershell-Skript namens Deployment.ps1, welches die Konfigurationsdatei einliest und den Befehl zum Erstellen einer neuen SiteCollection ausführt.

Einbinden der Konfigurationsdatei

   1: #Read XML Configuration files

   2: $configurationElement = Get-Content "Konfiguration.xml"

   3: #----- Variables -----

   4: $SiteCollectionURL = $configurationElement.Config_Content.Url

   5: $Title = $configurationElement.Config_Content.Title

   6: $Description = $configurationElement.Config_Content.Description

   7: $LCID = $configurationElement.Config_Content.LCID

   8: $WebTemplate = $configurationElement.Config_Content.WebTemplate

   9: $FirstOwnerLogin = $configurationElement.Config_Content.FirstOwnerLogin

  10: $FirstOwnerEmail = $configurationElement.Config_Content.FirstOwnerEmail

  11: $SecondOwnerLogin = $configurationElement.Config_Content.SecondaryOwnerLogin

  12: $SecondOwnerEmail = $configurationElement.Config_Content.SecondaryOwnerEmail

  13: $Webapplication = $configurationElement.Config_Content.Webapplication

  14:  

  15: $newCollectionSite = $Webapplication + $SiteCollectionURL

In den kommenden zwei Schritten überprüfen wir vorab, ob eine SiteCollection mit dem Namen MySites schon unter /sites/mySite">/sites/mySite">/sites/mySite">/sites/mySite">/sites/mySite">/sites/mySite">/sites/mySite">http://<Servername>/sites/mySite existiert. Falls ja, soll diese gelöscht werden.

   1: #----- Delete the Site Collection if exist -----

   2: $CollectionSiteExist = Get-SPSite -Identity $newCollectionSite -EA 0

   3: if($CollectionSiteExist -ne $null)

   4: {

   5:   Remove-SPSite -Identity $newCollectionSite -Confirm:$False

   6: }

Anschließend erstellen wir unsere neue SiteCollection.

   1: #----- Create the Site Collection ----

   2: Write-Host "Create Site Collection Start"

   3: New-SPSite -URL $newCollectionSite -Name $Title -Description $Description -Language $LCID -Template $WebTemplate -OwnerAlias $FirstOwnerLogin -OwnerEmail $FirstOwnerEmail -SecondaryOwnerAlias $SecondOwnerLogin -SecondaryEmail $SecondOwnerEmail 

   4: Write-Host "Create Site Collection End"

Mit dem Befehl Write-Host können Sie eine Zeile im Shell-Fenster ausgeben (gleich dem echo-Befehl im CMD-Kommandofenster).

Ausführen

Öffnen Sie nun auf dem Server das Microsoft SharePoint 2010 Management Shell und navigieren zum dem Verzeichnis, in dem Sie die neu erstellten Dateien gespeichert haben.

Sie können das Skript auch mit dem von Windows Server mitgelieferten Powershell-Fenster ausführen. Hierzu müssen Sie jedoch noch die SharePoint spezifischen Methoden einbinden. Geben Sie dafür folgenden Befehl ein, bevor Sie das Deployment-Skript ausführen:

   1: Add-PsSnapin Microsoft.SharePoint.PowerShell

Um die SharePoint spezifischen Methoden auch dauerhaft im Windows Server mitgelieferten Powershell-Fenster nutzen zu können, finden Sie hier einen interessanten Blogeintrag von Kirk Evans.

Führen Sie das Powershell-Skript Deployment.ps1 aus. Anschließend sollte folgendes Fenster erscheinen:

image

Fazit

Zusammenfassend kann man sagen, dass sich mit Hilfe von Powershell in SharePoint 2010 einfach administrative und konfigurierbare Schritte ausführen lassen. Dieses bedeutet, insbesondere für Administratoren und Entwickler, eine erhebliche Zeitersparnis.

Im zweiten Teil dieser Blogserie SharePoint 2010 Deployment beschäftigen wir uns mit dem Thema Wiederherstellen einer Backup-Datei (erscheint in Kürze).

Hier finden Sie eine Übersicht aller Blogbeiträge zu dieser Blogserie.

Teil 1                    Teil 2                    Teil 3                     Teil 4                    Teil 5

Corporate Design von SharePoint 2010 Seiten

Fachlicher Hintergrund

Viele Unternehmen nutzen den SharePoint 2010. Da diese Unternehmen meist auch ein Corporate Design verfolgen, müssen Designanpassungen am SharePoint – insbesondere an den Masterpages und css-Datein vorgenommen werden.
>> mehr…

Bing Maps SearchService


Einführung

In meinen vorherigen Blog-Beiträgen (Microsoft Bing Maps for Enterprise, BingMaps GeocodeService und BingMaps RouteService) zu dem Thema BingMaps, habe ich über die Darstellung von Punkten oder Routen auf einer BingMaps-Karte gesprochen. Im Gegensatz zu dem ersten Blog-Beitrag wurde bei den anderen die Webservices “GeocodeService” und “RouteService” mit eingebunden, die sich über die Webservices die Koordinaten der gesuchten Orte/Punkte holen bzw. die Route dazwischen berechnen.

Microsoft bietet aber 3 verschiedene WebServices für die Darstellung von Punkten, Linien oder Bereichen auf Karten an. Diese wären:

  1. GeocodeService (Punkte)
  2. RouteService (Punkte, Linien)
  3. SearchService (Punkte)

In diesem Blog Beitrag möchte ich die Verwendung des SearchService in BingMaps erläutern.

Wozu verwende ich den SearchService?

Mit Hilfe des SearchService wird eine Abfrage an den Webservice gestellt, der die Koordinaten (Längen- und Breitengrad im Format WGS84) der Adressen für einen bestimmten Suchbegriff (z.B. Pizza in Frankfurt) zurückliefert. Als Ergebnis erhält man eine Liste von Pizzerien in Frankfurt.

Der SearchService bietet sich besonders an, wenn man einen bestimmten Ort (z.B. die nächstgelegene Postfiliale) in seiner Nähe sucht.

Voraussetzungen

  • BingMaps-Key
  • Visual Studio
  • Silverlight

Falls Sie noch keinen BingMaps-Key haben, können Sie sich hier einen erstellen.

Silverlight-Applikation

Als erster Schritt wird eine neue Silverlight-Applikation erstellt und der SearchService als Service Reference hinzugefügt. Hierzu klicken Sie mit der rechten Maustaste auf das Projekt und wählen Add Service Reference. Geben Sie als Adresse http://dev.virtualearth.net/webservices/v1/SearchService/SearchService.svc ein und klicken auf Go. Nennen Sie den Namespace SearchService.

Im nächsten Schritt werden eine Textbox zum Eingeben der Suchparameter und ein Button zum Starten der Suche erstellt. Des Weiteren erstellen wir noch einen Textblock in dem wir ausgeben, wie viele Treffer gefunden wurden und eine Karte zum Darstellen der Suchergebnisse.

   1: <Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">

   2:  <Grid.RowDefinitions>

   3:   <RowDefinition Height="auto"/>

   4:   <RowDefinition Height="*"/>

   5:  </Grid.RowDefinitions>

   6:  <StackPanel Grid.Row="0">

   7:   <TextBlock TextWrapping="Wrap"/>

   8:   <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="12,0,12,0">

   9:    <TextBox x:Name="strParameter" Width="500" KeyDown="strParameter_KeyDown" GotFocus="strParameter_GotFocus" HorizontalAlignment="Stretch"/>

  10:    <Button Content="Suchen" Click="Button_Click"/>

  11:    <TextBlock x:Name="outResults" Margin="14,0,0,0" VerticalAlignment="Center"/>

  12:   </StackPanel>

  13:  </StackPanel>

  14:  <map:Map Name="searchMap" CredentialsProvider="<Your Key>" Grid.Row="1">

  15:   <map:MapLayer x:Name="SearchResultLayer"/>

  16:   <map:MapLayer x:Name="ContentTooltipLayer">

  17:    <Canvas x:Name="ContentTooltip" Visibility="Collapsed" Opacity=".85" MouseEnter="ContentPopup_MouseEnter" MouseLeave="ContentPopup_MouseLeave">

  18:     <Rectangle x:Name="ContentTooltipRectangle" Fill="White" Canvas.Left="0" Height="100" Width="300" RadiusX="20" RadiusY="20"/>

  19:     <StackPanel Canvas.Left="10" Canvas.Top="10">

  20:      <TextBlock x:Name="ContentTooltipText"/>

  21:      <TextBlock x:Name="ContentTooltipDescription"/>

  22:     </StackPanel>

  23:    </Canvas>

  24:   </map:MapLayer>

  25:  </map:Map>

  26: </Grid>

In den nächsten beiden Methoden (ExecuteSearch und searchClient_searchCompleted) übergeben wir den Suchbegriff aus unserer Textbox an den Webservice. Dieser gibt eine Liste von Suchergebnissen zurück.

   1: private void ExecuteSearch(string query)

   2: {

   3:  //Set up the search request

   4:  SearchService.SearchServiceClient searchClient;

   5:  

   6:  // OutOfBrowser is currently supported on the MapControl only for http pages

   7:  bool httpsUriScheme = !Application.Current.IsRunningOutOfBrowser && HtmlPage.Document.DocumentUri.Scheme.Equals(Uri.UriSchemeHttps);

   8:  BasicHttpBinding binding = new BasicHttpBinding(httpsUriScheme ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None);

   9:  binding.MaxReceivedMessageSize = int.MaxValue;

  10:  binding.MaxBufferSize = int.MaxValue;

  11:  UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/SearchService/SearchService.svc");

  12:  if (httpsUriScheme)

  13:  {

  14:   //For https, change the UriScheme to https and change it to use the default https port.

  15:   serviceUri.Scheme = Uri.UriSchemeHttps;

  16:   serviceUri.Port = -1;

  17:  }

  18:  

  19:  //Create the Service Client

  20:  searchClient = new SearchService.SearchServiceClient(binding, new EndpointAddress(serviceUri.Uri));

  21:  searchClient.SearchCompleted += new EventHandler<SearchService.SearchCompletedEventArgs>(searchClient_SearchCompleted);

  22:  

  23:  var request = new SearchService.SearchRequest

  24:  {

  25:   Culture = searchMap.Culture,

  26:   Query = query,

  27:   ExecutionOptions = new SearchService.ExecutionOptions { SuppressFaults = true }

  28:  };

  29:  

  30:  //Set up the Map View bounds for the request

  31:  LocationRect mapControlView = searchMap.BoundingRectangle;

  32:  

  33:  LocationRect mapBounds = new LocationRect(mapControlView);

  34:  request.UserProfile = new SearchService.UserProfile();

  35:  request.UserProfile.MapView = mapBounds;

  36:  

  37:  searchMap.CredentialsProvider.GetCredentials(

  38:    (Credentials credentials) =>

  39:  {

  40:   //Pass in credentials for web services call. Replace with your own Credentials.

  41:   request.Credentials = credentials;

  42:  

  43:   //execute the request

  44:   searchClient.SearchAsync(request);

  45:  });

  46: }

   1: private void searchClient_SearchCompleted(object sender, SearchService.SearchCompletedEventArgs e)

   2: {

   3:  string outputString;

   4:  try

   5:  {

   6:   if (e.Result.ResponseSummary.StatusCode == SearchService.ResponseStatusCode.Success)

   7:   {

   8:    outputString = string.Format(CultureInfo.InvariantCulture, "{0} Treffer", e.Result.ResultSets[0].Results.Count);

   9:    IList<Location> locations = new List<Location>();

  10:  

  11:    if ((e.Result != null) && (e.Result.ResultSets.Count > 0))

  12:    {

  13:     foreach (SearchService.SearchResultBase result in e.Result.ResultSets[0].Results)

  14:     {

  15:      searchResults.Add(result.Id, result);

  16:  

  17:      if (result.LocationData.Locations.Count > 0)

  18:      {

  19:       Location location = new Location(result.LocationData.Locations[0]);

  20:       //Create a pushpin

  21:       var pushpin = new Pushpin

  22:       {

  23:        Location = location,

  24:        Tag = result.Id

  25:       };

  26:  

  27:       SearchResultLayer.Children.Add(pushpin);

  28:       locations.Add(location);

  29:  

  30:       //Set up events for the pushpin

  31:       pushpin.MouseEnter += new MouseEventHandler(Pushpin_MouseEnter);

  32:       pushpin.MouseLeave += new MouseEventHandler(Pushpin_MouseLeave);

  33:  

  34:      }

  35:     }

  36:  

  37:     if (locations.Count > 0)

  38:     {

  39:      //If Search results were found, use the bounding area of the results

  40:      searchMap.SetView(new LocationRect(locations));

  41:     }

  42:     else

  43:     {

  44:      //Otherwise, if the SearchRegion is set from the service response, go ahead use that

  45:      LocationRect boundingArea = e.Result.ResultSets[0].SearchRegion.BoundingArea as LocationRect;

  46:      if (boundingArea != null)

  47:      {

  48:       searchMap.SetView(new LocationRect(boundingArea));

  49:      }

  50:     }

  51:    }

  52:   }

  53:   else

  54:   {

  55:    outputString = "error searching... status <" + e.Result.ResponseSummary.StatusCode.ToString() + ">";

  56:   }

  57:  }

  58:  catch (Exception)

  59:  {

  60:   outputString = "Exception Raised";

  61:  }

  62:  

  63:  outResults.Text = outputString;

  64: }

Damit wir auch wissen um wen es sich bei den Suchergebnissen handelt, erstellen wir noch zwei Events, die ausgeführt werden, wenn man einen Punkt mit der Maus berührt (Pushpin_MouseEnter) und wieder verlässt (Pushpin_MouseLeave). Beim Berühren eines Punktes mit der Maus sollen Informationen wie Name, Adresse und Telefonnummer in der Box angezeigt werden.

   1: void Pushpin_MouseLeave(object sender, MouseEventArgs e)

   2: {

   3:  //Hide the popup

   4:  UIElement content = sender as UIElement;

   5:  Canvas.SetZIndex(content, 100);

   6:  ContentTooltip.Visibility = Visibility.Collapsed;

   7: }

   8:  

   9: void Pushpin_MouseEnter(object sender, MouseEventArgs e)

  10: {

  11:  //Show the popup

  12:  Pushpin pushpin = sender as Pushpin;

  13:  Canvas.SetZIndex(pushpin, 500);

  14:  Point point = e.GetPosition(searchMap);

  15:  string resultId = pushpin.Tag as string;

  16:  SearchService.SearchResultBase searchResult = searchResults[resultId];

  17:  Location location = new Location(searchResult.LocationData.Locations[0].Latitude, searchResult.LocationData.Locations[0].Longitude);

  18:  MapLayer.SetPosition(ContentTooltip, location);

  19:  MapLayer.SetPositionOffset(ContentTooltip, new Point(25, -50));

  20:  

  21:  SearchService.SearchResultBase result = searchResults[resultId];

  22:  if (result != null)

  23:  {

  24:   ContentTooltipText.Text = result.Name;

  25:   SearchService.BusinessSearchResult businessResult = result as SearchService.BusinessSearchResult;

  26:   if (businessResult != null)

  27:   {

  28:    ContentTooltipDescription.Text = string.Format("{0}
{1}
", businessResult.Address.FormattedAddress, businessResult.PhoneNumber);

  29:   }

  30:   ContentTooltip.Visibility = Visibility.Visible;

  31:  }

  32: }

  33:  

  34: private void ContentPopup_MouseEnter(object sender, MouseEventArgs e)

  35: {

  36:  //Show the popup if mouse is hovering over it

  37:  ContentTooltip.Visibility = Visibility.Visible;

  38:  Canvas.SetZIndex(ContentTooltip, 10);

  39: }

  40:  

  41: private void ContentPopup_MouseLeave(object sender, MouseEventArgs e)

  42: {

  43:  //Hide the popup if mouse leaves it

  44:  ContentTooltip.Visibility = Visibility.Collapsed;

  45: }

Zum Schluss müssen wir noch eine Methode zum Ausführen der Suche erstellen, die ausgeführt wird, wenn der Button gedrückt wird.

   1: private void strParameter_KeyDown(object sender, KeyEventArgs e)

   2: {

   3:  if (e.Key == Key.Enter)

   4:  {

   5:   StartSearch();

   6:  }

   7: }

   8:  

   9: private void strParameter_GotFocus(object sender, RoutedEventArgs e)

  10: {

  11:  strParameter.SelectAll();

  12: }

  13:  

  14: private void Button_Click(object sender, RoutedEventArgs e)

  15: {

  16:  StartSearch();

  17: }

  18:  

  19: private void StartSearch()

  20: {

  21:  ClearSearch();

  22:  

  23:  searchMap.Focus();

  24:  

  25:  string query = strParameter.Text;

  26:  if (!string.IsNullOrEmpty(query))

  27:  {

  28:   ExecuteSearch(query);

  29:  }

  30: }

  31:  

  32: private void ClearSearch()

  33: {

  34:  foreach (UIElement pin in SearchResultLayer.Children)

  35:  {

  36:   pin.MouseEnter -= Pushpin_MouseEnter;

  37:   pin.MouseLeave -= Pushpin_MouseLeave;

  38:  }

  39:  

  40:  SearchResultLayer.Children.Clear();

  41:  searchResults.Clear();

  42: }

Ergebnis

Starten Sie nun die Solution und geben einen Suchbegriff ein. Klicken Sie anschließend auf Suchen um den SearchService anzusprechen.

image

Gesamtfazit

Abschließend kann man sagen, dass die unterschiedlichen Services (GeocodeService, RouteService und SearchService), die ich in dieser BlogSerie beschrieben habe, eine gute Ergänzung für die Entwicklung von BingMaps-Applikation sind.

Durch das frei verfügbare Bing Maps Silverlight Control SDK und Bing Maps Silverlight Control Interactive SDK werden dem Entwickler hilfreiche Tipps und Beispiele zur Verfügung gestellt.

BingMaps bietet mit den Services Unternehmen in den unterschiedlichsten Branchen eine gute Möglichkeit seine Daten geovisualisiert darzustellen. Durch die Sicht auf die geographische Lage können die Daten auf eine weitere Art analysiert werden.

Insbesondere der GeocodeService bietet dem Unternehmen eine gute Hilfe für die geovisualisierte Analyse seiner Daten.

BingMaps RouteService

Einführung

In meinen ersten beiden Blog-Beiträgen Microsoft Bing Maps for Enterprise und BingMaps GeocodeService, habe ich über die Darstellung von Punkten auf einer BingMaps-Karte gesprochen. Im Gegensatz zu dem ersten Blog-Beitrag wurde im Zweiten der WebService “GeocodeService” mit eingebunden, der sich über den Webservice die Koordinaten des gesuchten Ortes/Punktes holt.

Microsoft bietet aber 3 verschiedene WebServices für die Darstellung von Punkten, Linien oder Bereichen auf Karten an. Diese wären

  1. GeocodeService (Punkte)
  2. RouteService (Punkte, Linien)
  3. SearchService (Punkte)

In diesem Blog Beitrag möchte ich die Verwendung des RouteService in BingMaps erläutern.

Wozu verwende ich den RouteService?

Mit Hilfe des Route Service wird eine Abfrage an den Webservice gestellt, der die Koordinaten (Längen- und Breitengrad im Format WGS84) der Eckpunkte für die zu berechnende Route zurückliefert. Bei diesem RouteService kann die Unterscheidung erfolgen, ob die Route/Strecke mit einem Fahrzeug (z.B. Auto) oder zu Fuß (Wanderstrecke) zurückgelegt werden soll. Des Weiteren kann noch unterschieden werden, ob Autobahnen genutzt oder vermieden werden sollen. Diesbezüglich verändert sich auch der Verlauf der Route/Strecke.

Eine Verwendung des RouteService bietet sich insbesondere für die Berechnung von Routenplanungen für den Güterverkehr an.

Voraussetzungen

  • BingMaps-Key
  • Visual Studio
  • Silverlight

Falls Sie noch keinen BingMaps-Key haben, können Sie sich hier einen erstellen.

Silverlight-Applikation

Als erster Schritt wird eine neue Silverlight-Applikation erstellt und der GeocodeService als Service Reference hinzugefügt. Hierzu klicken Sie mit der rechten Maustaste auf das Projekt und wählen Add Service Reference. Geben Sie als Adresse http://dev.virtualearth.net/webservices/v1/GeocodeService/GeocodeService.svc ein und klicken auf Go. Nennen Sie den Namespace GeocodeService. Wiederholen Sie diesen Schritt mit der Adresse http://dev.virtualearth.net/webservices/v1/RouteService/RouteService.svc. Nennen Sie den Namespace RouteService.

image

Im nächsten Schritt werden zwei Textboxen zum Eingeben der Start- und Zieladresse, zwei RadioButton für die Auswahl des Routenmodus (Auto, Fußgänger) und ein Button zum Starten der Routenberechnung erstellt. Des Weiteren werden noch ein Textblock zum Ausgeben der Entfernung/Distanz und eine Karte zum Darstellen der Suchergebnisse erstellt.

   1: <Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">

   2:  <Grid.RowDefinitions>

   3:   <RowDefinition Height="auto" />

   4:   <RowDefinition Height="*" />

   5:  </Grid.RowDefinitions>

   6:  <StackPanel Grid.Row="0">

   7:   <TextBlock Text="Geben Sie den Start- und den Zielpunkt ein!" FontSize="13" TextWrapping="Wrap" />

   8:   <Grid>

   9:    <Grid.RowDefinitions>

  10:     <RowDefinition />

  11:     <RowDefinition />

  12:     <RowDefinition />

  13:     <RowDefinition />

  14:    </Grid.RowDefinitions>

  15:    <Grid.ColumnDefinitions>

  16:     <ColumnDefinition Width="auto" />

  17:     <ColumnDefinition Width="500" />

  18:     <ColumnDefinition Width="auto" />

  19:     <ColumnDefinition Width="auto" />

  20:     <ColumnDefinition Width="auto" />

  21:    </Grid.ColumnDefinitions>

  22:    <TextBlock Text="Start:" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" FontSize="10" />

  23:    <TextBox x:Name="inputStart" Grid.Row="0" Grid.Column="1" KeyDown="input_KeyDown" GotFocus="inputStart_GotFocus" Width="500" Text="Borsigallee 19, Frankfurt am Main" FontSize="13" />

  24:    <TextBlock x:Name="outputStart" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" FontSize="10" Foreground="Black" Margin="14,0,0,0" VerticalAlignment="Center" />

  25:    <TextBlock Text="Ziel:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" FontSize="10" />

  26:    <TextBox x:Name="inputZiel" Grid.Row="1" Grid.Column="1" KeyDown="input_KeyDown" GotFocus="inputZiel_GotFocus" Width="500" Text="Landwehrstraße 61, München" FontSize="13" />

  27:    <Button Content="Route berechnen" Grid.Row="1" Grid.Column="2" Click="Button_Click" />

  28:    <RadioButton x:Name="rdbDriving" Grid.Row="0" Grid.Column="2" Content="Auto" />

  29:    <RadioButton x:Name="rdbWalking" Grid.Row="0" Grid.Column="3" Content="Fußgänger" />

  30:    <TextBlock x:Name="outputZiel" Grid.Row="1" Grid.Column="3" FontSize="10" Foreground="Black" Margin="14,0,0,0" VerticalAlignment="Center" />

  31:    <TextBlock Text="Entfernung" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" FontSize="10"/>

  32:    <TextBlock x:Name="outputDistance" Grid.Row="3" Grid.Column="1" FontSize="13" Foreground="Black" FontWeight="Bold" Margin="14,0,0,0" VerticalAlignment="Center" />

  33:   </Grid>

  34:  </StackPanel>

  35:  <StackPanel Grid.Row="1">

  36:   <Grid>

  37:    <Grid.ColumnDefinitions>

  38:     <ColumnDefinition Width="*" />

  39:     <ColumnDefinition Width="auto" />

  40:    </Grid.ColumnDefinitions>

  41:    <map:Map x:Name="routedMap" CredentialsProvider="<Your Key>" Culture="de-de" Grid.Column="0" Margin="0,0,10,0" CopyrightVisibility="Collapsed" ScaleVisibility="Collapsed" NavigationVisibility="Collapsed">

  42:     <map:MapLayer x:Name="RouteLayer" />

  43:     <map:MapLayer x:Name="ContentPopupLayer">

  44:      <Canvas x:Name="ContentPopup" Visibility="Collapsed" Opacity="0.85" MouseEnter="ContentPopup_MouseEnter" MouseLeave="ContentPopup_MouseLeave">

  45:       <Rectangle x:Name="ContentPopupRectange" Fill="White" Canvas.Left="0" Canvas.Top="0" Height="100" Width="300" RadiusX="20" RadiusY="20" />

  46:       <StackPanel Canvas.Left="10" Canvas.Top="10">

  47:        <TextBlock x:Name="ContentPopupText" FontSize="12" FontWeight="Bold" TextWrapping="Wrap" Width="250" />

  48:       </StackPanel>

  49:      </Canvas>

  50:     </map:MapLayer>

  51:    </map:Map>

  52:    <TextBlock x:Name="outputRoute" Grid.Column="1" />

  53:   </Grid>

  54:  </StackPanel>

  55: </Grid>

In den beiden nachfolgenden Funktionen werden die Services GeocodeService und RouteService aufgerufen.

   1: private GeocodeService.GeocodeServiceClient geocodeClient;

   2: private GeocodeService.GeocodeServiceClient GeocodeClient

   3: {

   4:   get

   5:   {

   6:     if (null == geocodeClient)

   7:     {

   8:       //Handle http/https; OutOfBrowser is currently supported on the MapControl only for http pages

   9:       bool httpsUriScheme = !Application.Current.IsRunningOutOfBrowser && HtmlPage.Document.DocumentUri.Scheme.Equals(Uri.UriSchemeHttps);

  10:       BasicHttpBinding binding = new BasicHttpBinding(httpsUriScheme ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None);

  11:       UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/GeocodeService/GeocodeService.svc");

  12:       if (httpsUriScheme)

  13:       {

  14:         //For https, change the UriSceheme to https and change it to use the default https port.

  15:         serviceUri.Scheme = Uri.UriSchemeHttps;

  16:         serviceUri.Port = -1; 

  17:       }

  18:  

  19:       //Create the Service Client

  20:       geocodeClient = new GeocodeService.GeocodeServiceClient(binding, new EndpointAddress(serviceUri.Uri));

  21:       geocodeClient.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(client_GeocodeCompleted);

  22:     }

  23:     return geocodeClient;

  24:   }

  25: }

  26:  

  27: private RouteService.RouteServiceClient routeClient;

  28: private RouteService.RouteServiceClient RouteClient

  29: {

  30:   get

  31:   {

  32:     if (null == routeClient)

  33:     {

  34:       //Handle http/https; OutOfBrowser is currently supported on the MapControl only for http pages

  35:       bool httpsUriScheme = !Application.Current.IsRunningOutOfBrowser && HtmlPage.Document.DocumentUri.Scheme.Equals(Uri.UriSchemeHttps);

  36:       BasicHttpBinding binding = new BasicHttpBinding(httpsUriScheme ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None);

  37:       binding.MaxReceivedMessageSize = int.MaxValue;

  38:       binding.MaxBufferSize = int.MaxValue;

  39:       UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/RouteService/RouteService.svc");

  40:       if (httpsUriScheme)

  41:       {

  42:         //For https, change the UriSceheme to https and change it to use the default https port.

  43:         serviceUri.Scheme = Uri.UriSchemeHttps;

  44:         serviceUri.Port = -1; 

  45:       }

  46:  

  47:       //Create the Service Client

  48:       routeClient = new RouteService.RouteServiceClient(binding, new EndpointAddress(serviceUri.Uri));

  49:       routeClient.CalculateRouteCompleted += new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(client_RouteCompleted);

  50:     }

  51:     return routeClient;

  52:   }

  53: }

Mit der Funktion GeocodeAddress werden die Koordinaten für die beiden Punkte (Start- und Zieladresse) vom Webservice abgerufen.

   1: private void GeocodeAddress(string address, RoutingState state)

   2: {

   3:   var request = new GeocodeService.GeocodeRequest

   4:   {

   5:     ExecutionOptions = new GeocodeService.ExecutionOptions { SuppressFaults = true },

   6:     Culture = routedMap.Culture,

   7:     Query = address

   8:   };

   9:  

  10:   // Only accept results with high confidence.

  11:   request.Options = new GeocodeService.GeocodeOptions();

  12:   // Using ObservableCollection since this is the default for Silverlight proxy generation.

  13:   request.Options.Filters = new ObservableCollection<GeocodeService.FilterBase>();

  14:   GeocodeService.ConfidenceFilter filter = new GeocodeService.ConfidenceFilter();

  15:   filter.MinimumConfidence = GeocodeService.Confidence.High;

  16:   request.Options.Filters.Add(filter);

  17:  

  18:   if (null != state.output)

  19:   {

  20:     state.output.Text = "<Berechnung wird durchgeführt>";

  21:     state.output.Foreground = new SolidColorBrush(Colors.Black);

  22:   }

  23:  

  24:   routedMap.CredentialsProvider.GetCredentials(

  25:     (Credentials credentials) =>

  26:   {

  27:     // Pass in credentials for web services call. Replace with your own Credentials.

  28:     request.Credentials = credentials;

  29:  

  30:     // Make asynchronous call to fetch the data ... pass state object.

  31:     GeocodeClient.GeocodeAsync(request, state);

  32:   });

  33: }

   1: private object lockObject = new object();

   2: private void client_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)

   3: {

   4:   RoutingState state = e.UserState as RoutingState;

   5:   GeocodeService.GeocodeResult result = null;

   6:   string outString;

   7:  

   8:   try

   9:   {

  10:     if (e.Result.ResponseSummary.StatusCode != GeocodeService.ResponseStatusCode.Success)

  11:     {

  12:       outString = "Fehler bei der Berechnung ... Status <" + e.Result.ResponseSummary.StatusCode.ToString() + ">";

  13:     }

  14:     else if (0 == e.Result.Results.Count)

  15:     {

  16:       outString = "Kein Ergebnis";

  17:     }

  18:     else

  19:     {

  20:       // Only report on first result.

  21:       result = e.Result.Results[0];

  22:       outString = result.DisplayName;

  23:     }

  24:   }

  25:   catch (Exception)

  26:   {

  27:     outString = "Exception raised";

  28:   }

  29:           

  30:   // Update UI with geocode result.

  31:   if (null != state.output)

  32:   {

  33:     state.output.Text = outString;

  34:   }

  35:  

  36:   if (null == result)

  37:   {

  38:     result = new GeocodeService.GeocodeResult();

  39:   }

  40:  

  41:   // Update state object ... when all the results are set, call route.

  42:   bool doneGeocoding;

  43:   lock (lockObject)

  44:   {

  45:     state.results[state.locationNumber] = result;

  46:     doneGeocoding = state.GeocodesComplete;

  47:   }

  48:  

  49:   if (doneGeocoding && state.GeocodesSuccessful)

  50:   {

  51:     //Clear any existing routes

  52:     ClearRoute();

  53:  

  54:     //Calculate the route

  55:     CalculateRoute(state.results);

  56:   }

  57: }

   1: private RouteService.Waypoint GeocodeResultToWaypoint(GeocodeService.GeocodeResult result)

   2: {

   3:   var waypoint = new RouteService.Waypoint

   4:   {

   5:     Description = result.DisplayName,

   6:     Location = new Location { Latitude = result.Locations[0].Latitude, Longitude = result.Locations[0].Longitude }

   7:   };

   8:   return waypoint;

   9: }

Die beiden nachfolgenden Funktionen CalculateRoute und client_RouteCompleted, die wir nun erstellen, berechnen die Fahr- bzw. Laufstrecke der Route und geben entscheidende Eckpunkte (z.B. hier abbiegen, geradeaus fahren, usw…) zurück. Diese beinhalten die Koordinaten sowie Anweisungen.

   1: private void CalculateRoute(GeocodeService.GeocodeResult[] locations)

   2: {

   3:   var request = new RouteService.RouteRequest

   4:   {

   5:     Culture = routedMap.Culture,

   6:     Waypoints = new ObservableCollection<RouteService.Waypoint>(),

   7:     ExecutionOptions = new RouteService.ExecutionOptions { SuppressFaults = true },

   8:     Options = new RouteService.RouteOptions { RoutePathType = RouteService.RoutePathType.Points }

   9:   };

  10:  

  11:   foreach (GeocodeService.GeocodeResult result in locations)

  12:   {

  13:     request.Waypoints.Add(GeocodeResultToWaypoint(result));

  14:   }

  15:  

  16:   //Wenn “Auto” ausgewählt ist

  17:   if (rdbDriving.IsChecked == true)

  18:   {

  19:     request.Options.Mode = RouteService.TravelMode.Driving;

  20:   }

  21:   //Wenn “Fußgänger” ausgewählt ist

  22:   else if (rdbWalking.IsChecked == true)

  23:   {

  24:     request.Options.Mode = RouteService.TravelMode.Walking;

  25:   }

  26:   //Wenn weder “Auto” noch “Fußgänger” ausgewählt ist, dann gib eine Infobox aus

  27:   else

  28:   {

  29:     MessageBox.Show("Bitte wählen Sie Auto oder Fußgänger aus!");

  30:   }

  31:  

  32:   // Now that both locations were found ... use "to" for routing update.

  33:   outputStart.Text += " --> " + outputZiel.Text;

  34:   outputZiel.Text += " ... <routing>";

  35:  

  36:   routedMap.CredentialsProvider.GetCredentials(

  37:     (Credentials credentials) =>

  38:   {

  39:     //Pass in credentials for web services call. Replace with your own Credentials.

  40:     request.Credentials = credentials;

  41:                 

  42:     // Make asynchronous call to fetch the data ... pass state object.

  43:     RouteClient.CalculateRouteAsync(request);

  44:   });

  45: }

   1: private void client_RouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)

   2: {

   3:   string outString;

   4:   try

   5:   {

   6:     if (e.Result.ResponseSummary.StatusCode != RouteService.ResponseStatusCode.Success)

   7:     {

   8:       outString = "Fehler bei der Berechnung ... Status <" + e.Result.ResponseSummary.StatusCode.ToString() + ">";

   9:     }

  10:     else if (0 == e.Result.Result.Legs.Count)

  11:     {

  12:       outString = "Kann keine Route berechnen";

  13:     }

  14:     else

  15:     {

  16:       double distance = e.Result.Result.Summary.Distance;

  17:       outputDistance.Text = distance.ToString() + " km";

  18:                     

  19:       Color routeColor = Colors.Blue;

  20:       SolidColorBrush routeBrush = new SolidColorBrush(routeColor);

  21:       outString = "Route berechnet";

  22:       outputZiel.Foreground = routeBrush;

  23:  

  24:       var routeLine = new MapPolyline

  25:       {

  26:         Stroke = routeBrush,

  27:         Opacity = 0.65,

  28:         StrokeThickness = 5.0

  29:       };

  30:  

  31:       routeLine.Locations = new LocationCollection();

  32:       foreach (Location p in e.Result.Result.RoutePath.Points)

  33:       {

  34:         routeLine.Locations.Add(new Location(p.Latitude, p.Longitude));

  35:       }

  36:       RouteLayer.Children.Add(routeLine);

  37:       LocationRect rect = new LocationRect(routeLine.Locations[0], routeLine.Locations[routeLine.Locations.Count - 1]);

  38:                                   

  39:       int i = 1;

  40:       int anz = e.Result.Result.Legs[0].Itinerary.Count;

  41:       foreach (RouteService.ItineraryItem itineraryItem in e.Result.Result.Legs[0].Itinerary)

  42:       {

  43:         var point = new Ellipse

  44:         {

  45:           Width = 10,

  46:           Height = 10,

  47:           DataContext = i,

  48:           Opacity = 0.65,

  49:           Tag = itineraryItem

  50:         };

  51:  

  52:         if (i == 1) 

  53:         { 

  54:           point.Fill = new SolidColorBrush(Colors.Green);

  55:           point.Width = 20;

  56:           point.Height = 20;

  57:         }else if(i == anz)

  58:         {

  59:           point.Fill = new SolidColorBrush(Colors.Red);

  60:           point.Width = 20;

  61:           point.Height = 20;

  62:         }

  63:         else

  64:         {

  65:           point.Fill = new SolidColorBrush(Colors.Yellow);

  66:           point.Width = 15;

  67:           point.Height = 15;

  68:         }

  69:         Location location = new Location(itineraryItem.Location.Latitude, itineraryItem.Location.Longitude);

  70:         MapLayer.SetPosition(point, location);

  71:         MapLayer.SetPositionOrigin(point, PositionOrigin.Center);

  72:  

  73:         RouteLayer.Children.Add(point);

  74:         i++;

  75:       }

  76:       routedMap.SetView(rect);

  77:     }

  78:   }

  79:   catch (Exception)

  80:   {

  81:     outString = "Exception raised routine";

  82:   }

  83:   outputZiel.Text = outString;

  84: }

   1: private void GetRoute()

   2: {

   3:   bool errorFound = false;

   4:  

   5:   if (string.IsNullOrEmpty(inputZiel.Text))

   6:   {

   7:     outputZiel.Text = "Bitte eine Zieladresse eingeben!";

   8:     errorFound = true;

   9:   }

  10:   if (string.IsNullOrEmpty(inputStart.Text))

  11:   {

  12:     outputStart.Text = "Bitte eine Startadresse eingeben!";

  13:     errorFound = true;

  14:   }

  15:   if (errorFound)

  16:   {

  17:     return;

  18:   }

  19:  

  20:   // Geocode locations in parallel.

  21:   GeocodeService.GeocodeResult[] results = new GeocodeService.GeocodeResult[2];

  22:   // From location.

  23:   RoutingState state0 = new RoutingState(results, 0, outputStart);

  24:   GeocodeAddress(inputStart.Text, state0);

  25:   // To location.

  26:   RoutingState state1 = new RoutingState(results, 1, outputZiel);

  27:   GeocodeAddress(inputZiel.Text, state1);

  28: }

   1: internal class RoutingState

   2: {

   3:   internal RoutingState(GeocodeService.GeocodeResult[] resultArray, int index, TextBlock tb)

   4:   {

   5:     results = resultArray;

   6:     locationNumber = index;

   7:     output = tb;

   8:   }

   9:  

  10:   internal bool GeocodesComplete

  11:   {

  12:     get

  13:     {

  14:       for (int idx = 0; idx < results.Length; idx++)

  15:       {

  16:         if (null == results[idx])

  17:         return false;

  18:       }

  19:       return true;

  20:     }

  21:   }

  22:  

  23:   internal bool GeocodesSuccessful

  24:   {

  25:     get

  26:     {

  27:       for (int idx = 0; idx < results.Length; idx++)

  28:       {

  29:         if (null == results[idx] || null == results[idx].Locations || 0 == results[idx].Locations.Count)

  30:         {

  31:           return false;

  32:         }

  33:       }

  34:       return true;

  35:     }

  36:   }

  37:  

  38:   internal GeocodeService.GeocodeResult[] results;

  39:   internal int locationNumber;

  40:   internal TextBlock output;

  41: }

Abschließend muss noch eine Methode für den Button Route berechnen geschrieben werden, der die Berechnung der Route startet.

   1: private void Button_Click(object sender, RoutedEventArgs e)

   2: {

   3:   GetRoute();

   4: }

   5:  

   6: private void inputZiel_GotFocus(object sender, RoutedEventArgs e)

   7: {

   8:   inputZiel.SelectAll();

   9: }

  10:  

  11: private void inputStart_GotFocus(object sender, RoutedEventArgs e)

  12: {

  13:   inputStart.SelectAll();

  14: }

  15:  

  16: private void input_KeyDown(object sender, KeyEventArgs e)

  17: {

  18:   if (e.Key == Key.Enter)

  19:   {

  20:     GetRoute();

  21:   }

  22: }

Ergebnis

Starten Sie nun die Solution und geben Sie eine Start- und eine Zieladresse ein. Klicken Sie anschließend auf Route berechnen um den RouteService anzusprechen.

In dem folgenden Screenshot kann man erkennen, dass nachdem ich Start- und Zieladresse eingeben, Streckenmodus (hier: Auto) ausgewählt und auf den Button Route berechnen gedrückt habe, oberhalb der Karte die Entfernung und in der Karte mehrere verschieden farbige Punkte erstellt wurden. Hierbei wird der Startpunkt grün, der Endpunkt rot und die Zwischenpunkte gelb dargestellt. Wenn man mit dem Mauszeiger auf einen Punkt geht, werden der Name und die Beschreibung des Punktes angezeigt.

Rechts neben der Karte kann man den Verlauf der Strecke ablesen.

image

In meinem nächsten Blog-Beitrag werde ich die Verwendung des SearchService in BingMaps erläutern.

BingMaps GeocodeService

Einführung

In meinem ersten Blog-Beitrag Microsoft Bing Maps for Enterprise, habe ich über die Darstellung von Punkten, ohne Einbindung eines Webservices, auf einer BingMaps-Karte gesprochen.

Microsoft bietet aber auch 3 verschiedene WebServices für die Darstellung von Punkten, Linien oder Bereichen auf Karten an. Diese wären:

  1. Geocode Service (Punkte)
  2. Route Service (Punkte, Linien)
  3. Search Service (Punkte) 

In diesem Blog-Beitrag möchte ich die Verwendung des GeocodeService in BingMaps erläutern.

Wozu verwende ich den GeocodeService?

Mit Hilfe des GeocodeService wird eine Abfrage an den Webservice gestellt, der die Koordinaten (Längen- und Breitengrad) im Format WGS84 für die zu suchende Adresse zurückliefert. Daher bietet sich die Verwendung des GeocodeService besonders für die Suche bestimmter Adressen an.

Voraussetzungen

  • BingMaps-Key
  • Visual Studio
  • Silverlight

Falls Sie noch keinen BingMaps-Key haben, können Sie sich hier einen erstellen.

Silverlight Applikation

Als erster Schritt wird eine neue Silverlight-Applikation erstellt und der GeocodeService als Service Reference hinzugefügt. Hierzu klicken Sie mit der rechten Maustaste auf das Projekt und wählen Add Service Reference. Geben Sie als Adresse http://dev.virtualearth.net/webservices/v1/GeocodeService/GeocodeService.svc ein und klicken auf Go. Nennen Sie den Namespace GeocodeService.

image

Im nächsten Schritt werden eine Textbox zum Eingeben der Adresse und ein Button zum Abschicken erstellt. Des Weiteren werden noch zwei Textblöcke zum Ausgeben der Koordinaten (Längen- und Breitengrad) sowie eine Karte zum Darstellen der Suchergebnisse, erstellt.

   1: <Grid x:Name="LayoutRoot" Background="White">

   2:   <Grid.RowDefinitions>

   3:     <RowDefinition Height="auto"/>

   4:     <RowDefinition Height="*"/>

   5:   </Grid.RowDefinitions>

   6:   <Grid Grid.Row="0">

   7:     <Grid.RowDefinitions>

   8:       <RowDefinition Height="30" />

   9:       <RowDefinition Height="30" />

  10:       <RowDefinition Height="30" />

  11:     </Grid.RowDefinitions>

  12:     <Grid.ColumnDefinitions>

  13:       <ColumnDefinition Width="75"/>

  14:       <ColumnDefinition Width="auto" />

  15:       <ColumnDefinition Width="*" />

  16:     </Grid.ColumnDefinitions>

  17:     <TextBlock Grid.Row="0" Grid.Column="0" Text="Adresse:" HorizontalAlignment="Left"/>

  18:     <TextBox Name="txtStart" Grid.Row="0" Grid.Column="1" Text="Bitte Adresse oder Koordinaten eingeben!" Background="LightGray" Width="500" HorizontalAlignment="Left"/>

  19:     <Button Name="btnShowResult" Grid.Row="0" Grid.Column="2" Content="Finden" Background="Black" Width="100" HorizontalAlignment="Left" Click="btnShowResult_Click"/>

  20:     <TextBlock Grid.Row="1" Grid.Column="0" Text="Latitude" HorizontalAlignment="Left"/>

  21:     <TextBlock Name="txtLatitude" Grid.Row="1" Grid.Column="1"/>

  22:     <TextBlock Grid.Row="2" Grid.Column="0" Text="Longitude" HorizontalAlignment="Left"/>

  23:     <TextBlock Name="txtLongitude" Grid.Row="2" Grid.Column="1"/>

  24:   </Grid>

  25:   <map:Map x:Name="geocodeMap" CredentialsProvider="<Your Key>" Grid.Row="1">

  26:     <map:MapLayer x:Name="geocodeLayer" />

  27:   </map:Map>

  28: </Grid>

In den beiden nachfolgenden Funktionen werden die Geocode-Methoden des GeocodeService aufgerufen, um die Koordinaten (Längen- und Breitengrad) für die Adresse zu erhalten und ein Pushpin (Marker) für die Darstellung erstellt.

   1: public void GetGeocode()

   2: {

   3:     // Request an den Geocode-Webservice 

   4:     var request = new GeocodeService.GeocodeRequest

   5:     {

   6:         ExecutionOptions = new GeocodeService.ExecutionOptions { SuppressFaults = true },

   7:         Credentials = new Credentials { Token = "<Your Key>" },

   8:         Query = txtStart.Text

   9:     };

  10:  

  11:     var client = new GeocodeService.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");

  12:     client.GeocodeCompleted += client_FindGeocodeCompleted;

  13:     client.GeocodeAsync(request);

  14: }

   1: private void client_FindGeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)

   2: {

   3:     try

   4:     {

   5:         if (e.Result.ResponseSummary.StatusCode != GeocodeService.ResponseStatusCode.Success)

   6:         {

   7:             MessageBox.Show("Error geocoding ... status <" + e.Result.ResponseSummary.StatusCode.ToString() + ">");

   8:         }

   9:         else if (0 == e.Result.Results.Count)

  10:         {

  11:             MessageBox.Show("No Results");

  12:         }

  13:         else

  14:         {

  15:             Location location = new Location(e.Result.Results[0].Locations[0].Latitude, e.Result.Results[0].Locations[0].Longitude);

  16:             //Ausgeben des Längen- und Breitengrades

  17:             txtLatitude.Text = location.Latitude.ToString();

  18:             txtLongitude.Text = location.Longitude.ToString();

  19:  

  20:             //Setze die Sicht der Karte auf die aktuellen Koordinaten 

  21:             geocodeMap.SetView(location, 15);

  22:             Pushpin point = new Pushpin();

  23:             MapLayer.SetPosition(point, location);

  24:             MapLayer.SetPositionOrigin(point, PositionOrigin.Center);

  25:  

  26:             //Erstelle einen Pushpin auf der Karte um den Punkt anzuzeigen. 

  27:             geocodeLayer.Children.Add(point);

  28:         }

  29:     }

  30:     catch

  31:     {

  32:         MessageBox.Show("Exception raised");

  33:     }

  34: }

Anschließend ist in der Click-Methode des Finden-Buttons nur noch die GetGeocode-Methode aufzurufen.

   1: private void btnShowResult_Click(object sender, RoutedEventArgs e)

   2: {

   3:   //Durch Bestätigen des Buttons Finden führe die Funktion GetGeocode() aus.

   4:   GetGeocode();

   5: }

Ergebnis

Starten Sie nun die Solution und geben eine Adresse ein. Klicken Sie anschließend auf Finden um den GeocodeService anzusprechen.

In dem folgenden Screenshot kann man erkennen, dass nachdem ich eine Adresse eingeben und auf den Button Finden gedrückt habe, oberhalb der Karte die Koordinaten und in der Karte ein Pushpin an der gesuchten Adresse angezeigt werden.

image

 

In meinem nächsten Blog-Beitrag werde ich die Verwendung des RouteService in BingMaps erläutern.