private async Task UpdateGraph(object sender, EventArgs e) { try { //fetch latest UV data from service and then update model int?LocIndex = this.arpansaModel.LocIndexValue; if (LocIndex == null) { //no location has been selected. Nothing to paint. return; } MeasuredLocation curLocation = this.arpansaModel.MeasureLocations[LocIndex.Value]; CurrentLocName.Text = curLocation.SiteName; ArpansaUVResponse arpansaUV = await this.arpansaService.GetUVData(curLocation.SiteLongitude.Value, curLocation.SiteLatitude.Value); List <UVIndex> uvIndexes = arpansaService.GenerateUVIndexs(); //update model ArpansaUVData graphData = new ArpansaUVData(arpansaUV); graphData.ReferenceUVs = uvIndexes; this.arpansaModel.ArpansaUVData = graphData; } catch (Exception e1) { await DisplayAlert("Error", e1.Message, "Ok"); } }
//util method to find the closes datapoint for a given value on the x axis private GraphData GetClosestDataPointAtTime(TimeSpan targetTime) { ArpansaUVData arpansaData = this.arpansaModel.ArpansaUVData; if (arpansaData == null) { //no data to sort through return(null); } GraphData[] uvData = arpansaData.GraphData; GraphData closestPoint = null; double closestDiff = double.PositiveInfinity; for (int i = 0; i < uvData.Length; i++) { TimeSpan uvTime = DateTimeStringToTime(uvData[i].Date); double diff = Math.Abs(targetTime.TotalMinutes - uvTime.TotalMinutes); if (diff < closestDiff) { closestPoint = uvData[i]; closestDiff = diff; } } return(closestPoint); }
private void DrawHorizontalDashes() { SKCanvas graphCavnas = this.graphSurface.Canvas; ArpansaUVData arpansaData = this.arpansaModel.ArpansaUVData; if (arpansaData?.ReferenceUVs == null) { //need reference UV levels return; } //for each refernce UV level for (int i = 0; i < arpansaData.ReferenceUVs.Count; i++) { float uvValue = arpansaData.ReferenceUVs[i].LowerValue; float unitsHigh = uvValue * this.unitsPerUVLevel; float RefLineYPos = unitsHigh; SKPaint refLinePaint = new SKPaint { Color = arpansaData.ReferenceUVs[i].Colour.WithAlpha(0.5f), Style = SKPaintStyle.Stroke, StrokeWidth = 1f, PathEffect = SKPathEffect.CreateDash(new float[] { 6f, 3f }, 0), IsAntialias = true }; // draw line graphCavnas.DrawLine(0, RefLineYPos, this.gridWidth, RefLineYPos, refLinePaint); // write text above each reference point SKPaint refTextPaint = new SKPaint { Color = arpansaData.ReferenceUVs[i].Colour, TextAlign = SKTextAlign.Right, IsAntialias = true, TextSize = 15f }; float padding = 10f; float textPosX = this.gridWidth - padding; float textPosY = RefLineYPos + padding; DrawTextOnGraph(arpansaData.ReferenceUVs[i].DetailText, textPosX, textPosY, refTextPaint); } }
private void DrawCurrentUV() { SKCanvas graphCanvas = this.graphSurface.Canvas; ArpansaUVData arpansaData = this.arpansaModel.ArpansaUVData; if (arpansaData?.GraphData == null || arpansaData.GraphData.Length <= 0) { //need graph data return; } if (arpansaData?.CurrentUVIndex == null || arpansaData?.CurrentDateTime == null) { //need current data return; } if (arpansaData?.ReferenceUVs == null) { //need reference UV levels return; } //get latest datapoint float curUVLevel = float.Parse(arpansaData.CurrentUVIndex); TimeSpan lastMeasurementTime = DateTimeStringToTime(arpansaData.CurrentDateTime); float currentX = (float)(lastMeasurementTime.TotalMinutes - this.minX.TotalMinutes) * this.unitsPerMinute; float currentY = curUVLevel * this.unitsPerUVLevel; string uvString = curUVLevel.ToString("F1"); //create a paint based of the current reading //paint colour will be set to the current UV reference colour UVIndex curUVIndex = null; for (int i = 0; i < arpansaData.ReferenceUVs.Count; i++) { if (curUVLevel >= arpansaData.ReferenceUVs[i].LowerValue) { curUVIndex = arpansaData.ReferenceUVs[i]; } } SKPaint curColourAreaPaint = new SKPaint { Color = curUVIndex.Colour.WithAlpha(0.8f), Style = SKPaintStyle.Fill, IsAntialias = true }; SKPaint curColourLinePaint = new SKPaint { Color = curUVIndex.Colour.WithAlpha(0.4f), Style = SKPaintStyle.Stroke, IsAntialias = true, StrokeWidth = 2f }; //draw a nice circle on the endpoint float pointRadius = 3.6f; graphCanvas.DrawCircle(currentX, currentY, pointRadius, new SKPaint { Color = SKColors.White }); graphCanvas.DrawCircle(currentX, currentY, pointRadius, curColourLinePaint); //write the current text a little up and to the right of the current point float textX = currentX + 12f; float textY = currentY + 12f; SKRect uvRectFrame = new SKRect(); this.currentUVTextPaint.MeasureText(uvString, ref uvRectFrame); //do a check to see if current UV reading will appear off the screen float framePadding = 5f; if (textX + uvRectFrame.Width + framePadding >= this.gridWidth) { //being cut off on x axis GraphData closestPoint = this.GetClosestDataPointAtTime(this.maxX); textX = this.gridWidth - uvRectFrame.Width - framePadding - 4f; } if (textY + uvRectFrame.Height + framePadding >= this.gridHeight) { //being cut off on y axis textY = this.gridHeight - uvRectFrame.Height - framePadding - 4f; } uvRectFrame.Offset(textX, textY + uvRectFrame.Height); uvRectFrame.Inflate(framePadding, framePadding); graphCanvas.DrawRoundRect(uvRectFrame, 3f, 3f, curColourAreaPaint); this.DrawTextOnGraph(uvString, textX, textY, this.currentUVTextPaint); }
private void DrawUVPlots() { ArpansaUVData arpansaData = this.arpansaModel.ArpansaUVData; if (arpansaData?.GraphData == null || arpansaData.GraphData.Length <= 0) { //no graph data return; } if (arpansaData?.ReferenceUVs == null || arpansaData.ReferenceUVs.Count <= 0) { //need reference points for background colour return; } GraphData[] graphData = arpansaData.GraphData; SKCanvas graphCanvas = this.graphSurface.Canvas; SKPath approximatePath = null; SKPath measuredPath = null; //collect data for the UV plot lines TimeSpan timeValue = DateTimeStringToTime(graphData[0].Date); //approximate UV float?startForecast = graphData[0].Forecast; if (startForecast != null) { float approxX = (float)(timeValue.TotalMinutes - this.minX.TotalMinutes) * this.unitsPerMinute; float approxY = (float)startForecast * this.unitsPerUVLevel; approximatePath = new SKPath(); approximatePath.MoveTo(approxX, approxY); } //measured UV float?startMeasured = graphData[0].Measured; if (startMeasured != null) { float measuredX = (float)(timeValue.TotalMinutes - this.minX.TotalMinutes) * this.unitsPerMinute; float measuredY = (float)graphData[0].Measured * this.unitsPerUVLevel; measuredPath = new SKPath(); measuredPath.MoveTo(measuredX, measuredY); } //for each plot point for (int i = 1; i < graphData.Length; i++) { //get time TimeSpan curTimeValue = DateTimeStringToTime(graphData[i].Date); //approximate UV float?curApproxUV = graphData[i].Forecast; if (curApproxUV != null && approximatePath != null) { float curApproxX = (float)(curTimeValue.TotalMinutes - this.minX.TotalMinutes) * this.unitsPerMinute; float curApproxY = (float)curApproxUV * this.unitsPerUVLevel; approximatePath.LineTo(curApproxX, curApproxY); } //measured UV float?curMeasuredUV = graphData[i].Measured; if (curMeasuredUV != null && measuredPath != null) { float curMeasuredX = (float)(curTimeValue.TotalMinutes - this.minX.TotalMinutes) * this.unitsPerMinute; float curMeasuredY = (float)curMeasuredUV * this.unitsPerUVLevel; measuredPath.LineTo(curMeasuredX, curMeasuredY); } } //create an awesome rainbow shader SKPoint graphBottom = new SKPoint(0.0f, 0.0f); UVIndex highestUVRef = arpansaData.ReferenceUVs[arpansaData.ReferenceUVs.Count - 1]; float highestIVValue = highestUVRef.LowerValue; SKPoint highRefPoint = new SKPoint(0.0f, highestIVValue * this.unitsPerUVLevel); List <SKColor> colourList = new List <SKColor>(); List <float> posList = new List <float>(); foreach (var uvInfo in arpansaData.ReferenceUVs) { colourList.Add(uvInfo.Colour.WithAlpha(0.4f)); posList.Add(uvInfo.LowerValue / highestIVValue); } SKShader uvShader = SKShader.CreateLinearGradient(graphBottom, highRefPoint, colourList.ToArray(), posList.ToArray(), SKShaderTileMode.Clamp); this.measuredLinePaint.Shader = uvShader; ////draw approximate and measured plot lines if (approximatePath != null) { graphCanvas.DrawPath(approximatePath, this.predictedLinePaint); } if (measuredPath != null) { graphCanvas.DrawPath(measuredPath, this.measuredLinePaint); //now draw area under plot lines SKPoint firstMeasuredPoint = measuredPath.GetPoint(0); SKPoint lastMeasuredPoint = measuredPath.GetPoint(measuredPath.PointCount - 1); measuredPath.LineTo(lastMeasuredPoint.X, 0); measuredPath.LineTo(firstMeasuredPoint.X, 0); measuredPath.Close(); graphCanvas.DrawPath(measuredPath, this.measuredAreaPaint); } }