MapWindowDevelper:Random Notes

This is a back-up of the WIKI.
Not all links might work
We're working on a new wiki.

Main Page | Recent changes | View source | Page history | Log in / create account |

Printable version | Disclaimers | Privacy policy

Contents

Random Notes: collected from user forum postings and otherwise

These randomly collected notes will eventually be moved to appropriate pages and topics



10-08-2010 how to draw rubber band

Subject: Re: How to draw rubberband line and polygon an the map? Forum: MapWindow 4 - ActiveX Control Programming Link: http://www.mapwindow.org/phorum/read.php?3,19509,19567#msg-19567 Approved: Yes

Hey Guys,

I wrote some .NET code for MapWindow 4.x which does some very fast drawing for rubber bands. I had to pull it from the plug-in because the plug-in was proprietary and I was told specifically not to share it but this drawing code is free for use. Basically we get a GDI+ graphics object from the AxMap and draw on top of the map.

       //Contains a reference to the MapWindow ActiveX control
       private AxMapWinGIS.AxMap _AxMap;
       //Holds where the use click to start the rubber band in Map Coords
       private MapWinGIS.Point _startPt = null;
       //Holds where the location of the mouses last location Map Coords
       //Needed so we can invalidate the old line before drawing the new
       private MapWinGIS.Point _oldMousePt = null;
       //Pen used to draw the line set it to something you line
       private Pen _LinePen = new System.Drawing.Pen(System.Drawing.Color.Blue, 2F);
       /// 
       /// Draws a rubber band line attached to the cursor from _startPt(in Map coord) to ScreenX,ScreenY (in screen coords)
       /// So set _startPt when the user clicks and then call this method when the mouse moves. When you want to stop 
       /// drawing set _startPt and _oldMousePt to null and stop calling this function
       /// 
       /// 
       /// 
       private void UpdateScreen(int ScreenX, int ScreenY)
       {
               //This checks if we just starting and returns if theres nothing to draw yet
               if (_startPt == null && _oldMousePt == null)
                   return;
               //Location of the mouse on screen
               System.Drawing.Point mousePt = new System.Drawing.Point(ScreenX, ScreenY);
               //Convert the stored location into screen coord for drawing because
               //we store it in Map coordinants to allow the user to zoom / pan
               System.Drawing.Point start = ProjToPixel(_startPt);
               //Check if the mouse has moved from where it started
               if ((start.X == ScreenX && start.Y == ScreenY) || _oldMousePt == null)
               {
                   _oldMousePt = PixelToProj(new System.Drawing.Point(ScreenX, ScreenY));
                   return;
               }
               System.Drawing.Point old = ProjToPixel(_oldMousePt);
               System.Drawing.Rectangle mapRect = new Rectangle(0, 0, _AxMap.Width, _AxMap.Height);
               //Gets the maps graphics object
               System.Drawing.Graphics g = _AxMap.CreateGraphics();
               //Defines a invalidation rectangle where the line was drawn before
               g.SmoothingMode = SmoothingMode.AntiAlias;
               PointF invStartPt = new PointF(Math.Min(start.X, old.X), Math.Min(start.Y, old.Y));
               SizeF invSize = new SizeF(Math.Abs(old.X - start.X), Math.Abs(old.Y - start.Y));
               System.Drawing.RectangleF invRect = new System.Drawing.RectangleF(invStartPt, invSize);
               invRect.Inflate(10F, 10F);
               invRect.Intersect(new Rectangle(0, 0, _AxMap.Width, _AxMap.Height));
               //Converts the invalidation rectangle into a region and exclude where we are drawing so 
               //it doesnt flicker white
               Region invRegion = new Region(invRect);
               GraphicsPath linePath = CohenSutherland.cohenSutherland(start, mousePt, mapRect);
               if (linePath != null)
               {
                   linePath.Widen(_LinePen);
                   invRegion.Exclude(linePath);
               }
               //This draws the measure line once before and once after the map invalidates
               //so that it doesn't flicker white for a second. To speed up drawing we clip 
               //the line to the screen bounds so off screen drawind doesn't slow things down
               g.Clip = new Region(new Rectangle(0, 0, _AxMap.Width, _AxMap.Height));
               g.DrawLine(_LinePen, start, mousePt);
               _AxMap.Invalidate(invRegion);
               _AxMap.Update();
               g.DrawLine(_LinePen, start, mousePt);
               //Backups the mouses current location
               _oldMousePt = PixelToProj(mousePt);
      }
       //This event fires any time there is a zoom or pan that changes the extents of the map.
       public void MapExtentsChanged()
       {
           if (_oldMousePt == null) return;
           System.Drawing.Point screenPt = ProjToPixel(_oldMousePt);
           
           if (_Measuring == true)
               UpdateScreen(screenPt.X, screenPt.Y);
       }


   /// 
   /// This class implements the fast CohenSutherland line/rectangle intersect test
   /// We use this class to quickly eliminate line that hang off the screen to increase
   /// drawing speed significantly when lines start off screen!
   /// 
   public static class CohenSutherland
   {
       private static int RIGHT = 2;
       private static int TOP = 8;
       private static int BOTTOM = 4;
       private static int LEFT = 1;
       private static int computeOutCode(int x, int y, int xmin, int ymin, int xmax, int ymax)
       {
           int code = 0;
           if (y > ymax)
               code |= TOP;
           else if (y < ymin)
               code |= BOTTOM;
           if (x > xmax)
               code |= RIGHT;
           else if (x < xmin)
               code |= LEFT;
           return code;
       }
       /// 
       /// Calculates the intersection of a line (pt1,pt2) and a rectangle
       /// It returns a GraphicsPath composed of the line entirely withint rect or null if non exists
       /// 
       /// 
       /// 
       /// 
       /// 
       public static GraphicsPath cohenSutherland(Point pt1, Point pt2, Rectangle rect)
       {
           int x1 = pt1.X;
           int y1 = pt1.Y;
           int x2 = pt2.X;
           int y2 = pt2.Y;
           int xmin = rect.Left;
           int ymin = rect.Top;
           int xmax = rect.Bottom;
           int ymax = rect.Right;
           //Outcodes for P0, P1, and whatever point lies outside the clip rectangle
           int outcode0, outcode1, outcodeOut, hhh = 0;
           bool accept = false, done = false;
           //compute outcodes
           outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax);
           outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax);
           do
           {
               if ((outcode0 | outcode1) == 0)
               {
                   accept = true;
                   done = true;
               }
               else if ((outcode0 & outcode1) > 0)
               {
                   done = true;
               }
               else
               {
                   //failed both tests, so calculate the line segment to clip
                   //from an outside point to an intersection with clip edge
                   int x = 0, y = 0;
                   //At least one endpoint is outside the clip rectangle; pick it.
                   outcodeOut = outcode0 != 0 ? outcode0 : outcode1;
                   //Now find the intersection point;
                   //use formulas y = y0 + slope * (x - x0), x = x0 + (1/slope)* (y - y0)
                   if ((outcodeOut & TOP) > 0)
                   {
                       x = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1);
                       y = ymax;
                   }
                   else if ((outcodeOut & BOTTOM) > 0)
                   {
                       x = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1);
                       y = ymin;
                   }
                   else if ((outcodeOut & RIGHT) > 0)
                   {
                       y = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1);
                       x = xmax;
                   }
                   else if ((outcodeOut & LEFT) > 0)
                   {
                       y = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1);
                       x = xmin;
                   }
                   //Now we move outside point to intersection point to clip
                   //and get ready for next pass.
                   if (outcodeOut == outcode0)
                   {
                       x1 = x;
                       y1 = y;
                       outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax);
                   }
                   else
                   {
                       x2 = x;
                       y2 = y;
                       outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax);
                   }
               }
               hhh++;
           }
           while (done != true && hhh < 5000);
           if (accept)
           {
               GraphicsPath gp = new GraphicsPath();
               gp.AddLine(x1, y1, x2, y2);
               return (gp);
           }
           return (null);
       }
   }


