public CompactionSinglePatchResult[,] UnpackSubgrid(double cellSize, PatchSubgridOriginProtobufResult subgrid) { var subgridDimension = 32; var result = new CompactionSinglePatchResult[subgridDimension, subgridDimension]; var subGridIterator = 0; for (var x = 0; x < subgridDimension; x++) { for (var y = 0; y < subgridDimension; y++) { if (subgrid.ElevationOffsets[subGridIterator] > 0) { result[x, y] = new CompactionSinglePatchResult( Math.Round(((subgrid.SubgridOriginX + (cellSize / 2)) + (cellSize * x)), 5), Math.Round(((subgrid.SubgridOriginY + (cellSize / 2)) + (cellSize * y)), 5), // elevation offsets are in mm Math.Round((subgrid.ElevationOrigin + (subgrid.ElevationOffsets[subGridIterator] - 1) / 1000.0), 3), (new DateTime(1970, 1, 1, 0, 0, 0, 0)).AddSeconds(subgrid.TimeOrigin).AddSeconds(subgrid.TimeOffsets[subGridIterator]) ); } subGridIterator++; } } return(result); }
private PatchSubgridsRawResult ConvertPatchResult(Stream stream, bool includeTimeOffsets) { using (var reader = new BinaryReader(stream)) { // only set if patchId/patchNumber = 0 ( else -1 ) var totalPatchesRequired = reader.ReadInt32(); var subGridsInPatch = reader.ReadInt32(); // actual count in this patch var subGridsWithDataToReturn = subGridsInPatch; // sub-grids with data to be returned var cellSize = reader.ReadDouble(); var subgrids = new List <PatchSubgridOriginProtobufResult>(); for (var i = 0; i < subGridsInPatch; i++) { var subgridOriginX = reader.ReadDouble(); var subgridOriginY = reader.ReadDouble(); var isNull = reader.ReadBoolean(); if (isNull) { --subGridsWithDataToReturn; continue; } var elevationOrigin = reader.ReadSingle(); var elevationOffsetSizeInBytes = reader.ReadByte(); var timeOrigin = reader.ReadUInt32(); // UTC expressed as Unix time in seconds. var timeOffsetSizeInBytes = reader.ReadByte(); // Protobuf is limited to single dimension arrays so we cannot use the [32,32] layout used by other patch executors. const int arrayLength = 32 * 32; var elevationOffsets = new ushort[arrayLength]; var timeOffsets = new uint[arrayLength]; for (var j = 0; j < arrayLength; j++) { ushort elevationOffsetDelta; uint time = 0; switch (elevationOffsetSizeInBytes) { // Return raw elevation offset delta and the client will determine the elevation using the following equation: // float elevation = elevationOffsetDelta != 0xffff ? (float)(elevationOrigin + (elevOffset / 1000.0)) : -100000; // Cells[] is in column major order i.e. 0,0, 0,1; 0,2 .. 0.31 1,0, 1,1, 1.2..1.31 // Also increment all non-null values by 1. The consumer will subtract 1 from all non zero values to determine the true elevation offset. // These efforts are to further help the variant length operations that Protobuf applies to the byte stream. case 1: { uint elevationOffset = reader.ReadByte(); elevationOffsetDelta = (ushort)(elevationOffset != 0xff ? elevationOffset + 0x1 : 0x0); break; } case 2: { uint elevationOffset = reader.ReadUInt16(); elevationOffsetDelta = (ushort)(elevationOffset != 0xffff ? elevationOffset + 0x1 : 0x0); break; } case 4: { uint elevationOffset = reader.ReadUInt32(); elevationOffsetDelta = (ushort)(elevationOffset != 0xffffffff ? elevationOffset + 0x1 : 0x0); break; } default: { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, $"Invalid elevation offset size, '{elevationOffsetSizeInBytes}'.")); } } // timeOrigin and time Unix seconds switch (timeOffsetSizeInBytes) { case 1: { var timeOffset = reader.ReadByte(); time = (uint)(timeOffset != 0xff ? timeOffset : 0x0); break; } case 2: { var timeOffset = reader.ReadUInt16(); time = (uint)(timeOffset != 0xffff ? timeOffset : 0x0); break; } case 4: { var timeOffset = reader.ReadUInt32(); time = timeOffset != 0xffffffff ? timeOffset : 0x0; break; } default: { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, $"Invalid time offset size, '{timeOffsetSizeInBytes}'.")); } } elevationOffsets[j] = elevationOffsetDelta; timeOffsets[j] = includeTimeOffsets ? time : uint.MaxValue; } subgrids.Add(PatchSubgridOriginProtobufResult.Create(Math.Round(subgridOriginX, 5), Math.Round(subgridOriginY, 5), elevationOrigin, includeTimeOffsets ? timeOrigin : uint.MaxValue, elevationOffsets, timeOffsets)); // test: var doubleArrayResult = (new CompactionSinglePatchPackedResult()).UnpackSubgrid(cellSize, subgrids[subgrids.Count - 1]); } log.LogDebug($"{nameof(ConvertPatchResult)} totalPatchesRequired: {totalPatchesRequired} subGridsInPatch: {subGridsInPatch} subGridsWithDataToReturn: {subGridsWithDataToReturn} subgridsCount: {subgrids.Count}"); return(PatchSubgridsRawResult.Create(cellSize, subgrids.ToArray())); } }