How to speed up conversion of lat-long to a SqlGeometry? - c#

I have this code, it takes the geometry of countries and also a set of points, and then it only returns the points within those countries :
public static IEnumerable<Point> RemovePointsOutsideBorders(IEnumerable<Point> points, IEnumerable<Country> countries)
{
var cc = new List<Point>();
var SPAT_REF_ID = 4326;
foreach (var p in points)
{
var validText = new SqlChars(new SqlString(string.Format("POINT({0} {1})", p.Longitude, p.Latitude)));
var geoPoint = SqlGeometry.STPointFromText(validText, SPAT_REF_ID);
foreach (var c in countries)
{
if(c.Polygon.STIntersects(geoPoint))
{
cc.Add(p);
break;
}
}
}
return cc;
}
Current its quite slow, there are about 4000 points, with double lat/long values, the conversion from that into a SqlGeometry is slow (takes about 25 seconds -- I need this to be perhaps down to a second or two):
var s = new SqlChars(new SqlString(string.Format("POINT({0} {1})", p.Longitude, p.Latitude)));
var pGeo = SqlGeometry.STPointFromText(s, SPAT_REF_ID);
This is only done because the SqlGeometry.Point takes x,y instead of lat,long ... any tips on how can I speed this up?
I already know the SqlGeometry (c.Polygon) could be reduced to speed up things, however I am unable to control that. What I am after is a way to speed up the conversion from lat/long to SqlGeometry point.

This is the solution I came up with in the end, it does the whole thing in half a second :
public static IEnumerable<Point> RemovePointsOutsideBorders(IEnumerable<Point> points, IEnumerable<Country> countries)
{
// join all the country polygons into one (make a stamp)
var allCountryPolygon = countries.Select(x => x.Polygon).UnionAll();
// make a single geometry shape from our evenly spaced extent points (cookie dough)
var pointsGeo = PointsToGeometry(points);
// do an intersect (stamp country shape over extent based geometry)
var cookieOfPoints = pointsGeo.STIntersection(allCountryPolygon);
// how many points left inside? pick them all back out
for (int n = 1; n <= cookieOfPoints.STNumPoints(); n++)
{
var insidePoint = cookieOfPoints.STPointN(n);
yield return new Point
{
Longitude = insidePoint.STX.Value,
Latitude = insidePoint.STY.Value
};
}
}
public static SqlGeometry PointsToGeometry(IEnumerable<Point> points)
{
var bld = new SqlGeometryBuilder();
bld.SetSrid(4326);
bld.BeginGeometry(OpenGisGeometryType.MultiPoint);
foreach (var p in points)
{
bld.BeginGeometry(OpenGisGeometryType.Point);
bld.BeginFigure(p.Longitude, p.Latitude);
bld.EndFigure();
bld.EndGeometry();
}
bld.EndGeometry();
return bld.ConstructedGeometry;
}
public static class ExtensionMethods
{
/// <summary>
/// Joins many geometries into one
/// </summary>
/// <param name="geometries">geometries to join</param>
/// <returns>composite geometry</returns>
public static SqlGeometry UnionAll(this IEnumerable<SqlGeometry> geometries)
{
var compositeGeometry = geometries.First();
foreach (var g in geometries.Skip(1))
{
compositeGeometry = compositeGeometry.STUnion(g);
}
return compositeGeometry;
}
}

Related

