Clusters of Lat, Long that are near to each other - c#

I have a scenario where N number of items were delivered at N locations (lat, long). I want to find group of items which are delivered near to each other(100 meters). Say for example (Item1, Item9,...Item499) were delivered such that they had distance less than 100 between them. Item9 can be more than 100 meters away from Item499 but it will be in that group if it has less than 100 meters distance from any one of the item in the group.
Find all such groups.
I want someone to point out the right algorithm. I'll then work on that. Preferable Language is C#

Below is the algorithm to solve this problem which I found here
public class ClusterCreator
{
public Dictionary<int, SimpleCluster> GetClusters(List<SimpleCluster> clusterList, int maxDistance)
{
//LOAD DATA
//var clusterList = new List<SimpleCluster>(); // LoadSimpleClusterList();
//var latitudeSensitivity = 3;
//var longitutdeSensitivity = 3;
//CLUSTER THE DATA
return ClusterTheData(clusterList, maxDistance);
}
public Dictionary<int, SimpleCluster> ClusterTheData(List<SimpleCluster> clusterList, int maxDistance)
{
//CLUSTER DICTIONARY
var clusterDictionary = new Dictionary<int, SimpleCluster>();
//Add the first node to the cluster list
if (clusterList.Count > 0)
{
clusterDictionary.Add(clusterList[0].ID, clusterList[0]);
}
//ALGORITHM
for (int i = 1; i < clusterList.Count; i++)
{
SimpleCluster combinedCluster = null;
SimpleCluster oldCluster = null;
foreach (var clusterDict in clusterDictionary)
{
//Check if the current item belongs to any of the existing clusters
if (CheckIfInCluster(clusterDict.Value, clusterList[i], maxDistance))
{
//If it belongs to the cluster then combine them and copy the cluster to oldCluster variable;
combinedCluster = CombineClusters(clusterDict.Value, clusterList[i]);
oldCluster = new SimpleCluster(clusterDict.Value);
}
}
//This check means that no suitable clusters were found to combine, so the current item in the list becomes a new cluster.
if (combinedCluster == null)
{
//Adding new cluster to the cluster dictionary
clusterDictionary.Add(clusterList[i].ID, clusterList[i]);
}
else
{
//We have created a combined cluster. Now it is time to remove the old cluster from the dictionary and instead of it add a new cluster.
clusterDictionary.Remove(oldCluster.ID);
clusterDictionary.Add(combinedCluster.ID, combinedCluster);
}
}
return clusterDictionary;
}
public static SimpleCluster CombineClusters(SimpleCluster home, SimpleCluster imposter)
{
//Deep copy of the home object
var combinedCluster = new SimpleCluster(home);
combinedCluster.LAT_LON_LIST.AddRange(imposter.LAT_LON_LIST);
combinedCluster.NAMES.AddRange(imposter.NAMES);
//Combine the data of both clusters
//combinedCluster.LAT_LON_LIST.AddRange(imposter.LAT_LON_LIST);
//combinedCluster.NAMES.AddRange(imposter.NAMES);
//Recalibrate the new center
combinedCluster.LAT_LON_CENTER = new LAT_LONG
{
LATITUDE = ((home.LAT_LON_CENTER.LATITUDE + imposter.LAT_LON_CENTER.LATITUDE) / 2.0),
LONGITUDE = ((home.LAT_LON_CENTER.LONGITUDE + imposter.LAT_LON_CENTER.LONGITUDE) / 2.0)
};
return combinedCluster;
}
public bool CheckIfInCluster(SimpleCluster home, SimpleCluster imposter, int maxDistance)
{
foreach (var item in home.LAT_LON_LIST)
{
var sCoord = new GeoCoordinate(item.LATITUDE, item.LONGITUDE);
var eCoord = new GeoCoordinate(imposter.LAT_LON_CENTER.LATITUDE, imposter.LAT_LON_CENTER.LONGITUDE);
var distance = sCoord.GetDistanceTo(eCoord);
if (distance <= maxDistance)
return true;
}
return false;
//if ((home.LAT_LON_CENTER.LATITUDE + latitudeSensitivity) > imposter.LAT_LON_CENTER.LATITUDE
// && (home.LAT_LON_CENTER.LATITUDE - latitudeSensitivity) < imposter.LAT_LON_CENTER.LATITUDE
// && (home.LAT_LON_CENTER.LONGITUDE + longitutdeSensitivity) > imposter.LAT_LON_CENTER.LONGITUDE
// && (home.LAT_LON_CENTER.LONGITUDE - longitutdeSensitivity) < imposter.LAT_LON_CENTER.LONGITUDE
// )
//{
// return true;
//}
//return false;
}
}
public class SimpleCluster
{
#region Constructors
public SimpleCluster(int id, string name, double latitude, double longitude)
{
ID = id;
LAT_LON_CENTER = new LAT_LONG
{
LATITUDE = latitude,
LONGITUDE = longitude
};
NAMES = new List<string>();
NAMES.Add(name);
LAT_LON_LIST = new List<LAT_LONG>();
LAT_LON_LIST.Add(LAT_LON_CENTER);
}
public SimpleCluster(SimpleCluster old)
{
ID = old.ID;
LAT_LON_CENTER = new LAT_LONG
{
LATITUDE = old.LAT_LON_CENTER.LATITUDE,
LONGITUDE = old.LAT_LON_CENTER.LONGITUDE
};
LAT_LON_LIST = new List<LAT_LONG>(old.LAT_LON_LIST);
NAMES = new List<string>(old.NAMES);
}
#endregion
public int ID { get; set; }
public List<string> NAMES { get; set; }
public LAT_LONG LAT_LON_CENTER { get; set; }
public List<LAT_LONG> LAT_LON_LIST { get; set; }
}
public class LAT_LONG
{
public double LATITUDE { get; set; }
public double LONGITUDE { get; set; }
}

