Intersection between two geographic lines - c#

I'm using the DotSpatial C# library and I'm trying to use the following code to try to find the intersection point between two lines (I know they do intersect)
var geoproj = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984;
var d1 = new FeatureSet { Projection = geoproj };
//var c1 = new Coordinate(31.877484, 34.736723);
//var c2 = new Coordinate(31.879607, 34.732362);
var c1 = new Coordinate(0, -1);
var c2 = new Coordinate(0, 1);
var line1 = new LineString(new[] { c1, c2 });
d1.AddFeature(line1);
var d2 = new FeatureSet { Projection = geoproj };
//var c3 = new Coordinate(31.882391, 34.73352);
//var c4 = new Coordinate(31.875502, 34.734851);
var c3 = new Coordinate(-1, 0);
var c4 = new Coordinate(1, 0);
var line2 = new LineString(new[] { c3, c4 });
d2.AddFeature(line2);
var inters = d1.Intersection(d2, FieldJoinType.All, null);
var feats = inters.Features;
foreach (var feat in feats)
{
Console.WriteLine("{0}", feat.ToString());
}
The resulting feature set is always empty.
What am I doing wrong?
I also tried to swap the X and Y components for each coordinate (in case the first was assumed to be the longitude instead of the latitude)
Thanks!
EDIT: As per weston's comment below, I've changed the coordinates of the two lines to more obviously intersecting ones. The result is the same.

The intersection code you are using is basically a very limited shortcut. It is stuck with two conflicting ideas. The first idea is that featuresets all have the same feature type. That is, if you are working with a polygon featureset, all the features are polygons. The second idea is that the "intersection" of two lines is rarely a line. It is usually a point. So really that featureset intersecting code is designed to be used for intersecting polygons where the resulting shape is usually a polygon. It will also work for points where the results are always points. In your case, you probably want to find the union of the intersecting shapes, and throw out the shapes that do not intersect. You can accomplish this most easily by taking control of the features in a loop like the ones below. I have three examples, one that creates a point featureset from the intersections, and just assumes that all intersections will be points, one that keeps the original d1 feature if it intersects with a d2 feature, and another that unions all the intersecting d2 features with each d1 feature. This has the potential of creating some duplication of d2 content if a d2 feature intersects with more than one d1 feature. In any of these cases, it may not be clear what to do with the attributes, since the intersection could officially possess attributes that belong to multiple d2 shapes, not just one, so that would also have to be handled in a custom way that is meaningful for your specific application.
var geoproj = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984;
var d1 = new FeatureSet { Projection = geoproj };
//var c1 = new Coordinate(31.877484, 34.736723);
//var c2 = new Coordinate(31.879607, 34.732362);
var c1 = new Coordinate(0, -1);
var c2 = new Coordinate(0, 1);
var line1 = new LineString(new[] { c1, c2 });
d1.AddFeature(line1);
var d2 = new FeatureSet { Projection = geoproj };
//var c3 = new Coordinate(31.882391, 34.73352);
//var c4 = new Coordinate(31.875502, 34.734851);
var c3 = new Coordinate(-1, 0);
var c4 = new Coordinate(1, 0);
var line2 = new LineString(new[] { c3, c4 });
d2.AddFeature(line2);
// To create a Point featureset with the intersections
var result = new FeatureSet(FeatureType.Point) { Projection = geoproj };
foreach (IFeature feature in d1.Features)
{
foreach (IFeature other in d2.Features)
{
if (feature.Intersects(other))
{
result.AddFeature(feature.Intersection(other));
}
}
}
// To keep only d1 lines that intersect with d2
result = new FeatureSet { Projection = geoproj };
foreach(IFeature feature in d1.Features){
foreach(IFeature other in d2.Features){
if(feature.Intersects(other)){
result.AddFeature(feature);
}
}
}
// Alternately to combine the intersecting lines into a cross
result = new FeatureSet { Projection = geoproj };
foreach (IFeature feature in d1.Features)
{
IFeature union = feature;
Boolean keep = false;
foreach (IFeature other in d2.Features)
{
if (feature.Intersects(other))
{
union = union.Union(other);
keep = true;
}
}
if (keep)
{
result.AddFeature(union);
}
}

Related

C# Chart - multiple curves but color gets overwritten