If you have any trouble with this let us know and we'll try and give you a hand. Good luck,

Brian


24-07-2010" Debugging in VS2008 (not VS2008 Express)

What you can do is configure visual studio to launch MapWindow.exe when your plug-in is compiled. That way you can put break points in your application and as it runs it will stop at them. If you enable edit-and-continue you'd be able to modify you code while its running (Within a limited extent you can't do some things like add new methods or create new classes) but it works for debugging logic on the fly. That how I worked while creating all the plug-ins I've written like the mwLayout plug-in and the google geocoder.

Here's how to do it: (Works in Visual Studio 2008+ but not express) 1) In the solution explorer, right click on your project and click properties 2) Go to the Debug tab and set Start Action to Start external program and browse to MapWindow.exe 3) Click the Compile tab and change the configuration to All Configurations 4) Set the Build Output Path: to the plugins folder located in the folder where your copy of MapWindow.exe is

Thats it. Set a break point in your code somewhere and the hit debug. If the break point never turns into a solid red circle then your dll isn't being loaded and somethings wrong. Otherwise once your break point has been hit you should be able to just edit your code and when you hit debug again it will continue from where it was.


24-07-2010" Debugging in VS2008 Express

It is possible (but more difficult) to get this to work in the express edition. The user interface does not support specifying the .exe to run, but if you edit your project file as text you can tell it to start an external exe. Two other forum topics talk about how to do this:

