5using System.Text.RegularExpressions;
6using System.Threading.Tasks;
26 private const int defaultSubscriptionTtl = 60;
31 private const int maxSubscriptionTtl = 300;
36 private const int maxSubscriptionsPerClient = 5;
41 private const int maxPublicationsPerClient = 5;
46 private const int maxSearchResults = 100;
52 private readonly Dictionary<CaseInsensitiveString, ChunkedList<GeoSubscription>> geoSubscriptionsByBareJid =
53 new Dictionary<CaseInsensitiveString, ChunkedList<GeoSubscription>>();
54 private readonly Dictionary<CaseInsensitiveString, int> geoPublicationsByBareJid =
55 new Dictionary<CaseInsensitiveString, int>();
89 Server.ClientConnectionRemoved += this.ClientConnectionRemoved;
91 #region Neuro-Foundation V1 handlers
93 this.
RegisterIqSetHandler(
"subscribe", NamespaceGeoSpatialNeuroFoundationV1, this.SubscribeHandler,
true);
94 this.
RegisterIqSetHandler(
"unsubscribe", NamespaceGeoSpatialNeuroFoundationV1, this.UnsubscribeHandler,
false);
95 this.
RegisterIqSetHandler(
"publish", NamespaceGeoSpatialNeuroFoundationV1, this.PublishHandler,
false);
96 this.
RegisterIqSetHandler(
"delete", NamespaceGeoSpatialNeuroFoundationV1, this.DeleteHandler,
false);
97 this.
RegisterIqGetHandler(
"search", NamespaceGeoSpatialNeuroFoundationV1, this.SearchHandler,
false);
107 this.Server.ClientConnectionRemoved -= this.ClientConnectionRemoved;
109 #region Neuro-Foundation V1 handlers
111 this.
UnregisterIqSetHandler(
"subscribe", NamespaceGeoSpatialNeuroFoundationV1, this.SubscribeHandler,
true);
112 this.
UnregisterIqSetHandler(
"unsubscribe", NamespaceGeoSpatialNeuroFoundationV1, this.UnsubscribeHandler,
false);
129 internal async Task Load()
133 await
Database.Iterate(
new ReferenceLoader(
this),
"GeoObjects");
152 await this.
Delete(Object);
160 bool PrevEphemeral = Ref.EphemeralLocation;
161 PrevLocation = Ref.Location;
166 this.geoPositions.Moved(Ref);
168 if (Ref.EphemeralLocation)
170 if (!PrevEphemeral && !
string.IsNullOrEmpty(Ref.ObjectId))
178 if (!
string.IsNullOrEmpty(Ref.ObjectId))
187 this.AddReference(Ref,
false);
189 if (!Ref.EphemeralLocation)
195 Dictionary<string, GeoSubscription> PrevSubscriptions =
null;
198 if (!(PrevLocation is
null) && !PrevLocation.
Equals(Ref.Location))
200 Subscriptions = this.geoSubscriptions.Find(PrevLocation);
201 if (Subscriptions.Length > 0)
203 PrevSubscriptions =
new Dictionary<string, GeoSubscription>();
206 PrevSubscriptions[Subscription.
BoxId] = Subscription;
210 Subscriptions = this.geoSubscriptions.Find(Ref.Location);
212 if (Subscriptions.Length > 0)
218 if (PrevSubscriptions?.Remove(Subscription.
BoxId) ??
false)
230 if (!(PrevSubscriptions is
null))
250 lock (this.geoPublicationsByBareJid)
252 if (this.geoPublicationsByBareJid.TryGetValue(Ref.
Creator, out
int Count))
254 if (CheckLimit && Count >= maxPublicationsPerClient)
257 this.geoPublicationsByBareJid[Ref.
Creator] = Count + 1;
260 this.geoPublicationsByBareJid[Ref.
Creator] = 1;
264 this.geoPositions.Add(Ref);
269 private bool RemoveReference(
string GeoId)
271 return this.RemoveReference(GeoId, out
_);
274 private bool RemoveReference(PersistedGeoSpatialObjectReference Ref)
276 return this.RemoveReference(Ref.GeoId, out
_);
279 private bool RemoveReference(
string GeoId, out PersistedGeoSpatialObjectReference Ref)
281 if (!this.geoPositions.Remove(GeoId, out Ref))
284 if (Ref.Expires.HasValue)
292 lock (this.geoPublicationsByBareJid)
294 if (this.geoPublicationsByBareJid.TryGetValue(Ref.Creator, out
int Count))
297 this.geoPublicationsByBareJid[Ref.Creator] = Count - 1;
299 this.geoPublicationsByBareJid.Remove(Ref.Creator);
307 internal void Loaded(PersistedGeoSpatialObjectReference Ref)
309 if (!this.geoPositions.Contains(Ref.GeoId))
311 this.AddReference(Ref,
false);
313 if (Ref.Expires.HasValue)
338 if (!
string.IsNullOrEmpty(Ref.ObjectId))
341 GeoSubscription[] Subscriptions = this.geoSubscriptions.Find(Ref.Location);
343 if (Subscriptions.Length > 0)
360 private async Task SubscribeHandler(
object Sender,
IqEventArgs e)
367 double? MinAlt = e.
Query.HasAttribute(
"minAlt") ?
XML.
Attribute(e.
Query,
"minAlt",
double.NaN) : (
double?)
null;
368 double? MaxAlt = e.
Query.HasAttribute(
"maxAlt") ?
XML.
Attribute(e.
Query,
"maxAlt",
double.NaN) : (
double?)
null;
371 if (
double.IsNaN(MinLat) ||
372 double.IsNaN(MaxLat) ||
373 double.IsNaN(MinLon) ||
374 double.IsNaN(MaxLon) ||
375 (MinAlt.HasValue &&
double.IsNaN(MinAlt.Value)) ||
376 (MaxAlt.HasValue &&
double.IsNaN(MaxAlt.Value)))
382 if (MinLat < -90 || MinLat > 90 || MaxLat < -90 || MaxLat > 90)
384 await e.
IqErrorBadRequest(e.
To,
"Valid latitude values reside in the range [-90,90].",
"en");
388 if (MinLon < -180 || MinLon > 180 || MaxLon < -180 || MaxLon > 180)
390 await e.
IqErrorBadRequest(e.
To,
"Valid longitude values reside in the range [-180,180].",
"en");
396 await e.
IqErrorBadRequest(e.
To,
"Minimum latitude cannot be larger than the maximum latitude.",
"en");
400 if (MinAlt.HasValue && MaxAlt.HasValue && MinAlt.Value > MaxAlt.Value)
402 await e.
IqErrorBadRequest(e.
To,
"Minimum altitude cannot be larger than the maximum altitude.",
"en");
412 if (Ttl > maxSubscriptionTtl)
413 Ttl = maxSubscriptionTtl;
415 Dictionary<string, PersistedGeoSpatialObjectReference> PrevReferences =
null;
416 PersistedGeoSpatialObjectReference[] References;
417 GeoSubscription Subscription;
419 if (!
string.IsNullOrEmpty(Id))
421 if (!this.geoSubscriptions.TryGetBox(Id, out Subscription))
433 References = this.geoPositions.Find(Subscription);
434 PrevReferences =
new Dictionary<string, PersistedGeoSpatialObjectReference>();
436 foreach (PersistedGeoSpatialObjectReference Obj
in References)
437 PrevReferences[Obj.GeoId] = Obj;
439 Subscription.Min.Latitude = MinLat;
440 Subscription.Min.Longitude = MinLon;
441 Subscription.Min.Altitude = MinAlt;
442 Subscription.Max.Latitude = MaxLat;
443 Subscription.Max.Longitude = MaxLon;
444 Subscription.Max.Altitude = MaxAlt;
448 DateTime.Now.AddSeconds(Ttl), Subscription);
450 this.geoSubscriptions.Moved(Subscription);
454 Subscription =
new GeoSubscription(
460 lock (this.geoSubscriptionsByBareJid)
462 if (!this.geoSubscriptionsByBareJid.TryGetValue(e.
From.
BareJid,
466 this.geoSubscriptionsByBareJid[e.
From.
BareJid] = Subscriptions;
468 Subscriptions.Add(Subscription);
470 else if (Subscriptions.Count >= maxSubscriptionsPerClient)
473 Subscriptions.Add(Subscription);
476 if (Subscription is
null)
482 this.geoSubscriptions.Add(Subscription);
485 DateTime.Now.AddSeconds(Ttl), Subscription);
487 Subscription.Elapses = Elapses;
490 StringBuilder Xml =
new StringBuilder();
492 Xml.Append(
"<subscribed xmlns='");
493 Xml.Append(e.
Query.NamespaceURI);
494 Xml.Append(
"' ttl='");
497 if (
string.IsNullOrEmpty(Id))
499 Xml.Append(
"' id='");
500 Xml.Append(
XML.
Encode(Subscription.BoxId));
507 References = this.geoPositions.Find(Subscription);
509 foreach (PersistedGeoSpatialObjectReference Obj
in References)
511 if (PrevReferences is
null || !PrevReferences.Remove(Obj.GeoId))
512 await Subscription.ObjectAdded(Obj,
this);
515 if (!(PrevReferences is
null))
517 foreach (PersistedGeoSpatialObjectReference Obj
in PrevReferences.Values)
518 await Subscription.ObjectRemoved(Obj,
this);
530 lock (this.geoSubscriptionsByBareJid)
532 if (!this.geoSubscriptionsByBareJid.TryGetValue(Jid.
BareJid,
535 return Task.CompletedTask;
538 foreach (GeoSubscription Subscription
in Subscriptions)
540 if (Subscription.From.Address == e.
JID)
543 Removed.
Add(Subscription);
548 Kept.
Add(Subscription);
553 this.geoSubscriptionsByBareJid.Remove(Jid.
BareJid);
554 else if (!(Removed is
null))
555 this.geoSubscriptionsByBareJid[Jid.
BareJid] = Kept;
558 if (!(Removed is
null))
560 foreach (GeoSubscription Subscription
in Removed)
563 Subscription.Elapses = DateTime.MinValue;
565 this.geoSubscriptions.Remove(Subscription);
574 return Task.CompletedTask;
577 private async Task SubscriptionTimeout(
object State)
579 if (!(State is GeoSubscription Subscription))
582 this.geoSubscriptions.Remove(Subscription);
584 lock (this.geoSubscriptionsByBareJid)
586 if (this.geoSubscriptionsByBareJid.TryGetValue(Subscription.From.BareJid,
588 Subscriptions.Remove(Subscription) &&
589 Subscriptions.Count == 0)
591 this.geoSubscriptionsByBareJid.Remove(Subscription.From.BareJid);
595 await this.
Message(
string.Empty,
string.Empty, Subscription.From,
596 this.MainDomain,
string.Empty, UnsubscribedXml(Subscription.BoxId),
null);
599 private static string UnsubscribedXml(
string Id)
601 StringBuilder Xml =
new StringBuilder();
603 Xml.Append(
"<unsubscribed xmlns='");
605 Xml.Append(
"' id='");
609 return Xml.ToString();
612 private async Task UnsubscribeHandler(
object Sender,
IqEventArgs e)
616 if (
string.IsNullOrEmpty(Id))
622 if (!this.geoSubscriptions.TryGetBox(Id, out GeoSubscription Subscription))
635 Subscription.Elapses = DateTime.MinValue;
637 this.geoSubscriptions.Remove(Subscription);
642 private async Task PublishHandler(
object Sender,
IqEventArgs e)
649 DateTime? From = e.
Query.HasAttribute(
"from") ?
XML.
Attribute(e.
Query,
"from", DateTime.MinValue) : (DateTime?)
null;
650 DateTime? To = e.
Query.HasAttribute(
"to") ?
XML.
Attribute(e.
Query,
"to", DateTime.MinValue) : (DateTime?)
null;
652 if (
double.IsNaN(Lat) ||
654 (Alt.HasValue &&
double.IsNaN(Alt.Value)))
660 if (Lat < -90 || Lat > 90)
662 await e.
IqErrorBadRequest(e.
To,
"Valid latitude values reside in the range [-90,90].",
"en");
666 if (Lon < -180 || Lon > 180)
668 await e.
IqErrorBadRequest(e.
To,
"Valid longitude values reside in the range [-180,180].",
"en");
672 if (Ttl.HasValue && Ttl.Value <= 0)
680 if (From.Value == DateTime.MinValue)
686 if (From.Value.Kind != DateTimeKind.Utc)
695 if (To.Value == DateTime.MinValue)
701 if (To.Value.Kind != DateTimeKind.Utc)
708 int i = Id.IndexOf(
':');
711 string Scheme = Id[..i];
713 switch (Scheme.ToLower())
716 await e.
IqErrorForbidden(e.
To,
"Geo-spatial references to devices must be managed via the Thing Registry.",
"en");
720 string LegalId = Id[(i + 1)..];
723 if (Identity is
null)
725 await e.
IqErrorForbidden(e.
To,
"Geo-spatial references to legal identities only allowed for local identities.",
"en");
733 await e.
IqErrorForbidden(e.
To,
"You are only allowed to add geo-spatial references to your own legal identities.",
"en");
740 Dictionary<string, GeoSubscription> PrevSubscriptions =
null;
741 PersistedGeoSpatialObjectReference Ref =
null;
744 if (
string.IsNullOrEmpty(Id))
745 Id = Guid.NewGuid().ToString();
748 if (this.geoPositions.TryGetObject(Id, out Ref))
756 if (Ref.Expires.HasValue)
763 PrevSubscriptions ??=
new Dictionary<string, GeoSubscription>();
765 foreach (GeoSubscription Subscription
in this.geoSubscriptions.Find(Ref.Location))
766 PrevSubscriptions[Subscription.BoxId] = Subscription;
776 Ref =
new PersistedGeoSpatialObjectReference()
779 EphemeralLocation =
false,
783 ContentXml = e.
Query.InnerXml,
784 Created = DateTime.UtcNow,
791 Ref.EphemeralLocation =
false;
792 Ref.Expires =
Gateway.
ScheduleEvent(this.CheckItemExpired, DateTime.Now.AddSeconds(Ttl.Value), Id);
794 Ref.ContentXml = e.
Query.InnerXml;
795 Ref.Updated = DateTime.UtcNow;
804 Ref =
new PersistedGeoSpatialObjectReference()
807 EphemeralLocation =
true,
811 ContentXml = e.
Query.InnerXml,
812 Created = DateTime.UtcNow,
819 this.RemoveReference(Ref);
821 Ref.EphemeralLocation =
true;
824 Ref.ContentXml = e.
Query.InnerXml;
825 Ref.Updated = DateTime.UtcNow;
833 if (!this.AddReference(Ref,
true))
840 this.geoPositions.Moved(Ref);
842 if (Ref.EphemeralLocation)
844 if (!
string.IsNullOrEmpty(Ref.ObjectId))
850 else if (
string.IsNullOrEmpty(Ref.ObjectId))
855 StringBuilder Xml =
new StringBuilder();
857 Xml.Append(
"<published xmlns='");
859 Xml.Append(
"' id='");
864 Xml.Append(
"' ttl='");
865 Xml.Append(Ttl.Value);
872 GeoSubscription[] Subscriptions = this.geoSubscriptions.Find(Ref.Location);
874 foreach (GeoSubscription Subscription
in Subscriptions)
878 if (PrevSubscriptions?.Remove(Subscription.BoxId) ??
false)
879 await Subscription.ObjectUpdated(Ref,
this);
881 await Subscription.ObjectAdded(Ref,
this);
889 if ((PrevSubscriptions?.Count ?? 0) > 0)
891 foreach (GeoSubscription Subscription
in PrevSubscriptions.Values)
895 await Subscription.ObjectRemoved(Ref,
this);
905 private async Task CheckItemExpired(
object State)
907 if (!(State is
string GeoId))
910 if (!this.geoPositions.TryGetObject(GeoId, out PersistedGeoSpatialObjectReference Ref))
913 if (!Ref.Expires.HasValue || Ref.Expires.Value > DateTime.Now)
916 this.RemoveReference(GeoId);
918 if (!
string.IsNullOrEmpty(Ref.ObjectId))
921 foreach (GeoSubscription Subscription
in this.geoSubscriptions.Find(Ref.Location))
925 await Subscription.ObjectRemoved(Ref,
this);
934 private async Task DeleteHandler(
object Sender,
IqEventArgs e)
938 if (!this.geoPositions.TryGetObject(Id, out PersistedGeoSpatialObjectReference Ref))
950 this.RemoveReference(Id);
952 if (!
string.IsNullOrEmpty(Ref.ObjectId))
958 GeoSubscription[] Subscriptions = this.geoSubscriptions.Find(Ref.Location);
960 StringBuilder Xml =
new StringBuilder();
962 Xml.Append(
"<deleted xmlns='");
968 foreach (GeoSubscription Subscription
in Subscriptions)
972 await Subscription.ObjectRemoved(Ref,
this);
981 private async Task SearchHandler(
object Sender,
IqEventArgs e)
987 double? MinAlt = e.
Query.HasAttribute(
"minAlt") ?
XML.
Attribute(e.
Query,
"minAlt",
double.NaN) : (
double?)
null;
988 double? MaxAlt = e.
Query.HasAttribute(
"maxAlt") ?
XML.
Attribute(e.
Query,
"maxAlt",
double.NaN) : (
double?)
null;
993 Dictionary<string, string> Prefixes =
null;
996 if (
double.IsNaN(MinLat) ||
997 double.IsNaN(MaxLat) ||
998 double.IsNaN(MinLon) ||
999 double.IsNaN(MaxLon) ||
1000 (MinAlt.HasValue &&
double.IsNaN(MinAlt.Value)) ||
1001 (MaxAlt.HasValue &&
double.IsNaN(MaxAlt.Value)))
1007 if (MinLat < -90 || MinLat > 90 || MaxLat < -90 || MaxLat > 90)
1009 await e.
IqErrorBadRequest(e.
To,
"Valid latitude values reside in the range [-90,90].",
"en");
1013 if (MinLon < -180 || MinLon > 180 || MaxLon < -180 || MaxLon > 180)
1015 await e.
IqErrorBadRequest(e.
To,
"Valid longitude values reside in the range [-180,180].",
"en");
1019 if (MinLat > MaxLat)
1021 await e.
IqErrorBadRequest(e.
To,
"Minimum latitude cannot be larger than the maximum latitude.",
"en");
1025 if (MinAlt.HasValue && MaxAlt.HasValue && MinAlt.Value > MaxAlt.Value)
1027 await e.
IqErrorBadRequest(e.
To,
"Minimum altitude cannot be larger than the maximum altitude.",
"en");
1043 if (
string.IsNullOrEmpty(Pattern))
1044 ParsedPattern =
null;
1049 ParsedPattern =
new Regex(Pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.CultureInvariant);
1051 catch (Exception ex)
1053 await e.
IqErrorBadRequest(e.
To,
"Invalid regular expression pattern: " + ex.Message,
"en");
1058 if (
string.IsNullOrEmpty(Path))
1062 Prefixes =
new Dictionary<string, string>();
1064 foreach (XmlNode N
in e.
Query.ChildNodes)
1066 if (N is XmlElement E &&
1067 E.LocalName ==
"namespace" &&
1068 E.NamespaceURI == e.
Query.NamespaceURI)
1073 Prefixes[
Prefix] = Value;
1078 if (MaxCount > maxSearchResults)
1079 MaxCount = maxSearchResults;
1085 Predicate<PersistedGeoSpatialObjectReference> XPathFilter;
1087 if (Prefixes is
null)
1091 XPathFilter = (Obj) =>
1093 if (
string.IsNullOrEmpty(Obj.ContentXml))
1100 XmlNamespaceManager NamespaceManager =
new XmlNamespaceManager(Doc.NameTable);
1102 foreach (KeyValuePair<string, string> P
in Prefixes)
1104 if (!NamespaceManager.HasNamespace(P.Key))
1105 NamespaceManager.AddNamespace(P.Key, P.Value);
1108 return !(Doc.DocumentElement.SelectSingleNode(Path, NamespaceManager) is
null);
1120 PersistedGeoSpatialObjectReference[] Result = this.geoPositions.Find(Box,
1121 Offset, MaxCount, ParsedPattern, XPathFilter);
1123 StringBuilder Xml =
new StringBuilder();
1125 Xml.Append(
"<references xmlns='");
1126 Xml.Append(e.
Query.NamespaceURI);
1127 Xml.Append(
"' maxCount='");
1128 Xml.Append(MaxCount);
1131 foreach (PersistedGeoSpatialObjectReference Ref
in Result)
1134 Xml.Append(
"</references>");
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
static string Encode(string s)
Encodes a string for use in XML.
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Static class managing the runtime environment of the IoT Gateway.
static DateTime ScheduleEvent(Action< object > Callback, DateTime When, object State)
Schedules a one-time event.
static bool CancelScheduledEvent(DateTime When)
Cancels a scheduled event.
Client Connection event argument.
CaseInsensitiveString JID
JID of client connection.
Base class for components.
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
CaseInsensitiveString Subdomain
Subdomain name.
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
string Name
Component name.
async Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Message stanza.
XmppServer Server
XMPP Server.
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
Event arguments for IQ queries.
XmppAddress From
From address attribute
Task IqResult(string Xml, string From)
Returns a response to the current request.
Task IqErrorResourceConstraint(XmppAddress From, string ErrorText, string Language)
Returns a resource-constraint error.
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
XmlElement Query
Query element, if found, null otherwise.
XmppAddress To
To address attribute
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Contains information about one XMPP address.
CaseInsensitiveString BareJid
Bare JID
Represents a case-insensitive string.
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static async Task Update(object Object)
Updates an object in the database.
static async Task Delete(object Object)
Deletes an object in the database.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
A chunked list is a linked list of chunks of objects of type T .
void Add(T Item)
Adds an item to the collection.
Contains information about a geo-spatial bounding box using the Mercator Projection.
In-memory thread-safe geo-spatial collection of bounding boxes.
In-memory thread-safe geo-spatial collection of points (positions).
Contains information about a position in a geo-spatial coordinate system.
override bool Equals(object obj)
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Static class of application-wide semaphores that can be used to order access to editable objects.
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
static readonly string[] NamespacesGeoSpatial
Supported geo-spatial namespaces, ordered by preference
async Task Delete(string GeoId)
Deletes a geo-spatial object.
static bool IsNamespaceGeoSpatial(string Namespace)
If a namespace corresponds to a geo-spatial namespace.
async Task Publish(IGeoSpatialObject Object)
Publishes a new or updated geo-spatial object.
async Task Delete(IGeoSpatialObject Object)
Deletes a geo-spatial object.
const string NamespaceGeoSpatialNeuroFoundationV1
urn:nf:iot:geo:1.0
override bool SupportsAccounts
If the component supports accounts (true), or if the subdomain name is the only valid address.
GeoSpatialComponent(XmppServer Server, CaseInsensitiveString Subdomain, string Name)
Legal (digital identities, smart contracts) service component.
override void Dispose()
IDisposable.Dispose
Represents a Geo-spatial subscription.
Task ObjectUpdated(PersistedGeoSpatialObjectReference Reference, GeoSpatialComponent Sender)
Geo-spatial object has been updated within the area defined by the subscription.
Task ObjectAdded(PersistedGeoSpatialObjectReference Reference, GeoSpatialComponent Sender)
Geo-spatial object has been added within the area defined by the subscription.
string BoxId
The ID of the geo-spatial bounding box.
Task ObjectRemoved(PersistedGeoSpatialObjectReference Reference, GeoSpatialComponent Sender)
Geo-spatial object has been removed within the area defined by the subscription.
A persisted or persistable geo-spatial object reference.
CaseInsensitiveString Creator
Creator (Bare JID) of item.
static new async Task< PersistedGeoSpatialObjectReference > Create(IGeoSpatialObject Object)
Create a persisted or persistable geo-spatial object reference from a geo-spatial object.
Legal (digital identities, smart contracts) service component.
Interface for objects with a geo-spatial location
bool EphemeralLocation
If the location of the geo-spatial object is ephemeral.
Task< GeoPosition > GetLocation()
Gets the geo-spatial location of the object.
bool HasGeoLocation
If the object has a geo-spatial location.
string GeoId
The ID of the geo-spatial object.
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html