Render Modes in Detail |
Render modes control when an object that implements the IMapRenderer interface is drawn on the map. This can occur in one of four passes. Objects that are drawn in a later pass may obscure objects that were drawn in previous passes.
When rendering occurs, four passes are made over the objects in the RendererList. The order in which objects are rendered depends on their Mode.
Objects are rendered in this order:
The image below highlights the differences between the four modes. Each circle has been placed on the map with a different render mode, as follows:
As you can see, the red circle has been rendered before the map; the blue and yellow circles have been rendered before the labels (note the blue circle below the yellow circle); the green circle has been rendered last.
Note |
---|
This example was created by modifying code that can be found on the GeoZone site. To display the PreMap renderer, a .cam file with the clear brush and render [land] polygons code lines commented out has been used. |
A single object can use more than one render mode to achieve a complex layout. For example, BalloonPushPin renders the PushPin icon in PreLabelling, and then follows up by rendering the informational balloon in Labelling. This split-level rendering ensures that all PushPin icons are placed on the map before balloons are drawn, allowing the use of Test to ensure that balloons do not obscure PushPins.
In the PreLabelling and Labelling passes, it is possible to call the Test and Place methods on the RenderContext to control placement of items on the map. By passing a region to these methods you can either check to see if an area is clear of labels or can reserve an area for your Renderer to use.
Both the Test and Place methods define the area where an object appears on the map by a rectangular region, a buffer zone around that rectangle that provides a bit of visual separation from other objects, and an angle of rotation. You can specify the rectangular region using either the System.Drawing.Rectangle structure, or using four integer values (for the left edge, top edge, width, and height).
You are free to use these methods as you want. You do not have to ensure an area is free to draw in with Test before you Place a label in it. Similarly, you do not have to declare the areas you are drawing upon with Place. For example, the BalloonPushPin described above calls Place for each icon it draws, but does not Test. This ensures every icon is placed, but also allows the labelling pass to detect areas it cannot place a balloon on.
The following example shows a renderer that uses Place to prevent labels from appearing on top of an icon. It uses System.Drawing.Rectangle to define the region where the icon is rendered:
public void Render(Graphics graphics, RenderContext rc) { if (rc.Map.Contains(Location) && rc.RenderMode == RenderMode.PreLabelling) { // Convert location to pixel coordinates int px, py; rc.Map.LatLontoXY(out px, out py, mLocation); // Call Place( ) to prevent the Labelling pass from obscuring this icon rc.Place(ImageUtils.IconCache.Rectangle(Icon, px, py), 0, 0); // draw the icon ImageUtils.IconCache.DrawIcon(graphics, px, py, Icon, false); } }
Note that in this method, there is no call to the Test method. That means that the icon is always drawn, even if it obscures another object that also called Place.
The following example shows a renderer that uses both the Place and Test methods. In this case, the object will not appear on the map if that would cause it to obscure any other objects that called Place in the PreLabelling pass. Further, labels placed in the Labelling pass that use the Test method will not obscure this object.
public void Render(Graphics graphics, RenderContext rc) { if (rc.Mode == RenderMode.Labelling && rc.Map.Contains(mLocation)) { int px, py; //Figure out pixel location rc.Map.LatLontoXY(out px, out py, mLocation); // get the size of our text SizeF textSize = graphics.MeasureString(Message, mFont); Int32 spacer = (Int32) textSize.Height / 2; // check that the drawing space is free if (rc.Test(px, py, (Int32) textSize.Width, (Int32) textSize.Height, spacer, 0) // call place to prevent other labels from appearing on top rc.Place(px, py, (Int32) textSize.Width, (Int32) textSize.Height, spacer, 0); // write the message System.Drawing.PointF point; point.x = (Single) px; point.y = (Single) py; graphics.DrawString(Message, mFont, mBrush, point); } } }