[[1]] and [[2]]

I will add that you should be sure your plugin is being compiled with "pdb only" debugging enabled so you can set a breakpoint in your code and step through with the debugger. If you have already been able to do this kind of debugging, then you must already have this set.




20-07-2010: Subject: Improvements for images in ocx - 2

Forum: MapWindow Developer Team Link: http://www.mapwindow.org/phorum/read.php?5,19293,19293#msg-19293 Approved: Yes

Hi, I did some work for images in the last 2 weeks. Here are summarized results. Some code samples will be added in the next post. Post your questions/remarks/suggestions.

1. Added support for the 'rotation parameters' in the world file (the second and the third lines in the file). GDAL virtual dataset is created for such images in the loading process. Then this dataset will be used in the same way as the common one and the images will be displayed rotated. The same approach is used in QGIS.

2. Grouping of images. Seriously hastens the drawing process in case there is several images with the same parameters (Width, Height, Dx, Dy, XLLCenter, YLLCenter) and with the small amount of meaningful pixels. Works for all image types. Can be switched on/off by Image.CanUseGrouping and Map.CanUseImageGrouping.

3. Saving of NoDataValue value for GDAL-based images (at least PNG ones). Image.SetNoData is used to pass the value. Saving of GDAL images is performed through GDALDriver.CreateCopy call (embedded in the Image.Save function). The .aux file is created along with the image to hold NoDataValue.

4. Function for building histogram with frequencies of separate colors for all image types. For GDAL-based images it's possible to set the maximum size of buffer used to build the histogram. The function doesn't affect drawing buffer for the image so no reloading is needed.

5. More work for support of GDAL overviews (pyramids). Image.NumOverviews property added. Progress function implemented for Image.BuildOverviews method. Some tests with C# sample code was performed. For large .img files the the difference in speed with pyramids is more than significant.

6. New read/write properties added to image class to unify the behaviour of GDAL images and bitmaps: OriginalDX, OriginalDY, OriginalXLLCenter, OriginalYLLCenter. It's possible to change position of GDAL image and bitmaps using the same code now.

