4using System.Text.RegularExpressions;
15 private readonly Dictionary<string, GeoNode> nodesById =
new Dictionary<string, GeoNode>();
16 private readonly
object synchObj =
new object();
17 private volatile int token = 0;
18 private int count = 0;
19 private GeoNode root =
null;
23 public GeoNode(T Object)
33 public GeoNode Parent;
66 public void Add(T Object)
70 this.AddLocked(Object);
74 private void AddLocked(T Object)
76 if (this.nodesById.TryGetValue(Object.GeoId, out GeoNode Node))
78 if (Node.Object.Equals(Object))
81 throw new ArgumentException(
"An object with the same ID already exists in the collection.", nameof(Object));
84 Node =
new GeoNode(Object);
85 this.nodesById[Object.GeoId] = Node;
86 this.AddNodeLocked(Node);
91 private void AddNodeLocked(GeoNode Node)
93 if (this.root is
null)
97 GeoPosition Location = Node.Object.Location;
98 GeoNode Loop = this.root;
102 if (Location.Latitude >= Loop.Object.Location.Latitude)
104 if (Location.Longitude >= Loop.Object.Location.Longitude)
129 if (Location.Longitude >= Loop.Object.Location.Longitude)
163 this.nodesById.Clear();
179 if (this.nodesById.TryGetValue(Object.GeoId, out GeoNode Node))
181 if (Node.Object.Equals(Object))
200 return this.nodesById.ContainsKey(GeoId);
214 if (!this.nodesById.TryGetValue(GeoId, out GeoNode Node))
221 Object = Node.Object;
232 public void CopyTo(T[] Destination,
int Offset)
234 if (Destination is
null)
235 throw new ArgumentNullException(nameof(Destination));
237 if (Offset < 0 || Offset >= Destination.Length)
238 throw new ArgumentOutOfRangeException(nameof(Offset),
"Offset must be a non-negative number less than the size of the destination array.");
242 if (Offset + this.nodesById.Count > Destination.Length)
243 throw new ArgumentException(
"Offset must allow all elements to be copied into the destination array.", nameof(Offset));
245 foreach (GeoNode Node
in this.nodesById.Values)
246 Destination[Offset++] = Node.Object;
258 T[] Result =
new T[this.count];
261 foreach (GeoNode Node
in this.nodesById.Values)
262 Result[i++] = Node.Object;
276 return new GeoObjectEnumerator(
this, this.nodesById.Values.GetEnumerator());
284 IEnumerator IEnumerable.GetEnumerator()
289 private class GeoObjectEnumerator : IEnumerator<T>
291 private readonly IEnumerator<GeoNode> e;
292 private readonly GeoPositionCollection<T> collection;
293 private readonly
int token;
295 public GeoObjectEnumerator(GeoPositionCollection<T> Collection, IEnumerator<GeoNode> Enumerator)
297 this.collection = Collection;
298 this.token = Collection.token;
306 lock (this.collection.synchObj)
308 if (this.token != this.collection.token)
309 throw new InvalidOperationException(
"Collection has been modified.");
311 return this.e.Current.Object;
316 object IEnumerator.Current => this.Current;
318 public void Dispose()
320 lock (this.collection.synchObj)
326 public bool MoveNext()
328 lock (this.collection.synchObj)
330 if (this.token != this.collection.token)
331 throw new InvalidOperationException(
"Collection has been modified.");
333 return this.e.MoveNext();
339 lock (this.collection.synchObj)
341 if (this.token != this.collection.token)
342 throw new InvalidOperationException(
"Collection has been modified.");
358 return this.RemoveLocked(Object);
369 return this.
Remove(GeoId, out
_);
378 public bool Remove(
string GeoId, out T Object)
382 if (!this.nodesById.TryGetValue(GeoId, out GeoNode Node) ||
383 !
this.RemoveLocked(Node))
390 Object = Node.Object;
396 private bool RemoveLocked(T Object)
398 if (!this.nodesById.TryGetValue(Object.GeoId, out GeoNode Node))
401 if (!Node.Object.Equals(Object))
404 return this.RemoveLocked(Node);
407 private bool RemoveLocked(GeoNode Node)
409 T Object = Node.Object;
411 this.nodesById.Remove(Object.GeoId);
415 GeoNode Parent = Node.Parent;
416 if (!(Parent is
null))
420 if (Parent.NW == Node)
422 else if (Parent.NE == Node)
424 else if (Parent.SW == Node)
426 else if (Parent.SE == Node)
432 if (!(Node.NW is
null))
434 Node.NW.Parent =
null;
435 this.AddNodeLocked(Node.NW);
439 if (!(Node.NE is
null))
441 Node.NE.Parent =
null;
442 this.AddNodeLocked(Node.NE);
446 if (!(Node.SW is
null))
448 Node.SW.Parent =
null;
449 this.AddNodeLocked(Node.SW);
453 if (!(Node.SE is
null))
455 Node.SE.Parent =
null;
456 this.AddNodeLocked(Node.SE);
475 if (!this.RemoveLocked(Object))
478 this.AddLocked(Object);
491 return this.
Find(Box, 0,
int.MaxValue,
null,
null);
502 return this.
Find(Box, 0, MaxCount,
null,
null);
514 return this.
Find(Box, Offset, MaxCount,
null,
null);
526 return this.
Find(Box, 0,
int.MaxValue, GeoIdPattern,
null);
539 return this.
Find(Box, 0, MaxCount, GeoIdPattern,
null);
553 return this.
Find(Box, Offset, MaxCount, GeoIdPattern,
null);
568 Predicate<T> CustomFilter)
571 throw new ArgumentOutOfRangeException(nameof(Offset),
"Offset must be a non-negative number.");
574 return Array.Empty<T>();
585 if (this.root is
null)
586 return Array.Empty<T>();
590 while (Queue.HasFirstItem)
598 if (Pos.NorthOf(Box))
601 if (Pos.SouthOf(Box))
613 Pos.AltitudeCheck(Box) &&
614 (GeoIdPattern is
null || Obj.GeoId.GeoIdPatternCheck(GeoIdPattern)) &&
615 (CustomFilter is
null || CustomFilter(Obj)))
625 return Result.ToArray();
631 if ((i & 1) != 0 && !(Loop.NW is
null))
634 if ((i & 2) != 0 && !(Loop.NE is
null))
637 if ((i & 4) != 0 && !(Loop.SW is
null))
640 if ((i & 8) != 0 && !(Loop.SE is
null))
646 return Result?.ToArray() ?? Array.Empty<T>();
A chunked list is a linked list of chunks of objects of type T .
T RemoveFirst()
Removes the first item in the collection.
In-memory thread-safe geo-spatial collection of points (positions).
T[] Find(IGeoBoundingBox Box, int Offset, int MaxCount)
Finds items that reside inside a geo-spatial bounding box.
bool Contains(string GeoId)
Checks if the collection contains a geo-spatial object reference with a given ID.
T[] Find(IGeoBoundingBox Box, int Offset, int MaxCount, Regex GeoIdPattern)
Finds items that reside inside a geo-spatial bounding box.
void Clear()
Clears the collection.
T[] Find(IGeoBoundingBox Box, int MaxCount, Regex GeoIdPattern)
Finds items that reside inside a geo-spatial bounding box.
T[] Find(IGeoBoundingBox Box, Regex GeoIdPattern)
Finds items that reside inside a geo-spatial bounding box.
bool Remove(string GeoId)
Removes an object from the collection, given its ID.
T[] Find(IGeoBoundingBox Box, int Offset, int MaxCount, Regex GeoIdPattern, Predicate< T > CustomFilter)
Finds items that reside inside a geo-spatial bounding box.
IEnumerator< T > GetEnumerator()
Returns an enumerator for all the elements in the collection.
int Count
Number of items in collection
void Add(T Object)
Adds a geo-spatial object reference.
T[] Find(IGeoBoundingBox Box)
Finds items that reside inside a geo-spatial bounding box.
void CopyTo(T[] Destination, int Offset)
Copies the contents of the collection to a destination array.
bool Contains(T Object)
Checks if the collection contains a geo-spatial object reference.
GeoPositionCollection()
In-memory thread-safe geo-spatial collection of points (positions).
bool IsReadOnly
If collection is read-only.
T[] Find(IGeoBoundingBox Box, int MaxCount)
Finds items that reside inside a geo-spatial bounding box.
bool Remove(T Object)
Removes an object from the collection.
bool TryGetObject(string GeoId, out T Object)
Tries to get a geo-spatial object reference, given its ID.
bool Moved(T Object)
Each time an object has moved, the collection must be notified. This is done by calling this method....
T[] ToArray()
Returns the elements of the collection as an array.
bool Remove(string GeoId, out T Object)
Removes an object from the collection, given its ID.
Contains information about a position in a geo-spatial coordinate system.
Interface for a geo-spatial bounding box using the Mercator Projection.
Reference to an object with a geo-spatial location