Mapobjects Delphi
Mapobjects Delphi
0
In this introductory document you'll use MapObjects version 2.2 and Borland Delphi 5.0 to build an application that uses maps. Along the way you will learn how to . .
Display a map with multiple layers Control panning and zooming Create a toolbar control Display map layers based on scale Perform spatial and logical queries Draw simple graphics on the map Display features with thematic renderers Dynamically display real-time data with an event tracking layer Programmatically add data to a map
Loading MapObjects
Start Delphi and choose Import ActiveX Control from the Component menu. Find the ESRI MapObjects 2.2 reference in the list of available controls. If MapObjects 2.2 is not listed, click Add to include the MapObjects 2.2 reference in the list. Finally, click Install to rebuild the omponent library.
Notice that a new tool appears in the list of ActiveX icons on the toolbar of the component palette. This new tool is the MapObjects 2.2 map control.
The help system provides help for every object, property, method, event, and constant in MapObjects 2.2.
Adding a Map
Add the Map Control to the Form
1. 2. Double-click the map control in the ActiveX palette to add a new map to the form. Resize the map to fill the form.
2. 3. 4. 5.
Click the Color button to select a color for the States layer. Click OK to close the dialog. Select a color for the USHigh layer in the same manner. Click OK to close the property window.
TrackRectangle is a method that applies to a map. It tracks the movement of the mouse while the user presses the mouse button, rubber-banding a rectangle at the same time. When the user releases the mouse button, the TrackRectangle method returns a Rectangle object which the application assigns into the Extent property of the map, causing the map to be redrawn with a new map extent.
Getting Started with MapObjects 2.2 in Delphi 5.0 4. Close the form to return to design mode.
Add Panning
1. 2. Double-click the OnMouseDown event in the Object Inspector to display the code window again. Change the code for the MouseDown procedure for Map1.
procedure TForm1.Map1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if (Button = mbLeft) then Map1.Extent := Map1.TrackRectangle else Map1.Pan; end;
Use the left mouse button to zoom in to the map; use the right mouse button to invoke the Pan method.
6. 7.
Double-click Full Extent to display the code window. Add code for the Click event of the button.
procedure TForm1.Button1Click(Sender: TObject); begin Map1.Extent := Map1.FullExtent; end;
The FullExtent property of the map returns a Rectangle which defines the bounding box of all the layers of the map.
Adding a Toolbar
Your application's pan and zoom capabilities are somewhat hidden from the user. In this section you will create a toolbar with pan and zoom buttons.
Adding a Panel
Panels are a convenient way to group controls on a form and will be used to implement the toolbar. 1. 2. 3. 4. Delete the Full Extent button from the form. Select the Panel control from the Standard controls page and place a panel at the top of the form. Set the Caption property of the panel to blank and set the Align property to alTop. Set the BevelOuter property to bvNone. Set the Name of the panel to MapToolbar. Resize the map so that it is not obscured by the panel.
10
Selecting the first button in the toolbar sets the mouse to be a zoom tool; otherwise, if you click the map, you can use the mouse to pan.
Getting Started with MapObjects 2.2 in Delphi 5.0 The FullExtentBtnClick event is generated whenever the FullExtentBtn is clicked.
11
5.
Click on the full extent button (the globe) in your application's toolbar to draw the map at its full extent.
12
With the panel selected, click the Label control on the Standard page of the component palette and click the panel to add a text label control. Set the Caption property of the text label control to State:. With the panel selected, click the Edit control on the Standard page of the component palette and click the panel to add an edit control. Set the Text property of the edit control to blank. Select the label and edit controls. Select the Alignment Palette item from the View menu to display the Alignment Palette. Click the align vertical centers button, then center vertically in window button to position the new controls.
13
The code first builds a simple SQL query expression using the value of the Edit boxs Text property. Then the States layer is searched using the SearchExpression method with the result being a RecordSet object. If the value of the RecordSet's EOF property is False, the RecordSet is positioned on the first record that satisfies the search expression. In that case, the value of the Shape field is obtained for the first record. The shape is flashed three times, then the Extent of the shape is scaled and set to be the Extent of the map.
14
Handling Resize
When you run your application and resize the form, you'll notice that the map is not automatically resized.
Map1.Height := Panel2.Height;
15
If you run your application now you'll notice that every county in the US is displayed. At the full extent, there is no need to display that much detail, so in response to the BeforeLayerDraw event, you will selectively make the Counties and States layers visible or invisible depending on the current extent of the map.
:= := := := :=
ext := Map1.Extent; fullExt := Map1.FullExtent; ly1.Visible := ext.Width < (fullExt.Width / 4); ly2.Visible := ext.Width >= (fullExt.Width / 4); end; // counties // states
16
The value of the visible property of each layer is based on the current extent of the map. If the width of the current extent is less than or equal to one fourth of the full extent of the map, then the counties will be visible and the states will be invisible. Because this code is executed in response to the BeforeLayerDraw event for each layer, the value of the visible field is calculated before drawing occurs.
3. 4. 5.
Click the FullExtent button and the Counties are no longer visible. Close the form. Click the Save All button to save your changes.
17
18
When the current tool is the spatial query tool, two searches are performed. The first search is a point proximity search on the Ushigh layer. The point is obtained by converting the x and y coordinates of the event, which are in control units, into map units. If the first search is successful, the highway that is found is used as the input to the second search which is performed on the Counties layer. The result of the second search is stored in the variable, gSelection.
19
3. 4.
Close the form. Click the Save Project button to save your changes.
20
Statistical mapping
In this section you will modify your application so that the Counties layer is drawn using the underlying attribute information.
procedure TForm1.FormActivate(Sender: TObject); var breakVal : Double; i : Integer; lys : IMoLayers; ly1, ly2 : IMoMapLayer; cbr : IMoClassBreaksRenderer; ddr : IMoDotDensityRenderer; recset1, recset2 : IMoRecordset; stats1, stats2 : IMoStatistics; begin // Render lys := ly1 := ly1 := counties layer Map1.Layers; IMoMapLayer(CreateOleObject('MapObjects2.MapLayer')); IMoMapLayer(lys.Item('counties'));
ly1.Renderer := IMoClassBreaksRenderer(CreateOleObject('MapObjects2.ClassBreaksRenderer')); cbr := IMoClassBreaksRenderer(ly1.Renderer); cbr.Field := 'MOBILEHOME'; recset1 := IMoRecordset(ly1.Records); stats1 := IMoStatistics(recset1.CalculateStatistics('MOBILEHOME')); // calculate breaks away from the mean in both directions // but only add those bfreaks that are within the range of values breakVal := stats1.Mean - (stats1.StdDev * 3); for i := 0 to 6 do begin if (breakVal >= stats1.Min) and (breakVal <= stats1.Max) then begin cbr.BreakCount := cbr.BreakCount + 1; cbr.Break[cbr.BreakCount - 1] := breakVal; end; breakVal := breakVal + stats1.StdDev; end; cbr.RampColors(clLime, clRed); // you will later insert code here to render the states layer end;
21
Each MapLayer object has a Renderer property A Renderer object controls how the MapLayer is drawn. The ClassBreaksRenderer can be used to display continuous data, in this case the number of mobile homes per capita by county.
Getting Started with MapObjects 2.2 in Delphi 5.0 2. Zoom into an area so that the Counties layer becomes visible. Notice that the counties are now drawn in different colors, depending on the underlying attribute values.
22
3. 4.
Close the form. Click the Save Project button to save your changes.
Event Tracking
It is often desirable to display geographic entities on top of the map, especially if those entities have a tendency to move. For example, a vehicle tracking system would want to display vehicles on the map at the appropriate locations and update those locations over time without redrawing all the layers of the map each time a vehicle changes location. In this section you will add an event tracking layer to your application.
23
const moEdgeTouchOrAreaIntersect = 6; begin if (ZoomBtn.Down) then // zoom Map1.Extent := Map1.TrackRectangle; if (PanBtn.Down) then // pan Map1.Pan; if (QueryBtn.Down) then // query begin pt := Map1.ToMapPoint(x,y); lys := Map1.Layers; ly1 := IMoMapLayer(CreateOleObject('MapObjects2.MapLayer')); ly2 := IMoMapLayer(CreateOleObject('MapObjects2.MapLayer')); ly1 := IMoMapLayer(lys.Item('Ushigh')); ly2 := IMoMapLayer(lys.Item('Counties')); // search for a highway within a tolerance recs := ly1.SearchByDistance(pt, Map1.ToMapDistance(10),''); fields := recs.Fields; field := fields.Item('Shape'); if (recs.EOF) then // nothing is found gSelection := nil // varNull else // search for counties that intersect the highways gSelection := ly2.SearchShape(IDispatch(field.Value), moEdgeTouchOrAreaIntersect, ''); // trigger a redraw of the MapControl Map1.Refresh; end; if (AddEventBtn.Down) then // add an event begin pt := Map1.ToMapPoint(x,y); tLayer := Map1.TrackingLayer; // remember to insert tLayer in var declarations tLayer.AddEvent(pt, 0); end; end;
24
2. 3.
Click the event tool, then click in the map to add events. Close the form.
procedure TForm1.Timer1Timer(Sender: TObject); var maxDist : Double; nEventCount, iIndex : Integer; geoEvnt : IMoGeoEvent; rect : IMoRectangle; tLayer : IMoTrackingLayer; begin rect := IMoRectangle(Map1.Extent); maxDist := rect.Width / 20; tLayer := Map1.TrackingLayer; nEventCount := tLayer.EventCount; for iIndex := 0 to nEventCount - 1 do begin geoEvnt := IMoGeoEvent(tLayer.Event[iIndex]); // move each event randomly geoEvnt.Move(maxDist * (Random - 0.5), maxDist * (Random - 0.5));
25
3.
26
layer : IMoMapLayer; sym : IMoSymbol; lys : IMoLayers; begin dc := MoDataConnection(CreateOleObject('MapObjects2.DataConnection')); dc.Database := 'c:\Program Files\ESRI\MapObjects2\Samples\Data\Usa' ; if (dc.Connect) then begin layer := IMoMapLayer(CreateOleObject('MapObjects2.MapLayer')); layer.GeoDataset := IMoGeoDataset(dc.FindGeoDataset('States')); sym := layer.Symbol; sym.Color := clYellow; lys := Map1.Layers; lys.Add(layer); layer := IMoMapLayer(CreateOleObject('MapObjects2.MapLayer')); layer.GeoDataset := dc.FindGeoDataset('Counties'); lys := Map1.Layers; lys.Add(layer); layer := IMoMapLayer(CreateOleObject('MapObjects2.MapLayer')); layer.GeoDataset := dc.FindGeoDataset('ushigh'); // sym.Color := clRed; lys.Add(layer); end else raise Exception.Create('The data could not be located.'); end;
3.
Add a call (see bold below) to your procedure in the Form's OnActivate procedure.
procedure TForm1.FormActivate(Sender: TObject); var breakVal : Double; i : Integer; lys : IMoLayers; ly1, ly2 : IMoMapLayer; cbr : IMoClassBreaksRenderer; ddr : IMoDotDensityRenderer; recset1, recset2 : IMoRecordset; stats1, stats2 : IMoStatistics; begin // Initialize the map InitializeMap ; // counties layer