public void SetMostRecentMeterValue(WorkingData meter, RepresentationValue value) { if (_meterToPreviousValue.ContainsKey(meter)) { _meterToPreviousValue[meter] = value; } else { _meterToPreviousValue.Add(meter, value); } }
public static void DescribeSpatialData(Catalog catalog, LoggedData loggedData) { //Coincident Operations DescribeCoincidentOperations(loggedData); Console.WriteLine(); Console.WriteLine("-----------------------"); Console.WriteLine("Spatial Data"); Console.WriteLine("-----------------------"); Console.WriteLine(); foreach (OperationData operationData in loggedData.OperationData) { //1. Create some collections for tracking high-level/header information on the implement devices and sensors. List <WorkingData> operationDataWorkingDatas = new List <WorkingData>(); Dictionary <int, int> useToDeviceConfigurationMapping = new Dictionary <int, int>(); //ADAPT models spatial data in multiple depths according to a virtual hierarchy describing the data as it relates to the physical layout of the implement. //In the 2020 implementation, // depth 0 refers to a single device representing the entire width of the implement and any sensors reporting data across the implement. // depth 1 generally refers to row-by-row data, where multiple DeviceElements representing individual rows contain one or more sensors // depth 2 is present on planting data where there are multiple varieties configured in specific parts of the implement. In that case //depth 1 contains a description of the varieties //depth 2 contains the row-level data for (int depth = 0; depth <= operationData.MaxDepth; depth++) //MaxDepth defines the maximum depth of data on an OperationData { //A DeviceElementUse is an instance of a DeviceElement/DeviceElementConfiguration within a specific OperationData. //It contains the collection of all data elements (WorkingData objects) reported on that DeviceElement during the Operation. IEnumerable <DeviceElementUse> deviceElementUses = operationData.GetDeviceElementUses(depth); foreach (DeviceElementUse deviceElementUse in deviceElementUses) { //Track the DeviceConfiguration that this DeviceElementUse relates to for reconciling data values to implement offsets for precise location useToDeviceConfigurationMapping.Add(deviceElementUse.Id.ReferenceId, deviceElementUse.DeviceConfigurationId); //A WorkingData is essentially a Sensor. It is the definition of some object that will report data per spatial point. //List all such sensors on this DeviceElementUse within this Operation IEnumerable <WorkingData> workingDatas = deviceElementUse.GetWorkingDatas(); //Track these in the comprehensive list operationDataWorkingDatas.AddRange(workingDatas); } } //2. Illustrate any multivariety data present here. //If an OperationData from the 2020 plugin contains a maxdepth of 2, then level 1 describes parts of the planter with specific varieties if (operationData.OperationType == OperationTypeEnum.SowingAndPlanting && operationData.MaxDepth == 2) { //------------------ //Split Planter data //------------------- Console.WriteLine($"OperationData {operationData.Id.ReferenceId} is planter data containing multiple varieties assigned to specific rows:"); IEnumerable <DeviceElementUse> levelOneDeviceElementUses = operationData.GetDeviceElementUses(1); foreach (DeviceElementUse use in levelOneDeviceElementUses) { //Retrieve the DeviceElementConfiguration object that matches the DeviceElementUse on this specific operation DeviceElementConfiguration deviceElementConfig = catalog.DeviceElementConfigurations.First(d => d.Id.ReferenceId == use.DeviceConfigurationId); //We've named the Level 1 device elements with the varieties in this situation Console.WriteLine(deviceElementConfig.Description); //All rows planting that variety will be children of this device element, //and the level 1 DeviceElementUse will have a WorkingData called "vrProductIndex" that will map to the variety } Console.WriteLine(); } else if (operationData.OperationType == OperationTypeEnum.SowingAndPlanting && operationData.MaxDepth == 1 && operationDataWorkingDatas.Select(w => w.Representation.Code).Any(c => c == "vrProductIndex")) { //------------------------------------------------------ //vSetSelect & mSet data (variable multi-hybrid planting) //------------------------------------------------------ Console.WriteLine($"OperationData {operationData.Id.ReferenceId} is planter data containing multiple varieties dynamically assigned to each row."); //Make a dictionary of product names Dictionary <int, string> productNames = new Dictionary <int, string>(); foreach (int productID in operationData.ProductIds) { List <DeviceElementUse> productDeviceElementUses = new List <DeviceElementUse>(); Product product = catalog.Products.First(p => p.Id.ReferenceId == productID); productNames.Add(productID, product.Description); } Console.WriteLine($"The following varieties are planted at various points in the field: {string.Join(", ", productNames.Values)}"); SpatialRecord firstPoint = operationData.GetSpatialRecords().First(); Console.WriteLine("For example, on the first point..."); //Examine the content of each DeviceElementUse at the row level with a product index working data foreach (DeviceElementUse deviceElementUse in operationData.GetDeviceElementUses(1)) //1 is the row level where OperationData.MaxDepth == 1. { foreach (WorkingData productIndexWorkingData in deviceElementUse.GetWorkingDatas().Where(w => w.Representation.Code == "vrProductIndex")) { NumericRepresentationValue productValue = firstPoint.GetMeterValue(productIndexWorkingData) as NumericRepresentationValue; int productIndex = (int)productValue.Value.Value; DeviceElementConfiguration deviceElementConfiguration = catalog.DeviceElementConfigurations.First(d => d.Id.ReferenceId == deviceElementUse.DeviceConfigurationId); Console.WriteLine($"{deviceElementConfiguration.Description} planted {productNames[productIndex]}."); } } Console.WriteLine(); } //3. Read the point-by-point data //With the data definition of the OperationData now in-hand, we can iterate the collection of physical points on the field to read the data. //Rather than writing out each data value to the screen, we will assemble them into collections to summarize after iterating all data Dictionary <WorkingData, List <object> > operationSpatialDataValues = new Dictionary <WorkingData, List <object> >(); Dictionary <WorkingData, string> numericDataUnitsOfMeasure = new Dictionary <WorkingData, string>(); //Similarly, we will track the geospatial envelope of the data to illustrate lat/lon data present double maxLat = Double.MinValue; double minLat = Double.MaxValue; double maxLon = Double.MinValue; double minLon = Double.MaxValue; Console.WriteLine("Reading point-by-point data..."); Console.WriteLine(); //IMPORTANT //To effectively manage memory usage, avoid invoking the iterator multiple times or iterate the entire list in a Linq expression. //The linq expressions below do not necessarily take this advice as the focus here is illustrative. foreach (SpatialRecord spatialRecord in operationData.GetSpatialRecords()) { //2020 data will always be in point form Point point = spatialRecord.Geometry as Point; //Track the lat/lon to illustrate the envelope of the dataset double latitude = point.Y; double longitude = point.X; if (latitude < minLat) { minLat = latitude; } if (latitude > maxLat) { maxLat = latitude; } if (longitude < minLon) { minLon = longitude; } if (longitude > maxLon) { maxLon = longitude; } //Examine the actual data on the points foreach (WorkingData operationWorkingData in operationDataWorkingDatas) { //Create a List for data values on the first encounter with this WorkingData if (!operationSpatialDataValues.ContainsKey(operationWorkingData)) { operationSpatialDataValues.Add(operationWorkingData, new List <object>()); } //--------------- //Representations //--------------- //ADAPT publishes standard representations that often equate to ISO11783-11 Data Dictionary Identifiers (DDIs) //These representations define a common type of agricultural measurement. //Where Precision Planting has implemented representations that are not published with ADAPT, they are marked as UserDefined //and the Name describes what each is. //-------------------- //RepresentationValues //-------------------- //A Representation Value is a complex type that allows the value to //be augmented the full representation, the unit of measure and other data. RepresentationValue representationValue = spatialRecord.GetMeterValue(operationWorkingData); //Values reported may be of type Numeric or Enumerated if (representationValue is NumericRepresentationValue) { NumericRepresentationValue numericRepresentationValue = representationValue as NumericRepresentationValue; operationSpatialDataValues[operationWorkingData].Add(numericRepresentationValue.Value.Value); //Value is a double //-------------------- //Units of Measure //-------------------- //Store the UOM on the first encounter if (!numericDataUnitsOfMeasure.ContainsKey(operationWorkingData)) { numericDataUnitsOfMeasure.Add(operationWorkingData, numericRepresentationValue.Value.UnitOfMeasure.Code); //ADAPT units of measure are documented in the Resources/UnitSystem.xml that is installed in any ADAPT project //They take the form of unitCode[postive exponent]unitcode[negative exponent] //Where the exponents allow for complex units. E.g.s, //lb = pounds //ac = acres //lb1ac-1 = pounds per acre //mm3m-2 = cubic millimeters per square meter } } else if (representationValue is EnumeratedValue) { EnumeratedValue enumeratedValue = representationValue as EnumeratedValue; operationSpatialDataValues[operationWorkingData].Add(enumeratedValue.Value.Value); //Value is a string } } } Console.WriteLine(); Console.WriteLine("-----------------------"); Console.WriteLine($"{Enum.GetName(typeof(OperationTypeEnum), operationData.OperationType)} data"); Console.WriteLine("-----------------------"); Console.WriteLine(); Console.WriteLine($"Data logged within envelope bounded by {minLat},{minLon} and {maxLat},{maxLon}."); Console.WriteLine(); foreach (WorkingData workingData in operationSpatialDataValues.Keys) { //We can obtain a reference to the part of the machine that logged the data via the DeviceConfigurationId property. DeviceElementConfiguration deviceElementConfig = catalog.DeviceElementConfigurations.FirstOrDefault(d => d.Id.ReferenceId == useToDeviceConfigurationMapping[workingData.DeviceElementUseId]); string deviceElementName = deviceElementConfig.Description; if (operationSpatialDataValues[workingData].Any()) { if (workingData is NumericWorkingData) { double max = operationSpatialDataValues[workingData].Cast <double>().Max(); double average = operationSpatialDataValues[workingData].Cast <double>().Average(); double min = operationSpatialDataValues[workingData].Cast <double>().Min(); string uom = numericDataUnitsOfMeasure[workingData]; Console.WriteLine($"Numeric Working Data {deviceElementConfig.Description}-{workingData.Representation.Description} had a minimum value of {min}, and average of {average} and a maximum value of {max} {uom}."); Console.WriteLine(); } else if (workingData is EnumeratedWorkingData) { EnumeratedWorkingData enumeratedWorkingData = workingData as EnumeratedWorkingData; EnumeratedRepresentation enumeratedRepresentation = enumeratedWorkingData.Representation as EnumeratedRepresentation; IEnumerable <string> enumerationValues = enumeratedRepresentation.EnumeratedMembers.Select(e => e.Value); foreach (string enumerationValue in enumerationValues) { int count = operationSpatialDataValues[workingData].Cast <string>().Count(v => v == enumerationValue); Console.WriteLine($"Enumerated Working Data {deviceElementConfig.Description}-{workingData.Representation.Description} had {count} values of {enumerationValue}."); Console.WriteLine(); } } } } } }
public void SetMeterValue(WorkingData workingData, RepresentationValue value) { _meterValues.Add(workingData.Id.ReferenceId, value); }
/// <summary> /// Draws a Red polygon if all values 0, a DarkMagenta polygon if all values the same non-zero value, or else themes Red/DarkOrange/Gold/YellowGreen/LawnGreen/LimeGreen/ForestGreen/DarkGreen. /// </summary> /// <param name="operation"></param> /// <param name="workingDataKey"></param> public void ThemeMap(string workingDataKey) { using (var graphics = _spatialViewer.CreateGraphics()) { graphics.Clear(System.Drawing.Color.White); _drawingUtil = new DrawingUtil(_spatialViewer.Width, _spatialViewer.Height, graphics); List <Point> projectedPoints = new List <Point>(); List <double> doubleValues = null; foreach (SpatialRecord record in _spatialRecords) { Point point = record.Geometry as Point; projectedPoints.Add(point.ToUtm()); if (_workingDataDictionary.ContainsKey(workingDataKey)) { WorkingData workingData = _workingDataDictionary[workingDataKey]; RepresentationValue repValue = record.GetMeterValue(workingData); if (repValue is NumericRepresentationValue) { NumericRepresentationValue numericValue = repValue as NumericRepresentationValue; if (doubleValues == null) { doubleValues = new List <double>(); } doubleValues.Add(numericValue.Value.Value); } else if (repValue is EnumeratedValue) { EnumeratedValue enumValue = repValue as EnumeratedValue; if (enumValue.Representation.Code == "dtRecordingStatus") { if (doubleValues == null) { doubleValues = new List <double>(); } doubleValues.Add(enumValue.Value.Value == "On" ? 1d : -1d); } } } } if (!projectedPoints.Any()) { return; } _drawingUtil.SetMinMax(projectedPoints); var screenPolygon = projectedPoints.Select(point => point.ToXy(_drawingUtil.MinX, _drawingUtil.MinY, _drawingUtil.GetDelta())).ToArray(); if (screenPolygon.All(p => !double.IsNaN(p.X) && !double.IsNaN(p.Y))) { if (doubleValues == null) { //WorkingData is not numeric graphics.DrawPolygon(DrawingUtil.B_Black, screenPolygon); } else { if (doubleValues.Max() == doubleValues.Min()) { //All values are the same if (doubleValues.Max() <= 0d) { //Zero values graphics.DrawPolygon(DrawingUtil.E_Red, screenPolygon); } else { //Non-zero values graphics.DrawPolygon(DrawingUtil.L_DarkGreen, screenPolygon); } } else { double max = doubleValues.Max(); double min = doubleValues.Min(); double average = doubleValues.Average(); List <double> removedZeroValues = doubleValues.Where(dv => dv != 0).ToList(); double avarageWithoutZeroes = removedZeroValues.Average(); if (average != avarageWithoutZeroes) { min = removedZeroValues.Min(); } int i = 0; double range = (max - min) / 7.0; double e7th = min; double f7th = e7th + range; double g7th = f7th + range; double h7th = g7th + range; double i7th = h7th + range; double j7th = i7th + range; double k7th = j7th + range; double l7th = max; foreach (System.Drawing.PointF f in screenPolygon) { double dbl = i < doubleValues.Count ? doubleValues[i] : 0d; //Values will be in same order as points System.Drawing.Pen pen = DrawingUtil.B_Black; if (dbl <= e7th) { pen = DrawingUtil.E_Red; } else if (dbl <= f7th) { pen = DrawingUtil.F_DarkOrange; } else if (dbl <= g7th) { pen = DrawingUtil.G_Gold; } else if (dbl <= h7th) { pen = DrawingUtil.H_YellowGreen; } else if (dbl <= i7th) { pen = DrawingUtil.I_LawnGreen; } else if (dbl <= j7th) { pen = DrawingUtil.J_LimeGreen; } else if (dbl <= k7th) { pen = DrawingUtil.K_ForestGreen; } else if (dbl <= l7th) { pen = DrawingUtil.L_DarkGreen; } graphics.DrawEllipse(pen, new System.Drawing.RectangleF(f.X, f.Y, 2, 2)); i++; } } } } } }
public void SetMeterValue(Meter meter, RepresentationValue value) { _meterValues.Add(meter.Id.ReferenceId, value); }