ThinkGeo.com    |     Documentation    |     Premium Support

Drawing to a vector image format

Hi,


I would like to be able to get a vector image (.net Metafile class) of a rendered map.  I know in V2 I used to be able to get the actual metafiles from the map, but I don't see an option like that currently.  I plan on inserting the vector image of them into various report generating componets (Active Reports for example) and I won't know the specific pixel size when I render the map and would like to be able to scale the image in the report engine without distortion like a bitmap would.


I've attempted to pass a metafile into a GdiPlusGeoCanvas without luck.


Do I need to implement my own GeoCanvas or is there something else I'm missing. 


Thanks


Brian



I have been able to modify our report generation process to set the map size to the actual size of the image needed, but rendering to bitmaps justs makes the final reports large in file size and blurry.  Increasing the pixel count of the bitmap by 2 to 3 times helps, but also ups the size considerably.


I've looked at trying to implement my own GeoCanvas that could write to a System.Drawing.Imaging.Metafile, but it looks like I would have to re-create a lot of the drawing logic that should already be in the GdiPlusGeoCanvas.  Metafile inherits from image so it should be able to be written to like any other .net Image class, but GdiPlusGeoCanvas.BeginDrawing throws an exception if you pass it a metafile.


I've also looked at the GeoImage class to see if I could provide a subclass that would be backed by a metafile, but I don't see a way to do that either.


Thanks for the help in advance.



Brian, 
  
 We provide many kinds of RasterLayer in currently version, such as EcwRasterLayer, GdiPlusRasterLayer etc. You can find samples in our HowDoI sample->Satellite Image->, 
  
 Let me know if you have more questions. 
  
 Thanks, 
 James

Hi James, 



I not looking to load a vector image as a layer in the map...I need to export drawn feature layers as a vector image for high quality printing.  Like your PDFGeoCanvas does, except I need more control over the final layout then I think I can get with the PDF canvas.  Unless I could pre-create a PDF and then be able to draw the map in a specific extent of the PDF page. 



Thanks



Brian, 
  
 I don’t know how you do it in MapSuite 2.0, if you can give more information that will be helpful. 
 For Pdf requirement, if you have a pre-create pdf document, and you want to draw map in specific extent of pdf page, I think you only need to export current map extent to an image with suitable size, our DesktopEdition provide API GetBitmap method in WinformsMap, you can try to use it. 
  
 Let me know if you have more questions. 
  
 Thanks, 
 James 


Hi James,


I found that old code where I got vectors before, it was actually in V1 of Map Suite.  Back then each layer would have a vector image property that I could get and then I would redraw each layer into a new Metafile.  I know MapSuite is a completely different code base now days, but I would really like to be able to get vector images of the layers still, they produce much nicer images for inserting in things like PDFs.  Right now I'm making bitmaps 4 times the size I need so when they are scaled back in a PDF they look nice. 


Its all GDI+ based commands so I would think there could be a way to do with the GDIPlusCanvas, hope this helps you understand what I'm looking for.


Thanks


Brian


 


// Create a Bitmap

Bitmap bitmap = new Bitmap( 1, 1, PixelFormat.Format24bppRgb );

// Wrap a Graphics around the Bitmap

Graphics gRef = Graphics.FromImage( bitmap );

// Get an HDC from the Graphics

IntPtr    hDC = gRef.GetHdc();

// Create a recordable Metafile

Metafile mf = new Metafile( hDC,EmfType.EmfOnly);

// Release the HDC

gRef.ReleaseHdc( hDC );

gRef.Dispose();

bitmap.Dispose();

// Wrap a Graphics around the Metafile so we can draw on it

Graphics g = Graphics.FromImage(mf);


if(ArialPhoto!=null)

 g.DrawImageUnscaled(ArialPhoto,0,0);

foreach(MapSuite.Layer layer in map1.Layers)

{

 if(layer.VectorImage!=null)

  g.DrawImageUnscaled(layer.VectorImage,0,0);

}

if(labeler.LabelImage!=null)

 g.DrawImageUnscaled(labeler.LabelImage,0,0);


g.Dispose();



 Brian,