7. Work on the problem with position of images (bug 1742) http://bugs.mapwindow.org/view.php?id=1742. No solution was found so far. The problem with positioning of GDI+ bitmaps which is most likely the source of this bug is described here http://www.codeguru.com/forum/showthread.php?t=500043. Any suggestions are appreciated.

8. Various optimizations of code structure for image class. Memory for tkRaster, tkBitmap classes is allocated dynamically now. So no RAM will be used to hold instance of tkRaster class, when bmp image is used (it's relevant for point icons which potentially can be numerous). Revised function for calculation of GDAL image buffer size (at last it works the way I want ;).

9. Optimization of loading time for group of images in batch mode in the MW4 project. All the buffers won't be reloaded after each subsequent image. Loading of buffers will be performed after unlocking the map only. More tests is needed here, to be sure all works as expected.

Sergei


Subject: Re: Improvements for images in ocx - 2 Forum: MapWindow Developer Team Link: http://www.mapwindow.org/phorum/read.php?5,19293,19295#msg-19295 Approved: Yes

Here is code for building and clearing overviews (function is used in both cases). Callback is used to return progress information, as for large image the process can be rather long.

       private void btnBuildOverviews_MouseClick(object sender, MouseEventArgs e)
       {
           MapWinGIS.Image img = new MapWinGIS.Image();
           openFileDialog1.Filter = "Images|*.png";    // or any other GDAL supported format
           openFileDialog1.FilterIndex = 0;
           if (openFileDialog1.ShowDialog(this) == DialogResult.OK)
           {
               Callback cback = new Callback();
               img.Open(openFileDialog1.FileName, ImageType.USE_FILE_EXTENSION, false, cback);
               if (img.NumOverviews == 0)
               {
                   // building overviews
                   int[] anOverviewList = { 2, 4, 8, 16 };    // there will be 4 overviews of reduced size, reduction ratios are 2, 4 and so forth
                   if (img.BuildOverviews(tkGDALResamplingMethod.grmBicubic, 4, anOverviewList))
                   {
                       MessageBox.Show("Overviews were built successfully.");
                   }
                   else
                   {
                       MessageBox.Show(img.get_ErrorMsg(img.LastErrorCode));
                   }
               }
               else if (img.NumOverviews != 0)
               {
                   // clearing overviews
                   int[] anOverviewList = { };
                   if (img.BuildOverviews(tkGDALResamplingMethod.grmBicubic, 0, anOverviewList))
                   {
                       MessageBox.Show("Overviews were cleared.");
                   }
                   else
                   {
                       MessageBox.Show(img.get_ErrorMsg(img.LastErrorCode));
                   }
               }
               axMap1.RemoveAllLayers();
               axMap1.AddLayer(img, true);
               axMap1.ZoomToLayer(0);
           }
       }
       class Callback : MapWinGIS.ICallback
       {
           public void Error(string KeyOfSender, string ErrorMsg)
           {
               throw new NotImplementedException();
           }
           public void Progress(string KeyOfSender, int Percent, string Message)
           {
               // more relevant code is needed here, of course ;)
               MessageBox.Show(Convert.ToString(Percent));
           }
       }


Subject: Re: Improvements for images in ocx - 2 Forum: MapWindow Developer Team Link: http://www.mapwindow.org/phorum/read.php?5,19293,19296#msg-19296 Approved: Yes

Here is an example of setting no data value for the image. Was tested for png images only.

       private void btnSetNoDataValue_Click(object sender, EventArgs e)
       {
           MapWinGIS.Image img = new MapWinGIS.Image();
           openFileDialog1.Filter = img.CdlgFilter;
           if (openFileDialog1.ShowDialog() == DialogResult.OK)
           {
               img.Open(openFileDialog1.FileName, ImageType.USE_FILE_EXTENSION, false, null);
               if (!img.UseTransparencyColor)  // NoDataValue isn't set for this image 
                                               // or otherwise this property had been set to true
               {
                   object colors, frequences;
                   int count = img.GetUniqueColors(0.5, out colors, out frequences);   // 0.5 is maximum buffer size in megabytes
                   if (count > 0)  // number of unique colors
                   {
                       uint[] myColors = colors as uint[];
                       bool result = false;
                       img.SetNoDataValue(Convert.ToDouble(myColors[0]), ref result);  // it is the most common color
                       if (result)
                           MessageBox.Show("No data value was set successfully");
                       string ext = System.IO.Path.GetExtension(openFileDialog1.FileName);
                       string name = System.IO.Path.GetFileNameWithoutExtension(openFileDialog1.FileName);
                       string newFilename = name + "1" + ext;
                       img.Save(newFilename, true, ImageType.USE_FILE_EXTENSION, null);
                       img.Close();
                       img = new MapWinGIS.Image();
                       img.Open(newFilename, ImageType.USE_FILE_EXTENSION, true, null);
                       axMap1.AddLayer(img, true);
                       axMap1.ZoomToMaxExtents();
                   }
               }
           }
       }



15-07-2010:MapWindow 4 Print Layout Plug-in

There is an example of how to use the layout form on the second page of this thread.

[[3]]



13-07-2010:Grid vs Raster

Subject: Re: What are differences between a raster file loaded as grid and loaded as image?

Forum: MapWindow 4 - Desktop Application

Link: http://www.mapwindow.org/phorum/read.php?4,19180,19182#msg-19182

There is no reason to use an image as grid if it's only a background! Mapwindow load tif image as grid by default. You can change this behavior going to File --> Settings --> Application settings (tab) --> Tiff/IMG/DEM loading behavior == LoadAsImage.

That should resolve your trouble and make tif loading faster. enrico

ps. 'tif as raster' it's slow the first time you load it because MW creates a bitmap copy of it.


30/06/2010:Excellent ActiveX and Plug-in programming tutorial

Download [4]. This link is someehat hidden on the MW web pages. It deserves a more prominent link display.

Link added to Tutorial Wiki page (02/07/2010 --Gngdowid 04:06, 2 July 2010 (UTC))

Suggestion: add link to Tutorial Wiki pages to main menu on the left.


18/06/2010: Code Formater

The code formatter can convert your code to a forum format and a wiki format. Use the combobox at the top of the code formatter. You can download the code formatter here: CodeFormatter2005.exe

I also recently read you can use OpenOffice to create wiki pages. I haven't used it yet, but might be worthwhile looking into it. More info at Sun Wiki Publisher

Example of code formatted by CodeFormatter:

                 ' Define userdefined cursor 
                 'Set the map cursor action as cmNone
                 SI.g_MapWin.View.CursorMode = MapWinGIS.tkCursorMode.cmNone
                 'Set the map cursor shape as a user defined cursor
                 SI.g_MapWin.View.MapCursor = MapWinGIS.tkCursor.crsrUserDefined

18/06/2010: Interop.MapWinGIS.dll vs MapWinGIS.OCX

Interop.MapWinGIS.dll is a automatically created file that is generated by visual studio when you add a reference to MapWinGIS.OCX It allows .NET applications to access COM objects. If you want to debug MapWinGIS.OCX you need to get the source code for it from the SVN.


18/06/2010: Mouse Cursor shape and Mouse Cursor action

tkCursor sets the cursor shape and tkCursorMode sets the action connected to the cursor.

     Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
         g_MapWin.View.CursorMode = MapWinGIS.tkCursorMode.cmNone ' Set cursor action
         g_MapWin.View.MapCursor = MapWinGIS.tkCursor.crsrCross   ' Set cursor shape
     End Sub
 

Retrieved from "http://mapwindow.org/wiki/index.php/MapWindowDevelper:Random_Notes"

This page has been accessed 1,543 times. This page was last modified on 10 August 2010, at 05:37.