Introduction
CanvasGIS is an object oriented 2D graphics drawing component. It's a part of MVisionTech open source GIS and data visualization framework. CanvasGIS had been used in the GisMap component presented in my previous article.
Many applications require two dimensional graphics to provide graphical input and represent different types of data. Some examples of two dimensional data presentation are charts displaying numerical data, maps representing the geometry of roads and other geographical objects, diagrams representing object relations and CAD/CAM tools. Canvas is an attempt to create a component that will serve them all.
There are some functionalities and requirements which are common among different types of graphical applications:
- Draw different kinds of shapes and manipulate them graphically.
- Navigate through geometrical data including zooming, pining and data layer selection.
- Changing presentation parameters such as colors, line styles etc.
- Assign different presentations of data for different scales, for example, when displaying maps in course resolution show only the main roads and country boundaries and when the user zooms in show the streets and other finer details.
- Creating several views of the same geometrical data.
- Perform graphical data manipulation, create new objects and maintain connection between presentation and the actual data. Create, edit and delete geometrical objects.
- Handle data coming in different coordinate systems. Perform data conversions between different coordinate systems.
- Create handles for different shape events. Events can be shape creation, editing, movement and deletion.
Layers, scales and graphics optimization
Dealing with large amounts of graphics requires special data structure and optimization. Consider the case where we want to display the roads in a country. When the user zooms out to see them all together, the map will be cluttered with lines (even for a small country) and these lines take a lot of time to draw. On the other hand, when the user zooms into the map to see one single block of streets it takes too much time to find out the lines that should be displayed (polygon clipping).
One of the ways of dealing with different detail levels (multiple resolutions) of data is to introduce graphical layers with different visibility in different scales. In the example below we create different graphic layers for the Earth's image and aerial photo of some city with a polygon outlining the border of a building.
The Earth's image is visible in a range between 0.1 and 0.5 (using layer min and max scale parameters).
Arial photo is in a range between 0.5 and 8, so when we zoom into the map it switches off the Earth's image and turns on the aerial photo. GisMap GIS component uses this functionality to switch between different resolutions of geographical data.
Of course, it's not feasible to have different layers for every possible resolution we need, so we use additional optimization techniques:
- Polygon simplification- There is no point in displaying things that we can't see, for example, we can't see deviations less than one pixel on the screen. So, as per the specific resolution we can approximate the original polygon to a simpler one with less points. The approach that we take is consecutive subdivision of a simple polygon (approximation) till the distance from the simple polygon to the original one (approximation error) is less than 1 pixel.
- Geometrical data structure- There are various data structures that are used to accelerate geometrical data, such as range trees, quadtrees etc. We currently use simple subdivision of graphical shapes of the grid with equal cell dimensions. Every cell in the grid contains references to the geometrical shapes included in the cell. So, when user zooms into certain area, all the cells included in the area are displayed.
Using the component
Canvas consists of two namespaces: CanvasControl
and GeomModel
. CanvasControl
is a view in View/Model pattern and GeomModel
is a model:
using GeomModel;
using CanvasControl;
In order to draw shapes on the canvas, one of the "create shape" methods can be used. For example, to draw a line:
canvas1.CreateLine();
Once the method has been called the canvas is transferred into the drawing state, so that the user can draw a shape. There are create methods for lines, rectangles and polygons, it can also be reused by other shapes, for example, ellipse can be derived from a rectangle:
EllipseCanvasItem ei = new EllipseCanvasItem(canvas1.CurrentLayer);
canvas1.setMode(CanvasControl.Canvas.DrawingMode.Rectangle);
canvas1.CreateShape(ei);
The shape can also be created directly on the canvas without user drawings:
ArrayList pi = new ArrayList();
pi.Add(new PointF(0,0));
pi.Add(new PointF(10,0));
pi.Add(new PointF(10,10));
pi.Add(new PointF(20,20));
PolygonCanvasItem pi = new PolygonCanvasItem(pi,canvas1.CurrentLayer);
canvas1.AddShape(pi);
Shapes in the canvas are organized in layers. The layers define the shape colors and the order in which the shapes are presented in the canvas. A new shape can also be created for a group of shapes:
GeomModel.CanvasLayer lay =
new GeomModel.CanvasLayer("new-layer",Color.Blue,0,true);
Different attributes can be assigned to the layer, such as line color, fill color, line width, and scale range from min to max where the group of shapes will be visible. The last feature is used in displaying geographical maps where each scale has different detail level.
Delegates can be assigned to the shapes to handle different events, for example, to handle the find shape event which occurs when the user clicks on the shape (canvas should in "find" mode):
this.canvas1.ItemFindEvent +=
new CanvasControl.Canvas.ItemEventHandler(this.canvas1_ItemFindEvent);
...
private void canvas1_ItemFindEvent(object sender,
GeomModel.ItemEventArgs e)
{
m_selected = e.indx;
propertyGrid1.SelectedObject = canvas1.GetItem(e.indx);
MessageBox.Show("Item:"+e.indx+"found");
}
Design
Canvas consists of two main classes Canvas
and GeomModel
implementing the Model/View design pattern:
Canvas
is a view of the geometrical model (GeomModel
) that holds the client to real world transformations between the view and the geometrical model. When the view is changed as a result of zoom, translation or resize, the canvas passes the request to the geometrical model to draw all the shapes visible in its view using the changed transformation.
GeomModel
keeps all the shapes derived from the CanvasItem
base class organized in layers. Several canvases can be attached to the same model. When the model changes all the canvases receive notifications and accordingly update there views (model/view design pattern).
CanvasLayer
keeps the presentation attributes of a group of shapes.
CanvasItem
is the base class for the shape presented in the canvas. New shapes can be added to the canvas by implementing the CanvasItem
interface.
PolygonCanvasItem
, PointCanvasItem
, ImageCanvasItem
etc. are different implementations of the CanvasItem
interface.