I provide sample to show how to use GdiPluGeoCanvas to draw layer on a bitmap, but I am not sure if it's what your want.



            layer.Open();


            GdiPlusGeoCanvas canvas = new GdiPlusGeoCanvas();


            Bitmap vectorImage = new Bitmap(winformsMap1.Width, winformsMap1.Height);


            canvas.BeginDrawing(vectorImage, winformsMap1.CurrentExtent, winformsMap1.MapUnit);


            layer.Draw(canvas, new Collection<SimpleCandidate>());


            canvas.EndDrawing();


            layer.Close();


Please let me know if I misunderstanded.


Thanks,


James




Hi James,




That will draw to a bitmap, not a vector metafile


In my example before I created a new 1x1 pixel bitmap only to get a GDI graphics reference, to get the Hdc hangle in order to create a new metafile


A Metafile does inherit from the .net Image class, but if you pass it into the GdiPlusGeoCanvas it will throw an exception.


Try the code below.


Brian


 // Create a Bitmap

Bitmap bitmap = new Bitmap( 1, 1, PixelFormat.Format24bppRgb );

// Wrap a Graphics around the Bitmap

Graphics gRef = Graphics.FromImage( bitmap );

// Get an HDC from the Graphics

IntPtr hDC = gRef.GetHdc();

// Create a recordable Metafile

Metafile mf = new Metafile( hDC,EmfType.EmfOnly);

// Release the HDC

gRef.ReleaseHdc( hDC );

gRef.Dispose();

bitmap.Dispose();

 


layer.Open();


GdiPlusGeoCanvas canvas = new GdiPlusGeoCanvas();


Bitmap vectorImage = new Bitmap(winformsMap1.Width, winformsMap1.Height);


canvas.BeginDrawing(mf, winformsMap1.CurrentExtent, winformsMap1.MapUnit);


layer.Draw(canvas, new Collection<simplecandidate></simplecandidate>());


canvas.EndDrawing();


layer.Close();


 



 Brian,


Could you try to override DrawScreenImageWithoutScalingCore method in GdiPlusGeoCanvas? Here are the sample code you can refer to:



    public class CustomGdiPlusGeoCanvas : GdiPlusGeoCanvas


    {


        Bitmap canvasBitmap;


 


        protected override void BeginDrawingCore(object nativeImage, RectangleShape worldExtent, GeographyUnit drawingMapUnit)


        {


            canvasBitmap = nativeImage as Bitmap;


            base.BeginDrawingCore(nativeImage, worldExtent, drawingMapUnit);


        }


 


        protected override void DrawScreenImageWithoutScalingCore(GeoImage image, float centerXInScreen, float centerYInScreen, DrawingLevel drawingLevel, float xOffset, float yOffset, float rotateAngle)


        {


            Bitmap bitmap;


            lock (image)


            {


                bitmap = new Bitmap(image.GetImageStream(this));


            }


 


            bitmap.SetResolution(Dpi, Dpi);


            


            int imageWidth = bitmap.Width;


            int imageHeight = bitmap.Height;


 


            float screenX = centerXInScreen;


            float screenY = centerYInScreen;


 


            screenX += -imageWidth / 2.0f + xOffset;


            screenY += -imageHeight / 2.0f + yOffset;


 


            if (rotateAngle == 0)


            {


                try


                {


                    Graphics graphics = Graphics.FromImage(canvasBitmap);


                    graphics.DrawImageUnscaled(bitmap, (int)Math.Round(screenX), (int)Math.Round(screenY));


                }


                finally


                {


                    if (bitmap != null) { bitmap.Dispose(); }


                }


            }


            else


            {


                float upperLeftPointX = -imageWidth * 0.5f;


                float upperLeftPointY = imageHeight * 0.5f;


                double rotateRadient = rotateAngle * Math.PI / 180;


                double baseRadient = Math.PI - Math.Atan((double)imageHeight / (double)imageWidth);


                double radius = Math.Sqrt(imageWidth * imageWidth + imageHeight * imageHeight) * 0.5;


                double newRadient = baseRadient + rotateRadient;


                double newPointX = radius * Math.Cos(newRadient);


                double newPointY = radius * Math.Sin(newRadient);


                double xOffsetInScreen = newPointX - upperLeftPointX;


                double yOffsetInScreen = -(newPointY - upperLeftPointY);


                screenX += (float)xOffsetInScreen;


                screenY += (float)yOffsetInScreen;


 


                try


                {


                    Graphics graphics = Graphics.FromImage(canvasBitmap);


 


                    graphics.TranslateTransform(screenX, screenY);


                    graphics.RotateTransform(-rotateAngle);


 


                    graphics.DrawImageUnscaled(bitmap, 0, 0);


 


                    graphics.RotateTransform(rotateAngle);


                    graphics.TranslateTransform(-screenX, -screenY);


                }


                finally


                {


                    if (bitmap != null) { bitmap.Dispose(); }


                }


            }


        }


    }


 


