Previously, I showed you how to get a basic mg-desktop application up and running.
For this post, I am going to introduce some of the more advanced features and functionality of mg-desktop
Viewer Properties
The mg-desktop viewer (MgMapViewer) exposes a whole bunch of properties that can control the behaviour and appearance of certain features in the map viewer.
Each property is explained below
- ConvertTiledGroupsToNonTiled - Tiled Layer Groups will be treated as regular map groups, allowing for such groups and layers to be shown in the viewer. This is a simple workaround for the viewer's current inability to display map tiles.
- SelectionColor - Controls the color of selected features in the viewer
- ShowVertexCoordinatesWhenDigitizing - When you are digitizing, the vertex coordinates are shown at each node of the currently digitized geometry. For circles and points, no vertex coordinates are displayed because such shapes have no vertices. For example, digitizing a polygon would look like this:
- ZoomInFactor - The zoom scale multiplier to apply for zooming in
- ZoomOutFactor - The zoom scale multiplier to apply for zooming out
Digitizing and Measuring
Like the AJAX and Fusion viewers, the mg-desktop viewer has built-in geometry digitizing functionality.
1: public interface IMapViewer
2: {
3: //
4: // Summary:
5: // Starts the digitization process for a circle
6: //
7: // Parameters:
8: // callback:
9: // The callback to be invoked when the digitization process completes
10: void DigitizeCircle(CircleDigitizationCallback callback);
11: //
12: // Summary:
13: // Starts the digitization process for a line
14: //
15: // Parameters:
16: // callback:
17: // The callback to be invoked when the digitization process completes
18: void DigitizeLine(LineDigitizationCallback callback);
19: //
20: // Summary:
21: // Starts the digitization process for a line string (polyline)
22: //
23: // Parameters:
24: // callback:
25: // The callback to be invoked when the digitization process completes
26: void DigitizeLineString(LineStringDigitizationCallback callback);
27: //
28: // Summary:
29: // Starts the digitization process for a point
30: //
31: // Parameters:
32: // callback:
33: // The callback to be invoked when the digitization process completes
34: void DigitizePoint(PointDigitizationCallback callback);
35: //
36: // Summary:
37: // Starts the digitization process for a polygon
38: //
39: // Parameters:
40: // callback:
41: // The callback to be invoked when the digitization process completes
42: void DigitizePolygon(PolygonDigitizationCallback callback);
43: //
44: // Summary:
45: // Starts the digitization process for a rectangle
46: //
47: // Parameters:
48: // callback:
49: // The callback to be invoked when the digitization process completes
50: void DigitizeRectangle(RectangleDigitizationCallback callback);
51: }
The digitization API is very similar to the AJAX viewer. To digitize a circle for example, you would call the API like so:
1: mgMapViewer1.DigitizeCircle(OnCircleDigitized);
The OnCircleDigitized method must match the signature of the CircleDigitizationCallback delegate, which looks like this:
1: private void OnCircleDigitized(double x, double y, double radius)
2: {
3: //x, y is the circle center in the map's coordinates
4: //radius is the circle's radius
5: }
While digitizing, the digitization process can be aborted by pressing the ESC key. If digitization is aborted, the digitization callback will not be called by the viewer.
Tools like measuring can be built on this functional primitive of digitization. Here's an example using a line digitizer:
1: private void btnMeasure_Click(object sender, EventArgs e)
2: {
3: mgMapViewer1.DigitizeLine(OnLineDigitized);
4: }
5:
6: private void OnLineDigitized(double x1, double y1, double x2, double y2)
7: {
8: MgMapBase map = mgMapViewer1.GetMap();
9: //Create a coordiante system from the map's SRS
10: MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
11: MgCoordinateSystem mapCs = csFactory.Create(map.GetMapSRS());
12:
13: //Invoke the appropriate measure method depending on the type
14: //of coordinate system
15: double dist = 0.0;
16: if (mapCs.GetType() == MgCoordinateSystemType.Geographic)
17: dist = mapCs.MeasureGreatCircleDistance(x1, y1, x2, y2);
18: else
19: dist = mapCs.MeasureEuclideanDistance(x1, y1, x2, y2);
20:
21: //Convert this distance to meters
22: dist = mapCs.ConvertCoordinateSystemUnitsToMeters(dist);
23:
24: MessageBox.Show("Distance is: " + dist + " meters");
25: }
Using the digitizing API combined with the existing MgGeometry and MgCoordinateSystem APIs, you can measure in ways other than simple point A - point B distance measuring.
Redlining
When you combine digitization with feature manipulation provided by the MapGuide API, you have the basis for redlining. Here's an example of creating point features from digitized points:
1: private MgdLayer _pointLayer;
2:
3: private void btnDrawPoint_Click(object sender, EventArgs e)
4: {
5: mgMapViewer1.DigitizePoint(OnPointDrawn);
6: }
7:
8: private void OnPointDrawn(double x, double y)
9: {
10: if (_pointLayer == null) //Our point layer doesn't exist
11: CreateRedlineLayer();
12:
13: //Now insert our point. This code should look familiar
14: //to you, setting up the MgPropertyCollection for insertion
15: MgPropertyCollection props = new MgPropertyCollection();
16: MgWktReaderWriter wktRw = new MgWktReaderWriter();
17: MgAgfReaderWriter agfRw = new MgAgfReaderWriter();
18:
19: MgGeometry geom = wktRw.Read("POINT (" + x + " " + y + ")");
20: MgByteReader agf = agfRw.Write(geom);
21:
22: MgGeometryProperty geomProp = new MgGeometryProperty("Geometry", agf);
23: props.Add(geomProp);
24:
25: //Here's where we differ from the official MapGuide API
26: //instead of a monolithic UpdateFeatures() that tries to
27: //do everything, we have individual InsertFeatures/DeleteFeatures/UpdateFeatures
28: //methods. So here's the mg-desktop way
29:
30: MgFeatureReader result = _pointLayer.InsertFeatures(props);
31: result.Close();
32:
33: //Or if you have have access to the MgdLayer instance
34: /*
35: MgResourceIdentifier fsId = new MgResourceIdentifier(_pointLayer.GetFeatureSourceId());
36: MgServiceFactory factory = new MgServiceFactory();
37: MgdFeatureService featSvc = (MgdFeatureService)factory.CreateService(MgServiceType.FeatureService);
38: MgFeatureReader fr = featSvc.InsertFeatures(fsId, "Default:Redline", props);
39: fr.Close();
40: */
41:
42: //Now refresh to see your newly drawn point
43: mgMapViewer1.RefreshMap();
44: }
45:
46: private void CreateRedlineLayer()
47: {
48: MgMapBase map = mgMapViewer1.GetMap();
49: MgServiceFactory fact = new MgServiceFactory();
50: MgdFeatureService featSvc = (MgdFeatureService)fact.CreateService(MgServiceType.FeatureService);
51: MgResourceService resSvc = (MgResourceService)fact.CreateService(MgServiceType.ResourceService);
52:
53: //Note that mg-desktop does not have a concept of sessions like the
54: //official MapGuide API, but it *does* allow session-based resources
55: //as a way of having temporary resources. Such resources will reside
56: //in a special directory for session resources (specified in Platform.ini)
57: //
58: //You can plug whatever string as the session id, but the resource identifier
59: //must satisfy the session id pattern:
60: //
61: // Session:<session id string>//Path/To/Your.ResourceType
62: //
63: //These files are removed with MgPlatform.Terminate(), which is called in this
64: //application as part of the exiting process.
65: string sessionId = Guid.NewGuid().ToString();
66: MgResourceIdentifier fsId = new MgResourceIdentifier("Session:" + sessionId + "//Redline.FeatureSource");
67: MgResourceIdentifier ldfId = new MgResourceIdentifier("Session:" + sessionId + "//Redline.LayerDefinition");
68:
69: //Create our point redline schema. It looks like this:
70: //
71: // Default
72: // Redline
73: // ID (int32, autogenerated)
74: // Geometry (coordinate system same as map
75: string featureClass = "Default:Redline";
76: string geometry = "Geometry";
77:
78: MgFeatureSchema schema = new MgFeatureSchema("Default", "Redline schema");
79: MgClassDefinition cls = new MgClassDefinition();
80: cls.Name = "Redline";
81:
82: MgDataPropertyDefinition id = new MgDataPropertyDefinition("ID");
83: id.DataType = MgPropertyType.Int32;
84: id.SetAutoGeneration(true);
85:
86: MgGeometricPropertyDefinition geom = new MgGeometricPropertyDefinition(geometry);
87: geom.SpatialContextAssociation = "Default";
88: geom.GeometryTypes = MgFeatureGeometricType.Curve | MgFeatureGeometricType.Point | MgFeatureGeometricType.Solid | MgFeatureGeometricType.Surface;
89:
90: MgPropertyDefinitionCollection clsProps = cls.GetProperties();
91: clsProps.Add(id);
92: clsProps.Add(geom);
93:
94: MgPropertyDefinitionCollection idProps = cls.GetIdentityProperties();
95: idProps.Add(id);
96:
97: cls.DefaultGeometryPropertyName = geometry;
98: MgClassDefinitionCollection classes = schema.GetClasses();
99: classes.Add(cls);
100:
101: //Create the feature source with this schema. We use the map's
102: //coordinate system for the feature source to ensure features
103: //that we create, will line up with the map
104: MgCreateSdfParams create = new MgCreateSdfParams("Default", map.GetMapSRS(), schema);
105: featSvc.CreateFeatureSource(fsId, create);
106:
107: //Then create the layer definition. RedlineLayer.xml contains the template
108: string xml = string.Format(File.ReadAllText("RedlineLayer.xml"), fsId.ToString(), featureClass, geometry);
109: var bytes = Encoding.UTF8.GetBytes(xml);
110: MgByteSource source = new MgByteSource(bytes, bytes.Length);
111: resSvc.SetResource(ldfId, source.GetReader(), null);
112:
113: //Now create the runtime layer and add to map
114: _pointLayer = new MgdLayer(ldfId, resSvc);
115: _pointLayer.LegendLabel = "Redlining";
116: _pointLayer.Name = "Redline";
117: _pointLayer.Visible = true;
118: _pointLayer.Selectable = true;
119: _pointLayer.DisplayInLegend = true;
120:
121: var layers = map.GetLayers();
122: layers.Insert(0, _pointLayer);
123: }
An updated sample (building on the previous sample application) containing the above snippets is available for download
here