Compare points inside a list - c#

I have a simple list of points List<Point> which I populate using the mouse. Like, when I click the mouse, the location is added into the list. The problem is that the new location is added at the bottom of the list. What I want to do is, when I click the mouse, search all the existing points in the list and return the closest point to the mouse location, and insert the new point after that one.
I've been searching the web for hours and I can't seem to find a solution. Any help will be appreciated. Thanks!

The List<> class contains an .Insert() method to do just that. When you search the list and find the "closest" element (however you define that logic), you can get the index of that object in the list. Then just insert the new one after that index. Something like:
var closestPoint = FindClosestPoint(listOfPoints, newPoint);
var index = listOfPoints.IndexOf(closestPoint);
listOfPoints.Insert(index + 1, newPoint);
Getting the closest point itself should be a simple matter of geometry. You have two X/Y coordinates on a plane. The distance between them is the square root of the sum of the squares of the axes. So you just need the element where that value is the smallest. Something like this:
var closestPoint = listOfPoints
.Select(p => new {
Point = p,
Distance = Math.Sqrt(
Math.Pow(Math.Abs(p.X - closestPoint.X), 2) +
Math.Pow(Math.Abs(p.Y - closestPoint.Y), 2)
)
})
.OrderByDesc(p => p.Distance)
.First();

Related

AnimateCamera in GoogleMaps is not displaying all location included in builder

I am working with GoogleMap and I am trying to focus my map on a region that displayed my list of locations. The list gets "included" into the builder one by one, but when I actually call this method a lot of my locations are cropped out. Seems to me like the zoom level on the NewLatLngBounds is too high.
I thought about getting the Northeast and Southwest Corners and then centering the map but that doesn't work either. Not sure what else to try.
public void DisplayRegion(List<Position> positions, int padding = 0)
{
if (_googleMap == null) throw new InvalidOperationException("Map is not ready");
LatLngBounds.Builder builder = new LatLngBounds.Builder();
foreach(var p in positions)
{
builder.Include(new LatLng(p.Latitude, p.Longitude);
}
LatLngBounds bounds = builder.Build();
//var ne = bounds.Northeast;
// var se = bounds.Southwest;
_googleMap.AnimateCamera(CameraUpdateFactory.NewLatLngBounds(bounds, padding));
}
This does work but like I said above a good chunk of the locations passed in are cropped off screen. I also tried factoring in the width and height of the and basing padding on that but it's like I didn't do anything. All I want is for every item in my List of Positions to display on screen at an appropriate zoom level. Any suggestions?
First , select a location as the center of the map,just get the midpoint from Northeast and Southwest Corners , let's say we get a point ,it has centerLatitude and centerLongitude .
Calculate how far are the positions between the center location , and get the farthest distance , call it farthestDistance.
Use map.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (centerLatitude,centerLongitude), Distance.FromKilometers (farthestDistance)));
Then the map will display including all the positions and use a proper zoom level .
Refer to https://stackoverflow.com/a/53735393/8187800 .

C# get all AOIs from list in which lies a specific point

I have a List of AOIs (area of interest) where AOI is defined by 2 points top-left and bottom-right corner (Start and End points).
And I have a point defined by PosX and PosY.
I want to get all such AOIs from list in which specific point lies. How can I do it most effectively? I wanted to do it with select and where functions with lambda expressions but can't figure out how to achieve it.
I created rectangle bounding an AOI and tested if a point lies in it:
var rect = new RectangleF(aoi.Start.X, aoi.Start.Y, aoi.End.X - aoi.Start.X, aoi.End.Y - aoi.Start.Y));
var liesIn = rect.Contains(pt.PosX, pt.PosY);
But I have an List<AOIs>. If I want all AOIs from list which contains point pt must I iterate through all list or can I do it somehow effectively?
EDIT: ok I figured it out. This works for me.
var result = AOIs.Where(a => new RectangleF(a.Start.X, a.Start.Y, a.Start.X - a.End.X, a.Start.Y - a.End.Y).Contains(pt.PosX, pt.PosY)).ToList();
I don't know what was with me I was doing some weird things but solution is easy.
I suppose something like this:
AOIs.Where(x =>
x.Start.X < posX
&& x.Start.Y < posY
&& x.End.X > posX
&& x.End.Y > posY)

Finding nearest coordinate to other coordinate

Given an array of geo coordinates and another geo coordinate, I would like to find the nearest coordinate(s) to it.
For example, given the array:
lat long
52.525782 13.316927
52.526409 13.319083
52.525678 13.320317
And the point: 52.525730, 13.314556, then the first point 52.525782, 13.316927 will be returned, as it is the closest one.
Is the only way of acheiving it is looping through all the array and finding the distance between the points? What happens if the array contains too much coordinates?
You can try it using LINQ, but the inner workings of LINQ would still loop over your collection. For example:
//Your list with coordinates
List<GeoCoordinate> coords = new List<GeoCoordinate>();
//The coord you want to compare
GeoCoordinate crd = new GeoCoordinate();
//Sorts the list based on the proximity of the coords to crd
var sortedCoords = coords.OrderBy(x => x.GetDistanceTo(crd)).ToList();
I know it doesn't use an array, but I find using lists is easier.
I think that should work, let me know if it does!
struct coord
{
public double lat;
public double lon;
}
public void Main(coord coord)
{
var coords = new[]{ new coord(){lat=1, lon=1} };
var closest = coords.Min(p => Math.Abs(p.lat - coord.lat) + Math.Abs(p.lon - coord.lon));
}

