Clirio Cloud
Eine webbasierte Sammlung von Tools zur Verwaltung von Arbeitsbereichen in Clirio View. Enthält ein Benutzer-Dashboard, Tools zur Erstellung von Arbeitsbereichen und Beobachtungen sowie interaktive Bing-Karten.
Übersicht
Clirio Cloud ist eine Sammlung von Tools, die im Laufe der Zeit gewachsen ist. Diese Tools werden den Nutzern der Clirio Suite auf einer mit Blazor entwickelten Website zur Verfügung gestellt. Clirio entwickelt Lösungen, die den Nutzern helfen, geodätische Arbeitsbereiche in 3D und XR zu inspizieren, zu dokumentieren und zusammenzuarbeiten.
<p>Eines der Hauptziele von Clirio Cloud ist es, die Upload- und Organisationsfunktionen der iOS-Apps von Clirio zu ergänzen, die als primäre Erfassungsplattform dienen. Während ein Benutzer mit den iOS-Apps von Clirio problemlos 3D-Photogrammetriescans erfassen und hochladen kann, möchte er möglicherweise auch andere 3D-Modelle hochladen, für die Clirio Cloud eine Schnittstelle bereitstellt. Weitere Werkzeuge für den Arbeitsbereich sind das Hochladen und Verwalten von benutzerdefinierten Basiskarten, das Hochladen von Bohrlöchern, das Einladen von Gästen, das Erstellen von Points of Interest und das Löschen von Arbeitsbereichen.</p>
<p>Darüber hinaus bietet Clirio Cloud den Nutzern ein Analyse-Dashboard sowie eine einfache Möglichkeit, auf die Clirio-Wissensdatenbank und Schulungsvideos zuzugreifen. Schließlich gibt es auch Tools zur Abonnement- und Kontoverwaltung.</p>
Entwicklung
Die Website wurde ursprünglich eingerichtet von Timothy Thibault unter der verwendung von Blazor, um ein Web-Framework mit C# als Programmiersprache auszuprobieren, was ein Vorteil wäre, wenn man bedenkt, dass wir C# auch für die Unity-Entwicklung verwenden. Nach der Einrichtung der Authentifizierung, der Abonnementverwaltung und der Auflistung der Arbeitsbereiche mit Metadaten wurde die Entwicklung größtenteils an mich übergeben.
<p></p>
<p>Nachdem ich alle Funktionen zur Verwaltung des Arbeitsbereichs, wie z. B. den Upload von 3D-Modellen und Benutzereinladungen, hinzugefügt hatte, begann ich mit der Arbeit an einer weiteren Reihe von Geschichten, die sich um das Dashboard drehten. Ich habe mich gepartnert mit<a href="https://www.linkedin.com/in/jordan-wischmann-32a4b380/" target="_blank">Jordan Wischmann</a>, der Mockups entwarf, die ich dann umsetzte.</p>
<p></p>
<h3>Lehren</h3>
<p></p>
Code Beispiel
Ein Beispiel für die Datenbindung zwischen dem Formular und dem Datenmodell für die POI Erstellen Seite.
<!-- CODE SNIPPET, NOT THE FULL FILE --><EditForm EditContext="@EC" class="w-100 flex flex-column align-items-start" OnSubmit="@HandleCreatePoiSubmit"> <DataAnnotationsValidator /> <div class="font-bold text-xl mt-3 mb-6">POI Details</div> <div class="form-group mb-3 w-100"> <label for="poiCreateDisplayName">Name:</label> <InputTextOnInput id="poiCreateDisplayName" name="poiCreateDisplayName" @bind-Value="poi.DisplayName" class="form-control" /> <ValidationMessage For="() => poi.DisplayName" /> </div> <div class="form-group mb-3 w-100"> <label for="poiCreateDescription">Description:</label> <InputTextArea id="poiCreateDescription" name="poiCreateDescription" @bind-Value="poi.Description" class="form-control" /> </div> <div class="flex flex-col w-100 lg:flex-row lg:gap-4"> <div class="form-group mb-3 w-100"> <label for="poiCreateLocationLatitude">Latitude:</label> <InputNumber id="poiCreateLocationLatitude" name="poiCreateLocationLatitude" @bind-Value="poi.Latitude" class="form-control" step="any" /> <ValidationMessage For="() => poi.Latitude" /> </div> <div class="form-group mb-3 w-100"> <label for="poiCreateLocationLongitude">Longitude:</label> <InputNumber id="poiCreateLocationLongitude" name="poiCreateLocationLongitude" @bind-Value="poi.Longitude" class="form-control" step="any" /> <ValidationMessage For="() => poi.Longitude" /> </div> <div class="form-group mb-3 w-100"> <label for="poiCreateLocationAltitude">Altitude:</label> <div class="input-group"> <InputNumber id="poiCreateLocationAltitude" name="poiCreateLocationAltitude" @bind-Value="poi.Altitude" class="form-control" step="any" /> <div class="input-group-append"> <span class="input-group-text">m</span> </div> </div> <ValidationMessage For="() => poi.Altitude" /> </div> </div> <!-- Map + Submit / Cancel btns --> <div id="map" style="@(!bingMapLoaded ? "height:0px;width:100%;position:relative;" : "height:500px;width:100%;position:relative;")" class="cursor-pointer mb-2"> </div> <div class="text-gray-500">Click on map to place cursor in your desired location or enter coordinates manually, including altitude (in meters). Click and hold to drag map. To switch map views, select icon in top right corner of map.</div> <div class="flex gap-4 mt-2"> <button @onclick="@(e => CancelButtonClicked())" class="btn text-white bg-red-500 hover:bg-red-600"> Cancel </button> <button type="submit" id="submitButton" class="@(EC.Validate() ? "btn text-white bg-clirio-500 hover:bg-clirio-600" : "btn text-white bg-gray-500 hover:bg-gray-600"))"> Create POI </button> </div></EditForm>@code { // SOME CODE OMITTED // [Parameter] public string? workspaceId { get; set; } [Parameter] public string? workspaceName { get; set; } private EditContext EC { get; set; } private static POICreate poi = new(); private HttpClient? workspaceClient; private bool processingRequest = false; protected override async Task OnInitializedAsync() { EC = new EditContext(poi); poi.WorkspaceId = workspaceId; poi.POIDateTime = DateTime.UtcNow; await base.OnInitializedAsync(); workspaceClient = HttpClientFactory.CreateClient("WorkspaceApi"); workspaceClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true }; LoadWorkspace(); } private async Task CreatePoi() { try { processingRequest = true; Console.WriteLine($"CreatePoi()"); string serialized = JsonConvert.SerializeObject(poi); await workspaceClient.PostAsJsonAsync<POICreate>("POI", poi); processingRequest = false; CancelButtonClicked(); } catch (Exception ex) { Console.WriteLine(ex); processingRequest = false; StateHasChanged(); return; } }}namespace Sitescan.Models{ public class POICreate { [Required] public string WorkspaceId { get; set; } [Required(ErrorMessage = "Name is required.")] [MinLength(1)] public string DisplayName { get; set; } public string Description { get; set; } [Required(ErrorMessage = "Altitude is required.")] public double? Altitude { get; set; } [Required(ErrorMessage = "Latitude is required.")] [Range(-90, 90, ErrorMessage = "Latitude is required, and must be between -90 and 90.")] public double? Latitude { get; set; } [Required(ErrorMessage = "Longitude is required.")] [Range(-180, 180, ErrorMessage = "Longitude is required, and must be between -180 and 180.")] public double? Longitude { get; set; } [Required] public DateTime POIDateTime { get; set; } }}