So i'm displaying multiple curves in my chart. However, when i generate a random color, all the other curves will get this color too.
int fileIndex=0;
Random r = new Random();
foreach (var i in graphContainer)
{
fileIndex++;
var series = new Series
{
Name = legendNames[fileIndex],
Color = Color.FromArgb(r.Next(0, 256), r.Next(0, 256), r.Next(0, 256)),
IsVisibleInLegend = true,
IsXValueIndexed = false,
ChartType = SeriesChartType.Line
};
foreach (var j in i)
{
series.Points.AddXY (j.Item2, j.Item1);
}
chart.Invalidate ();
chart.Series.Add (series);
}
Note: all the curves except one has the same values, but you get the idea.
Why is my curves getting the same colors?
Entire function:
private void generateWaveformsFromFileBtn_Click(object sender, EventArgs e)
{
int fileIndex = -1;
string folder = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + #"\WaveForms\";
string filter = "*.csv";
string[] filePath = Directory.GetFiles(folder, filter);
List<string> legendNames = new List<string>();
List<Tuple<double, double>> graph = new List<Tuple<double, double>>();
List<List<Tuple<double, double>>> graphContainer = new List<List<Tuple<double, double>>>();
chart.Series.Clear();
foreach(var fileName in filePath) {
legendNames.Add(Path.GetFileName(fileName));
using (TextFieldParser csvParser = new TextFieldParser(fileName))
{
csvParser.SetDelimiters (new string[] { ";" });
csvParser.ReadLine ();
while (!csvParser.EndOfData)
{
string[] fields = csvParser.ReadFields();
double current = Double.Parse(fields[0]);
double inductance = Double.Parse(fields[1]);
graph.Add (new Tuple<double,double>(current, inductance));
}
graphContainer.Add(graph);
}
}
Random r = new Random();
foreach (var i in graphContainer)
{
fileIndex++;
var series = new Series
{
Name = legendNames[fileIndex],
Color = Color.FromArgb(r.Next(0, 256), r.Next(0, 256), r.Next(0, 256)),
IsVisibleInLegend = true,
IsXValueIndexed = false,
ChartType = SeriesChartType.Line
};
foreach (var j in i)
{
series.Points.AddXY (j.Item2, j.Item1);
}
chart.Invalidate ();
chart.Series.Add (series);
}
}
Suggestion: (?)
You have already posted one solution:
Before adding new data in a loop, make sure the old data are deleted. You do it by creating a new instance:
graph = new List<Tuple<double, double>>();
This is a valid approach.
But for completeness' sake let's look at the more direct aproach as well:
graph.Clear();
This clears the data from the list object. But you are adding the graph to the graphContainer list like this:
graphContainer.Add(graph);
which means that the very object gets put into the list which, before the next loop, will be cleared. This is a common mistake. The solution is simple: Don't add the graph object itself into the graphContainer list but a copy of it :
graphContainer.Add(graph.ToList());
The ToList() call is a simple way to create a copy of a List<T>.
As for the post you linked to: The old post is strictly about runtime performance. And not about creating copies of the lists.
But even there, the recommended way to do it is depending on whether you can predict the number of elements you will put in each list.
If you can predict it and if it is somewhat largish, let's say more than a few hundred of large or a many thousands of smallish elements you should tell the list which capacity it should reserve.
This avoids multiple growing pains. After a Clear() the Capacity is retained, avoiding new allocations.
Your elements (Tuples) are small, so you probably don't need to worry about the list growing performance..
As suggested by user #Taw, i forgot to create a new instance of the graph or empty it, between runs.
graph = new List<Tuple<double, double>>(); // <-- solved it
while (!csvParser.EndOfData)
{
string[] fields = csvParser.ReadFields();
...
}
EDIT:
Do not use yourList.Clear() see here why: https://stackoverflow.com/a/35848659/2902996

How do we assign a Series to a ChartArea?

I have an array of ChartArea area[] and an array of Series series[]
What I need to do is use a different ChartArea for each Series.
This is what is usually done :
Chart Chart0 = new Chart();
ChartArea ChartArea0 = new ChartArea("name");
Chart0.ChartAreas.Add(ChartArea0);
Series Series0 = new Series();
Chart0.Series.Add(Series0);
// link series to area here
Series0.ChartArea = "name";
But in my case, I cannot have a string name for each. It needs to be an array. How can I work around this?
I asked a few clarifying questions in my comment, but if I go off my assumptions of the situation you expressed, here's what I would do:
var sourceStringArray = new string[] { "ChartArea1Name" };
Chart Chart0 = new Chart();
ChartArea ChartArea0 = new ChartArea(sourceStringArray[0]);
Chart0.ChartAreas.Add(ChartArea0);
Series Series0 = new Series();
Chart0.Series.Add(Series0);
// link series to area here
Series0.ChartArea = sourceStringArray[0];
I will edit my answer based on the responses to my comment.
Edit:
Based on your comment:
I need it to be an array as the number of ChartAreas I need is dependant on a variable number of parameters
You would probably want to make a method that does the creation of your ChartArea objects. Something like:
private List<ChartArea> CreateChartAreas(string[] chartAreaNames)
{
var chartAreaInstances = new List<ChartArea>();
foreach (var chartAreaName in chartAreaNames)
{
var tempChart = new Chart();
var tempChartArea = new ChartArea(chartAreaName);
tempChart.ChartAreas.Add(tempChartArea);
var tempSeries = new Series();
tempChart.Series.Add(tempSeries);
tempSeries.ChartArea = chartAreaName;
}
return chartAreaInstances;
}
Then in the main code, do something like:
var sourceStringArray = new string[] { "ChartArea1Name", "ChartArea2Name" };
var chartAreas = CreateChartAreas(sourceStringArray);
foreach (var chartArea in chartAreas)
{
// Do something with your chart area instances.
}
Disclaimer: I haven't tested this code as I don't know what your specific use case is for this.

