public ContourAnimationInfo getContourAnimations(ContourQuery contourQuery) { List <List <TrendingDataLocation> > frames = GetFramesFromHistorian(contourQuery); PiecewiseLinearFunction colorScale = GetColorScale(contourQuery); Func <double, double> colorFunc = colorScale; // The actual startDate is the timestamp of the // first frame after contourQuery.GetStartDate() DateTime startDate = contourQuery.GetStartDate(); int stepSize = contourQuery.StepSize; int startTimeOffset = (int)Math.Ceiling((startDate - startDate.Date).TotalMinutes / stepSize); startDate = startDate.Date.AddMinutes(startTimeOffset * stepSize); double latDif = frames.Min(frame => frame.Max(location => location.Latitude)) - frames.Min(frame => frame.Min(location => location.Latitude)); double lonDif = frames.Min(frame => frame.Max(location => location.Longitude)) - frames.Min(frame => frame.Min(location => location.Longitude)); double minLat = frames.Min(frame => frame.Min(location => location.Latitude)) - (latDif * 0.1D); double maxLat = frames.Min(frame => frame.Max(location => location.Latitude)) + (latDif * 0.1D); double minLng = frames.Min(frame => frame.Min(location => location.Longitude)) - (lonDif * 0.1D); double maxLng = frames.Min(frame => frame.Max(location => location.Longitude)) + (lonDif * 0.1D); GeoCoordinate topLeft = new GeoCoordinate(maxLat, minLng); GeoCoordinate bottomRight = new GeoCoordinate(minLat, maxLng); GSF.Drawing.Point topLeftPoint = s_crs.Translate(topLeft, contourQuery.Resolution); GSF.Drawing.Point bottomRightPoint = s_crs.Translate(bottomRight, contourQuery.Resolution); topLeftPoint = new GSF.Drawing.Point(Math.Floor(topLeftPoint.X), Math.Floor(topLeftPoint.Y)); bottomRightPoint = new GSF.Drawing.Point(Math.Ceiling(bottomRightPoint.X), Math.Ceiling(bottomRightPoint.Y)); topLeft = s_crs.Translate(topLeftPoint, contourQuery.Resolution); bottomRight = s_crs.Translate(bottomRightPoint, contourQuery.Resolution); int width = (int)(bottomRightPoint.X - topLeftPoint.X + 1); int height = (int)(bottomRightPoint.Y - topLeftPoint.Y + 1); int animationID; string timeZoneID = null; using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("INSERT INTO ContourAnimation(ColorScaleName, StartTime, EndTime, StepSize) VALUES({0}, {1}, {2}, {3})", contourQuery.ColorScaleName, contourQuery.GetStartDate(), contourQuery.GetEndDate(), contourQuery.StepSize); animationID = connection.ExecuteScalar <int>("SELECT @@IDENTITY"); if (contourQuery.IncludeWeather) { timeZoneID = connection.ExecuteScalar <string>("SELECT Value FROM Setting WHERE Name = 'XDATimeZone'"); } } GSF.Threading.CancellationToken cancellationToken = new GSF.Threading.CancellationToken(); s_cancellationTokens[animationID] = cancellationToken; ProgressCounter progressCounter = new ProgressCounter(frames.Count); s_progressCounters[animationID] = progressCounter; Action <int> createFrame = i => { List <TrendingDataLocation> frame = frames[i]; IDWFunc idwFunction = GetIDWFunction(contourQuery, frame); uint[] pixelData; if (contourQuery.IncludeWeather) { TimeZoneInfo tzInfo = !string.IsNullOrEmpty(timeZoneID) ? TimeZoneInfo.FindSystemTimeZoneById(timeZoneID) : TimeZoneInfo.Local; // Weather data is only available in 5-minute increments DateTime frameTime = TimeZoneInfo.ConvertTimeToUtc(startDate.AddMinutes(stepSize * i), tzInfo); double minutes = (frameTime - frameTime.Date).TotalMinutes; int weatherMinutes = (int)Math.Ceiling(minutes / 5) * 5; NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty); queryString["service"] = "WMS"; queryString["request"] = "GetMap"; queryString["layers"] = "nexrad-n0r-wmst"; queryString["format"] = "image/png"; queryString["transparent"] = "true"; queryString["version"] = "1.1.1"; queryString["time"] = frameTime.Date.AddMinutes(weatherMinutes).ToString("o"); queryString["height"] = height.ToString(); queryString["width"] = width.ToString(); queryString["srs"] = "EPSG:3857"; GSF.Drawing.Point topLeftProjected = s_crs.Projection.Project(topLeft); GSF.Drawing.Point bottomRightProjected = s_crs.Projection.Project(bottomRight); queryString["bbox"] = string.Join(",", topLeftProjected.X, bottomRightProjected.Y, bottomRightProjected.X, topLeftProjected.Y); string weatherURL = "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi?" + queryString.ToString(); using (WebClient client = new WebClient()) using (MemoryStream stream = new MemoryStream(client.DownloadData(weatherURL))) using (Bitmap bitmap = new Bitmap(stream)) { pixelData = bitmap.ToPixelData(); } } else { pixelData = new uint[width * height]; } if (cancellationToken.IsCancelled) { return; } for (int x = 0; x < width; x++) { if (cancellationToken.IsCancelled) { return; } for (int y = 0; y < height; y++) { if (cancellationToken.IsCancelled) { return; } if (pixelData[y * width + x] > 0) { continue; } GSF.Drawing.Point offsetPixel = new GSF.Drawing.Point(topLeftPoint.X + x, topLeftPoint.Y + y); GeoCoordinate pixelCoordinate = s_crs.Translate(offsetPixel, contourQuery.Resolution); double interpolatedValue = idwFunction(pixelCoordinate.Longitude, pixelCoordinate.Latitude); pixelData[y * width + x] = (uint)colorFunc(interpolatedValue); } } if (cancellationToken.IsCancelled) { return; } using (Bitmap bitmap = BitmapExtensions.FromPixelData(width, pixelData)) using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, ImageFormat.Png); using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("INSERT INTO ContourAnimationFrame VALUES({0}, {1}, {2})", animationID, i, stream.ToArray()); } } progressCounter.Increment(); }; Task.Run(() => { ICancellationToken token; ProgressCounter counter; Parallel.For(0, frames.Count, createFrame); s_cancellationTokens.TryRemove(animationID, out token); s_progressCounters.TryRemove(animationID, out counter); if (cancellationToken.IsCancelled) { using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("DELETE FROM ContourAnimationFrame WHERE ContourAnimationID = {0}", animationID); connection.ExecuteNonQuery("DELETE FROM ContourAnimation WHERE ID = {0}", animationID); } } }); s_cleanUpAnimationOperation.TryRunOnceAsync(); return(new ContourAnimationInfo() { AnimationID = animationID, ColorDomain = colorScale.Domain, ColorRange = colorScale.Range, MinLatitude = bottomRight.Latitude, MaxLatitude = topLeft.Latitude, MinLongitude = topLeft.Longitude, MaxLongitude = bottomRight.Longitude, Infos = frames.Select((frame, index) => new ContourInfo() { Locations = frame, URL = string.Format("./mapService.asmx/getContourAnimationFrame?animation={0}&frame={1}", animationID, index), Date = contourQuery.GetStartDate().AddMinutes(index * contourQuery.StepSize).ToString() }).ToList() }); }
public void getContourTile() { ContourQuery contourQuery = new ContourQuery() { StartDate = HttpContext.Current.Request.QueryString["StartDate"], EndDate = HttpContext.Current.Request.QueryString["EndDate"], ColorScaleName = HttpContext.Current.Request.QueryString["ColorScaleName"], DataType = HttpContext.Current.Request.QueryString["DataType"], UserName = HttpContext.Current.Request.QueryString["Username"], Meters = HttpContext.Current.Request.QueryString["Meters"], MeterIds = HttpContext.Current.Request.QueryString["MeterIds"] }; ContourTileData contourTileData = GetContourTileData(contourQuery); double minLat = contourTileData.MinLatitude; double maxLat = contourTileData.MaxLatitude; double minLng = contourTileData.MinLongitude; double maxLng = contourTileData.MaxLongitude; CoordinateReferenceSystem crs = s_crs; IDWFunc idwFunction = contourTileData.IDWFunction; Func <double, double> colorFunction = contourTileData.ColorFunction; int tileX = Convert.ToInt32(HttpContext.Current.Request.QueryString["x"]); int tileY = Convert.ToInt32(HttpContext.Current.Request.QueryString["y"]); int zoom = Convert.ToInt32(HttpContext.Current.Request.QueryString["zoom"]); int tileSize = 256; int offsetX = tileSize * tileX; int offsetY = tileSize * tileY; uint[] pixelData = new uint[tileSize * tileSize]; for (int x = 0; x < tileSize; x++) { GSF.Drawing.Point validationPixel = new GSF.Drawing.Point(offsetX + x, 0.0D); GeoCoordinate validationCoordinate = crs.Translate(validationPixel, zoom); if (validationCoordinate.Longitude < minLng || validationCoordinate.Longitude > maxLng) { continue; } for (int y = 0; y < tileSize; y++) { GSF.Drawing.Point offsetPixel = new GSF.Drawing.Point(offsetX + x, offsetY + y); GeoCoordinate pixelCoordinate = crs.Translate(offsetPixel, zoom); if (pixelCoordinate.Latitude < minLat || pixelCoordinate.Latitude > maxLat) { continue; } double interpolatedValue = idwFunction(pixelCoordinate.Longitude, pixelCoordinate.Latitude); uint color = (uint)colorFunction(interpolatedValue); pixelData[y * tileSize + x] = color; } } using (Bitmap bitmap = BitmapExtensions.FromPixelData(256, pixelData)) { HttpContext.Current.Response.ContentType = "image/png"; HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename=tile{0}x{1}.png", tileX, tileY)); bitmap.Save(HttpContext.Current.Response.OutputStream, ImageFormat.Png); } }