If you still could make it, I have no other ways.


Sorry for inconvenience.


James




Hi James, 
  
 Thanks for the suggestion, but I believe the problem is it appears to me that  your GdiPlusGeoCanvas must have a Bitmap object to draw to.  Because only your GeoImage objects or Bitmaps are accepted by the BeginDrawing method.  Redrawing a bitmap to vector image does not produce the desired result, because all the layers that have been drawn are already rasterized by the Gdi canvas using a bitmap. 
  
 It appears to me that only solution to this is a new sub class of GeoCanvas that can handle only drawing to a System.Drawing.Graphics object and not need a bitmap to start with.  But it appears that the drawing logic for a Graphics object is contained in the Gdi canvas and not in the GeoCanvas base class.  Which means all that logic can’t be reused and would need to be recreated and not having to handle that drawing is why I’m using MapSuite to begin with. 
  
 I would be happy to attempt my own subclass of GeoCanvas but any pointers on what you need to do to create a geo canvas subclass would be very helpful.

Brian, 
  
 Thanks for you understanding this. 
  
 Please refer to PdfGeoCanvas which can guide you how to create a custom GeoCanvas. 
  
 Thanks, 
 James

 Posted By Brian on 08-12-2011 09:29 AM 

Hi James, 



Thanks for the suggestion, but I believe the problem is it appears to me that  your GdiPlusGeoCanvas must have a Bitmap object to draw to.  Because only your GeoImage objects or Bitmaps are accepted by the BeginDrawing method.  Redrawing a bitmap to vector image does not produce the desired result, because all the layers that have been drawn are already rasterized by the Gdi canvas using a bitmap. 



It appears to me that only solution to this is a new sub class of GeoCanvas that can handle only drawing to a System.Drawing.Graphics object and not need a bitmap to start with.  But it appears that the drawing logic for a Graphics object is contained in the Gdi canvas and not in the GeoCanvas base class.  Which means all that logic can’t be reused and would need to be recreated and not having to handle that drawing is why I’m using MapSuite to begin with. 



I would be happy to attempt my own subclass of GeoCanvas but any pointers on what you need to do to create a geo canvas subclass would be very helpful. 

I am looking for the exact same functionality. Exporting to a vector format instead of a bitmap format. Have you found a solution for this?


 



 Elm,


 
Have you tried to create your own subclass of GeoCanvas? As my suggestion, you can look at PdfGeoCavnas which be contained in Pdf extension. gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/16/aft/5280/afv/topic/Default.aspx
 Let me know if you have questions.
 
Thanks,
James

I don't have the source of the PDFGeoCanvas object so I can't see how to make my own class.


 


What is the nativeImage of BeginDrawing? Is there an example somewhere of creating your own extension of GoeCanvas?


 


Thanks!



 Hello Elm,


 
Attached sample is a demo that shows how to inheriting the GeoCanvas and create your own GeoCanvas, this sample converts a line based shape (we use Austin Streets data in this sample, you can find it in the sample data) to KML, you can refer this and write your own function to export the vector format.
 
I hope this can help you.
 
Regards,
 
Gary

KML_Demo.zip (15.1 KB)

Brian,


Did you ever come to a resolution?  I am also trying to get vector images out of ThinkGeo.


Thanks.



Hello Joe, 
  
 Have you try the sample above? Meet any problem? 
  
 Regards, 
  
 Gary

I didn't try the sample above, but I did find the solution I was looking for here:


gis.thinkgeo.com/Support/Discussion...fault.aspx


Thanks,


Joe



Hi Joe,  
  
 Thanks for your feedback and please let us know if you have additional questions.