Sorting and iterating widget starting from anywhere

List<Widget> list = List<Widget>();
Widget widgetA = new Widget();
Widget widgetB = new Widget();
Widget widgetC = new Widget();
Widget widgetD = new Widget();
Widget widgetE = new Widget();
Widget widgetF = new Widget();
list.AddRange(new[] { widgetA, widgetB, widgetC, widgetD, widgetE, widgetF });
What would I need to do to sort starting somewhere in the middle?
for example,
list.AddRange(new[] { widgetD, widgetE, widgetF, widgetA, widgetB, widgetC });
Or, in other words, start the index somewhere in the middle and then wrap the remaining objects in order.
You can use a combination of Skip, Take and Union.
List<Widget> widgets = ...
// now to split and wrap
int startIndex = 3;
var newList = widgets.Skip(startIndex)
.Union(widgets.Take(startIndex)
.ToList();
First sort them, then take first few elements and put them at the end.
var mid = 2; // starting point
var arr = new[] { widgetA, widgetB, widgetC, widgetD, widgetE, widgetF };
arr.Sort(); //you can use your own comparator for sorting
list.AddRange(arr.Skip(mid).Concat(arr.Take(mid)));

Draw Polygon in Googlemap based marker time

I want to draw a polygon by connecting markers in googlemap.Time is associated with each marker.So i want connect each points based on the time.How can i do this.Currently code is impleneted like this.Where i need to change.
var marker = new Array();
var points = new Array();
for(var i=0;i<value.length;i++)
{
var tempar=value[i].split(',');
var center = new GLatLng(tempar[0], tempar[1]);
var mar = new GMarker(center, icon);
var imgpth=tempar[3];
var tme=tempar[2];
marker.push(mar);
marker[i].time = tempar[2];
points.push(marker[i].getLatLng());
drawMarker(mar,imgpth,tme);
}
for(i=;i<marker.length;i++)
{
map.addOverlay(marker[i]);
}
var polyline = new GPolygon(points, "#f33f00", 2, 1, "#ff0000", 0.2);
map.addOverlay(polyline);
The easiest way to do this is to sort marker array by time. Assuming that the time property has some sensible values (not strings):
marker.sort(function(a,b){return a.time-b.time});
If the time is a string then:
marker.sort(function(a,b) {
var date1 = new Date(a.time);
var date2 = new Date(b.time);
return date1.getTime() - date2.getTime();
});

Converting data from discrete properties of objects in an IEnumerable into an array/series for charting?

I have an IEnumerable (List<>) of a particular Type. The Type has a set of properties which I would like to chart as individual series (MVC3/Razor).
Is there a way I can transpose the data in the list of objects into a list of the properties?
e.g.
Given the code below, the end result I am trying to acheive is a set of Series so I have
Bucket1: IEnumerable<Date, Value>
Bucket2: IEnumerable<Date, Value>
etc
So that I can then chart each series to get a line chart with Date on x and value on y, for (in this example) 3 series/lines. This code works perfectly, but just feels wrong somehow?
var buckets = new List<Bucket>(){
new Bucket{
Date=DateTime.Today.AddDays(-3),
Bucket1=1000,
Bucket2=2000,
Bucket3=3000},
new Bucket{
Date=DateTime.Today.AddDays(-2),
Bucket1=1000,
Bucket2=2020,
Bucket3=3300},
new Bucket{
Date=DateTime.Today.AddDays(-1),
Bucket1=1000,
Bucket2=2040,
Bucket3=3600}
};
var chart = new Chart(){ Height = 400, Width = 600 };
var area = new ChartArea();
chart.ChartAreas.Add(area);
var series1 = chart.Series.Add("Bucket 1");
var series2 = chart.Series.Add("Bucket 2");
var series3 = chart.Series.Add("Bucket 3");
foreach (var series in chart.Series)
{
series.ChartType = SeriesChartType.Line;
}
foreach (var item in buckets)
{
series1.Points.AddXY(item.Date, item.Bucket1);
series2.Points.AddXY(item.Date, item.Bucket2);
series3.Points.AddXY(item.Date, item.Bucket3);
}
What about just using a Lambda expression for each serie (code below was not tested - it's just a prototype):
var series1 = buckets.Select(b => new { b.Date, b.Bucket1 });
var series2 = buckets.Select(b => new { b.Date, b.Bucket2 });
Thanks to the comments posted I kept my original code and don't think there's an easier way to do this.

Categories