How to get the max value of a function in a list of objects (C#) ? -Beginner- [duplicate]

This question already has answers here:
How to use LINQ to select object with minimum or maximum property value
(20 answers)
How to perform .Max() on a property of all objects in a collection and return the object with maximum value [duplicate]
(9 answers)
Closed 1 year ago.
first post here so I hope this is how I should ask questions here,
So, I have a function used in different classes (override), and I need to display the MaxValue of this function across different data. (don't know if I said it correcty).
Tried this first but it kept giving me the same answer even though it wasn't the max value:
class Program
{
static void Main(string[] args)
{
Trapezoid[] Shape = new Trapezoid[5];
Shape[0] = new Trapezoid(4,3.5,2);
Shape[1] = new Rectangle(9,2);
Shape[2] = new Square(5);
Shape[3] = new Trapezoid(2,6.8,10);
Shape[4] = new Square(8);
Trapezoid maximumArea = null;
foreach (Trapezoid trapezoid1 in Shape)
{
double area1 = trapezoid1.Area();
foreach (Trapezoid trapezoid2 in Shape)
{
double area2 = trapezoid1.Area();
if (area1 >= area2)
{
maximumArea = trapezoid1;
}
else
{
maximumArea = trapezoid2;
}
}
Console.WriteLine(maximumArea.ToString());
Console.ReadLine();
}
}
the second time I tried this but couldn't make it work:
class Program
{
static void Main(string[] args)
{
Trapezoid[] Shape = new Trapezoid[5];
Shape[0] = new Trapezoid(4,3.5,2);
Shape[1] = new Rectangle(9,2);
Shape[2] = new Square(5);
Shape[3] = new Trapezoid(2,6.8,10);
Shape[4] = new Square(8);
Trapezoid maximumArea = null;
foreach (Trapezoid trapezoid1 in Shape)
{
double area1 = trapezoid1.Area();
foreach (Trapezoid trapezoid2 in Shape)
{
double area2 = trapezoid2.Area();
maximumArea = Math.Max (area1 , area2);
}
}
Console.WriteLine(maximumArea.ToString());
Console.ReadLine();
}
}
the function is Area, if you need to see it:
class Trapezoid
{
protected double a,b,h;
public Trapezoid(double a, double b, double h)
{
this.a = a;
this.b = b;
this.h = h;
}
public virtual double Area()
{
return ((a + b) * h) / 2;
}
public override string ToString()
{
return "The area of this Trapezoid is " + Area() + ".";
}
}
did the same thing in rectangle and square classes by overriding (inherited classes).
I hope you can help me, thank you. :)
You need to use maximumArea to store the max of the list, not the max of the current pair.
Trapezoid maximumArea = new Square(0); //Smallest possible trapezoid
foreach (Trapezoid t in Shape)
{
if (t.Area() > maximumArea.Area()) maximumArea = t;
}
Console.WriteLine(maximumArea);
You can also shorten this considerably using LINQ:
var maximumArea = shape.OrderByDescending( x => x.Area() ).First();
And if there's a chance that some trapezoids will tie, maybe you should get a list:
var max = shape.Max( x => x.Area() );
var maximumAreas = shape.Where( x => x.Area() == max).ToList();
Use the power of Linq:
using System.Linq;
then
var trapezoidWithTheLargestArea = Shape.OrderByDescending(s => s.Area()).First()

C# examining and replacing tuple values based on other tuple

I'm starting with programming and C# and I have two tuples. One tuple is representing a list of points:
static List<(string, string, string)> PR { get; set; } = new List<(string, string, string)>()
{
("P1", "0", "0"),
("P2", "P1", "P1+Height"),
("P3", "P1+Width", "P2"),
("P4", "P3", "P3+Height")
};
where Item1 in the list of tuples stands for a Point name (P1, P2, P3, P4) and Item2 and Item3 represent a parametric formula for respectively the x- and y-value of a point.
"P1" in the second item in the above list should look for the tuple starting with "P1", and then for the second item in that tuple, in this case, 0.
I have a second list of tuples that represent the parameters that I need to calculate the above point values.
static List<(string, double)> PAR { get; set; } = new List<(string, double)>()
{
("Height", 500),
("Width", 1000)
};
Say I want to calculate the value of the parametric formula "P3+Height" as follows:
P3+Height --> P2 (+Height) --> P1+Height (+Height) --> 0 (+Height (+Height) --> 0 + Height + Height;
In the end I want to replace the parameter strings with the actual values (0 + 500 + 500 -> P3+Height = 1000) but thats of later concern.
Question: I'm trying to make a function that recursively evaluates the list of tuples and keeps the parameter names, but also looks for the corresponding tuple until we reach an end or exit situation. This is where I'm at now but I have a hard time getting my thought process in actual working code:
static void Main(string[] args)
{
//inputString = "P3+Height"
string inputString = PR[3].Item3;
string[] returnedString = GetParameterString(inputString);
#region READLINE
Console.ReadLine();
#endregion
}
private static string[] GetParameterString(string inputString)
{
string[] stringToEvaluate = SplitInputString(inputString);
for (int i = 0; i < stringToEvaluate.Length; i++)
{
//--EXIT CONDITION
if (stringToEvaluate[0] == "P1")
{
stringToEvaluate[i] = "0";
}
else
{
if (i % 2 == 0)
{
//Check if parameters[i] is point string
var value = PAR.Find(p => p.Item1.Equals(stringToEvaluate[i]));
//Check if parameters[i] is double string
if (double.TryParse(stringToEvaluate[i], out double result))
{
stringToEvaluate[i] = result.ToString();
}
else if (value == default)
{
//We have a point identifier
var relatingPR = PR.Find(val => val.Item1.Equals(stringToEvaluate[i])).Item2;
//stringToEvaluate[i] = PR.Find(val => val.Item1.Equals(pointId)).Item2;
stringToEvaluate = SearchParameterString(relatingPR);
}
else
{
//We have a parameter identifier
stringToEvaluate[i] = value.Item2.ToString();
}
}
}
}
return stringToEvaluate;
}
private static string[] SplitInputString(string inputString)
{
string[] splittedString;
splittedString = Regex.Split(inputString, Delimiters);
return splittedString;
}
Can anyone point me in the right direction of how this could be done with either recursion or some other, better, easier way?
In the end, I need to get a list of tuples like this:
("P1", "0", "0"),
("P2", "0", "500"),
("P3", "1000", "500"),
("P4", "1000", "1000")
Thanks in advance!
I wrote something that does this - I changed a bit of the structure to simplify the code and runtime, but it still returns the tuple you expect:
// first I used dictionaries so we can search for the corresponding value efftiantly:
static Dictionary<string, (string width, string height)> PR { get; set; } =
new Dictionary<string, (string width, string height)>()
{
{ "P1", ("0", "0") },
{ "P2", ("P1", "P1+Height")},
{ "P3", ("P1+Width", "P2") },
{ "P4", ("P3", "P3+Height") }
};
static Dictionary<string, double> PAR { get; set; } = new Dictionary<string, double>()
{
{ "Height", 500 },
{ "Width", 1000 }
};
static void Main(string[] args)
{
// we want to "translate" each of the values height and width values
List<(string, string, string)> res = new List<(string, string, string)>();
foreach (var curr in PR)
{
// To keep the code generic we want the same code to run for height and width-
// but for functionality reasons we need to know which it is - so sent it as a parameter.
res.Add((curr.Key,
GetParameterVal(curr.Value.width, false).ToString(),
GetParameterVal(curr.Value.height, true).ToString()));
}
#region READLINE
Console.ReadLine();
#endregion
}
private static double GetParameterVal(string inputString, bool isHeight)
{
// for now the only delimiter is + - adapt this and the code when \ as needed
// this will split string with the delimiter ("+height", "-500", etc..)
string[] delimiters = new string[] { "\\+", "\\-" };
string[] stringToEvaluate =
Regex.Split(inputString, string.Format("(?=[{0}])", string.Join("", delimiters)));
// now we want to sum up each "part" of the string
var sum = stringToEvaluate.Select(x =>
{
double result;
int factor = 1;
// this will split from the delimiter, we will use it as a factor,
// ["+", "height"], ["-", "500"] etc..
string[] splitFromDelimiter=
Regex.Split(x, string.Format("(?<=[{0}])", string.Join("|", delimiters)));
if (splitFromDelimiter.Length > 1) {
factor = int.Parse(string.Format($"{splitFromDelimiter[0]}1"));
x = splitFromDelimiter[1];
}
if (PR.ContainsKey(x))
{
// if we need to continue recursively translate
result = GetParameterVal(isHeight ? PR[x].height : PR[x].width, isHeight);
}
else if (PAR.ContainsKey(x))
{
// exit condition
result = PAR[x];
}
else
{
// just in case we didnt find something - we should get here
result = 0;
}
return factor * result;
}).Sum();
return sum;
}
}
}
I didnt add any validity checks, and if a value wasn't found it recieves a val of 0, but go ahead and adapt it to your needs..
Here a a working example for your question... It took me a lot of time so I hope you appreciate it: The whole code is comented line by line. If you have any question do not hesitate to ask me !
First of all we create a class named myEntry that will represent an entry. The name has to be unique e.g P1, P2, P3
public class myEntry
{
public string Name { get; private set; } //this field should be unique
public object Height { get; set; } //Can contain a reference to another entry or a value also as many combinations of those as you want.
// They have to be separated with a +
public object Width { get; set; } //same as for height here
public myEntry(string name, object height, object width)
{
//Set values
this.Name = name;
this.Height = height;
this.Width = width;
}
}
Now I create a dummy Exception class for an exception in a further class (you will see the use of this further on. Just ignore it for now)
public class UnknownEntry : Exception
{
//Create a new Class that represents an exception
}
Now we create the important class that will handle the entries and do all the work for us. This might look complicated but if you don't want to spend time understanding it you can just copy paste it, its a working solution!
public class EntryHolder
{
private Dictionary<string, double> _par = new Dictionary<string, double>(); //Dictionary holding our known variables
private List<myEntry> _entries; //List holding our entries
public EntryHolder()
{
_entries = new List<myEntry>(); //Create list
//Populate dictionary
_par.Add("Height", 500);
_par.Add("Width", 1000);
}
public bool Add(myEntry entry)
{
var otherEntry = _entries.FirstOrDefault(x => x.Name.Equals(entry.Name)); //Get entry with same name
if(otherEntry != null)
{
//Entry with the same name as another entry
//throw new DuplicateNameException(); //Throw an exception if you want
return false; //or just return false
}
//Entry to add is valid
_entries.Add(entry); //Add entry
return true; //return success
}
public void Add(List<myEntry> entries)
{
foreach (var entry in entries) //Loop through entries
{
Add(entry);
}
}
public myEntry GetEntry(string uniqueName)
{
var entry = GetRawEntry(uniqueName); //Get raw entry
var heightToCalculate = entry.Height.ToString(); //Height to calculate to string
var widthToCalculate = entry.Width.ToString(); //Width to calculate to string
entry.Height = Calculate(heightToCalculate, true); //Calculate height
entry.Width = Calculate(widthToCalculate, false); //Calculate width
return entry; //return entry
}
public List<myEntry> CalculateAllEntries()
{
List<myEntry> toReturn = new List<myEntry>(); //Create list that we will return after the calculation finished
foreach (var entryToCalculate in _entries) //Loop through all entries
{
toReturn.Add(GetEntry(entryToCalculate.Name)); //calculate entry values and add them to the list we will return after
}
return toReturn; //return list after the whole calculation finished
}
private double Calculate(string toCalculate, bool isHeight)
{
if (!toCalculate.Contains("+"))
{
//String doesn't contain a + that means it has to be a number or a key in our dictionary
object toConvert = toCalculate; //Set the object we want to convert to double
var entryCorrespondingToThisValue = _entries.FirstOrDefault(x => x.Name.Equals(toCalculate)); //Check if the string is a reference to another entry
if (entryCorrespondingToThisValue != null)
{
//It is the name of another object
toConvert = isHeight ? entryCorrespondingToThisValue.Height : entryCorrespondingToThisValue.Width; //Set object to convert to the height or width of the object in entries
}
try
{
return Convert.ToDouble(toConvert); //try to convert and return if success
}
catch (Exception e)
{
//the given height object has the wrong format
//Format: (x + Y + z ...)
throw new FormatException();
}
}
//Contains some +
var spitedString = toCalculate.Split(new char[] {'+'}); //Split
double sum = 0d; //Whole sum
foreach (var splited in spitedString) //Loop through all elements
{
double toAdd = 0; //To add default = 0
if (_par.ContainsKey(splited)) //Check if 'splited' is a key in the dictionary
{
//part of object is in the par dictionary so we get the value of it
toAdd = _par[splited]; //get value corresponding to key in dictionary
}
else
{
//'splited' is not a key in the dictionary
object toConvert = splited; //set object to convert
var entryCorrespondingToThisValue = _entries.FirstOrDefault(x => x.Name.Equals(splited)); //Does entries contain a reference to this value
if (entryCorrespondingToThisValue != null)
{
//It is a reference
toConvert = isHeight ? entryCorrespondingToThisValue.Height : entryCorrespondingToThisValue.Width; //Set to convert to references height or width
}
try
{
toAdd = Convert.ToDouble(toConvert); //Try to convert object
}
catch (Exception e)
{
//A part of the given height is not a number or is known in the par dictionary
throw new FormatException();
}
}
sum += toAdd; //Add after one iteration
}
return sum; //return whole sum
}
public myEntry GetRawEntry(string uniqueName)
{
var rawEntry = _entries.FirstOrDefault(x => x.Name.Equals(uniqueName)); //Check for entry in entries by name (unique)
if (rawEntry == null)
{
//Entry is not in the list holding all entries
throw new UnknownEntry(); //throw an exception
return null; //Or just return null
}
return rawEntry; //return entry
}
}
And here the end, the test and prove that it works:
public void TestIt()
{
List<myEntry> entries = new List<myEntry>()
{
new myEntry("P1", 0, 0),
new myEntry("P2", "P1", "P1+Height"),
new myEntry("P3", "P1+Height", "P2"),
new myEntry("P4", "P3", "P3+Height"),
};
EntryHolder myEntryHolder = new EntryHolder();
myEntryHolder.Add(entries);
var calculatedEntries = myEntryHolder.CalculateAllEntries();
}
Here an image of how it looks like:

Check if an stl file may contain two models

An stl file may contain 2 3D models. Is there any way I can detect if there are 2 or more models stored in one stl file?
In my current code, it can detect that there are 2 models in the example, but there are instances that it detects a lot of model even though it only has one.
The Triangle class structure has Vertices that contains 3 points (x, y, z)..
Sample STL File:
EDIT: Using #Gebb's answer this is how I implemented it:
private int GetNumberOfModels(List<TopoVertex> vertices)
{
Vertex[][] triangles = new Vertex[vertices.Count() / 3][];
int vertIdx = 0;
for(int i = 0; i < vertices.Count() / 3; i++)
{
Vertex v1 = new Vertex(vertices[vertIdx].pos.x, vertices[vertIdx].pos.y, vertices[vertIdx].pos.z);
Vertex v2 = new Vertex(vertices[vertIdx + 1].pos.x, vertices[vertIdx + 1].pos.y, vertices[vertIdx + 1].pos.z);
Vertex v3 = new Vertex(vertices[vertIdx + 2].pos.x, vertices[vertIdx + 2].pos.y, vertices[vertIdx + 2].pos.z);
triangles[i] = new Vertex[] { v1, v2, v3 };
vertIdx += 3;
}
var uniqueVertices = new HashSet<Vertex>(triangles.SelectMany(t => t));
int vertexCount = uniqueVertices.Count;
// The DisjointUnionSets class works with integers, so we need a map from vertex
// to integer (its id).
Dictionary<Vertex, int> indexedVertices = uniqueVertices
.Zip(
Enumerable.Range(0, vertexCount),
(v, i) => new { v, i })
.ToDictionary(vi => vi.v, vi => vi.i);
int[][] indexedTriangles =
triangles
.Select(t => t.Select(v => indexedVertices[v]).ToArray())
.ToArray();
var du = new XYZ.view.wpf.DisjointUnionSets(vertexCount);
// Iterate over the "triangles" consisting of vertex ids.
foreach (int[] triangle in indexedTriangles)
{
int vertex0 = triangle[0];
// Mark 0-th vertexes connected component as connected to those of all other vertices.
foreach (int v in triangle.Skip(1))
{
du.Union(vertex0, v);
}
}
var connectedComponents =
new HashSet<int>(Enumerable.Range(0, vertexCount).Select(x => du.Find(x)));
return connectedComponents.Count;
}
In some cases, it produces the correct output, but for the example image above, it outputs 3 instead of 2. I am now trying to optimize the snippet #Gebb gave to use float values since I believe that the floating points are necessary to the comparisons. Does anyone have a way to do that as well? Maybe I need another perspective.
You could do this by representing vertices and connections between them as a graph and finding the number of connected components of the graph with the help of the Disjoint-set data structure.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Vertex = System.ValueTuple<double,double,double>;
namespace UnionFindSample
{
internal class DisjointUnionSets
{
private readonly int _n;
private readonly int[] _rank;
private readonly int[] _parent;
public DisjointUnionSets(int n)
{
_rank = new int[n];
_parent = new int[n];
_n = n;
MakeSet();
}
// Creates n sets with single item in each
public void MakeSet()
{
for (var i = 0; i < _n; i++)
// Initially, all elements are in
// their own set.
_parent[i] = i;
}
// Finds the representative of the set
// that x is an element of.
public int Find(int x)
{
if (_parent[x] != x)
{
// if x is not the parent of itself, then x is not the representative of
// his set.
// We do the path compression by moving x’s node directly under the representative
// of this set.
_parent[x] = Find(_parent[x]);
}
return _parent[x];
}
// Unites the set that includes x and
// the set that includes x
public void Union(int x, int y)
{
// Find representatives of two sets.
int xRoot = Find(x), yRoot = Find(y);
// Elements are in the same set, no need to unite anything.
if (xRoot == yRoot)
{
return;
}
if (_rank[xRoot] < _rank[yRoot])
{
// Then move x under y so that depth of tree remains equal to _rank[yRoot].
_parent[xRoot] = yRoot;
}
else if (_rank[yRoot] < _rank[xRoot])
{
// Then move y under x so that depth of tree remains equal to _rank[xRoot].
_parent[yRoot] = xRoot;
}
else
{
// if ranks are the same
// then move y under x (doesn't matter which one goes where).
_parent[yRoot] = xRoot;
// And increment the result tree's
// rank by 1
_rank[xRoot] = _rank[xRoot] + 1;
}
}
}
internal class Program
{
private static void Main(string[] args)
{
string file = args[0];
Vertex[][] triangles = ParseStl(file);
var uniqueVertices = new HashSet<Vertex>(triangles.SelectMany(t => t));
int vertexCount = uniqueVertices.Count;
// The DisjointUnionSets class works with integers, so we need a map from vertex
// to integer (its id).
Dictionary<Vertex, int> indexedVertices = uniqueVertices
.Zip(
Enumerable.Range(0, vertexCount),
(v, i) => new {v, i})
.ToDictionary(vi => vi.v, vi => vi.i);
int[][] indexedTriangles =
triangles
.Select(t => t.Select(v => indexedVertices[v]).ToArray())
.ToArray();
var du = new DisjointUnionSets(vertexCount);
// Iterate over the "triangles" consisting of vertex ids.
foreach (int[] triangle in indexedTriangles)
{
int vertex0 = triangle[0];
// Mark 0-th vertexes connected component as connected to those of all other vertices.
foreach (int v in triangle.Skip(1))
{
du.Union(vertex0, v);
}
}
var connectedComponents =
new HashSet<int>(Enumerable.Range(0, vertexCount).Select(x => du.Find(x)));
int count = connectedComponents.Count;
Console.WriteLine($"Number of connected components: {count}.");
var groups = triangles.GroupBy(t => du.Find(indexedVertices[t[0]]));
foreach (IGrouping<int, Vertex[]> g in groups)
{
Console.WriteLine($"Group id={g.Key}:");
foreach (Vertex[] triangle in g)
{
string tr = string.Join(' ', triangle);
Console.WriteLine($"\t{tr}");
}
}
}
private static Regex _triangleStart = new Regex(#"^\s+outer loop");
private static Regex _triangleEnd = new Regex(#"^\s+endloop");
private static Regex _vertex = new Regex(#"^\s+vertex\s+(\S+)\s+(\S+)\s+(\S+)");
private static Vertex[][] ParseStl(string file)
{
double ParseCoordinate(GroupCollection gs, int i) =>
double.Parse(gs[i].Captures[0].Value, CultureInfo.InvariantCulture);
var triangles = new List<Vertex[]>();
bool isInsideTriangle = false;
List<Vertex> triangle = new List<Vertex>();
foreach (string line in File.ReadAllLines(file))
{
if (isInsideTriangle)
{
if (_triangleEnd.IsMatch(line))
{
isInsideTriangle = false;
triangles.Add(triangle.ToArray());
triangle = new List<Vertex>();
continue;
}
Match vMatch = _vertex.Match(line);
if (vMatch.Success)
{
double x1 = ParseCoordinate(vMatch.Groups, 1);
double x2 = ParseCoordinate(vMatch.Groups, 2);
double x3 = ParseCoordinate(vMatch.Groups, 3);
triangle.Add((x1, x2, x3));
}
}
else
{
if (_triangleStart.IsMatch(line))
{
isInsideTriangle = true;
}
}
}
return triangles.ToArray();
}
}
}
I'm also using the fact that System.ValueTuple implements Equals and GetHashCode in an appropriate way, so we can easily compare vertices (this is used implicitly by HashSet) and use them as keys in a dictionary.

How to do radial collections querying in C#?

I have lots of points (with .X and .Y coordinates) in a 2d space For example a KDTree or some other IEnumerable<AnyXYPointImpl> collection. I want to do query that is not square but Circle\Radius based:
What I want is simple: having a point P, direction D and half angle a we want to get all points in the collection area covered by a sector formed by them.
So how to do such radial collections querying in C# with LINQ?
Here is a solution:
Example usage:
var field = new List<Vector2>() {
new Vector2(-1,1), new Vector2(1,1),
new Vector2(-1,-1), new Vector2(1,-1)
};
Console.WriteLine(string.Join("; ", field.IsInSector(new Vector2(0,0), new Vector2(2,2), 45.0)));
Console.WriteLine(string.Join("; ", field.IsInSector(new Vector2(0,0), new Vector2(2,-2), 45.0)));
Console.WriteLine(string.Join("; ", field.IsInSector(new Vector2(0,0), new Vector2(2,0), 90.0)));
Main logic to check point is it in sector via IEnumerable extension:
public static class Extenctions {
public static IEnumerable<IXYPoint> IsInSector(this IEnumerable<IXYPoint> source, Vector2 pointToCheck, Vector2 direction, double halfAngle)
{
if(!source.Any() || pointToCheck == null || halfAngle <= 0)
return new IXYPoint[0];
var dirVector = new Vector2() {
X = direction.X - pointToCheck.X,
Y = direction.Y - pointToCheck.Y
};
var radius = Distance(direction, pointToCheck);
var result = new List<IXYPoint>();
foreach(var p in source)
{
if(Distance(p, pointToCheck) > radius){ // check is point in circle
continue;
}
if(IsPointInSector(dirVector, p, halfAngle)) // main check
result.Add(p);
}
return result;
}
private static double Distance(IXYPoint from, IXYPoint to)
{
return Math.Sqrt(Math.Pow(to.X - from.X,2) + Math.Pow(from.Y-to.Y,2));
}
private static bool IsPointInSector(IXYPoint directionVector, IXYPoint pointToCheck, double halfAngle)
{
var rq0 = Math.Pow(directionVector.X,2) + Math.Pow(directionVector.Y,2);
var rq = Math.Pow(pointToCheck.X,2) + Math.Pow(pointToCheck.Y,2);
return rq0 >= rq && (directionVector.X*pointToCheck.X + directionVector.Y*pointToCheck.Y)/Math.Sqrt(rq0*rq) >= Math.Cos(halfAngle/180.0*Math.PI);
}
}
Here's a non-LINQ solution.
var filteredPoints = new List<Point2D>();
foreach (var point in points)
if (point.IsInSector(origin, d, a))
filteredPoints.Add(point);

LINQ command for data reduction of GPS data

I have this:
public class LatLon
{
public double lat {get;set;}
public double lon {get;set;}
}
in a List<LatLon>. It contains of
p1 { lat = 49.9429989, lon = 3.9542134 }
p2 { lat = 49.9429989, lon = 3.9542133 }
p3 { lat = 49.9429989, lon = 3.9542136 }
etc..
My goal is to remove coordinates from this list whose difference to other coordinates is lower than the boundaries of lat_bound and lon_bound, so even though the person recording stood at a place for a long time, it means there is only one coordinate left. What would be the LINQ command?
Example:
p1 { lat = 4.555, lon = 6.555 }
p2 { lat = 4.556, lon = 6.556 }
.
Then Math.Abs(p1.lat - p2.lat) = 0.001 and Math.Abs(p1.lon - p2.lon) = 0.001. p1.lon - p2.lon is the lon difference to one other coordinate's lon-value. Let's say lon_bound equals 0.0005 then this very coordinate is being removed if lat_bound is also 0.0005, as 0.001 > 0.0005.
EDIT: I decided to pipe to http://www.gpsbabel.org instead.
LINQ Does not make wonders. The problem you are referring to is not just a "Distict" type problem.
1st You have to make a function to measure distance between 2 points.
2nd You need to detect clusters of points..(organize close points into groups)
Finally the easiest thing to do is to Group By a Belonging cluster and keep only 1point from each group.....
But then again.....there are several other problems which might not produce accurate results.
For example whats the one point that represents its group best?
You can use Math.Round to round the values to the precision that you want.
Then use Linq Distinct to remove the duplicates.
void Main()
{
var list = new List<Coordinate>()
{
new Coordinate(25.25251, 100.21254),
new Coordinate(25.25252, 100.21255),
new Coordinate(25.25253, 100.21256),
new Coordinate(25.80000, 100.90000)
};
int precision = 4;
var res = list.Select(x => new Coordinate(
Math.Round(x.Lon, precision),
Math.Round(x.Lat, precision))).Distinct().ToList();
}
public struct Coordinate
{
private double lon;
private double lat;
public Coordinate(double lon, double lat)
{
this.lon = lon;
this.lat = lat;
}
public double Lat { get { return lat; } }
public double Lon { get { return lon; } }
}
(Note that I have I'm using a struct and not a class for the Coordinate's)
If you have a function Func<LatLon, LatLon, bool> bounded that returns true if the two points are within your bound and false if not then this query works:
var keeps =
latlons
.Aggregate(new List<LatLon>(), (xs, y) =>
{
if (!xs.Any(x => bounded(x, y)))
{
xs.Add(y);
}
return xs;
});
You can filter by proximity like this:
public class LatLon
{
public double lat {get;set;}
public double lon {get;set;}
}
class ProximityFilter
{
private LatLon m_ref = null;
internal bool DifferentFromPrevious(LatLon arg)
{
if (m_ref == null)
{
m_ref = arg;
return true;
}
var are_different = Math.Abs(arg.lat - m_ref.lat) > 0.001 || Math.Abs(arg.lon - m_ref.lon) > 0.001;
if (are_different)
m_ref = arg;
return are_different;
}
}
class Program
{
static int Main(string[] args)
{
var p1 = new LatLon { lat = 49.9429989, lon = 3.9542134 };
var p2 = new LatLon { lat = 49.9529989, lon = 3.9642134 };
var p3 = new LatLon { lat = 49.9429989, lon = 3.9542133 };
var p4 = new LatLon { lat = 49.9429989, lon = 3.9542136 };
var list = new List<LatLon> {p1, p2, p3, p4};
var filter = new ProximityFilter();
var cleaned = list.Where(filter.DifferentFromPrevious);
// ...
}
}
You can't use Distinct as it will remove the point with a value seen before, even if there is a different value between them.
Additionally, this approach has O(N) complexity, so at least theoretically it performs better than Distinct. It also works both with structs and classes.

Categories

Resources