/// <summary> /// Construct the default reference surface name /// e.g. if the parent design file is bob.ttm, the offset 1.5 meters and the user units meters then the reference surface name is "bob +1.5m" /// </summary> /// <param name="prefProxy"></param> /// <param name="offset"></param> /// <param name="parentName"></param> /// <returns></returns> private async Task <string> DefaultReferenceSurfaceName(IPreferenceProxy prefProxy, double offset, string parentName) { const double imperialFeetToMetres = 0.3048; const double usFeetToMetres = 0.304800609601; var displayOffset = offset; var unitsString = string.Empty; var userPreferences = await prefProxy.GetUserPreferences(UserId, Request.Headers.GetCustomHeaders()); switch (userPreferences.Units.UnitsType()) { case UnitsTypeEnum.Metric: displayOffset = offset; unitsString = "m"; break; case UnitsTypeEnum.Imperial: displayOffset = offset / imperialFeetToMetres; unitsString = "ft"; break; case UnitsTypeEnum.US: displayOffset = offset / usFeetToMetres; unitsString = "ft"; break; } var sign = offset > 0 ? "+" : "-"; displayOffset = Math.Abs(displayOffset); return($"{parentName} {sign}{displayOffset:F3}{unitsString}"); }
/// <summary> /// Get user preferences /// </summary> private async Task <UserPreferenceData> GetUserPreferences() { var userPreferences = await prefProxy.GetUserPreferences(GetUserId(), CustomHeaders); if (userPreferences == null) { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, "Failed to retrieve preferences for current user")); } return(userPreferences); }
public async Task <FileResult> GetMapTileDataTtm( [FromQuery] Guid projectUid, [FromQuery] Guid?filterUid, [FromQuery] Guid?designUid, [FromQuery] DisplayMode mode, [FromServices] IPreferenceProxy prefProxy, [FromServices] ITRexCompactionDataProxy tRexCompactionDataProxy, #if RAPTOR [FromServices] IASNodeClient raptorClient, #endif [FromServices] IProductionDataRequestFactory requestFactory) { const double SURFACE_EXPORT_TOLERANCE = 0.05; const byte COORDS_ARRAY_LENGTH = 3; var tins = new List <TrimbleTINModel>(); var projectTask = ((RaptorPrincipal)User).GetProject(projectUid); var projectSettings = GetProjectSettingsTargets(projectUid); var userPreferences = prefProxy.GetUserPreferences(GetUserId(), CustomHeaders); var filter = GetCompactionFilter(projectUid, filterUid); var designTask = GetAndValidateDesignDescriptor(projectUid, designUid); if (userPreferences == null) { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, "Failed to retrieve preferences for current user")); } await Task.WhenAll(projectTask, projectSettings, userPreferences, designTask); var project = projectTask.Result; var design = designTask.Result; // Get the terrain mesh var exportRequest = requestFactory.Create <ExportRequestHelper>(r => r .ProjectUid(projectUid) .ProjectId(project.ShortRaptorProjectId) .Headers(CustomHeaders) .ProjectSettings(projectSettings.Result) .Filter(filter.Result)) .SetUserPreferences(userPreferences.Result) #if RAPTOR .SetRaptorClient(raptorClient) #endif .SetProjectDescriptor(project) .CreateExportRequest( null, //startUtc, null, //endUtc, CoordType.LatLon, ExportTypes.SurfaceExport, "test.zip", true, false, OutputTypes.VedaAllPasses, string.Empty, SURFACE_EXPORT_TOLERANCE); exportRequest.Validate(); // First get the export of production data from Raptor // comes in a zip file var result = await WithServiceExceptionTryExecuteAsync(() => RequestExecutorContainerFactory.Build <CompactionExportExecutor>(LoggerFactory, #if RAPTOR raptorClient, #endif configStore : ConfigStore, trexCompactionDataProxy : tRexCompactionDataProxy, customHeaders : CustomHeaders, userId : GetUserId(), fileImportProxy : FileImportProxy) .ProcessAsync(exportRequest)) as CompactionExportResult; if (result != null) { var zipStream = (await transferProxyFactory.NewProxy(TransferProxyType.Temporary).Download(result.DownloadLink)).FileStream; // If we didn't get a valid file, then we failed to read the ttm from raptor if (zipStream == null) { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, "Failed to retrieve data")); } using (var archive = new ZipArchive(zipStream)) { // The zip file will have exactly one file in it if (archive.Entries.Count == 1) { try { var tin = new TrimbleTINModel(); using (var stream = archive.Entries[0].Open() as DeflateStream) using (var ms = new MemoryStream()) { // Unzip the file, copy to memory as the TIN file needs the byte array, and stream stream.CopyTo(ms); ms.Seek(0, SeekOrigin.Begin); tin.LoadFromStream(ms, ms.GetBuffer()); tins.Add(tin); } } catch (TTMFileReadException e) { // Not valid, continue Log.LogWarning(e, "Failed to parse ttm in zip file"); } } } } // If we didn't get a valid file, then we failed to read the ttm from raptor if (tins.Count == 0) { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, "Failed to retrieve data")); } // If we have a design request, get the ttm and add it for parsing if (design != null) { //TODO: This used to get the file from TCC. This code to get from s3 needs testing. //Leave for now as this end point is not currently supported. // Retrieve the stored file from AWS var s3FullPath = $"{projectUid}/{design.File.FileName}"; var transferProxy = transferProxyFactory.NewProxy(TransferProxyType.Temporary); var fileResult = await transferProxy.Download(s3FullPath); if (fileResult?.FileStream != null) { using (var ms = new MemoryStream()) { fileResult.FileStream.CopyTo(ms); ms.Seek(0, SeekOrigin.Begin); var tin = new TrimbleTINModel(); tin.LoadFromStream(ms, ms.GetBuffer()); tins.Add(tin); } } } // Calculating the bounding box for the model (including design if supplied) var minEasting = tins.Select(t => t.Header.MinimumEasting).Min(); var maxEasting = tins.Select(t => t.Header.MaximumEasting).Max(); var minNorthing = tins.Select(t => t.Header.MinimumNorthing).Min(); var maxNorthing = tins.Select(t => t.Header.MaximumNorthing).Max(); var centerEasting = (maxEasting + minEasting) / 2.0; var centerNorthing = (maxNorthing + minNorthing) / 2.0; TwoDConversionCoordinate[] convertedCoordinates; #if RAPTOR if (UseTRexGateway("ENABLE_TREX_GATEWAY_TILES")) { #endif var conversionCoordinates = new [] { new TwoDConversionCoordinate(minEasting, minNorthing), new TwoDConversionCoordinate(maxEasting, maxNorthing), new TwoDConversionCoordinate(centerEasting, centerNorthing) } ; var conversionRequest = new CoordinateConversionRequest(projectUid, TwoDCoordinateConversionType.NorthEastToLatLon, conversionCoordinates); var conversionResult = await trexCompactionDataProxy.SendDataPostRequest <CoordinateConversionResult, CoordinateConversionRequest>(conversionRequest, "/coordinateconversion", CustomHeaders); if (conversionResult.Code != 0 || conversionResult.ConversionCoordinates.Length != COORDS_ARRAY_LENGTH) { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, "Failed to retrieve long lat for boundary")); } convertedCoordinates = conversionResult.ConversionCoordinates; #if RAPTOR } else { var points = new TWGS84FenceContainer { FencePoints = new[] { TWGS84Point.Point(minEasting, minNorthing), TWGS84Point.Point(maxEasting, maxNorthing), TWGS84Point.Point(centerEasting, centerNorthing), } }; // Convert the northing easting values to long lat values var res = raptorClient.GetGridCoordinates(project.LegacyProjectId, points, TCoordConversionType.ctNEEtoLLH, out var coordPointList); if (res != TCoordReturnCode.nercNoError) { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, "Failed to retrieve long lat for boundary")); } convertedCoordinates = coordPointList.Points.Coords.Select(c => new TwoDConversionCoordinate(c.X, c.Y)).ToArray(); } #endif // The values returned from Raptor/TRex are in rads, where we need degrees for the bbox var minLat = convertedCoordinates[0].Y * Coordinates.RADIANS_TO_DEGREES; var minLng = convertedCoordinates[0].X * Coordinates.RADIANS_TO_DEGREES; var maxLat = convertedCoordinates[1].Y * Coordinates.RADIANS_TO_DEGREES; var maxLng = convertedCoordinates[1].X * Coordinates.RADIANS_TO_DEGREES; var centerLat = convertedCoordinates[2].Y * Coordinates.RADIANS_TO_DEGREES; var centerLng = convertedCoordinates[2].X * Coordinates.RADIANS_TO_DEGREES; var bbox = $"{minLat},{minLng},{maxLat},{maxLng}"; var outputStream = new MemoryStream(); using (var zipArchive = new ZipArchive(outputStream, ZipArchiveMode.Create, true)) { var textureZipEntry = zipArchive.CreateEntry("texture.png"); using (var stream = textureZipEntry.Open()) { // Write the texture to the zip var textureFileStream = await GetTexture(projectUid, designUid, projectSettings.Result, filter.Result, mode, bbox); textureFileStream.FileStream.CopyTo(stream); } // Write the model to the zip var modelZipEntry = zipArchive.CreateEntry("model.obj"); using (var stream = modelZipEntry.Open()) { var modelFileStream = ConvertMultipleToObj(tins, centerEasting, centerNorthing); modelFileStream.FileStream.CopyTo(stream); } // Add some metadata to help with positioning of the model var metaDataEntry = zipArchive.CreateEntry("metadata.json"); using (var stream = metaDataEntry.Open()) { var metaData = new { Minimum = new { Lat = minLat, Lng = minLng }, Maximum = new { Lat = maxLat, Lng = maxLng }, Center = new { Lat = centerLat, Lng = centerLng }, HasDesign = design != null }; var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(metaData)); stream.Write(bytes, 0, bytes.Length); } } // Don't forget to seek back, or else the content length will be 0 outputStream.Seek(0, SeekOrigin.Begin); return(new FileStreamResult(outputStream, ContentTypeConstants.ApplicationZip)); }