====== Source Code ServicesEditionSample OledbPointFeatureSource CS 090728.zip ====== ====OledbPointFeatureLayer.cs==== using ThinkGeo.MapSuite.Core; namespace OledbPointFeatureSource { // The FeatureLayer is a wrapper to the FeatureSource and provides all of the drawing specific // methods such as the ZoomLevels and Styles. As you can see it is just a simple wrapper and the // real work is already done for you in the FeatureLayer base class. class OledbPointFeatureLayer: FeatureLayer { OledbPointFeatureSource oledbPointFeatureSource; // It is important for compatability that you always have a default constructor that // takes no parameters. This constructor will just chain to the more complex one. public OledbPointFeatureLayer() : this(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty) { } // We have provided another constructor that has all of the parameters we need for the FeatureLayer // to work. public OledbPointFeatureLayer(string tableName, string idColumnName, string xColumnName, string yColumnName, string connectionString) : base() { // Here is where we create our FeatureSource and set the internal property on the FeatureLayer oledbPointFeatureSource = new OledbPointFeatureSource(tableName, idColumnName, xColumnName, yColumnName, connectionString); this.FeatureSource = oledbPointFeatureSource; } // The next dozen lines are all of the properties we need. It is good form to always match // the parameters in your constructors with properties of the exact same name. public string TableName { get { return oledbPointFeatureSource.TableName; } set { oledbPointFeatureSource.TableName = value; } } public string ConnectionString { get { return oledbPointFeatureSource.ConnectionString; } set { oledbPointFeatureSource.ConnectionString = value; } } public string XColumnName { get { return oledbPointFeatureSource.XColumnName; } set { oledbPointFeatureSource.XColumnName = value; } } public string YColumnName { get { return oledbPointFeatureSource.YColumnName; } set { oledbPointFeatureSource.YColumnName = value; } } public string IdColumnName { get { return oledbPointFeatureSource.IdColumnName; } set { oledbPointFeatureSource.IdColumnName = value; } } } } ====OledbPointFeatureSource.cs==== using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data.OleDb; using ThinkGeo.MapSuite.Core; namespace OledbPointFeatureSource { class OledbPointFeatureSource : FeatureSource { private OleDbConnection connection; private string connectionString; private string tableName; private string idColumnName; private string xColumnName; private string yColumnName; // It is important for compatability that you always have a default constructor that // takes no parameters. This constructor will just chain to the more complex one. public OledbPointFeatureSource() : this(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty) { } // We have provided another constructor that has all of the parameters we need for the FeatureSource // to work. public OledbPointFeatureSource(string tableName, string idColumnName, string xColumnName, string yColumnName, string connectionString) : base() { TableName = tableName; IdColumnName = idColumnName; XColumnName = xColumnName; YColumnName = yColumnName; ConnectionString = connectionString; } // The next dozen lines are all of the properties we need. It is good form to always match // the parameters in your constructors with properties of the exact same name. public string TableName { get { return tableName; } set { tableName = value; } } public string ConnectionString { get { return connectionString; } set { connectionString = value; } } public string XColumnName { get { return xColumnName; } set { xColumnName = value; } } public string YColumnName { get { return yColumnName; } set { yColumnName = value; } } public string IdColumnName { get { return idColumnName; } set { idColumnName = value; } } // Use the OpenCore to initialize your underlying data source. The concreat Open method will ensure // that if the user calls the Open method multiple times in a row that you only get one call to // OpenCore. protected override void OpenCore() { connection = new OleDbConnection(connectionString); connection.Open(); } // Use the CloseCore to close your underlying data source. It is important to note that the // CloseCore is not like the Dispose on other objects. The FeatureSource is meant to be opened // and closed many times durring its lifetime. Make sure that you clean up any objects that have // unmanaged resources but do not put the object in a state that when Open is called it will fail. // The concreat Close method will ensure that if the user calle the Close multiple times in // a row that you only get one call to CloseCore. protected override void CloseCore() { connection.Close(); } // Here you need to query all of the features in your data source. We use this method // as the basis of nearly all other virtual methods. For example if you choose not to // override the GetCountCore then we will get all the features and count them as the default. // This is ineffecient however it produces the correct results. protected override Collection GetAllFeaturesCore(IEnumerable columnNames) { OleDbCommand command = null; OleDbDataReader dataReader = null; Collection returnFeatures = new Collection(); // Alays be sure to wrap imporant code that accesses resources that need // to be closed. In the Finally we will ensure they always get cleaned up. try { // We need to construct the part of the SQL statement for retuning the // column data. We only return the columns asked for byt the columnNames // parameter of the function. This ensures we do not get more than we need. string columnsSQL = string.Empty; foreach (string columnName in columnNames) { columnsSQL += "," + columnName; } // Here we build up and execute the query string using the columns the users defined in the properties // such as the XColumnName and IdColumnName. command = new OleDbCommand("SELECT " + idColumnName + " as TG_ID, " + xColumnName + " as TG_X, " + yColumnName + " as TG_Y " + columnsSQL + " FROM " + tableName, connection); dataReader = command.ExecuteReader(); // We now loop though all of the results and build up our features that we need to return. while (dataReader.Read()) { PointShape pointShape = new PointShape(Convert.ToDouble(dataReader["TG_X"]), Convert.ToDouble(dataReader["TG_Y"])); pointShape.Id = dataReader["TG_ID"].ToString(); Feature feature = pointShape.GetFeature(); // This small part populates the column values that were requested. They are data // such as columns used for ClassBreakStyles or TextStyles for labeling. foreach (string columnName in columnNames) { feature.ColumnValues.Add(columnName, dataReader[columnName].ToString()); } returnFeatures.Add(feature); } } finally { // Cleanup any of the objects that need to be closed or disposed. if (command != null) { command.Dispose(); } if (dataReader != null) { dataReader.Dispose(); } } return returnFeatures; } // Though this method is not required for creating our new class it is an important one. // This method is used to get only the features inside of the bounding box passed in. The reason // this is critical is that many other methods on the QueryTools such as Touches, Overlaps, Intersects, // and many others use this method as a first pass filter. If you do not override this method then // the default code calls the GetAllFeatures and look at each to see if it is in the bounding box. // While this method will produce the correct result it will not perform as well as your custom code. protected override Collection GetFeaturesInsideBoundingBoxCore(RectangleShape boundingBox, IEnumerable returningColumnNames) { OleDbCommand command = null; OleDbDataReader dataReader = null; Collection returnFeatures = new Collection(); // Alays be sure to wrap imporant code that accesses resources that need // to be closed. In the Finally we will ensure they always get cleaned up. try { // We need to construct the part of the SQL statement for retuning the // column data. We only return the columns asked for byt the columnNames // parameter of the function. This ensures we do not get more than we need. string columnsSQL = string.Empty; foreach (string columnName in returningColumnNames) { columnsSQL += "," + columnName; } // This whereSql is important becasue it is what is used to determine if the point is in // the bounding box passed in. While it is a bit complex it get your results quickly in // large datasets so long as the X & Y columns are indexed. string whereSql = " " + xColumnName + " <= " + boundingBox.LowerRightPoint.X + " AND " + xColumnName + " >= " + boundingBox.UpperLeftPoint.X + " AND " + yColumnName + " <= " + boundingBox.UpperLeftPoint.Y + " AND " + yColumnName + " >= " + boundingBox.LowerRightPoint.Y; command = new OleDbCommand("SELECT " + idColumnName + " as TG_ID, " + xColumnName + " as TG_X, " + yColumnName + " as TG_Y " + columnsSQL + " FROM " + tableName + " WHERE " + whereSql, connection); dataReader = command.ExecuteReader(); // We now loop though all of the results and build up our features that we need to return. while (dataReader.Read()) { Feature feature = new Feature(Convert.ToDouble(dataReader["TG_X"]), Convert.ToDouble(dataReader["TG_Y"]), dataReader["TG_ID"].ToString()); // This small part populates the column values that were requested. They are data // such as columns used for ClassBreakStyles or TextStyles for labeling. foreach (string columnName in returningColumnNames) { feature.ColumnValues.Add(columnName, dataReader[columnName].ToString()); } returnFeatures.Add(feature); } } finally { // Cleanup any of the objects that need to be closed or disposed. if (command != null) { command.Dispose(); } if (dataReader != null) { dataReader.Dispose(); } } return returnFeatures; } // This method returns all of the columns in the data source. This method is not required however // if it is not overridden then the FeatureSource will not have any columns available to it. // Since having access to the column data is usefull for labeling and such we suggest you override it. protected override Collection GetColumnsCore() { OleDbCommand command = null; OleDbDataReader dataReader = null; Collection returnColumns = new Collection(); // Alays be sure to wrap imporant code that accesses resources that need // to be closed. In the Finally we will ensure they always get cleaned up. try { // Here we have a query that will quickly return nothing. As strange as it sounds it is a // good way to get the table structure back without having to return any column data. command = new OleDbCommand("SELECT * FROM " + tableName + " WHERE 1=2", connection); dataReader = command.ExecuteReader(); // We now loop through and create our column list. In the FeatureSourceColumn we can // optionally provide the column type but it is just informational so we didn't code it. for (int i = 0; i < dataReader.FieldCount; i++) { FeatureSourceColumn featureSourceColumn = new FeatureSourceColumn(dataReader.GetName(i)); returnColumns.Add(featureSourceColumn); } } finally { // Cleanup any of the objects that need to be closed or disposed. if (command != null) { command.Dispose(); } if (dataReader != null) { dataReader.Dispose(); } } return returnColumns; } // This is another method that does not need to be overridden but we suggest that you do. // This method gets the cound of all the features in the data source. If you choose not to // override it then the default will call the GetAllFeatures and count them. This is not very // effecient so we suggest you override it. protected override int GetCountCore() { OleDbCommand command = null; int count = 0; // Alays be sure to wrap imporant code that accesses resources that need // to be closed. In the Finally we will ensure they always get cleaned up. try { // Here we do a standard SQL count statement and return the results. command = new OleDbCommand("SELECT COUNT(*) FROM " + tableName, connection); count = Convert.ToInt32(command.ExecuteScalar()); } finally { // Cleanup any of the objects that need to be closed or disposed. if (command != null) { command.Dispose(); } } return count; } } } ====Program.cs==== using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace OledbPointFeatureSource { static class Program { /// /// The main entry point for the application. /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new TestForm()); } } } ====TestForm.cs==== using System; using System.Drawing; using System.Windows.Forms; using ThinkGeo.MapSuite.Core; namespace OledbPointFeatureSource { public partial class TestForm : Form { private MapEngine mapEngine = new MapEngine(); private Bitmap bitmap = null; public TestForm() { InitializeComponent(); } private void TestForm_Load(object sender, EventArgs e) { // Set the full extent and the background color mapEngine.CurrentExtent = ExtentHelper.GetDrawingExtent(new RectangleShape(-180.0, 83.0, 180.0, -90.0), Map.Width, Map.Height); mapEngine.BackgroundFillBrush = new GeoSolidBrush(GeoColor.GeographicColors.DeepOcean); // Setup the cities layer - OledbPointFeatureSource string connectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=..\..\data\Cities.mdb;User Id=admin;Password=;"; OledbPointFeatureLayer citiesLayer = new OledbPointFeatureLayer("Cities", "ID", "X", "Y", connectionString); citiesLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = PointStyles.City1; citiesLayer.ZoomLevelSet.ZoomLevel01.DefaultTextStyle = TextStyles.City1("CityName"); ; citiesLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; // Setup the countries layer ShapeFileFeatureLayer worldLayer = new ShapeFileFeatureLayer(@"..\..\data\Countries02.shp",ShapeFileReadWriteMode.ReadOnly); worldLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.Country1; worldLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; // Add the new layers to the MapEngine mapEngine.StaticLayers.Add("WorldLayer", worldLayer); mapEngine.StaticLayers.Add("CitiesLayer", citiesLayer); DrawImage(); } private void btnGetCount_Click(object sender, EventArgs e) { FeatureLayer citiesLayer = (FeatureLayer)mapEngine.StaticLayers["CitiesLayer"]; citiesLayer.Open(); MessageBox.Show("Number of cities: " + citiesLayer.FeatureSource.GetCount(), "Get Count"); citiesLayer.Close(); } private void DrawImage() { if (bitmap != null) { bitmap.Dispose(); } bitmap = new Bitmap(Map.Width, Map.Height); mapEngine.OpenAllLayers(); mapEngine.DrawStaticLayers(bitmap, GeographyUnit.DecimalDegree); mapEngine.CloseAllLayers(); Map.Image = bitmap; } private void ToolBar_ButtonClick(object sender, ToolBarButtonClickEventArgs e) { switch (e.Button.Tag.ToString()) { case "Zoom In": mapEngine.CurrentExtent.ScaleDown(50); break; case "Zoom Out": mapEngine.CurrentExtent.ScaleUp(50); break; case "Full Extent": mapEngine.CurrentExtent = ExtentHelper.GetDrawingExtent(new RectangleShape(-180.0, 83.0, 180.0, -90.0), Map.Width, Map.Height); break; case "Pan Left": mapEngine.CurrentExtent = ExtentHelper.Pan(mapEngine.CurrentExtent, PanDirection.Left, 40); break; case "Pan Right": mapEngine.CurrentExtent = ExtentHelper.Pan(mapEngine.CurrentExtent, PanDirection.Right, 40); break; case "Pan Up": mapEngine.CurrentExtent = ExtentHelper.Pan(mapEngine.CurrentExtent, PanDirection.Up, 20); break; case "Pan Down": mapEngine.CurrentExtent = ExtentHelper.Pan(mapEngine.CurrentExtent, PanDirection.Down, 20); break; default: break; } DrawImage(); } private void btnClose_Click(object sender, EventArgs e) { this.Close(); } } }