MSDN Magazine - December 2007 - (Page 52) Figure 9 Enclose Aggregate Function _ Function Enclose(Of T)(ByVal collection As IEnumerable(Of T), ByVal _ selector As Func(Of T, Rect)) As Rect Dim ret As Rect Dim first As Boolean = True For Each item In collection If first Then ret = selector(item) first = False Else ret.Enclose(selector(item)) End If ply groups the list of vertices by their containing polygon and then calculates the polygon’s bounding box. The region containing the polygon is also included in the grouping key, thus enabling it to be used later in the query. The polygon’s bounding box is computed by calculating the minimum and maximum values of its vertex’s X and Y attributes. This is done by calling the custom aggregate functions MinDBL and MaxDBL, which are defined by two extension methods: _ Function MinDBL(Of T)(ByVal x As IEnumerable(Of T), _ ByVal y As Func(Of T, String)) As Double Return x.Min(Function(z) CDbl(y(z))) End Function _ Function MaxDBL(Of T)(ByVal x As IEnumerable(Of T), _ ByVal y As Func(Of T, String)) As Double Return x.Max(Function(z) CDbl(y(z))) End Function Next Return ret End Function _ Sub Enclose(ByRef theBox As Rect, ByVal otherBox As Rect) Dim X = Math.Min(theBox.X, otherBox.X) Dim Y = Math.Min(theBox.Y, otherBox.Y) Dim Width = Math.Max(theBox.BottomRight.X, otherBox.BottomRight.X) _ -X+1 Dim Height = Math.Max(theBox.BottomRight.Y, otherBox.BottomRight.Y) _ -Y+1 theBox.X = X theBox.Y = Y theBox.Width = Width theBox.Height = Height End Sub These simply define aggregate functions that calculate minimum or maximum values on their provided arguments after first converting it to type Double. The Group By clause is followed by a Select clause that converts the raw group by results into a more usable form. In particular, it defines a subquery that takes the group resulting from the Group By, projects out just the vertex from it, and then converts the resulting collection into an instance of the MapPolygon class using the ToMapPolygon extension method: _ Function ToMapPolygon(ByVal items As IEnumerable(Of XElement)) As _ MapPolygon Dim ret As New MapPolygon ret.Points.AddRange(From item In items Select item.ToPoint()) Return ret End Function _ Function ToPoint(ByVal x As XElement) As Point Return New Point(x.@X, x.@Y) End Function Figure 10 XAML Code for Displaying a Map The body of LoadFile is extremely simple. The procedure works by first loading the provided file into an XDocument instance, defining a query to traverse the document and convert it into a collection of MapRegion instances, and then finally executing the query and inserting its results into the provided output collection. The query is composed of several different pieces. The first piece is its From clause, which uses the XML Member access expression doc. . . . to retrieve all polygon Vertex tags in the document as the basis for the query. It’s worth noting that this does not use doc , which would grab all Vertex tags in the document, including those defined inside of Island tags. Instead, it only includes Vertex tags that are directly defined inside Polygon tags. The second part of the query is the Let clause. This clause introduces two new variables—Polygon and Region—which are assigned to the Polygon and Region tags that contain each vertex. The third part of the query is its first Group By clause. This sim52 msdnmagazine Map LINQ It also converts the raw MinX, MinY, MaxX, and MaxY variables into an instance of the Rect class. The next part of the query is its second Group By clause, which aggregates the results of the preceding select by Region and calculates the bounding box containing all the region’s polygons. The bounding box is calculated using the Enclose custom aggregate function, whose definition is shown in Figure 9. The last part of the query is the final Select clause, which constructs a MapRegion instance for each result returned from the second Group By. It does this using two subqueries, one of which projects out the polygon instance stored in every member of Group, and the other of which extracts the set of FIPS codes stored in Region. Drawing the Map Once a map has been loaded into memory, displaying it in a window is trivial. The XAML code in Figure 10 shows an easy way to do this. Here I define a simple Canvas control containing two transformations for its Render Transform, and a single ItemsControl named MapViewer as its content. The transformations are used to translate the coordinates of the regions in the map so that they will correctly display within the canvas. The ItemsControl is used to
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.