Using the Infragistics xamGeographicMap control, trying to add shapes from SQL server geometry data.
The data is valid; a select in SSMS shows the shapes properly
Points show properly when querying SP_GEOMETRY (see sample) -- so GeographicSymbolSeries works, and the shape columns contain actual data
GeographicShapeSeries does not work
GeographicPolyLine does not work
So this works:
var majorCitySeries = new GeographicSymbolSeries
{
ItemsSource = data.cities,
LatitudeMemberPath = "SP_GEOMETRY.YCoordinate",
LongitudeMemberPath = "SP_GEOMETRY.XCoordinate"
};
GeoMap.Series.Add(majorCitySeries);
But these show nothing:
var countySeries = new GeographicShapeSeries
{
ItemsSource = data.counties,
ShapeMemberPath = "SP_GEOMETRY"
};
GeoMap.Series.Add(countySeries);
var br = new GeographicPolylineSeries
{
ItemsSource = data.rivers,
ShapeMemberPath = "SP_GEOMETRY"
};
GeoMap.Series.Add(br);
Do I need to add a converter? The samples, they tell nothing. What gives?
Ok, fixed it. Here's a semi-generic converter:
public static class SqlGeometryToShapeConverter
{
public static ShapefileConverter Create<T>(IEnumerable<T> items,
Func<T, DbGeometry> geoFunc,
Func<T, string> nameFunc)
where T : class
{
var converter = new ShapefileConverter();
foreach (var item in items)
{
var rec = new ShapefileRecord();
var points = new List<Point>();
var geometry = geoFunc(item);
Debug.Assert(geometry.PointCount != null, "geometry.PointCount != null");
// Points are 1 based in DbGeometry
var pointCount = geometry.PointCount;
for (var pointIndex = 1; pointIndex <= pointCount; pointIndex++)
{
var point = geometry.PointAt(pointIndex);
Debug.Assert(point.XCoordinate != null, "point.XCoordinate != null");
Debug.Assert(point.YCoordinate != null, "point.YCoordinate != null");
points.Add(new Point(point.XCoordinate.Value, point.YCoordinate.Value));
}
rec.Fields = new ShapefileRecordFields { { "Name", nameFunc(item) } };
rec.Points = new List<List<Point>> { points };
converter.Add(rec);
}
return converter;
}
}
Use it like this:
var countySeries = new GeographicShapeSeries
{
ItemsSource = SqlGeometryToShapeConverter.Create(data.counties, x => x.SP_GEOMETRY, x => x.County_Name),
ShapeMemberPath = "Points"
};
GeoMap.Series.Add(countySeries);
This was a good start for me, but I was running into some issues. Here's my perhaps less elegant approach. Compared to the default Infragistics background, the map is slightly off. Something might be off with my loading of my data in SQL server.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.SqlClient;
using System.Windows;
using Infragistics.Controls.Maps;
using Microsoft.SqlServer.Types;
namespace TestMVVMLightProject.Model
{
public class SqlGeometryToShapeConverter : ObservableCollection<ShapefileRecord>
{
public SqlGeometryToShapeConverter()
{
//connect
//load sql
//go thorugh points
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "localhost";
builder.InitialCatalog = "RefDB_91_DistToCoast";
builder.IntegratedSecurity = true;
SqlConnection conn = new SqlConnection(builder.ConnectionString);
conn.Open();
string sql = "SELECT huc_2, geom FROM Polygon2";
using (SqlCommand oCmd = new SqlCommand(sql, conn))
{
oCmd.CommandTimeout = 3000;
using (SqlDataReader oDr = oCmd.ExecuteReader())
{
int ordGeom = oDr.GetOrdinal("geom");
int ordHucZone = oDr.GetOrdinal("huc_2");
double minX = double.MaxValue;
double minY = double.MaxValue;
double maxX = double.MinValue;
double maxY = double.MinValue;
while (oDr.Read())
{
var rec = new ShapefileRecord();
rec.Points = new List<List<Point>>();
SqlGeography coast = (SqlGeography)oDr.GetValue(ordGeom);
int numPolygons = (int)coast.STNumGeometries();
string hucZone = oDr.GetString(ordHucZone);
int hucInt = int.Parse(hucZone);
for (int geomIndex = 1; geomIndex <= coast.STNumGeometries(); geomIndex++)
{
SqlGeography polygon = coast.STGeometryN(geomIndex);
var points = new List<Point>();
for (int verticeIndex = 1; verticeIndex <= polygon.STNumPoints(); verticeIndex++)
{
points.Add(new Point(polygon.STPointN(verticeIndex).Long.Value, polygon.STPointN(verticeIndex).Lat.Value));
if (hucInt < 19)
{
minX = minX < polygon.STPointN(verticeIndex).Long.Value ? minX : polygon.STPointN(verticeIndex).Long.Value;
minY = minY < polygon.STPointN(verticeIndex).Lat.Value ? minY : polygon.STPointN(verticeIndex).Lat.Value;
maxX = maxX > polygon.STPointN(verticeIndex).Long.Value ? maxX : polygon.STPointN(verticeIndex).Long.Value;
maxY = maxY > polygon.STPointN(verticeIndex).Lat.Value ? maxY : polygon.STPointN(verticeIndex).Lat.Value;
}
}
rec.Points.Add(points);
}
rec.Fields = new ShapefileRecordFields { { "HUC_2", hucZone.ToString() } };
this.Add(rec);
}
worldRect = new Rect(new Point(minX, minY), new Point(maxX, maxY));
}
}
conn.Close();
}
private Rect worldRect;
public Rect WorldRect
{
get
{
return worldRect;
}
}
}
}
I called this from my view model (I'm using MVVM Light). This was the code in my view model.
public MainViewModel()
{
mapData = new SqlGeometryToShapeConverter();
}
private SqlGeometryToShapeConverter mapData;
public SqlGeometryToShapeConverter MapData
{
get
{
return mapData;
}
set
{
Set(() => MapData, ref mapData, value);
}
}
This is a snippet from my View
<ig:XamGeographicMap Zoomable="True"
Height="400"
WorldRect="{Binding MapData.WorldRect}">
<ig:XamGeographicMap.Series>
<ig:GeographicShapeSeries ItemsSource="{Binding MapData}"
ShapeMemberPath="Points"
ShapeStyleSelector="{StaticResource shapeStyleSelector}"
MarkerCollisionAvoidance="Fade">
<!-- custom marker with bindings to data loaded from database file (DBF) -->
<ig:GeographicShapeSeries.MarkerTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=Item.Fields[HUC_2]}"
Foreground="#FF333333"
FontWeight="Bold"
Margin="1 1 0 0" />
</Grid>
</DataTemplate>
</ig:GeographicShapeSeries.MarkerTemplate>
</ig:GeographicShapeSeries>
</ig:XamGeographicMap.Series>
</ig:XamGeographicMap>
Related
My goal is to create a chart that will sit inside of a panel restricting it's size.
I managed to achieve this some time ago but today I noticed that the chart was growing inside of the panel, not allowing the data to be seen.
I have attached a picture bellow which should help understand the issue.
UPDATE
I noticed that if I rmeove 'bottom' from the Anchor property of the panel the chart does not exceed the parent panel but it does not increase with the change of the form which is what I'm looking for.
I also noticed that there was also another chart on the form that was exceeding the parent form, this time the chart would extend to the right not allowing to see the data.
This is the code that generates this second chart and places is inside of the parent panel.
panel_chart.Controls.Clear();
chart1 = new Chart();
chart1.MouseMove += chart1_MouseMove;
chart1.ChartAreas.Add(new ChartArea("chartArea1"));
chart1.Series.Clear();
chart1.Titles.Clear();
var serieOEE = new Series("OEE");
serieOEE.ChartType = SeriesChartType.Line;
serieOEE.XValueType = ChartValueType.String;
var serieProd = new Series("Prod");
serieProd.ChartType = SeriesChartType.Column;
serieProd.XValueType = ChartValueType.String;
var serieDisp = new Series("Disp");
serieDisp.ChartType = SeriesChartType.Column;
serieDisp.XValueType = ChartValueType.String;
var serieQual = new Series("Qual");
serieQual.ChartType = SeriesChartType.Column;
serieQual.XValueType = ChartValueType.String;
DateTime DataReg = DateTime.MinValue;
List<AreaOEE> listaChart = new List<AreaOEE>();
foreach (var item in ListaGrafico) //listaOEE
{
if (item.Designacao == DesignacaoLista)
{
listaChart.Add(item);
}
}
listaChart = listaChart.OrderBy(a => a.IDReg).ToList();
DateTime DataUltimoReg = DateTime.MinValue;
int j = 0;
foreach (var item in listaChart)
{
string HoraGraf = Convert.ToDateTime(item.Hora).ToString("HH:mm");
if (j == 0 || j == listaChart.Count - 1 ||
Math.Abs(Convert.ToDateTime(item.Hora).Subtract(DataUltimoReg).TotalMinutes) >= 30)
{
serieOEE.Points.AddXY(HoraGraf, item.OEE);
serieProd.Points.AddXY(HoraGraf, item.Produtividade);
serieQual.Points.AddXY(HoraGraf, item.Qualidade);
serieDisp.Points.AddXY(HoraGraf, item.Disponibilidade);
DataUltimoReg = Convert.ToDateTime(item.Hora);
if (j == listaChart.Count - 2)
{
break;
}
}
j++;
}
//Adicionar o ultimo
foreach (var item in listaOEE)
{
if (item.Designacao == DesignacaoLista)
{
string sHora = "";
try
{
sHora = item.Hora.Substring(1, 5);
}
catch (Exception ex)
{
string sEx = ex.Message;
}
foreach (var itemOee in serieOEE.Points)
{
if (itemOee.AxisLabel == sHora)
{
itemOee.YValues[0] = item.OEE;
}
}
foreach (var itemP in serieProd.Points)
{
if (itemP.AxisLabel == sHora)
itemP.YValues[0] = item.Produtividade;
}
foreach (var itemD in serieDisp.Points)
{
if (itemD.AxisLabel == sHora)
itemD.YValues[0] = item.Disponibilidade;
}
foreach (var itemQ in serieQual.Points)
{
if (itemQ.AxisLabel == sHora)
itemQ.YValues[0] = item.Qualidade;
}
}
}
chart1.Series.Add(serieProd);
chart1.Series.Add(serieQual);
chart1.Series.Add(serieDisp);
chart1.Series.Add(serieOEE);
serieOEE.BorderWidth = 4;
chart1.ChartAreas[0].AxisX.LabelStyle.Angle = 90;
chart1.ChartAreas[0].AxisX.Interval = 1;
chart1.ChartAreas[0].AxisY.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = 140;
chart1.Legends.Clear();
chart1.Legends.Add(serieOEE.Legend);
chart1.Titles.Add(DesignacaoLista + " " + DataTitulo.ToString("dd-MM HH:mm"));
chart1.Titles[0].Font = new Font("Arial", 13, FontStyle.Bold);
chart1.Visible = true;
chart1.Dock = DockStyle.Fill;
panel_chart.Controls.Add(chart1);
you can change the size of Chart with Chart.Size:
Chart1.Size = new Size(1000, 200); //1000px * 200px
How to iterate through all ToolStripMenuItem in a MenuStrip
con.connection3.Open();
string query = "SELECT * FROM tblrole WHERE role = ?role";
using (MySqlCommand cmd3 = new MySqlCommand(query,con.connection3))
{
cmd3.Parameters.AddWithValue("?role", roled);
using (MySqlDataReader mdr = cmd3.ExecuteReader())
{
while (mdr.Read())
{
for (int o = 0, i = 2; o < fileMaintenanceToolStripMenuItem.DropDownItems.Count; o++, i++)
{
fileMaintenanceToolStripMenuItem.DropDownItems[o].Visible = mdr.GetBoolean(i);
}
for (int o = 0, i = 19; o < transactionToolStripMenuItem.DropDownItems.Count; o++, i++)
{
transactionToolStripMenuItem.DropDownItems[o].Visible = mdr.GetBoolean(i);
}
for (int o = 0, i = 45; o < reportsToolStripMenuItem.DropDownItems.Count; o++, i++)
{
reportsToolStripMenuItem.DropDownItems[o].Visible = mdr.GetBoolean(i);
}
for (int o = 0, i = 55; o < utilitiesToolStripMenuItem.DropDownItems.Count; o++, i++)
{
utilitiesToolStripMenuItem.DropDownItems[o].Visible = mdr.GetBoolean(i);
}
if (!fileMaintenanceToolStripMenuItem.HasDropDownItems)fileMaintenanceToolStripMenuItem.Visible = false;
else fileMaintenanceToolStripMenuItem.Visible = true;
if (!transactionToolStripMenuItem.HasDropDownItems)transactionToolStripMenuItem.Visible = false;
else transactionToolStripMenuItem.Visible = true;
if (!reportsToolStripMenuItem.HasDropDownItems)reportsToolStripMenuItem.Visible = false;
else reportsToolStripMenuItem.Visible = true;
if (!utilitiesToolStripMenuItem.HasDropDownItems) utilitiesToolStripMenuItem.Visible = false;
else utilitiesToolStripMenuItem.Visible = true;
}
}
}
con.connection3.Close();
Works fine if the ToolStripMenuItem has no subitems but I need to iterate through them also
You can use a recursive method as a good option for traversing a tree structure. Here is an extension method that you can use for listing ToolStripMenuItem descendants of a given ToolStripMenuItem:
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
public static class ToolStripMenuItemExtensions
{
public static List<ToolStripMenuItem> Descendants(this ToolStripMenuItem item)
{
var items = item.DropDownItems.OfType<ToolStripMenuItem>().ToList();
return items.SelectMany(x => Descendants(x)).Concat(items).ToList();
}
}
It will add Descendants to ToolStripMenuItem elements, so you can use it this way:
var allMenuItems = fileMaintenanceToolStripMenuItem.Descendants();
I am creating collection view with several size of labels. These labels all have the same height but their widths are changed dynamically.
This is the code of my collection view layout:
EstimatedItemSize = new CGSize(50f, 35f);
MinimumInteritemSpacing = 10f;
MinimumLineSpacing = 10f;
public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(CGRect rect)
{
var attributes = base.LayoutAttributesForElementsInRect(rect);
for (var i = 1; i < attributes.Length; ++i)
{
var currentLayoutAttributes = attributes[i];
var previousLayoutAttributes = attributes[i - 1];
var maximumSpacing = MinimumInteritemSpacing;
var previousLayoutEndPoint = previousLayoutAttributes.Frame.Right;
if (previousLayoutEndPoint + maximumSpacing + currentLayoutAttributes.Frame.Size.Width >= CollectionViewContentSize.Width)
{
continue;
}
var frame = currentLayoutAttributes.Frame;
frame.X = previousLayoutEndPoint + maximumSpacing;
currentLayoutAttributes.Frame = frame;
}
return attributes;
}
My question is: When I have one item in my collection view it's displayed in the center, and LayoutAttributesForElementsInRect method will not be called. But I need to display it on the left side.
If I change EstimatedItemSize = new CGSize(50f, 35f) to ItemSize = new CGSize(50f, 35f) it displays correctly but then the width is not changed dynamically.
You can add some codes to change the position of the first cell, when you use the EstimatedItemSize, like this:
public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(CoreGraphics.CGRect rect)
{
var attributes = base.LayoutAttributesForElementsInRect(rect);
//Add these lines to change the first cell's position of the collection view.
var firstCellFrame = attributes[0].Frame;
firstCellFrame.X = 0;
attributes[0].Frame = firstCellFrame;
for (var i = 1; i < attributes.Length; ++i)
{
var currentLayoutAttributes = attributes[i];
var previousLayoutAttributes = attributes[i - 1];
var maximumSpacing = MinimumInteritemSpacing;
var previousLayoutEndPoint = previousLayoutAttributes.Frame.Right;
if (previousLayoutEndPoint + maximumSpacing + currentLayoutAttributes.Frame.Size.Width >= CollectionViewContentSize.Width)
{
continue;
}
var frame = currentLayoutAttributes.Frame;
frame.X = previousLayoutEndPoint + maximumSpacing;
currentLayoutAttributes.Frame = frame;
}
return attributes;
}
It works fine like this:
I am trying to make a Genetic Algorithm implementation for my thesis. There are two main class: Facility as chromosome and FacilityCell as gene. But I am getting an error while getting the fitness value from Facility class.
The necessary values are set in the Form.cs and after the algorithm has been run, these properties are null in the Facility instance. These properties are Facility.Flows and Facility.Demands. I can't understand why. Please help.
Code part from Form.cs
fac = new Facility();
List<FacilityCell> gens = new List<FacilityCell>();
for (int i = 0; i < 6; i++)
{
gens.Add(new FacilityCell(i.ToString(), i));
}
fac.Genes = gens.ToArray();
fac.Cells = gens.ToArray();
float[] dems = new float[3];
dems[0] = 300;
dems[1] = 60;
dems[2] = 160;
fac.Demands = dems;
FacilityCell[][] fl = new FacilityCell[3][];
fl[0] = new FacilityCell[] {
fac.Cells[0],
fac.Cells[2],
fac.Cells[4],
fac.Cells[1],
fac.Cells[3],
fac.Cells[5] };
fl[1] = new FacilityCell[] {
fac.Cells[2],
fac.Cells[4],
fac.Cells[1],
fac.Cells[5],
fac.Cells[3],
fac.Cells[4] };
fl[2] = new FacilityCell[] {
fac.Cells[1],
fac.Cells[0],
fac.Cells[4],
fac.Cells[2],
fac.Cells[3],
fac.Cells[5] };
fac.Flows = fl;
Code from Facility.cs:
public class Facility : IChromosome
{
public Facility()
{
}
public Facility(FacilityCell[] cells)
{
this.cells = cells;
flows = null;
demands = null;
for (int i = 0; i < cells.Length; i++)
{
cells[i].Order = i;
}
}
private IGene[] cells;
private float[] demands;
private FacilityCell[][] flows;
public FacilityCell[][] Flows
{
get { return flows; }
set { flows = value; }
}
public FacilityCell[] Cells
{
get
{
return cells as FacilityCell[];
}
set
{
cells = value;
}
}
public float[] Demands
{
get { return demands; }
set { demands = value; }
}
public float FitValue
{
get
{
float total = 0;
//I AM GETTING ERROR IN THIS LINE OF CODE, THE FOR LOOP
//It throws NullReferenceException for both this.Demands and this.Flows
for (int i = 0; i < flows.Length; i++)
{
for (int j = 0; j < flows[i].Length - 1; j++)
{
int dist = Math.Abs(flows[i][j + 1].Order - flows[i][j].Order);
float totflow = dist * demands[i];
total += totflow;
}
}
return total;
}
}
public IGene[] Genes
{
get
{
return cells;
}
set
{
cells = value;
}
}
}
This code: FacilityCell[][] fl = new FacilityCell[3][]; will in the constructor set demands to null, You call ths code AFTER you set the demands.
I need to draw some charts using DynamicDataDisplay3. Everything works fine, except I can't find a way to change X axis to strings instead of dates or integers. This is how I tried to do it but I get only 1 value on X axis:
int i = 0;
using (MySqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
i++;
Analyze build = new Analyze();
build.id = i;
build.build = Convert.ToString(reader[0]);
builds.Add(build);
n1.Add(Convert.ToInt32(reader[1]));
}
}
var datesDataSource = new EnumerableDataSource<Analyze>(builds);
datesDataSource.SetXMapping(x => x.id);
var numberOpenDataSource = new EnumerableDataSource<int>(n1);
numberOpenDataSource.SetYMapping(y => y);
CompositeDataSource compositeDataSource1 = new CompositeDataSource(datesDataSource, numberOpenDataSource);
chBuild.AddLineGraph(compositeDataSource1, new Pen(Brushes.Blue, 2), new CirclePointMarker { Size = 6, Fill = Brushes.Blue }, new PenDescription(Convert.ToString(cmbBuildVertical.SelectedItem)));
chBuild.Viewport.FitToView();
I made my own LabelProvider to handle something similar to this. I wanted to override my DateTime labels into integers, to represent something different. In your case you could use something like this :
public class StringLabelProvider : NumericLabelProviderBase {
private List<String> m_Labels;
public List<String> Labels {
get { return m_Labels; }
set { m_Labels = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="ToStringLabelProvider"/> class.
/// </summary>
public StringLabelProvider(List<String> labels) {
Labels = labels;
}
public override UIElement[] CreateLabels(ITicksInfo<double> ticksInfo) {
var ticks = ticksInfo.Ticks;
Init(ticks);
UIElement[] res = new UIElement[ticks.Length];
LabelTickInfo<double> tickInfo = new LabelTickInfo<double> { Info = ticksInfo.Info };
for (int i = 0; i < res.Length; i++) {
tickInfo.Tick = ticks[i];
tickInfo.Index = i;
string labelText = "";
labelText = Labels[Convert.ToInt32(tickInfo.Tick)];
TextBlock label = (TextBlock)GetResourceFromPool();
if (label == null) {
label = new TextBlock();
}
label.Text = labelText;
res[i] = label;
ApplyCustomView(tickInfo, label);
}
return res;
}
}
You can construct your list of ticks, and send it to the LabelProvider you create. Like this :
StringLabelProvider labelProvider = new StringLabelProvider(yourLabelList);
yourAxis.LabelProvider = labelProvider;