Related

C# methood to scrape a line into an array - does not pass the array on return

i am quite new to C# and i have been doing my progress via projects with goals i`m trying to achieve.
i am stuck in something rather general and simple, and it will assist me to understand and move forward.
public Array ExtractRakeFromHand(string theline)
{
int rri = 0;
var rakeandpot = theline.Split().Where(x => x.StartsWith("("))
.Select(x => x.Replace("(", string.Empty).Replace(")", string.Empty))
.ToList();
var playerssplit = theline.Substring(theline.IndexOf("ers ("));
var pbreakdown = playerssplit.Substring(theline.IndexOf("("));
pbreakdown = pbreakdown.Remove(pbreakdown.Length - 1, 1);
var numofplayers = pbreakdown.Split(',');
(string player, double player_portion, double potsize, double rakesize, string hand)[] myar = new (string player, double player_portion, double potsize, double rakesize, string hand)[numofplayers.Length];
for (int iii = 0; iii < myar.Length; iii++)
{
myar[iii].rakesize = Double.Parse(rakeandpot[0]);
myar[iii].potsize = Double.Parse(rakeandpot[1]);
}
var values = pbreakdown.Split(',');
(string name, double value)[] result = new (string, double)[values.Length];
for (int ii = 0; ii < values.Length; ii++)
{
var splittedValue = values[ii].Split(':');
result[ii] = (splittedValue[0], Double.Parse(splittedValue[1], CultureInfo.InvariantCulture));
myar[rri].player = result[rri].name;
var ppot = (result[rri].value / myar[rri].potsize);
var ppotr = ppot * myar[rri].rakesize;
myar[rri].player_portion = ppotr;
rri++;
}
return myar;
}
i call this method via var thedata = ExtractRakeFromHand(d);
the return is an object that does not contain the "Name" and "Rake" fields.
how can i make it return the array in the same way it was created ?
I tried to add this class:
public class RakeHandParse
{
public string player { get; set; }
public double player_portion { get; set; }
public double potsize { get; set; }
public double rakesize { get; set; }
public string hand { get; set; }
}
but i`m not sure how to use it inside the code to get back the full details.
thanks so much !
replace public Array ExtractRakeFromHand(string theline)
with public (string player, double player_portion, double potsize, double rakesize, string hand)[] ExtractRakeFromHand(string theline)
and call like:
(string player, double player_portion, double potsize, double rakesize, string hand)[] myar = ExtractRakeFromHand(d);

Looking for elegant / efficient solution for integer keyed collection of models

The "AddToLeg" method seems long winded is there a better pattern or more efficient way to implement this? I thought about using a dictionary but I want the key to remain an integer. I'm pretty new to linq / generics so I maybe missing something more obvious. When I've looked through documentation there aren't really any examples that match my scenario.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
public class Program
{
public static void Main()
{
var model = new TrainspotterItenaryViewModel();
var manchester = new Station() { Name = "Manchester", ExpectedTime = "13:30" };
var leeds = new Station() { Name = "Leeds", ExpectedTime = "15:00" };
var york = new Station() { Name = "York", ExpectedTime = "15:30" };
var london = new Station() { Name = "London", ExpectedTime = "21:00" };
model.AddToLeg(1, manchester);
model.AddToLeg(1, leeds);
model.AddToLeg(2, leeds);
model.AddToLeg(2, london);
model.AddToLeg(1, york); //another destination added to leg 1
//any number of legs can be added...
model.AddToLeg(3, manchester);
//show results contents
for(var i=1; i <= model.Legs.Count; i++)
{
var displayLeg = model.Legs.Single(x=>x.LegNumber==i);
foreach(var station in displayLeg.Stations){
string output = $"leg: {displayLeg.LegNumber} station: {station.Name}, expected:{station.ExpectedTime}";
Console.WriteLine(output);
}
}
}
}
public class TrainspotterItenaryViewModel
{
public List<Leg> Legs { get; set; }
public void AddToLeg(int legNumber, Station station)
{
if (Legs == null)
{
Legs = new List<Leg>();
}
var legCount = Legs.Count(x => x.LegNumber == legNumber);
if (legCount == 0)
{
var leg = new Leg
{
LegNumber = legNumber,
Stations = new List<Station> {station}
};
Legs.Add(leg);
Console.WriteLine($"Leg {leg.LegNumber} Not Found- Added new leg and {station.Name}");
}
else
{
foreach (var leg in Legs)
{
if (leg.LegNumber == legNumber)
{
leg.Stations.Add(station);
Console.WriteLine($"Leg {legNumber} Found- adding {station.Name}");
}
}
}
}
}
public class Leg
{
public int LegNumber { get; set; }
public List<Station> Stations { get; set; }
}
public class Station
{
public string Name { get; set; }
public string ExpectedTime { get; set; }
}
}
Use Dictionary for this task:
public class TrainspotterItenaryViewModel
{
private Dictionary<int, Leg> _legNumberToLegIndex { get; set; }
public IEnumerable<Leg> Legs => _legNumberToLegIndex?.Values
public void AddToLeg(int legNumber, Station station)
{
if (_legNumberToLegIndex == null)
{
_legNumberToLegIndex = new Dictionary<int, Leg>();
}
Leg leg;
if (!_legNumberToLegIndex.TryGetValue(legNumber, out leg))
{
leg = new Leg
{
LegNumber = legNumber,
Stations = new List<Station>()
};
_legNumberToLegIndex.Add(legNumber, leg);
}
leg.Stations.Add(station);
}
}
The method FirstOrDefault already returns the object that you're searching and so you can simplify your code
public List<Leg> Legs { get; set; } = new List<Leg>();
public void AddToLeg(int legNumber, Station station)
{
var leg = Legs.FirstOrDefault(x => x.LegNumber == legNumber);
if (leg == null)
{
leg = new Leg
{
LegNumber = legNumber,
Stations = new List<Station> { station }
};
Legs.Add(leg);
Console.WriteLine($"Leg {leg.LegNumber} Not Found- Added new leg and {station.Name}");
}
else
{
leg.Stations.Add(station);
Console.WriteLine($"Leg {legNumber} Found- adding {station.Name}");
}
}

How could I genericize a range expansion function?

I have the following class:
public class State {
public DateTime Created { get; set; }
public Double A { get; set; }
public Double B { get; set; }
}
Then I have two states:
State start = new State { Created = DateTime.UtcNow, A = 10.2, B = 20.5 }
State end = new State { Created = DateTime.UtcNow.AddDays(40), A = 1, B = 80 }
I would like to create a List between start and end where the values of A and B evolve in a linear way between their start and end values.
I was able to create a List as follows:
IList<DateTime> dates = new Range<DateTime>(start.Created, end.Created,).Expand(DateInterval.Day, 1)
Range and Expand are a few helpers I created ...
What is the best way to create the values evolution?
My idea would to do something like:
IList<State> states = new Range<State>( // here "tell" how each property would evolve ...)
UPDATE
Range Class
public class Range<T> where T : IComparable<T> {
private T _minimum;
private T _maximum;
public T Minimum { get { return _minimum; } set { value = _minimum; } }
public T Maximum { get { return _maximum; } set { value = _maximum; } }
public Range(T minimum, T maximum) {
_minimum = minimum;
_maximum = maximum;
} // Range
public Boolean Contains(T value) {
return (Minimum.CompareTo(value) <= 0) && (value.CompareTo(Maximum) <= 0);
} // Contains
public Boolean ContainsRange(Range<T> Range) {
return this.IsValid() && Range.IsValid() && this.Contains(Range.Minimum) && this.Contains(Range.Maximum);
} // ContainsRange
public Boolean IsInsideRange(Range<T> Range) {
return this.IsValid() && Range.IsValid() && Range.Contains(this.Minimum) && Range.Contains(this.Maximum);
} // IsInsideRange
public Boolean IsValid() {
return Minimum.CompareTo(Maximum) <= 0;
} // IsValid
public override String ToString() {
return String.Format("[{0} - {1}]", Minimum, Maximum);
} // ToString
} // Range
A few Range Extensions:
public static IEnumerable<Int32> Expand(this Range<Int32> range, Int32 step = 1) {
Int32 current = range.Minimum;
while (current <= range.Maximum) {
yield return current;
current += step;
}
} // Expand
public static IEnumerable<DateTime> Expand(this Range<DateTime> range, TimeSpan span) {
DateTime current = range.Minimum;
while (current <= range.Maximum) {
yield return current;
current = current.Add(span);
}
} // Expand
var magicNumber = 40;
var start = new State {Created = DateTime.UtcNow, A = 10.2, B = 20.5};
var end = new State {Created = DateTime.UtcNow.AddDays(magicNumber), A = 1, B = 80};
var stateList = new List<State>(magicNumber);
for (var i = 0; i < magicNumber; ++i)
{
var newA = start.A + (end.A - start.A) / magicNumber * (i + 1);
var newB = start.B + (end.B - start.B) / magicNumber * (i + 1);
stateList.Add(new State { Created = DateTime.UtcNow.AddDays(i + 1), A = newA, B = newB });
}
So you have moved to the stage of genericization where a "mutation step" injectable method that will apply your interpolation becomes logical. Because you don't constrain far enough to know how to interpolate steps, you may need a factory method to create your Func<T,T> instances.
For example, if there are two properties whose values shift in your T, you would be able to control their rate of change this way.
public class Point
{
int X {get;set;}
int Y {get;set;}
}
public static IEnumerable<T> Expand(this Range<T> range, Func<T,T> stepFunction)
where T: IComparable<T>
{
var current = range.Minimum;
while (range.Contains(current))
{
yield return current;
current = stepFunction(current);
}
}
// in method
var pointRange = new Range<Point>(
new Point{ X=0,Y=0}, new Point{ X=5, Y=20});
foreach (Point p in pointRange.Expand(p => new Point{ p.X + 1, p.Y + 4})

Combining numbers and names collections

I have 2 List collections. One contains numbers, the other names. There are twice as many numbers as names(always). I want to take the first name from the collection and the first two numbers from the other collection then put them together in a 3rd user collection of (VentriloUser). Then the second name needs to be matched with the 3rd and 4th numbers and so on.
I was thinking something with a for or foreach loop, but I can't wrap my head around it right now.
public class VentriloUser
{
public VentriloUser(string name, int seconds, int ping)
{
Name = name; Seconds = seconds; Ping = ping;
}
public string Name { get; set; }
public int Ping { get; set; }
public int Seconds { get; set; }
}
public class Ventrilo
{
public Ventrilo(string statusurl)
{
StatusURL = statusurl;
}
public string StatusURL { get; set; }
public string HTML { get; set; }
public List<VentriloUser> Users { get; set; }
private Regex findNumbers = new Regex("\\<td width=\"10%\" bgcolor=\"#\\w{6}\"\\>\\<font color=\"#000000\">\\<div style=\"overflow:hidden;text-overflow:ellipsis\"\\>-?\\d+\\<");
private Regex findNames = new Regex("\\<td width=\"20%\" bgcolor=\"#\\w{6}\"\\>\\<font color=\"#000000\">\\<div style=\"overflow:hidden;text-overflow:ellipsis\"\\>\\b\\w+\\<");
private WebClient wClient = new WebClient();
public void DownloadHTML()
{
HTML = wClient.DownloadString(StatusURL);
}
public List<int> GetNumbers()
{
var rawnumbers = findNumbers.Matches(HTML);
var numbers = new List<int>();
foreach (var rawnumber in rawnumbers)
{
var match = Regex.Match(rawnumber.ToString(), "\\>\\-?\\d+\\<");
string number = Regex.Replace(match.ToString(), "\\<|\\>", "");
numbers.Add(Convert.ToInt32(number));
}
return numbers;
}
public List<string> GetNames()
{
var rawnames = findNames.Matches(HTML);
var names = new List<string>();
foreach (var rawname in rawnames)
{
var match = Regex.Match(rawname.ToString(), "\\>\\w+<");
string name = Regex.Replace(match.ToString(), "\\<|\\>", "");
names.Add(name);
}
return names;
}
public List<VentriloUser> GenerateUsers()
{
var numbers = GetNumbers();
var names = GetNames();
var users = new List<VentriloUser>();
}
}
I am a hobbyist, but hope to pursue a career one day. Any help is much appreciated. Thank you for your time.
Using LINQ:
var users = names.Select((name,idx) => new VentriloUser(name, numbers[idx*2], numbers[idx*2+1]))
.ToList();
Using loops:
var users = new List<VentriloUser>();
for (int i = 0; i < names.Count; i++)
{
var name = names[i];
int j = i * 2;
if (j >= numbers.Count - 1)
break; // to be safe...
users.Add(new VentriloUser(name, numbers[j], numbers[j + 1]));
}

C# AStar problems, won't do it correctly

I am currently working on A* pathfinding, but I am having some problems.
It does the wrong path before taking the best path to the end.
What am I doing wrong?
Source code: http://basic.apayostudios.com/AStar.zip
Online:
Game.cs http://pastie.org/1656955
Node.cs http://pastie.org/1656956
Enums:
public enum NodeType
{
None,
Solid,
Start,
End
}
Thanks!
Here's my A-star path finder, it works and you're free to use it for learning and comparing your solution against it, rip me off if you like. But there are dependencies that are missing from this code and I'm using fixed-point arithmetic. This won't build without some changes. This code is relatively high level and should be easy enough to reverse engineer.
public class AgentPathfinder
{
class SolutionComparer : IComparer<Solution>
{
public int Compare(Solution x, Solution y)
{
return x.Cost.CompareTo(y.Cost);
}
}
class Solution
{
public List<FixedVector> Path { get; private set; }
public FixedVector LastPosition { get { return Path[Path.Count - 1]; } }
public Fixed32 Heuristic { get; set; }
public Fixed32 Cost { get; set; }
public Solution(FixedVector position, Fixed32 heuristic)
{
Path = new List<FixedVector>(2) { position };
Heuristic = heuristic;
Cost = Path.Count + heuristic;
}
public Solution(FixedVector position
, Fixed32 heuristic
, List<FixedVector> path)
{
Path = new List<FixedVector>(path) { position };
Heuristic = heuristic;
Cost = Path.Count + heuristic;
}
}
// TODO: replace with pathable terrain data
public Map Map { get; set; }
public FixedVector Position { get; set; }
public FixedVector Destination { get; set; }
public List<FixedVector> Path { get; set; }
public void Compute()
{
var visited = new bool[(int)Map.Size.Width, (int)Map.Size.Height];
var pq = new PriorityQueue<Solution>(new SolutionComparer());
var bestFit = new Solution(new FixedVector((int)(Position.X + 0.5)
, (int)(Position.Y + 0.5))
, (Destination - Position).Length
);
pq.Enqueue(bestFit);
while (pq.Count > 0)
{
var path = pq.Dequeue(); // optimal, thus far
if (path.Heuristic < bestFit.Heuristic) // best approximation?
{
// if we starve all other paths we
// fallback to this, which should be the best
// approximation for reaching the goal
bestFit = path;
}
for (int i = 0; i < FixedVector.Adjacent8.Length; i++)
{
var u = path.LastPosition + FixedVector.Adjacent8[i];
if (Map.Size.Contains(u))
{
if (Map.IsPathable(u))
{
if (!visited[(int)u.X, (int)u.Y])
{
// heuristic (straight-line distance to the goal)
var h = (Destination - u).Length;
var solution = new Solution(u, h, path.Path);
if (h < 1)
{
Path = solution.Path;
return; // OK, done
}
else
{
// keep looking
pq.Enqueue(solution);
}
visited[(int)u.X, (int)u.Y] = true;
}
}
}
}
}
Path = bestFit.Path;
}
}

Categories