Im letzten Teil der Serie beschäftigten wir uns mit dem Laden und Anzeigen von ESRI Shapefiles im G#.Viewer. Nun werden wir die einfach zugänglichen Funktionen zum Bedienen der Kartenansicht in unseren GIS-Viewer einbauen.

Im Namespace SharpMap.Forms.MapImage befindet sich ein Enumerator, der die verfügbaren Kartenfunktionalitäten auflistet.

Die wichtigsten sind:

  • Pan (Verschieben)
  • ZoomIn
  • ZoomOut
  • Query

Da diese Tools ja bereits „out-of-the-box“ mitgeliefert werden, brauchen wir nichts anderes tun, als an jedem LabelButton in unserem ToolStrip ein Click-Event zu erzeugen und den Wechsel des aktuellen Werkzeugs zu vollziehen. Was sich etwas umständlich anhört ist mit C# und SharpDevelop ganz simpel umzusetzen:

Ein Doppelklick auf den LabelButton „btnPan“ erzeugt einen Click-Event im Designer und im Code der Klasse frmMain. Dort werden wir ja auch sofort hingeführt. Nun müssen wir lediglich das entsprechende Werkzeug des MapImage-Controls aktiv schalten:

void BtnPanClick(object sender, EventArgs e)
{
	mapImage1.ActiveTool = SharpMap.Forms.MapImage.Tools.Pan;
}

Dasselbe Vorgehen gilt für die Buttons „btnZoomIn“, „btnZoomOut“:

void BtnZoomInClick(object sender, EventArgs e)
{
	mapImage1.ActiveTool = SharpMap.Forms.MapImage.Tools.ZoomIn;
}

void BtnZoomOutClick(object sender, EventArgs e)
{
	mapImage1.ActiveTool = SharpMap.Forms.MapImage.Tools.ZoomOut;
}

Außerdem setzen wir noch das Default-Werkzeug beim Initialisieren auf ZoomIn in den Eigenschaften des MapImage-Controls:

Was jetzt noch fehlt ist die Funktion für den Button „FullExtent“. Auch hier erzeugen wir in der Design-Ansicht mit einem Doppelklick auf den LabelButton „btnFullExtent“ einen Click-Event.  Um zur vollen Ausdehnung aller Layer im Map-Objekt des MapImage-Controls zu zoomen brauchts folgenden Code:

void BtnFullExtentClick(object sender, EventArgs e)
{
	mapImage1.Cursor = Cursors.WaitCursor;
	if (mapImage1.Map.Layers.Count > 0)
	{
		mapImage1.Map.ZoomToExtents();
		mapImage1.Refresh();
	}
	mapImage1.Cursor = Cursors.Default;
}

Die Funktion Refresh() zeichnet den Inhalt des mapImage-Controls neu und aktualisiert somit den Kartenausschnitt visuell, nachdem im Map-Objekt auf die volle Ausdehnung gezoomt wurde.

Das Beste zum Schluss

Nun brauchen wir noch eine Info-Funktion (Identify), die einen aktiven Layer mit einem Klick in die Karte über seine Attribute abfragen kann.
SharpMap kann immer nur einen Layer zu einem sogenannten „Query“-Layer machen. Deshalb muss vom Benutzer festgelegt werden, welcher Layer denn nun abgefragt werden soll.
Dazu benötigen wir zwei Funktionen. Eine, die uns den vom Benutzer selektierten Layer im TreeView zurückgibt. Diese wird folgendermaßen definiert:

private string getSelectedLayer(TreeView treeView)
{
	string layername = String.Empty;

	rootNode = treeView.Nodes[0];
	TreeNodeCollection subNodes = rootNode.Nodes;

	foreach (TreeNode node in subNodes)
	{
		if (node.IsSelected)
		{
			layername = node.Text;
		}
	}
	return layername;
}

Und eine Funktion, die den übergebenen Layernamen zum zugehörigen Layerindex in der LayerCollection auflöst:

private int getLayerIndex(string layername, SharpMap.Map _map)
{
	int layerIndex = 0;
	ILayer testLayer = mapImage1.Map.GetLayerByName(layername);
	layerIndex = _map.Layers.IndexOf(testLayer);
	return layerIndex;
}

Anschließend verfahren wir wie oben: Doppelklick auf den Label-Button „Info“. Die nun generierte Code-Schablone füllen wir mit folgenden Inhalten:

  • Abrufen des Layernamens, der vom Benutzer gewählt wurde
  • Auflösen des Layernamens zu seinem Index in der Layer-Collection
  • Setzen des QueryLayerIndex im Map-Objekt auf den ermittelten Layerindex

Als Code:

void BtnInfoClick(object sender, EventArgs e)
{
	//Überprüfe, welcher Layer im TreeView selektiert wurde
	string layername;
	int layerIndex;

	layername = getSelectedLayer(treeView1);

	if (layername != String.Empty)
	{
		toolStripStatusLabel2.Text = "Layer "+  layername + " wird abgefragt.";
		layerIndex = getLayerIndex(layername, mapImage1.Map);
		if (layerIndex >= 0)
		{
			mapImage1.QueryLayerIndex = layerIndex;
			mapImage1.ActiveTool = SharpMap.Forms.MapImage.Tools.Query;
			mapImage1.Cursor = Cursors.Help;
		}
	}
	else
	{
		MessageBox.Show("Fehler bei der Abfrage. Bitte zuerst einen Layer auswählen.", "Fehler!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
	}
}

Leider war es das auch noch nicht ganz. Es gibt da nämlich noch einen Event des MapImage-Objekts, das aufgerufen wird, sobald die Karte abgefragt wird:

In diesem EventHandler muss man jetzt noch die DataTable des abgefragten Layers an unser DataGridView als Datenquelle hängen:

void MapImage1MapQueriedDataSet(SharpMap.Data.FeatureDataSet data)
{
	//Binde die Ergebnisse der Kartenabfrage (Info-Button) an das DataGridView
	dgvAttributes.DataSource = data.Tables[0];
	//Zeige Attribut-Infos
	tabControl1.SelectedIndex = 1;
}

Damit sollte die Abfrage nun klappen. Ein Hinweis noch: Die Abfragen werden mit dieser „Query“-Funktion im BoundingBox-Modus durchgeführt, da sie auf der ExecuteIntersectionQuery()-Funktion von SharpMap basiert. Also nicht wundern, wenn vermeintlich zu viele Ergebnisse in den Sachdaten auftauchen.

Ein Letztes noch: Wenn das Hauptfenster momentan in seiner Größe verändert wird, bleibt das Kartenbild „stehen“. Als Anwender würde man erwarten, dass sich das Kartenbild mit vergrößert. Dies hat man relativ schnell umgesetzt. Es gibt einen Event im MapImage-Objekt mit dem Namen „SizeChanged“. Doppelklick auf diesen Event und flugs noch folgenden Code eingefügt – dann reagiert die Anwendung auch wie man es erwartet:

void MapImage1SizeChanged(object sender, EventArgs e)
{
	mapImage1.Refresh();
}

Download als SharpDevelop-Projekt