How to compare each element with every other in LINQ?

The problem is, for an IEnumerable of rectangles I need to know for each rectangle whether it intersects any of the others. So the output would be a sequence with the same count containing one boolean value for every rectangle.
So it is kind of a nested loop problem but it should not compare the element with itself and if rect A intersects rect B there is no need to check rect B again.
Is this possible with LINQ?
//for every rectangle in your list.
//search the list, eliminating the same one,
//and find if any of the others intersect with it.
var intersectingRectangles = rectangles
.Where(rect => rectangles
.Where(r => r != rect)
.Any(r => DoIntersect(rect, r)));
This assumes that you have a function DoIntersect taking two rectangles and telling you if they intersect.
There are MANY improvements that could be made to this if it is not effecient enough for your needs. you could potentially sort based on coordinates and be able to eliminate a lot of comparisons. But, that all depends on what your data looks like and how fast this needs to be.
If you want to have the output contain each of the original IEnumerable along with a status about whether is intersects any others than switch to using a Select() as below.
var allWithIntersectionStatus = rectangles.Select(rect => new
{
Value = rect,
DoesIntersect = rectangles
.Where(rectWhere => rectWhere != rect)
.Any(rectAnt => DoIntersect(rect, rectAnt))
});

C# Revit API, CreateIndependentTag method failing to add tag to ceilings, exception at line location point

This code attempts to add a tag to all ceilings in the ceiling views list. The ceiling view list populates and I can get the ceiling elements from the document, but it seems to be failing when trying to acquire the centre point of the ceiling element. I have googled all the blogs and I cant find a reference to tagging floors in revit either as it may be a similar scenario.
public IndependentTag CreateIndependentTag(Document doc)
{
List<View> viewList = ceilingViewCollector(doc);
foreach (View view in viewList)
{
// Find all ceiling elements in the document by using category filter
ElementCategoryFilter filter = new ElementCategoryFilter(BuiltInCategory.OST_Ceilings);
// Use shortcut WhereElementIsNotElementType() to find ceiling instances only
FilteredElementCollector collector = new FilteredElementCollector(doc);
IList<Element> CeilingList = collector.WherePasses(filter).WhereElementIsNotElementType().ToElements();
foreach (Ceiling ceiling in CeilingList)
{
TaskDialog.Show("Ceiling", ceiling.Name);
// define tag mode and tag orientation for new tag
TagMode tagMode = TagMode.TM_ADDBY_CATEGORY;
TagOrientation tagorn = TagOrientation.Horizontal;
//add tag to centre of ceiling?
LocationPoint p = ceiling.Location as LocationPoint;
p.Point = new XYZ(0.0, p.Point.Y, p.Point.Z);
ceilingCentre = p.Point;
string coords = "point = " + ceilingCentre.ToString();
TaskDialog.Show("Source room Center", coords);
IndependentTag newTag = doc.Create.NewTag(view, ceiling, true, tagMode, tagorn, ceilingCentre);
if (null == newTag)
{
throw new Exception("Create IndependentTag Failed.");
}
// set leader mode free
// otherwise leader end point move with elbow point
newTag.LeaderEndCondition = LeaderEndCondition.Free;
XYZ elbowPnt = ceilingCentre + new XYZ(5.0, 5.0, 0.0);
newTag.LeaderElbow = elbowPnt;
XYZ headerPnt = ceilingCentre + new XYZ(10.0, 10.0, 0.0);
newTag.TagHeadPosition = headerPnt;
return newTag;
}
}
return null;
}
I'm no expert but, aren't you forgetting your X-component?
//add tag to centre of ceiling?
LocationPoint p = ceiling.Location as LocationPoint;
p.Point = new XYZ(p.Point.X / 2, p.Point.Y / 2, p.Point.Z);
ceilingCentre = p.Point;
It would seem you'd want to center this Point p in terms of X and Y, and keep the Z component.
A little late but I just ran across this. Revit elements can be located by a point (most family instances), a line (walls, line based components), or a sketch (ceilings, floors, etc.). Your code will not work because you are casting the location to a location point and it isn't a location point.
With sketches you can't cast the location to anything useful so you will need to figure out how you want to determine the center of the ceiling on your own. The simple answer is to find the bounding box of the ceiling and calculate it's center as suggested by mtumminello. This will work for most ceilings except if you have a large L shape or something the bounding box center may not be over the ceiling at all. If you need this covered you would have to come up with some other algorithm to find the center point.

Categories