/// <summary> /// prepares gdal dataset /// </summary> protected void PrepareDriver() { //create mapserver object try { //make sure gdal is properly set up ConfigureGdal(); //extract the data source name if not already set //the driver constructor has a 'layerName' param that can be used to explicitly specify how a layer should be named if (string.IsNullOrEmpty(this.DataSourceName)) { this.DataSourceName = System.IO.Path.GetFileNameWithoutExtension(this.DataSource); } //open gdal dataset this.GdalDataset = Gdal.Open(DataSource, Access.GA_ReadOnly); //work out the raster bounds double[] adfGeoTransform = new double[6]; this.GdalDataset.GetGeoTransform(adfGeoTransform); double minx_pix = 0; double miny_pix = this.GdalDataset.RasterYSize; double maxx_pix = this.GdalDataset.RasterXSize; double maxy_pix = 0; this.RasterMinX = adfGeoTransform[0] + adfGeoTransform[1] * minx_pix + adfGeoTransform[2] * miny_pix; this.RasterMinY = adfGeoTransform[3] + adfGeoTransform[4] * minx_pix + adfGeoTransform[5] * miny_pix; this.RasterMaxX = adfGeoTransform[0] + adfGeoTransform[1] * maxx_pix + adfGeoTransform[2] * maxy_pix; this.RasterMaxY = adfGeoTransform[3] + adfGeoTransform[4] * maxx_pix + adfGeoTransform[5] * maxy_pix; this.RasterWidth = Math.Abs(this.RasterMaxX - this.RasterMinX); this.RasterHeight = Math.Abs(this.RasterMaxY - this.RasterMinY); //collect the roaster overview resolutions //assume the horisontal and vertical resolutions are the same (which of course may not be true in all cases, but will be ok with this ode usage) Band band = GdalDataset.GetRasterBand(1); RasterResolutions = new List <double>(); for (int i = 0; i < band.GetOverviewCount(); i++) { var ov = band.GetOverview(i); RasterResolutions.Add(this.RasterWidth / ov.XSize); } //driver seems to be ok so far this.DriverReady = true; } catch (Exception ex) { this.DriverReady = false; this.DriverExceptionMsg = ex.Message; } }
/// <summary> /// Renders the requested image /// NOTE: jp2 rendering using GDAL can become extremaly heavy on the cpu... /// so app pool cpu throttling may have to be enabled... /// in such case though of course all the stuff gonna be a bit rough /// </summary> /// <param name="bbox"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="imageEncoder"></param> /// <param name="backColor"></param> /// <returns></returns> protected byte[] Render(WmsBoundingBox bbox, int width, int height, ImageCodecInfo imageEncoder, Color backColor) { byte[] output = null; //prepare the output bitmap var outB = new Bitmap(width, height, PixelFormat.Format32bppArgb); //Make sure to make the bitmap transparent initially! -> Format32bppArgb //TODO sometime in the future... //Add some more sophisticated intersection testing - see hgis/issues/3 //do not render the map if the bbox is outside of the map's bbox //if any of the below tests yield true, bbox does not intersect with the raster bounds if ( !( bbox.MaxX < RasterMinX || bbox.MinX > RasterMaxX || bbox.MinY > RasterMaxY || bbox.MaxY < RasterMinY ) ) { //since got here, bbox intersects with the raster bounds so need to read the data //work out which overview should be used - based on the requested resolution //the array of resolutions is sorted as it has been collected off the raster var requestedRes = bbox.Width / width; var minDiff = Math.Abs(RasterResolutions[0] - requestedRes); var overviewIdx = 0; for (int i = 1; i < RasterResolutions.Count; i++) { var currentDiff = Math.Abs(RasterResolutions[i] - requestedRes); if (currentDiff > minDiff) { break; } overviewIdx = i; } //get the overview - need to go through a band var ov = GdalDataset.GetRasterBand(1).GetOverview(overviewIdx); //get the resolution of the overview var ovres = RasterResolutions[overviewIdx]; //work out render area in map units and pixels double gdal_render_area_left = bbox.MinX < RasterMinX ? RasterMinX : bbox.MinX; double gdal_render_area_right = bbox.MaxX > RasterMaxX ? RasterMaxX : bbox.MaxX; double gdal_render_area_top = bbox.MaxY > RasterMaxY ? RasterMaxY : bbox.MaxY; double gdal_render_area_bottom = bbox.MinY < RasterMinY ? RasterMinY : bbox.MinY; int gdal_pix_left = (int)((gdal_render_area_left - RasterMinX) / ovres); int gdal_pix_top = (int)((RasterMaxY - gdal_render_area_top) / ovres); int gdal_pix_width = (int)((gdal_render_area_right - gdal_render_area_left) / ovres); int gdal_pix_heigh = (int)((gdal_render_area_top - gdal_render_area_bottom) / ovres); //bitmap to extract gdal data var gdalB = new Bitmap( gdal_pix_width, gdal_pix_heigh, PixelFormat.Format32bppArgb ); //Make sure to make the bitmap transparent initially! -> Format32bppArgb ////example of extracting data for each channel and painting it pixel by pixel ////this is not the most efficient - see below ////----------- //var sw = System.Diagnostics.Stopwatch.StartNew(); //Band redBand = GdalDataset.GetRasterBand(1).GetOverview(overviewIdx); //Band greenBand = GdalDataset.GetRasterBand(2).GetOverview(overviewIdx); //Band blueBand = GdalDataset.GetRasterBand(3).GetOverview(overviewIdx); //Band alphaBand = GdalDataset.GetRasterBand(4).GetOverview(overviewIdx); //int[] R = new int[gdalB.Width * gdalB.Height]; //int[] G = new int[gdalB.Width * gdalB.Height]; //int[] B = new int[gdalB.Width * gdalB.Height]; //int[] A = new int[gdalB.Width * gdalB.Height]; //redBand.ReadRaster(gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, R, gdalB.Width, gdalB.Height, 4, 0); //greenBand.ReadRaster(gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, G, gdalB.Width, gdalB.Height, 4, 0); //blueBand.ReadRaster(gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, B, gdalB.Width, gdalB.Height, 4, 0); //alphaBand.ReadRaster(gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, A, gdalB.Width, gdalB.Height, 4, 0); //for (int i = 0; i < gdalB.Width; i++) //{ // for (int j = 0; j < gdalB.Height; j++) // { // Color newColor = Color.FromArgb( // A[i + j * gdalB.Width], // R[i + j * gdalB.Width], // G[i + j * gdalB.Width], // B[i + j * gdalB.Width] // ); // gdalB.SetPixel(i, j, newColor); // } //} //sw.Stop(); ////--------------- //lock the actual part of the bitmap that should be painted!!!! BitmapData bitmapData = gdalB.LockBits( new Rectangle(0, 0, gdalB.Width, gdalB.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb //Make sure to make the bitmap transparent initially! -> Format32bppArgb; otherwise bitmap will not be transparent even though apha channel is extracted! ); try { int stride = bitmapData.Stride; IntPtr buf = bitmapData.Scan0; //Notes //so far expecting rgb or argb image! //TODO //properly handle grayscale / palletted images ////example of readin raster off the dataset ////--------- //int[] bandmap = null; //if (GdalDataset.RasterCount == 4) //{ // //rgba -> bgra // bandmap = new int[] { 3, 2, 1, 4 }; //} //else if (GdalDataset.RasterCount == 3) //{ // //rgb -> bgr // bandmap = new int[] { 3, 2, 1 }; //} //else //{ // throw new Exception("So far hgis.Gdal wms driver so far does not support neither grayscale nor palleted rasters"); //} ////How to read a particular overview????? //GdalDataset.ReadRaster( // gdal_pix_left - gdal_margin_left, // gdal_pix_top - gdal_margin_top, // gdalB.Width, // gdalB.Height, // buf, // gdalB.Width, // gdalB.Height, // DataType.GDT_Byte, // GdalDataset.RasterCount, // bandmap, // 4, // stride, // 1 //); ///--------- //get the bands for an overview Band redBand = GdalDataset.GetRasterBand(1).GetOverview(overviewIdx); Band greenBand = GdalDataset.GetRasterBand(2).GetOverview(overviewIdx); Band blueBand = GdalDataset.GetRasterBand(3).GetOverview(overviewIdx); //read blue blueBand.ReadRaster( gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, buf, gdalB.Width, gdalB.Height, DataType.GDT_Byte, 4, stride ); //green greenBand.ReadRaster( gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, new IntPtr(buf.ToInt64() + 1), gdal_pix_width, gdal_pix_heigh, DataType.GDT_Byte, 4, stride ); //red redBand.ReadRaster( gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, new IntPtr(buf.ToInt64() + 2), gdalB.Width, gdalB.Height, DataType.GDT_Byte, 4, stride ); if (GdalDataset.RasterCount > 3) { Band alphaBand = GdalDataset.GetRasterBand(4).GetOverview(overviewIdx); alphaBand.ReadRaster( gdal_pix_left, gdal_pix_top, gdalB.Width, gdalB.Height, new IntPtr(buf.ToInt64() + 3), gdalB.Width, gdalB.Height, DataType.GDT_Byte, 4, stride ); } } finally { gdalB.UnlockBits(bitmapData); } //at this stage raster data should have been extracted, so now need to paint it over the output bitmap //work out the offset of the gdal raster double requestedResX = bbox.Width / width; double requestedResY = bbox.Height / height; //initially assume the image is completely within the raster bounds int pixl = 0, pixt = 0, pixw = width, pixh = height; //adjust the destination rect in a case bbox intersects with the raster bounds if (bbox.MinX < gdal_render_area_left) { pixl = (int)(Math.Abs(gdal_render_area_left - bbox.MinX) / requestedResX); pixw -= pixl; } if (bbox.MaxX > gdal_render_area_right) { pixw -= (int)(Math.Abs(bbox.MaxX - gdal_render_area_right) / requestedResX); } if (bbox.MaxY > gdal_render_area_top) { pixt = (int)(Math.Abs(bbox.MaxY - gdal_render_area_top) / requestedResY); pixh -= pixt; } if (bbox.MinY < gdal_render_area_bottom) { pixh -= (int)(Math.Abs(gdal_render_area_bottom - bbox.MinY) / requestedResY); } //paint the gdal raster on the output bitmap using (Graphics g = Graphics.FromImage(outB)) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; //make sure the edge pixels are not taken into account when interpolating //for details check out http://www.codeproject.com/Articles/11143/Image-Resizing-outperform-GDI using (ImageAttributes wrapMode = new ImageAttributes()) { wrapMode.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY); //next the rendered bitmap g.DrawImage( gdalB, new Rectangle(pixl, pixt, pixw, pixh), //dest rect 0, 0, gdalB.Width, gdalB.Height, //source rect GraphicsUnit.Pixel, wrapMode ); } } } //if transparent, just use the out bitmap as it is already transparent //if not transparent, but back color set, then paint the pitmap with that color if (backColor != Color.Transparent) { //TODO var backB = new Bitmap(width, height, PixelFormat.Format24bppRgb); //use 24 bit bitmap as it is not transparent //paint the base image with the required back colour using (Graphics g = Graphics.FromImage(backB)) { //first paint the background color g.FillRectangle( new SolidBrush(backColor), 0, 0, backB.Width, backB.Height ); //next the rendered bitmap g.DrawImage( outB, new Rectangle(0, 0, backB.Width, backB.Height), //dest rect new Rectangle(0, 0, outB.Width, outB.Height), //source rect GraphicsUnit.Pixel ); } outB = backB; } using (var ms = new MemoryStream()) { outB.Save(ms, imageEncoder, null); output = ms.ToArray(); } return(output); }