Added a new version (0.8.3) of FloatCanvas from Chris Barker. It's
authorRobin Dunn <robin@alldunn.com>
Fri, 4 Jun 2004 21:29:41 +0000 (21:29 +0000)
committerRobin Dunn <robin@alldunn.com>
Fri, 4 Jun 2004 21:29:41 +0000 (21:29 +0000)
now in a subpackage of wx.lib.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@27637 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

wxPython/demo/FloatCanvas.py
wxPython/demo/Main.py
wxPython/distrib/DIRLIST
wxPython/distrib/make_installer.py
wxPython/docs/CHANGES.txt
wxPython/setup.py
wxPython/wx/lib/floatcanvas.py [deleted file]
wxPython/wx/lib/floatcanvas/FloatCanvas.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/NavCanvas.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/Resources.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/__init__.py [new file with mode: 0644]

index 4b48dac6066d3c4be707001aa19b1eeb7de4fbd7..94f2eab77cb4194a3200362e37ecc33bbf677d64 100644 (file)
 
-import  wx
-
-# Stuff to integrate FloatCanvas into wxPython Demo
 try:
     import Numeric
+    import RandomArray
     haveNumeric = True
 except ImportError:
-    haveNumeric = False
-
+    try:
+        import numarray as Numeric
+        import numarray.random_array as RandomArray
+        haveNumeric = True
+    except ImportError:
+        haveNumeric = False
 
 if not haveNumeric:
-    errorText = """\
-The FloatCanvas requires the Numeric module:
-You can get it at:
+    errorText = """
+The FloatCanvas requires either the Numeric or Numarray module:
+You can get them at:
      http://sourceforge.net/projects/numpy
-"""     
-    def runTest(frame, nb, log):
-        dlg = wx.MessageDialog(
-                frame, errorText, 'Sorry', wx.OK | wx.ICON_INFORMATION
-                )
 
+NOTE: The Numeric module is substantially faster than numarray for this
+purpose, if you have lot's of objects
+"""
+
+    def runTest(frame, nb, log):
+        dlg = wx.MessageDialog(frame, errorText, 'Sorry', wx.OK |
+                               wx.ICON_INFORMATION)
         dlg.ShowModal()
         dlg.Destroy()
 
     overview = ""
-
+    
 else:
-    from wx.lib import floatcanvas
-    import wx.lib.colourdb
+    StartUpDemo = "all"
+    if __name__ == "__main__": # parse options if run stand-alone
+        # check options:
+        import sys, getopt
+        optlist, args = getopt.getopt(sys.argv[1:],'l',["local","all","text","map","stext","hit","hitf","animate","speed","temp"])
+        
+        for opt in optlist:
+            if opt[0] == "--all":
+                StartUpDemo = "all"
+            elif opt[0] == "--text":
+                StartUpDemo = "text"
+            elif opt[0] == "--map":
+                StartUpDemo = "map"
+            elif opt[0] == "--stext":
+                StartUpDemo = "stext"
+            elif opt[0] == "--hit":
+                StartUpDemo = "hit"
+            elif opt[0] == "--hitf":
+                StartUpDemo = "hitf"
+            elif opt[0] == "--animate":
+                StartUpDemo = "animate"
+            elif opt[0] == "--speed":
+                StartUpDemo = "speed"
+            elif opt[0] == "--temp":
+                StartUpDemo = "temp"
+    import wx
+    import time, random
     
-    ID_ABOUT_MENU = wx.NewId()          
-    ID_EXIT_MENU  = wx.NewId() 
-    ID_ZOOM_TO_FIT_MENU = wx.NewId()
-    ID_DRAWTEST_MENU = wx.NewId()
-    ID_LINETEST_MENU = wx.NewId()
-    ID_DRAWMAP_MENU = wx.NewId()
-    ID_DRAWMAP2_MENU = wx.NewId()
-    ID_CLEAR_MENU = wx.NewId()
-
-
-    colors = []
-    LineStyles = floatcanvas.draw_object.LineStyleList.keys()
+    def runTest(frame, nb, log):
+        """
+        This method is used by the wxPython Demo Framework for integrating
+        this demo with the rest.
+        """
+        win = DrawFrame(None, -1, "FloatCanvas Drawing Window",wx.DefaultPosition,(500,500))
+        frame.otherWin = win
+        win.Show(True)
+        win.DrawTest()
 
+    try:
+        from floatcanvas import NavCanvas, FloatCanvas
+    except ImportError: # if it's not there locally, try the wxPython lib.
+        from wx.lib.floatcanvas import NavCanvas, FloatCanvas
 
+    import wxPython.lib.colourdb
     
     class DrawFrame(wx.Frame):
     
         """
-    
         A frame used for the FloatCanvas Demo
         
         """
         
-        def __init__(self, parent, id, title, position, size):
+        
+        def __init__(self,parent, id,title,position,size):
             wx.Frame.__init__(self,parent, id,title,position, size)
             
-            # Set up the MenuBar
-            
+            ## Set up the MenuBar
             MenuBar = wx.MenuBar()
             
             file_menu = wx.Menu()
-            file_menu.Append(ID_EXIT_MENU, "&Close","Close this frame")
-            self.Bind(wx.EVT_MENU, self.OnQuit, id=ID_EXIT_MENU)
+            item = file_menu.Append(-1, "&Close","Close this frame")
+            self.Bind(wx.EVT_MENU, self.OnQuit, item)
             MenuBar.Append(file_menu, "&File")
             
             draw_menu = wx.Menu()
-            draw_menu.Append(ID_DRAWTEST_MENU, "&Draw Test","Run a test of drawing random components")
-            self.Bind(wx.EVT_MENU, self.DrawTest, id=ID_DRAWTEST_MENU)
-            draw_menu.Append(ID_LINETEST_MENU, "&Line Test","Run a test of drawing random lines")
-            self.Bind(wx.EVT_MENU, self.LineTest, id=ID_LINETEST_MENU)
-            draw_menu.Append(ID_DRAWMAP_MENU, "Draw &Map","Run a test of drawing a map")
-            self.Bind(wx.EVT_MENU, self.DrawMap, id=ID_DRAWMAP_MENU)
-            draw_menu.Append(ID_CLEAR_MENU, "&Clear","Clear the Canvas")
-            self.Bind(wx.EVT_MENU, self.Clear, id=ID_CLEAR_MENU)
-            MenuBar.Append(draw_menu, "&Draw")
 
+            item = draw_menu.Append(-1, "&Draw Test","Run a test of drawing random components")
+            self.Bind(wx.EVT_MENU, self.DrawTest, item)
+
+            item = draw_menu.Append(-1, "&Line Test","Run a test of drawing random lines")
+            self.Bind(wx.EVT_MENU, self.LineTest, item)
+            
+            item = draw_menu.Append(-1, "Draw &Map","Run a test of drawing a map")
+            self.Bind(wx.EVT_MENU, self.DrawMap, item)
+            item = draw_menu.Append(-1, "&Text Test","Run a test of text drawing")
+            self.Bind(wx.EVT_MENU, self.TestText, item)
+            item = draw_menu.Append(-1, "&ScaledText Test","Run a test of text drawing")
+            self.Bind(wx.EVT_MENU, self.TestScaledText, item)
+            item = draw_menu.Append(-1, "&Clear","Clear the Canvas")
+            self.Bind(wx.EVT_MENU, self.Clear, item)
+            item = draw_menu.Append(-1, "&Hit Test","Run a test of the hit test code")
+            self.Bind(wx.EVT_MENU, self.TestHitTest, item)
+            item = draw_menu.Append(-1, "&Hit Test Foreground","Run a test of the hit test code with a foreground Object")
+            self.Bind(wx.EVT_MENU, self.TestHitTestForeground, item)
+            item = draw_menu.Append(-1, "&Animation","Run a test of Animation")
+            self.Bind(wx.EVT_MENU, self.TestAnimation, item)
+            item = draw_menu.Append(-1, "&Speed","Run a test of Drawing Speed")
+            self.Bind(wx.EVT_MENU, self.SpeedTest, item)
+            MenuBar.Append(draw_menu, "&Tests")
+            
             view_menu = wx.Menu()
-            view_menu.Append(ID_ZOOM_TO_FIT_MENU, "Zoom to &Fit","Zoom to fit the window")
-            self.Bind(wx.EVT_MENU, self.ZoomToFit, id=ID_ZOOM_TO_FIT_MENU)
+            item = view_menu.Append(-1, "Zoom to &Fit","Zoom to fit the window")
+            self.Bind(wx.EVT_MENU, self.ZoomToFit, item)
             MenuBar.Append(view_menu, "&View")
             
             help_menu = wx.Menu()
-            help_menu.Append(ID_ABOUT_MENU, "&About",
+            item = help_menu.Append(-1, "&About",
                                     "More information About this program")
-            self.Bind(wx.EVT_MENU, self.OnAbout, id=ID_ABOUT_MENU)
+            self.Bind(wx.EVT_MENU, self.OnAbout, item)
             MenuBar.Append(help_menu, "&Help")
             
             self.SetMenuBar(MenuBar)
             
-            self.CreateStatusBar()
-            self.SetStatusText("")
-            
-            self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
-            
-            # Other event handlers:
-            self.Bind(wx.EVT_RIGHT_DOWN, self.RightButtonEvent)
-            
+            self.CreateStatusBar()            
             # Add the Canvas
-            self.Canvas = floatcanvas.FloatCanvas(self,-1,(500,500),
-                                      ProjectionFun = 'FlatEarth',
-                                      Debug = 0,
-                                      EnclosingFrame = self,
-                                      BackgroundColor = "DARK SLATE BLUE",
-                                      UseBackground = 0,
-                                      UseToolbar = 1)
-            self.Show(True)
-            
-            self.object_list = []
-            
+            self.Canvas = NavCanvas.NavCanvas(self,
+                                              -1,
+                                              (500,500),
+                                              Debug = 1,
+                                              BackgroundColor = "DARK SLATE BLUE")
+
+            wx.EVT_CLOSE(self, self.OnCloseWindow)
+
+            FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove ) 
+            #FloatCanvas.EVT_LEFT_UP(self.Canvas, self.OnLeftUp ) 
+
+            self.EventsAreBound = False
+
+            ## getting all the colors and linestyles  for random objects
+            wxPython.lib.colourdb.updateColourDB()
+            self.colors = wxPython.lib.colourdb.getColourList()
+            #self.LineStyles = FloatCanvas.DrawObject.LineStyleList.keys()
+
+
             return None
-            
-        def RightButtonEvent(self,event):
+
+        def BindAllMouseEvents(self):
+            if not self.EventsAreBound:
+                ## Here is how you catch FloatCanvas mouse events
+                FloatCanvas.EVT_LEFT_DOWN(self.Canvas, self.OnLeftDown ) 
+                FloatCanvas.EVT_LEFT_UP(self.Canvas, self.OnLeftUp )
+                FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, self.OnLeftDouble ) 
+
+                FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, self.OnMiddleDown ) 
+                FloatCanvas.EVT_MIDDLE_UP(self.Canvas, self.OnMiddleUp ) 
+                FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, self.OnMiddleDouble ) 
+
+                FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, self.OnRightDown ) 
+                FloatCanvas.EVT_RIGHT_UP(self.Canvas, self.OnRightUp ) 
+                FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, self.OnRightDouble ) 
+
+                FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, self.OnWheel ) 
+            self.EventsAreBound = True
+
+        def UnBindAllMouseEvents(self):
+            ## Here is how you catch FloatCanvas mouse events
+            FloatCanvas.EVT_LEFT_DOWN(self.Canvas, None ) 
+            FloatCanvas.EVT_LEFT_UP(self.Canvas, None )
+            FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, None) 
+
+            FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, None )
+            FloatCanvas.EVT_MIDDLE_UP(self.Canvas, None )
+            FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, None )
+
+            FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, None )
+            FloatCanvas.EVT_RIGHT_UP(self.Canvas, None )
+            FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, None )
+
+            FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, None )
+
+            self.EventsAreBound = False
+
+        def PrintCoords(self,event):
+            print "coords are: %s"%(event.Coords,)
+            print "pixel coords are: %s\n"%(event.GetPosition(),)
+
+        def OnLeftDown(self, event):
+            print "Left Button has been clicked in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnLeftUp(self, event):
+            print "Left up in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnLeftDouble(self, event):
+            print "Left Double Click in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnMiddleDown(self, event):
+            print "Middle Button clicked in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnMiddleUp(self, event):
+            print "Middle Button Up in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnMiddleDouble(self, event):
+            print "Middle Button Double clicked in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnRightDown(self, event):
             print "Right Button has been clicked in DrawFrame"
-            print "coords are: %i, %i"%(event.GetX(),event.GetY())
-            event.Skip()
-            
+            self.PrintCoords(event)
+
+        def OnRightUp(self, event):
+            print "Right Button Up in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnRightDouble(self, event):
+            print "Right Button Double clicked in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnWheel(self, event):
+            print "Mouse Wheel Moved in DrawFrame"
+            self.PrintCoords(event)
+
+        def OnMove(self, event):
+            """
+            Updates the staus bar with the world coordinates
+            """
+            self.SetStatusText("%.2f, %.2f"%tuple(event.Coords))
+
         def OnAbout(self, event):
+            print "OnAbout called"
+            
             dlg = wx.MessageDialog(self, "This is a small program to demonstrate\n"
                                                       "the use of the FloatCanvas\n",
                                                       "About Me", wx.OK | wx.ICON_INFORMATION)
             dlg.ShowModal()
             dlg.Destroy()
             
-        def SetMode(self,event):
-            for id in [ID_ZOOM_IN_BUTTON,ID_ZOOM_OUT_BUTTON,ID_MOVE_MODE_BUTTON]:
-                self.ToolBar.ToggleTool(id,0)
-                
-            self.ToolBar.ToggleTool(event.GetId(),1)
-
-            if event.GetId() == ID_ZOOM_IN_BUTTON:
-                self.Canvas.SetGUIMode("ZoomIn")
-
-            elif event.GetId() == ID_ZOOM_OUT_BUTTON:
-                self.Canvas.SetGUIMode("ZoomOut")
-
-            elif event.GetId() == ID_MOVE_MODE_BUTTON:
-                self.Canvas.SetGUIMode("Move")
-                
         def ZoomToFit(self,event):
             self.Canvas.ZoomToBB()
             
         def Clear(self,event = None):
-            self.Canvas.RemoveObjects(self.object_list)
-            self.object_list = []
+            self.UnBindAllMouseEvents()
+            self.Canvas.ClearAll()
+            self.Canvas.SetProjectionFun(None)
             self.Canvas.Draw()
             
         def OnQuit(self,event):
@@ -151,45 +266,47 @@ else:
         def OnCloseWindow(self, event):
             self.Destroy()
             
-        def DrawTest(self,event):
-            wx.GetApp().Yield()
-
-            import random
-            import RandomArray
-
+        def DrawTest(self,event=None):
+            wx.GetApp().Yield(True)
+#            import random
+#            import RandomArray
             Range = (-10,10)
-            
+            colors = self.colors
+
+            self.BindAllMouseEvents()
             Canvas = self.Canvas
-            object_list = self.object_list
-            
-            #          Random tests of everything:
+
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+            ##         Random tests of everything:
             
             # Rectangles
-            for i in range(5):
+            for i in range(3):
                 x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
                 lw = random.randint(1,5)
                 cf = random.randint(0,len(colors)-1)
                 h = random.randint(1,5)
                 w = random.randint(1,5)
-                object_list.append(Canvas.AddRectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+                Canvas.AddRectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
               
-                # Ellipses
-            for i in range(5):
+            # Ellipses
+            for i in range(3):
                 x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
                 lw = random.randint(1,5)
                 cf = random.randint(0,len(colors)-1)
                 h = random.randint(1,5)
                 w = random.randint(1,5)
-                object_list.append(Canvas.AddEllipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+                Canvas.AddEllipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
               
-                # Dots
-            for i in range(5):
-                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
-                D = random.randint(1,50)
-                lw = random.randint(1,5)
-                cf = random.randint(0,len(colors)-1)
-                cl = random.randint(0,len(colors)-1)
-                object_list.append(Canvas.AddDot(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf]))
+##            # Dots -- Does anyone need this?
+##            for i in range(5):
+##                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+##                D = random.randint(1,50)
+##                lw = random.randint(1,5)
+##                cf = random.randint(0,len(colors)-1)
+##                cl = random.randint(0,len(colors)-1)
+##                Canvas.AddDot(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf])
               
                 # Circles
             for i in range(5):
@@ -198,8 +315,8 @@ else:
                 lw = random.randint(1,5)
                 cf = random.randint(0,len(colors)-1)
                 cl = random.randint(0,len(colors)-1)
-                object_list.append(Canvas.AddCircle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf]))
-                self.object_list.append(self.Canvas.AddText("Circle # %i"%(i),x,y,Size = 12,BackGround = None,Position = "cc"))
+                Canvas.AddCircle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf])
+                Canvas.AddText("Circle # %i"%(i),x,y,Size = 12,BackgroundColor = None,Position = "cc")
               
                 # Lines
             for i in range(5):
@@ -210,7 +327,7 @@ else:
                 lw = random.randint(1,10)
                 cf = random.randint(0,len(colors)-1)
                 cl = random.randint(0,len(colors)-1)
-                self.object_list.append(self.Canvas.AddLine(points, LineWidth = lw, LineColor = colors[cl]))
+                Canvas.AddLine(points, LineWidth = lw, LineColor = colors[cl])
               
                 # Polygons
             for i in range(3):
@@ -221,84 +338,623 @@ else:
                 lw = random.randint(1,6)
                 cf = random.randint(0,len(colors)-1)
                 cl = random.randint(0,len(colors)-1)
-                self.object_list.append(self.Canvas.AddPolygon(points,
-                                                               LineWidth = lw,
-                                                               LineColor = colors[cl],
-                                                               FillColor = colors[cf],
-                                                               FillStyle = 'Solid'))
-                
-                
+                Canvas.AddPolygon(points,
+                                       LineWidth = lw,
+                                       LineColor = colors[cl],
+                                       FillColor = colors[cf],
+                                       FillStyle = 'Solid')
+                                
             ## Pointset
             for i in range(4):
                 points = []
                 points = RandomArray.uniform(Range[0],Range[1],(100,2))
                 cf = random.randint(0,len(colors)-1)
                 D = random.randint(1,4)
-                self.object_list.append(self.Canvas.AddPointSet(points, Color = colors[cf], Diameter = D))
+                Canvas.AddPointSet(points, Color = colors[cf], Diameter = D)
             
             # Text
-            String = "Some text"
-            for i in range(10):
+            String = "Unscaled text"
+            for i in range(3):
                 ts = random.randint(10,40)
                 cf = random.randint(0,len(colors)-1)
                 x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
-                self.object_list.append(self.Canvas.AddText(String,x,y,Size = ts,ForeGround = colors[cf],Position = "cc"))
+                Canvas.AddText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+            # Scaled Text
+            String = "Scaled text"
+            for i in range(3):
+                ts = random.random()*3 + 0.2
+                cf = random.randint(0,len(colors)-1)
+                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+                Canvas.AddScaledText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+            Canvas.ZoomToBB()
+
+        def TestAnimation(self,event=None):
+            """
+
+            In this test, a relatively complex background is drawn, and
+            a simple object placed in the foreground is moved over
+            it. This demonstrates how to use the InForeground attribute
+            to make an object in the foregorund draw fast, without
+            having to re-draw the whole background.
+
+            """
+            print "Running TestAnimation"
+            wx.GetApp().Yield(True)
+            Range = (-10,10)
+            self.Range = Range
+
+            self.UnBindAllMouseEvents()
+            Canvas = self.Canvas
+
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+            ##         Random tests of everything:
+            colors = self.colors
+            # Rectangles
+            for i in range(3):
+                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+                lw = random.randint(1,5)
+                cf = random.randint(0,len(colors)-1)
+                h = random.randint(1,5)
+                w = random.randint(1,5)
+                Canvas.AddRectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
+              
+            # Ellipses
+            for i in range(3):
+                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+                lw = random.randint(1,5)
+                cf = random.randint(0,len(colors)-1)
+                h = random.randint(1,5)
+                w = random.randint(1,5)
+                Canvas.AddEllipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
+              
+            # Circles
+            for i in range(5):
+                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+                D = random.randint(1,5)
+                lw = random.randint(1,5)
+                cf = random.randint(0,len(colors)-1)
+                cl = random.randint(0,len(colors)-1)
+                Canvas.AddCircle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf])
+                Canvas.AddText("Circle # %i"%(i),x,y,Size = 12,BackgroundColor = None,Position = "cc")
+              
+            # Lines
+            for i in range(5):
+                points = []
+                for j in range(random.randint(2,10)):
+                    point = (random.randint(Range[0],Range[1]),random.randint(Range[0],Range[1]))
+                    points.append(point)
+                lw = random.randint(1,10)
+                cf = random.randint(0,len(colors)-1)
+                cl = random.randint(0,len(colors)-1)
+                Canvas.AddLine(points, LineWidth = lw, LineColor = colors[cl])
+              
+            # Polygons
+            for i in range(3):
+                points = []
+                for j in range(random.randint(2,6)):
+                    point = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+                    points.append(point)
+                lw = random.randint(1,6)
+                cf = random.randint(0,len(colors)-1)
+                cl = random.randint(0,len(colors)-1)
+                Canvas.AddPolygon(points,
+                                       LineWidth = lw,
+                                       LineColor = colors[cl],
+                                       FillColor = colors[cf],
+                                       FillStyle = 'Solid')
+                                
+            # Scaled Text
+            String = "Scaled text"
+            for i in range(3):
+                ts = random.random()*3 + 0.2
+                cf = random.randint(0,len(colors)-1)
+                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+                Canvas.AddScaledText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+
+            # Now the Foreground Object:
+            C = Canvas.AddCircle(0,0,7,LineWidth = 2,LineColor = "Black",FillColor = "Red", InForeground = True)
+            T = Canvas.AddScaledText("Click to Move",0,0, Size = 0.8, Position = 'cc', InForeground = True)
+            C.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.MoveMe)
+            C.Text = T
+
+            self.Timer = wx.PyTimer(self.ShowFrame)
+            self.FrameDelay = 50 # milliseconds
+            
+            Canvas.ZoomToBB()
+
+        def ShowFrame(self):
+            Object = self.MovingObject
+            Range = self.Range
+            if  self.TimeStep < self.NumTimeSteps:
+                x,y = Object.XY
+                if x > Range[1] or x < Range[0]:
+                    self.dx = -self.dx
+                if y > Range[1] or y < Range[0]:
+                    self.dy = -self.dy
+                Object.Move( (self.dx,self.dy) )
+                Object.Text.Move( (self.dx,self.dy))
+                self.Canvas.Draw()
+                self.TimeStep += 1
+                wx.GetApp().Yield(True)
+            else:
+                self.Timer.Stop()
+            
+
+        def MoveMe(self, Object):
+            self.MovingObject = Object
+            Range = self.Range
+            self.dx = random.uniform(Range[0]/4,Range[1]/4)
+            self.dy = random.uniform(Range[0]/4,Range[1]/4)
+            #import time
+            #start = time.time()
+            self.NumTimeSteps = 500
+            self.TimeStep = 1
+            self.Timer.Start(self.FrameDelay)
+            #print "Did %i frames in %f seconds"%(N, (time.time() - start) )
                 
+        def TestHitTest(self,event=None):
+            wx.GetApp().Yield(True)
+
+            self.UnBindAllMouseEvents()
+            Canvas = self.Canvas
+
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+            #Add a HitAble rectangle
+            w, h = 60, 20
+
+            dx = 80
+            dy = 40
+            x,y = 20, 20
+            
+            #Add one that is not HitAble
+            Canvas.AddRectangle(x, y, w, h, LineWidth = 2)
+            Canvas.AddText("Not Hit-able", x, y, Position = "bl")
+            
+
+            x += dx
+            R = Canvas.AddRectangle(x, y, w, h,LineWidth = 2)
+            R.Name = "Line Rectangle"
+            R.HitFill = False
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
+            Canvas.AddText("Left Click Line", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+            
+
+            x += dx
+            color = "Red"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + "Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
+            Canvas.AddText("Left Click Fill", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x = 20
+            y += dy
+            color = "LightBlue"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + " Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHit)
+            Canvas.AddText("Right Click Fill", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "Grey"
+            R = Canvas.AddEllipse(x, y, w, h,LineWidth = 2,FillColor = color)
+            R.Name = color +" Ellipse"
+            R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHit)
+            Canvas.AddText("Right Click Fill", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "Brown"
+            R = Canvas.AddCircle(x+dx/2, y+dy/2, dx/4, LineWidth = 2, FillColor = color)
+            R.Name = color + " Circle"
+            R.HitFill = True
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DCLICK, self.RectGotHit)
+            Canvas.AddText("Left D-Click Fill", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x = 20
+            y += dy
+            color = "Pink"
+            R = Canvas.AddCircle(x+dx/2, y+dy/2, dx/4, LineWidth = 2,FillColor = color)
+            R.Name = color +  " Circle"
+            R.Bind(FloatCanvas.EVT_FC_LEFT_UP, self.RectGotHit)
+            Canvas.AddText("Left Up Fill", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "White"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + " Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_MIDDLE_DOWN, self.RectGotHit)
+            Canvas.AddText("Middle Down", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "AQUAMARINE"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + " Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_MIDDLE_UP, self.RectGotHit)
+            Canvas.AddText("Middle Up", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x = 20
+            y += dy
+            color = "CORAL"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + " Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_MIDDLE_DCLICK, self.RectGotHit)
+            Canvas.AddText("Middle DoubleClick", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "CYAN"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + " Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_RIGHT_UP, self.RectGotHit)
+            Canvas.AddText("Right Up", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "LIME GREEN"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + " Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_RIGHT_DCLICK, self.RectGotHit)
+            Canvas.AddText("Right Double Click", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x = 20
+            y += dy
+            color = "MEDIUM GOLDENROD"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color
+            R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHitRight)
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+            Canvas.AddText("L and R Click", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "SALMON"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color + " Rectangle"
+            R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+            Canvas.AddText("Mouse Enter", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "MEDIUM VIOLET RED"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color
+            R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+            Canvas.AddText("Mouse Leave", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x = 20
+            y += dy
+            color = "SKY BLUE"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+            R.Name = color
+            R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+            R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+            Canvas.AddText("Enter and Leave", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "WHEAT"
+            R = Canvas.AddRectangle(x, y, w+12, h, LineColor = None, FillColor = color)
+            R.Name = color
+            R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+            R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+            Canvas.AddText("Mouse Enter&Leave", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "KHAKI"
+            R = Canvas.AddRectangle(x-12, y, w+12, h, LineColor = None, FillColor = color)
+            R.Name = color
+            R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+            R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+            Canvas.AddText("Mouse ENter&Leave", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x = 20
+            y += dy
+            L = Canvas.AddLine(( (x, y), (x+10, y+10), (x+w, y+h) ), LineWidth = 2, LineColor = "Red")
+            L.Name = "A Line"
+            L.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+            Canvas.AddText("Left Down", x, y, Position = "bl")
+            Canvas.AddText(L.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "SEA GREEN"
+            Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+            R = Canvas.AddPolygon(Points,  LineWidth = 2, FillColor = color)
+            R.Name = color + " Polygon"
+            R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHitRight)
+            Canvas.AddText("RIGHT_DOWN", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x += dx
+            color = "Red"
+            Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+            R = Canvas.AddPointSet(Points,  Diameter = 4, Color = color)
+            R.Name = "PointSet"
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.PointSetGotHit)
+            Canvas.AddText("LEFT_DOWN", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            x = 20
+            y += dy
+            T = Canvas.AddText("Hit-able Text", x, y, Size = 15, Color = "Red", Position = 'tl')
+            T.Name = "Hit-able Text"
+            T.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+            Canvas.AddText("Left Down", x, y, Position = "bl")
+
+            x += dx
+            T = Canvas.AddScaledText("Scaled Text", x, y, Size = 1./2*h, Color = "Pink", Position = 'bl')
+            Canvas.AddPointSet( (x, y), Diameter = 3)
+            T.Name = "Scaled Text"
+            T.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+            Canvas.AddText("Left Down", x, y, Position = "tl")
+
+            self.Canvas.ZoomToBB()
+
+        def TestHitTestForeground(self,event=None):
+            print "Running: TestHitTestForeground"
+            wx.GetApp().Yield(True)
+
+            self.UnBindAllMouseEvents()
+            Canvas = self.Canvas
+
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+            #Add a Hitable rectangle
+            w, h = 60, 20
+
+            dx = 80
+            dy = 40
+            x,y = 20, 20
+            
+            color = "Red"
+            R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color, InForeground = False)
+            R.Name = color + "Rectangle"
+            R.HitFill = True
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
+            Canvas.AddText("Left Click Fill", x, y, Position = "bl")
+            Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+            ## A set of Rectangles that move together
+            
+            ## NOTE: In a real app, it might be better to create a new
+            ## custom FloatCanvas DrawObject
+            
+            self.MovingRects = []
+            x += dx
+            color = "LightBlue"
+            R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+            R.HitFill = True
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveLeft)
+            L = Canvas.AddText("Left", x + w/4, y + h/4, Position = "cc", InForeground = True)
+            self.MovingRects.extend( (R,L) )
+
+            x += w/2
+            R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+            R.HitFill = True
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveRight)
+            L = Canvas.AddText("Right", x + w/4, y + h/4, Position = "cc", InForeground = True)
+            self.MovingRects.extend( (R,L) )
+
+            x -= w/2
+            y += h/2
+            R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+            R.HitFill = True
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveUp)
+            L = Canvas.AddText("Up", x + w/4, y + h/4, Position = "cc", InForeground = True)
+            self.MovingRects.extend( (R,L) )
+
+
+            x += w/2
+            R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+            R.HitFill = True
+            R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveDown)
+            L = Canvas.AddText("Down", x + w/4, y + h/4, Position = "cc", InForeground = True)
+            self.MovingRects.extend( (R,L) )
+
+            self.Canvas.ZoomToBB()
+
+        def RectMoveLeft(self,Object):
+            self.MoveRects("left")
+            
+        def RectMoveRight(self,Object):
+            self.MoveRects("right")
+                        
+        def RectMoveUp(self,Object):
+            self.MoveRects("up")
+                        
+        def RectMoveDown(self,Object):
+            self.MoveRects("down")
+                        
+        def MoveRects(self, Dir):
+            for Object in self.MovingRects:
+                X,Y = Object.XY
+                if Dir == "left": X -= 10
+                elif Dir == "right": X += 10
+                elif Dir == "up": Y += 10
+                elif Dir == "down": Y -= 10
+                Object.SetXY(X,Y)
+            self.Canvas.Draw()
+                
+                        
+        def PointSetGotHit(self, Object):
+            print Object.Name, "Got Hit\n"
+
+        def RectGotHit(self, Object):
+            print Object.Name, "Got Hit\n"
+
+        def RectGotHitRight(self, Object):
+            print Object.Name, "Got Hit With Right\n"
+
+        def RectGotHitLeft(self, Object):
+            print Object.Name, "Got Hit with Left\n"
+
+        def RectMouseOver(self, Object):
+            print "Mouse entered:", Object.Name
+
+        def RectMouseLeave(self, Object):
+            print "Mouse left ", Object.Name
+
+
+        def TestText(self, event= None):
+            wx.GetApp().Yield(True)
+
+            self.BindAllMouseEvents()
+            Canvas = self.Canvas
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+            x,y  = (0, 0)
+
+            ## Add a non-visible rectangle, just to get a Bounding Box
+            ## Text objects have a zero-size bounding box, because it changes with zoom
+            Canvas.AddRectangle(-10,-10,20,20,LineWidth = 1, LineColor = None)
+
+            # Text
+            String = "Some text"
+##            for i in range(10):
+##                ts = random.randint(10,40)
+##                cf = random.randint(0,len(colors)-1)
+##                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+            self.Canvas.AddText("Top Left",x,y,Size = 14,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
+            self.Canvas.AddText("Bottom Left",x,y,Size = 14,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
+            self.Canvas.AddText("Top Right",x,y,Size = 14,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
+            self.Canvas.AddText("Bottom Right",x,y,Size = 14,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
+            Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+
+            x,y  = (0, 2)
+            
+            Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+            self.Canvas.AddText("Top Center",x,y,Size = 14,Color = "Black",Position = "tc")
+            self.Canvas.AddText("Bottom Center",x,y,Size = 14,Color = "White",Position = "bc")
+
+            x,y  = (0, 4)
+            
+            Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+            self.Canvas.AddText("Center Right",x,y,Size = 14,Color = "Black",Position = "cr")
+            self.Canvas.AddText("Center Left",x,y,Size = 14,Color = "Black",Position = "cl")
+
+            x,y  = (0, -2)
+            
+            Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+            self.Canvas.AddText("Center Center",x,y,Size = 14,Color = "Black",Position = "cc")
+
+            self.Canvas.AddText("40 Pixels",-10,8,Size = 40)
+            self.Canvas.AddText("20 Pixels",-10,5,Size = 20)
+            self.Canvas.AddText("10 Pixels",-10,3,Size = 10)
+
+            self.Canvas.AddText("MODERN Font", -10, 0, Family = wx.MODERN)
+            self.Canvas.AddText("DECORATIVE Font", -10, -1, Family = wx.DECORATIVE)
+            self.Canvas.AddText("ROMAN Font", -10, -2, Family = wx.ROMAN)
+            self.Canvas.AddText("SCRIPT Font", -10, -3, Family = wx.SCRIPT)
+            self.Canvas.AddText("ROMAN BOLD Font", -10, -4, Family = wx.ROMAN, Weight=wx.BOLD)
+            self.Canvas.AddText("ROMAN ITALIC BOLD Font", -10, -5, Family = wx.ROMAN, Weight=wx.BOLD, Style=wx.ITALIC)
+
+            # NOTE: this font exists on my Linux box..who knows were else you'll find it!
+            Font = wx.Font(20, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
+            self.Canvas.AddText("zapf chancery Font", -10, -6, Font = Font)
+
+            self.Canvas.ZoomToBB()
+
+        def TestScaledText(self, event= None):
+            wx.GetApp().Yield(True)
+
+            self.BindAllMouseEvents()
+            Canvas = self.Canvas
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+            x,y  = (0, 0)
+
+            T = Canvas.AddScaledText("Top Left",x,y,Size = 5,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
+            T = Canvas.AddScaledText("Bottom Left",x,y,Size = 5,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
+            T = Canvas.AddScaledText("Top Right",x,y,Size = 5,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
+            T = Canvas.AddScaledText("Bottom Right",x,y,Size = 5,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
+            Canvas.AddPointSet((x,y), Color = "Red", Diameter = 4)
+
+
+            x,y  = (0, 20)
+            
+            Canvas.AddScaledText("Top Center",x,y,Size = 7,Color = "Black",Position = "tc")
+            Canvas.AddScaledText("Bottom Center",x,y,Size = 7,Color = "White",Position = "bc")
+            Canvas.AddPointSet((x,y), Color = "White", Diameter = 4)
+
+            x,y  = (0, -20)
+          
+            Canvas.AddScaledText("Center Right",x,y,Size = 9,Color = "Black",Position = "cr")
+            Canvas.AddScaledText("Center Left",x,y,Size = 9,Color = "Black",Position = "cl")
+            Canvas.AddPointSet((x,y), Color = "White", Diameter = 4)
+
+            x = -200
+
+            self.Canvas.AddScaledText("MODERN Font", x, 0, Size = 7, Family = wx.MODERN, Color = (0,0,0))
+            self.Canvas.AddScaledText("DECORATIVE Font", x, -10, Size = 7, Family = wx.DECORATIVE, Color = (0,0,1))
+            self.Canvas.AddScaledText("ROMAN Font", x, -20, Size = 7, Family = wx.ROMAN)
+            self.Canvas.AddScaledText("SCRIPT Font", x, -30, Size = 7, Family = wx.SCRIPT)
+            self.Canvas.AddScaledText("ROMAN BOLD Font", x, -40, Size = 7, Family = wx.ROMAN, Weight=wx.BOLD)
+            self.Canvas.AddScaledText("ROMAN ITALIC BOLD Font", x, -50, Size = 7, Family = wx.ROMAN, Weight=wx.BOLD, Style=wx.ITALIC)
+            Canvas.AddPointSet((x,0), Color = "White", Diameter = 4)
+
+
+            # NOTE: this font exists on my Linux box..who knows were else you'll find it!
+            x,y = (-100, 50)
+            Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
+            T = self.Canvas.AddScaledText("zapf chancery Font", x, y, Size = 20, Font = Font, Position = 'bc')
+
+            x,y = (-50, -50)
+            Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "bookman")
+            T = self.Canvas.AddScaledText("Bookman Font", x, y, Size = 8, Font = Font)
+
             self.Canvas.ZoomToBB()
             
         def DrawMap(self,event = None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
             import os, time
+            self.BindAllMouseEvents()
+            
         ## Test of Actual Map Data
-            self.Clear()
-            start = time.clock()
+            self.Canvas.ClearAll()
+            self.Canvas.SetProjectionFun("FlatEarth")
+            #start = time.clock()
             Shorelines = Read_MapGen(os.path.join("data",'world.dat'),stats = 0)
-            print "It took %f seconds to load %i shorelines"%(time.clock() - start,len(Shorelines) )
-            start = time.clock()
+            #print "It took %f seconds to load %i shorelines"%(time.clock() - start,len(Shorelines) )
+            #start = time.clock()
             for segment in Shorelines:
-                self.object_list.append(self.Canvas.AddLine(segment))
-            print "It took %f seconds to add %i shorelines"%(time.clock() - start,len(Shorelines) )
-            start = time.clock()
+                self.Canvas.AddLine(segment)
+            #print "It took %f seconds to add %i shorelines"%(time.clock() - start,len(Shorelines) )
+            #start = time.clock()
             self.Canvas.ZoomToBB()
-            print "It took %f seconds to draw %i shorelines"%(time.clock() - start,len(Shorelines) )
-    
-    ##    def LineTest(self,event = None):
-    ##        wxGetApp().Yield()
-    ##        import os, time
-    ##        import random
-    ##        Range = (-10,10)
-    ##    ## Test of drawing lots of lines
-    ##        self.Clear()
-    ##        start = time.clock()
-    ##        linepoints = []
-    ##        linecolors = []
-    ##        linewidths = []
-    ##        linestyles = []
-    ##        for i in range(500):
-    ##            points = (random.randint(Range[0],Range[1]),
-    ##                     random.randint(Range[0],Range[1]))
-    ##            linepoints.append(points)
-    ##            points = (random.randint(Range[0],Range[1]),
-    ##                     random.randint(Range[0],Range[1]))
-    ##            linepoints.append(points)
-    ##            linewidths.append(random.randint(1,10) )
-    ##            linecolors.append(colors[random.randint(0,len(colors)-1) ])
-    ##            linestyles.append(LineStyles[random.randint(0, len(LineStyles)-1)])
-    
-    ##        self.object_list.append(self.Canvas.AddLineSet(linepoints, LineWidths = linewidths, LineColors = linecolors, LineStyles = linestyles))
-    ##        print "It took %f seconds to add %i lines"%(time.clock() - start,len(linepoints) )
-    ##        start = time.clock()
-    ##        self.Canvas.ZoomToBB()
-    ##        print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
+            #print "It took %f seconds to draw %i shorelines"%(time.clock() - start,len(Shorelines) )
     
         def LineTest(self,event = None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
             import os, time
-            import random
+#            import random
+            colors = self.colors
             Range = (-10,10)
         ## Test of drawing lots of lines
-            self.Clear()
-            start = time.clock()
+            Canvas = self.Canvas
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+            #start = time.clock()
             linepoints = []
             linecolors = []
             linewidths = []
@@ -311,12 +967,201 @@ else:
                 linewidths.append(random.randint(1,10) )
                 linecolors.append(random.randint(0,len(colors)-1) )
             for (points,color,width) in zip(linepoints,linecolors,linewidths):
-                self.object_list.append(self.Canvas.AddLine((points[0:2],points[2:4]), LineWidth = width, LineColor = colors[color]))
-            print "It took %f seconds to add %i lines"%(time.clock() - start,len(linepoints) )
+                Canvas.AddLine((points[0:2],points[2:4]), LineWidth = width, LineColor = colors[color])
+            #print "It took %f seconds to add %i lines"%(time.clock() - start,len(linepoints) )
+            #start = time.clock()
+            Canvas.ZoomToBB()
+            #print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
+
+        def SpeedTest(self,event=None):
+            wx.GetApp().Yield(True)
+#            import random
+#            import RandomArray
+            BigRange = (-1000,1000)
+            colors = self.colors
+
+            self.UnBindAllMouseEvents()
+            Canvas = self.Canvas
+
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+            # Lots of Text
+            String = "Unscaled text"
+            coords = []
+            text = []
+            for i in range(5000):
+                x,y = (random.uniform(BigRange[0],BigRange[1]),random.uniform(BigRange[0],BigRange[1]))
+                coords.append( (x,y) )
+            print "Drawing the Numbers"
             start = time.clock()
-            self.Canvas.ZoomToBB()
-            print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
-    
+            for i in xrange( len(coords) ):
+                Canvas.AddText("%i"%(i),
+                               coords[i][0],
+                               coords[i][1],
+                               Size = 12,
+                               Position = "cc",
+                               BackgroundColor = "White")
+            print "It took %s seconds to add the numbers"%(time.clock() - start)
+
+
+##            ObjectList = []
+
+##            print "Building a list of lots of random objects"
+##            ##               Random tests of everything:
+##            def MakeRange():
+##                while True:
+##                    Range = ( random.randint(BigRange[0],BigRange[1]), random.randint(BigRange[0],BigRange[1]) ) 
+##                    if abs (Range[0] -  Range[1]) < 10:
+##                        continue
+##                    if Range[0] > Range[1]:
+##                        Range = ( Range[1], Range[0] )
+##                    break
+##                return Range
+
+##            # Rectangles
+##            for i in range(300):
+##                Range = MakeRange()
+##                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+##                lw = random.randint(1,5)
+##                cf = random.randint(0,len(colors)-1)
+##                h = random.randint(1, Range[1] - Range[0])
+##                w = random.randint(1, Range[1] - Range[0])
+##                ObjectList.append(FloatCanvas.Rectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+              
+##            # Ellipses
+##            for i in range(300):
+##                Range = MakeRange()
+##                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+##                lw = random.randint(1,5)
+##                cf = random.randint(0,len(colors)-1)
+##                h = random.randint(1, Range[1] - Range[0])
+##                w = random.randint(1, Range[1] - Range[0])
+##                ObjectList.append(FloatCanvas.Ellipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+              
+            
+##            # Circles
+##            for i in range(500):
+##                Range = MakeRange()
+##                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+##                D = random.randint(1, (Range[1] - Range[0]) / 2)
+##                lw = random.randint(1,5)
+##                cf = random.randint(0,len(colors)-1)
+##                cl = random.randint(0,len(colors)-1)
+##                ObjectList.append(FloatCanvas.Circle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf]))
+##                #ObjectList.append(FloatCanvas.Text("Circle # %i"%(i),x,y,Size = 12,BackgroundColor = None,Position = "cc"))
+              
+##                # Lines
+##            for i in range(500):
+##                Range = MakeRange()
+##                points = []
+##                for j in range(random.randint(2,100)):
+##                    point = (random.randint(Range[0],Range[1]),random.randint(Range[0],Range[1]))
+##                    points.append(point)
+##                lw = random.randint(1,10)
+##                cf = random.randint(0,len(colors)-1)
+##                cl = random.randint(0,len(colors)-1)
+##                ObjectList.append(FloatCanvas.Line(points, LineWidth = lw, LineColor = colors[cl]) )
+              
+##                # Polygons
+##            for i in range(300):
+##                Range = MakeRange()
+##                points = []
+##                for j in range(random.randint(2,60)):
+##                    point = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+##                    points.append(point)
+##                lw = random.randint(1,6)
+##                cf = random.randint(0,len(colors)-1)
+##                cl = random.randint(0,len(colors)-1)
+##                ObjectList.append(FloatCanvas.Polygon(points,
+##                                       LineWidth = lw,
+##                                       LineColor = colors[cl],
+##                                       FillColor = colors[cf],
+##                                       FillStyle = 'Solid') )
+##            random.shuffle(ObjectList)
+##            print "Adding lots of random objects"
+##            start = time.clock()
+##            for Object in ObjectList:
+##                Canvas.AddObject(Object)
+##            print "It took %s Seconds to add %i objects "%(time.clock() - start, len(ObjectList) )
+
+##            ## Pointset
+##            for i in range(100):
+##                points = []
+##                points = RandomArray.uniform(Range[0],Range[1],(100,2))
+##                cf = random.randint(0,len(colors)-1)
+##                D = random.randint(1,4)
+##                Canvas.AddPointSet(points, Color = colors[cf], Diameter = D)
+            
+
+##            # Scaled Text
+##            String = "Scaled text"
+##            for i in range(30):
+##                ts = random.random()*3 + 0.2
+##                cf = random.randint(0,len(colors)-1)
+##                x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+##                Canvas.AddScaledText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+            Canvas.ZoomToBB()
+
+
+        def TempTest(self, event= None):
+            "Running the Temporary test"
+            wx.GetApp().Yield(True)
+
+            self.UnBindAllMouseEvents()
+            Canvas = self.Canvas
+            Canvas.ClearAll()
+            Canvas.SetProjectionFun(None)
+
+#            import random
+#            import RandomArray
+            Range = (-10,10)
+
+            # Create a random Polygon
+            points = []
+            for j in range(6):
+                point = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+                points.append(point)
+            Poly = Canvas.AddPolygon(points,
+                                     LineWidth = 2,
+                                     LineColor = "Black",
+                                     FillColor = "LightBlue",
+                                     FillStyle = 'Solid')
+
+            Poly.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.SelectPoly)
+
+            self.SelectedPoly = None
+            self.SelectPoints = []
+            self.SelectedPoint = None
+            
+            Canvas.ZoomToBB()
+
+        def SelectPoly(self, Object):
+            print "In SelectPoly"
+            Canvas = self.Canvas
+            if Object is self.SelectedPoly:
+                pass
+            else:
+                #fixme: Do something to unselect the old one
+                self.SelectedPoly = Object
+                Canvas.RemoveObjects(self.SelectPoints)
+                self.SelectPoints = []
+            # Draw points on the Vertices of the Selected Poly:
+            for i, point in enumerate(Object.Points):
+                P = Canvas.AddPointSet(point, Diameter = 6, Color = "Red")
+                P.VerticeNum = i
+                P.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.SelectPointHit)
+                self.SelectPoints.append(P)
+            #Canvas.ZoomToBB()
+            Canvas.Draw()
+
+        def SelectPointHit(self, Point):
+            print "Point Num: %i Hit"%Point.VerticeNum
+            self.SelectedPoint = Point
+
+            
+   
     class DemoApp(wx.App):
         """
         How the demo works:
@@ -366,17 +1211,41 @@ else:
         Chris.Barker@noaa.gov
     
         """
+
+        def __init__(self, *args, **kwargs):
+            wx.App.__init__(self, *args, **kwargs)
         
         def OnInit(self):
-            global colors
-            wx.lib.colourdb.updateColourDB()
-            colors = wx.lib.colourdb.getColourList()
-            
-            frame = DrawFrame(None, -1, "FloatCanvas Demo App",
-                              wx.DefaultPosition, (700,700))
+            wx.InitAllImageHandlers()
+            frame = DrawFrame(None, -1, "FloatCanvas Demo App",wx.DefaultPosition,(700,700))
     
             self.SetTopWindow(frame)
-    
+            frame.Show()
+
+            ## check to see if the demo is set to start in a particular mode.
+            if StartUpDemo == "text":
+                frame.TestText()
+            if StartUpDemo == "stext":
+                frame.TestScaledText()
+            elif StartUpDemo == "all":
+                frame.DrawTest()
+            elif StartUpDemo == "map":
+                frame.DrawMap()
+            elif StartUpDemo == "hit":
+                frame.TestHitTest()
+            elif StartUpDemo == "hitf":
+                "starting TestHitTestForeground"
+                frame.TestHitTestForeground()
+            elif StartUpDemo == "animate":
+                "starting TestAnimation"
+                frame.TestAnimation()
+            elif StartUpDemo == "speed":
+                "starting SpeedTest"
+                frame.SpeedTest()
+            elif StartUpDemo == "temp":
+                "starting temp Test"
+                frame.TempTest()
+                
             return True
                 
     def Read_MapGen(filename,stats = 0,AllLines=0):
@@ -391,7 +1260,6 @@ else:
         
         """
         import string
-        from Numeric import array
         file = open(filename,'rt')
         data = file.readlines()
         data = map(string.strip,data)
@@ -401,11 +1269,11 @@ else:
         for line in data:
             if line:
                 if line == "# -b": #New segment beginning
-                    if segment: Shorelines.append(array(segment))
+                    if segment: Shorelines.append(Numeric.array(segment))
                     segment = []
                 else:
                     segment.append(map(float,string.split(line)))
-        if segment: Shorelines.append(array(segment))
+        if segment: Shorelines.append(Numeric.array(segment))
         
         if stats:
             NumSegments = len(Shorelines)
@@ -423,49 +1291,43 @@ else:
                     Lines.append(point)
                     Lines.append(point)
                 Lines.append(segment[-1])
-            #print Shorelines
-            #for point in Lines: print point
             return Lines
         else:
             return Shorelines
-
-
-    #----------------------------------------------------------------------
-    
-    def runTest(frame, nb, log):
-        """
-        This method is used by the wxPython Demo Framework for integrating
-        this demo with the rest.
-        """
-        global colors
-        wx.lib.colourdb.updateColourDB()
-        colors = wx.lib.colourdb.getColourList()
-
-        win = DrawFrame(None, -1, "FloatCanvas Drawing Window",
-                        wx.DefaultPosition, (500,500))
-        frame.otherWin = win
-        win.Show(True)
-
-
-
     
     ## for the wxPython demo:
-    overview = floatcanvas.FloatCanvas.__doc__
-
+    try:
+        import floatcanvas
+    except ImportError: # if it's not there locally, try the wxPython lib.
+        from wx.lib import floatcanvas
 
+    overview = floatcanvas.__doc__
       
 if __name__ == "__main__":
     if not haveNumeric:
         print errorText
     else:
-        app = DemoApp(0)
-
-        import  wx.lib.colourdb
-        wx.lib.colourdb.updateColourDB()
-        colors = wx.lib.colourdb.getColourList()
-
+        app = DemoApp(False)# put in True if you want output to go to it's own window.
         app.MainLoop()
     
     
     
     
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
index 884b8ee32c65e2b343ffc866b6b84d0ed1bc9ca0..bd0785da86b3839327e4b9c8400e7886ab84df24 100644 (file)
@@ -32,6 +32,7 @@ _treeList = [
     # new stuff
     ('Recent Additions and Updates', [
         'OGL',
+        'FloatCanvas',
         ]),
 
     # managed windows == things with a (optional) caption you can close
index 2fc48e13ae2a13814c6ee6cc51443694308493b8..0ebfdd7032fa4ccf50f514311b2fb3facd87f5a9 100644 (file)
@@ -72,6 +72,7 @@ wxPython/wx/lib/editor
 wxPython/wx/lib/masked
 wxPython/wx/lib/mixins
 wxPython/wx/lib/ogl
+wxPython/wx/lib/floatcanvas
 wxPython/wx/py
 wxPython/wx/py/tests
 wxPython/wxPython
index 740c5632aea9165cebb14388e2194d9ca0a19efb..c78854abaaf0765952c6e928c9bc5663433f2756 100644 (file)
@@ -108,6 +108,7 @@ Source: "wx\lib\editor\*.txt";                 DestDir: "{app}\wx\lib\editor"; C
 Source: "wx\lib\mixins\*.py";                  DestDir: "{app}\wx\lib\mixins"; Components: core
 Source: "wx\lib\masked\*.py";                  DestDir: "{app}\wx\lib\masked"; Components: core
 Source: "wx\lib\ogl\*.py";                     DestDir: "{app}\wx\lib\ogl"; Components: core
+Source: "wx\lib\floatcanvas\*.py";             DestDir: "{app}\wx\lib\floatcanvas"; Components: core
 Source: "wx\py\*.py";                          DestDir: "{app}\wx\py"; Components: core
 Source: "wx\py\*.txt";                         DestDir: "{app}\wx\py"; Components: core
 Source: "wx\py\*.ico";                         DestDir: "{app}\wx\py"; Components: core
@@ -278,6 +279,13 @@ Type: files; Name: "{app}\wx\lib\editor\*.pyc";
 Type: files; Name: "{app}\wx\lib\editor\*.pyo";
 Type: files; Name: "{app}\wx\lib\mixins\*.pyc";
 Type: files; Name: "{app}\wx\lib\mixins\*.pyo";
+Type: files; Name: "{app}\wx\lib\masked\*.pyc";
+Type: files; Name: "{app}\wx\lib\masked\*.pyo";
+Type: files; Name: "{app}\wx\lib\ogl\*.pyc";
+Type: files; Name: "{app}\wx\lib\ogl\*.pyo";
+Type: files; Name: "{app}\wx\lib\floatcanvas\*.pyc";
+Type: files; Name: "{app}\wx\lib\floatcanvas\*.pyo";
+
 Type: files; Name: "{app}\wx\py\*.pyc";
 Type: files; Name: "{app}\wx\py\*.pyo";
 Type: files; Name: "{app}\wx\py\tests\*.pyc";
index cdd70241ee3cde329480193a602dbfd0006c4006..90d10c04858090d921036cd64f80d25cea80d999 100644 (file)
@@ -109,6 +109,9 @@ Removed the deprecated ErrorDialogs and PythonBitmaps modules.  If you
 were using these in your apps then please join wxPython-dev and assist
 with a more modern reimplementation.
 
+Added a new version (0.8.3)of FloatCanvas from Chris Barker.  It's now
+in a subpackage of wx.lib.
+
 
 
 
index 4960145ec4d2a1decc30b5f6f5be622112bfe992..63c3f232023b95a09b775539d7104d774c174b2c 100755 (executable)
@@ -705,6 +705,7 @@ if __name__ == "__main__":
                           'wx.lib',
                           'wx.lib.colourchooser',
                           'wx.lib.editor',
+                          'wx.lib.floatcanvas',
                           'wx.lib.masked',
                           'wx.lib.mixins',
                           'wx.lib.ogl',
diff --git a/wxPython/wx/lib/floatcanvas.py b/wxPython/wx/lib/floatcanvas.py
deleted file mode 100644 (file)
index 536b161..0000000
+++ /dev/null
@@ -1,1355 +0,0 @@
-import wx
-
-from Numeric import array,Float,cos,pi,sum,minimum,maximum,Int32
-from time import clock, sleep
-import types
-import os        
-
-ID_ZOOM_IN_BUTTON = wx.NewId()
-ID_ZOOM_OUT_BUTTON = wx.NewId()
-ID_ZOOM_TO_FIT_BUTTON = wx.NewId()
-ID_MOVE_MODE_BUTTON = wx.NewId()
-ID_TEST_BUTTON = wx.NewId()
-
-ID_ABOUT_MENU = wx.NewId()          
-ID_EXIT_MENU  = wx.NewId() 
-ID_ZOOM_TO_FIT_MENU = wx.NewId()
-ID_DRAWTEST_MENU = wx.NewId()
-ID_DRAWMAP_MENU = wx.NewId()
-ID_CLEAR_MENU = wx.NewId()
-
-ID_TEST = wx.NewId()
-
-
-### These are some functions for bitmaps of icons.
-import cPickle, zlib
-
-def GetHandData():
-    return cPickle.loads(zlib.decompress(
-'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
-\x01\xc8S\xb6t\x06A(\x1f\x0b\xa0\xa9\x8c\x9e\x1e6\x19\xa0\xa8\x1e\x88\xd4C\
-\x97\xd1\x83\xe8\x80 \x9c2zh\xa6\xc1\x11X\n\xab\x8c\x02\x8a\x0cD!\x92\x12\
-\x98\x8c\x1e\x8a\x8b\xd1d\x14\xf4\x90%\x90LC\xf6\xbf\x1e\xba\xab\x91%\xd0\
-\xdc\x86C\x06\xd9m\xe8!\xaa\x87S\x86\x1a1\xa7\x07\x00v\x0f[\x17' ))
-
-def GetHandBitmap():
-    return wx.BitmapFromXPMData(GetHandData())
-
-#----------------------------------------------------------------------
-def GetPlusData():
-    return cPickle.loads(zlib.decompress(
-'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
-\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=l2`\r\
-\xe82HF\xe9a\xc8\xe8\xe9A\x9c@\x8a\x0c\x0e\xd3p\xbb\x00\x8f\xab\xe1>\xd5\xd3\
-\xc3\x15:P)l!\n\x91\xc2\x1a\xd6`)\xec\xb1\x00\x92\xc2\x11?\xb8e\x88\x8fSt\
-\x19=\x00\x82\x16[\xf7' ))
-
-def GetPlusBitmap():
-    return wx.BitmapFromXPMData(GetPlusData())
-
-#----------------------------------------------------------------------
-def GetMinusData():
-    return cPickle.loads(zlib.decompress(
-'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
-\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=\xa2e\
-\x10\x16@\x99\xc82zz\x10\'\x90"\x83\xc34r\xdc\x86\xf0\xa9\x9e\x1e\xae\xd0\
-\x81Ja\x0bQ\x88\x14\xd6\xb0\x06Ka\x8f\x05\x90\x14\x8e\xf8\xc1-C|\x9c\xa2\xcb\
-\xe8\x01\x00\xed\x0f[\x87' ))
-
-def GetMinusBitmap():
-    return wx.BitmapFromXPMData(GetMinusData())
-
-## This is a bunch of stuff for implimenting interactive use: catching
-## when objects are clicked on by the mouse, etc. I've made a start, so if
-## you are interesed in making that work, let me know, I may have gotten
-## it going by then
-
-#### I probably want a full set of events someday:
-## #### mouse over, right click, left click mouse up etc, etc.
-## ##FLOATCANVAS_EVENT_LEFT_DOWN = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_LEFT_UP = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_RIGHT_DOWN = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_RIGHT_UP = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_MOUSE_OVER = wx.NewEventType()
-
-##WXFLOATCANVASEVENT = wx.NewEventType()
-
-##def EVT_WXFLOATCANVASEVENT( window, function ): 
-
-##    """Your documentation here""" 
-
-##    window.Connect( -1, -1, WXFLOATCANVASEVENT, function ) 
-
-##class wxFloatCanvasObjectEvent(wx.PyCommandEvent):
-##    def __init__(self, WindowID,Object):
-##        wx.PyCommandEvent.__init__(self, WXFLOATCANVASEVENT, WindowID)
-##        self.Object = Object
-
-##    def Clone( self ): 
-##        self.__class__( self.GetId() ) 
-
-##class ColorGenerator:
-
-##    """ An instance of this class generates a unique color each time
-##    GetNextColor() is called. Someday I will use a proper Python
-##    generator for this class.
-
-##    The point of this generator is for the hit-test bitmap, each object
-##    needs to be a unique color. Also, each system can be running a
-##    different number of colors, and it doesn't appear to be possible to
-##    have a wxMemDC with a different colordepth as the screen so this
-##    generates colors far enough apart that they can be distinguished on
-##    a 16bit screen. Anything less than 16bits won't work.
-##    """
-
-##    def __init__(self,depth = 16):
-##        self.r = 0
-##        self.g = 0
-##        self.b = 0
-##        if depth == 16:
-##            self.step = 8
-##        elif depth >= 24:
-##            self.step = 1
-##        else:
-##            raise "ColorGenerator does not work with depth = %s"%depth
-
-##    def GetNextColor(self):
-##        step = self.step
-##        ##r,g,b = self.r,self.g,self.b
-##        self.r += step
-##        if self.r > 255:
-##            self.r = step
-##            self.g += step
-##            if self.g > 255:
-##                self.g = step
-##                self.b += step
-##                if self.b > 255:
-##                    ## fixme: this should be a derived exception
-##                    raise "Too many objects for HitTest"
-##        return (self.r,self.g,self.b)
-
-
-class draw_object:
-        """
-        This is the base class for all the objects that can be drawn.
-        
-        each object has the following properties; (incomplete)
-        
-        BoundingBox :  is of the form: array((min_x,min_y),(max_x,max_y)) 
-        Pen
-        Brush
-
-        """
-
-        def __init__(self,Foreground  = 0):
-            self.Foreground = Foreground
-
-            self._Canvas = None
-
-        # I pre-define all these as class variables to provide an easier
-        # interface, and perhaps speed things up by caching all the Pens
-        # and Brushes, although that may not help, as I think wx now
-        # does that on it's own. Send me a note if you know!
-
-        BrushList = {
-                ( None,"Transparent")  : wx.TRANSPARENT_BRUSH,
-                ("Blue","Solid")       : wx.BLUE_BRUSH,
-                ("Green","Solid")      : wx.GREEN_BRUSH,
-                ("White","Solid")      : wx.WHITE_BRUSH,
-                ("Black","Solid")      : wx.BLACK_BRUSH,
-                ("Grey","Solid")       : wx.GREY_BRUSH,
-                ("MediumGrey","Solid") : wx.MEDIUM_GREY_BRUSH,
-                ("LightGrey","Solid")  : wx.LIGHT_GREY_BRUSH,
-                ("Cyan","Solid")       : wx.CYAN_BRUSH,
-                ("Red","Solid")        : wx.RED_BRUSH
-                        }
-        PenList = {
-                (None,"Transparent",1)   : wx.TRANSPARENT_PEN,
-                ("Green","Solid",1)      : wx.GREEN_PEN,
-                ("White","Solid",1)      : wx.WHITE_PEN,
-                ("Black","Solid",1)      : wx.BLACK_PEN,
-                ("Grey","Solid",1)       : wx.GREY_PEN,
-                ("MediumGrey","Solid",1) : wx.MEDIUM_GREY_PEN,
-                ("LightGrey","Solid",1)  : wx.LIGHT_GREY_PEN,
-                ("Cyan","Solid",1)       : wx.CYAN_PEN,
-                ("Red","Solid",1)        : wx.RED_PEN
-                }
-        
-        FillStyleList = {
-                "Transparent"    : wx.TRANSPARENT,
-                "Solid"          : wx.SOLID,
-                "BiDiagonalHatch": wx.BDIAGONAL_HATCH,
-                "CrossDiagHatch" : wx.CROSSDIAG_HATCH,
-                "FDiagonal_Hatch": wx.FDIAGONAL_HATCH,
-                "CrossHatch"     : wx.CROSS_HATCH,
-                "HorizontalHatch": wx.HORIZONTAL_HATCH,
-                "VerticalHatch"  : wx.VERTICAL_HATCH
-                }
-        
-        LineStyleList = {
-                "Solid"      : wx.SOLID,
-                "Transparent": wx.TRANSPARENT,
-                "Dot"        : wx.DOT,
-                "LongDash"   : wx.LONG_DASH,
-                "ShortDash"  : wx.SHORT_DASH,
-                "DotDash"    : wx.DOT_DASH,
-                }
-        
-        def SetBrush(self,FillColor,FillStyle):
-            if FillColor is None or FillStyle is None:
-                self.Brush = wx.TRANSPARENT_BRUSH
-                self.FillStyle = "Transparent"
-            else:
-                if not self.BrushList.has_key((FillColor,FillStyle)):
-                    self.BrushList[(FillColor,FillStyle)] = wx.Brush(FillColor,self.FillStyleList[FillStyle])
-                self.Brush = self.BrushList[(FillColor,FillStyle)]
-                
-        def SetPen(self,LineColor,LineStyle,LineWidth):
-            if (LineColor is None) or (LineStyle is None):
-                self.Pen = wx.TRANSPARENT_PEN
-                self.LineStyle = 'Transparent'
-            else:
-                if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
-                    self.PenList[(LineColor,LineStyle,LineWidth)] = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
-                self.Pen = self.PenList[(LineColor,LineStyle,LineWidth)]
-
-        def SetPens(self,LineColors,LineStyles,LineWidths):
-            """
-            This method used when an object could have a list of pens, rather than just one
-            It is used for LineSet, and perhaps others in the future.
-
-            fixme: this is really kludgy, there has got to be a better way!
-            
-            """
-
-            length = 1
-            if type(LineColors) == types.ListType:
-                length = len(LineColors)
-            else:
-                LineColors = [LineColors]
-
-            if type(LineStyles) == types.ListType:
-                length = len(LineStyles)
-            else:
-                LineStyles = [LineStyles]
-
-            if type(LineWidths) == types.ListType:
-                length = len(LineWidths)
-            else:
-                LineWidths = [LineWidths]
-
-            if length > 1:
-                if len(LineColors) == 1:
-                    LineColors = LineColors*length
-                if len(LineStyles) == 1:
-                    LineStyles = LineStyles*length
-                if len(LineWidths) == 1:
-                    LineWidths = LineWidths*length
-
-            self.Pens = []
-            for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):
-                if LineColor is None or LineStyle is None:
-                    self.Pens.append(wx.TRANSPARENT_PEN)
-                    # what's this for?> self.LineStyle = 'Transparent'
-                if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
-                    Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
-                    self.Pens.append(Pen)
-                else:
-                    self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])
-            if length == 1:
-                self.Pens = self.Pens[0]
-
-        def PutInBackground(self):
-            if self._Canvas and self.Foreground:
-                self._Canvas._TopDrawList.remove(self)
-                self._Canvas._DrawList.append(self)
-                self._Canvas._BackgroundDirty = 1
-                self.Foreground = 0
-
-        def PutInForeground(self):
-            if self._Canvas and (not self.Foreground):
-                self._Canvas._TopDrawList.append(self)
-                self._Canvas._DrawList.remove(self)
-                self._Canvas._BackgroundDirty = 1
-                self.Foreground = 1
-            
-        
-class Polygon(draw_object):
-
-    """
-
-    The Polygon class takes a list of 2-tuples, or a NX2 NumPy array of
-    point coordinates.  so that Points[N][0] is the x-coordinate of
-    point N and Points[N][1] is the y-coordinate or Points[N,0] is the
-    x-coordinate of point N and Points[N,1] is the y-coordinate for
-    arrays.
-
-    """
-    def __init__(self,Points,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
-        draw_object.__init__(self,Foreground)
-        self.Points = array(Points,Float)
-        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
-        self.LineColor = LineColor
-        self.LineStyle = LineStyle
-        self.LineWidth = LineWidth
-        self.FillColor = FillColor
-        self.FillStyle = FillStyle
-
-        self.SetPen(LineColor,LineStyle,LineWidth)
-        self.SetBrush(FillColor,FillStyle)
-
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        Points = WorldToPixel(self.Points)
-        dc.SetPen(self.Pen)
-        dc.SetBrush(self.Brush)
-        #dc.DrawPolygon(map(lambda x: (x[0],x[1]), Points.tolist()))
-        dc.DrawPolygon(Points)
-
-class PolygonSet(draw_object):
-    """
-    The PolygonSet class takes a Geometry.Polygon object.
-    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-    
-    it creates a set of line segments, from (x1,y1) to (x2,y2)
-    
-    """
-    
-    def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,Foreground = 0):
-        draw_object.__init__(self, Foreground)
-
-        ##fixme: there should be some error checking for everything being the right length.
-
-        
-        self.Points = array(Points,Float)
-        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
-        self.LineColors = LineColors
-        self.LineStyles = LineStyles
-        self.LineWidths = LineWidths
-        self.FillColors = FillColors
-        self.FillStyles = FillStyles
-
-        self.SetPens(LineColors,LineStyles,LineWidths)
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        Points = WorldToPixel(self.Points)
-        Points.shape = (-1,4)
-        dc.DrawLineList(Points,self.Pens)
-
-class Line(draw_object):
-    """
-    The Line class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
-    so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
-    or  Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
-
-    It will draw a straight line if there are two points, and a polyline if there are more than two.
-
-    """
-    def __init__(self,Points,LineColor,LineStyle,LineWidth,Foreground = 0):
-        draw_object.__init__(self, Foreground)
-
-        self.Points = array(Points,Float)
-        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
-        self.LineColor = LineColor
-        self.LineStyle = LineStyle
-        self.LineWidth = LineWidth
-
-        self.SetPen(LineColor,LineStyle,LineWidth)
-
-    def SetPoints(self,Points):
-        self.Points = Points
-        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-        if self._Canvas:
-            # It looks like this shouldn't be private
-            self._Canvas.BoundingBoxDirty = 1
-            
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        Points = WorldToPixel(self.Points)
-        dc.SetPen(self.Pen)
-        #dc.DrawLines(map(lambda x: (x[0],x[1]), Points.tolist()))
-        dc.DrawLines(Points)
-
-
-class LineSet(draw_object):
-    """
-    The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
-    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-    
-    it creates a set of line segments, from (x1,y1) to (x2,y2)
-    
-    """
-    
-    def __init__(self,Points,LineColors,LineStyles,LineWidths,Foreground = 0):
-        draw_object.__init__(self, Foreground)
-
-        NumLines = len(Points) / 2
-        ##fixme: there should be some error checking for everything being the right length.
-
-        
-        self.Points = array(Points,Float)
-        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
-        self.LineColors = LineColors
-        self.LineStyles = LineStyles
-        self.LineWidths = LineWidths
-
-        self.SetPens(LineColors,LineStyles,LineWidths)
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        Points = WorldToPixel(self.Points)
-        Points.shape = (-1,4)
-        dc.DrawLineList(Points,self.Pens)
-
-class PointSet(draw_object):
-        """
-        The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
-        so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
-        or  Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
-        
-        Each point will be drawn the same color and Diameter. The Diameter is in screen points,
-        not world coordinates.
-        
-        """
-        def __init__(self,Points,Color,Diameter,Foreground = 0):
-            draw_object.__init__(self,Foreground)
-
-            self.Points = array(Points,Float)
-            self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
-            self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-            
-            self.Color = Color
-            self.Diameter = Diameter
-            
-            self.SetPen(Color,"Solid",1)
-            self.SetBrush(Color,"Solid")
-
-        def SetPoints(self,Points):
-            self.Points = Points
-            self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-            if self._Canvas:
-                # It looks like this shouldn't be private
-                self._Canvas.BoundingBoxDirty = 1
-            
-        def _Draw(self,dc,WorldToPixel,ScaleFunction):
-            dc.SetPen(self.Pen)
-            Points = WorldToPixel(self.Points)
-            if self.Diameter <= 1:
-                dc.DrawPointList(Points)
-            elif self.Diameter <= 2:
-                # A Little optimization for a diameter2 - point
-                dc.DrawPointList(Points)
-                dc.DrawPointList(Points + (1,0))
-                dc.DrawPointList(Points + (0,1))
-                dc.DrawPointList(Points + (1,1))
-            else:
-                dc.SetBrush(self.Brush)
-                radius = int(round(self.Diameter/2))
-                for (x,y) in Points:
-                    dc.DrawEllipse((x - radius), (y - radius), self.Diameter, self.Diameter)
-
-
-                    
-class Dot(draw_object):
-    """
-    The Dot class takes an x.y coordinate pair, and the Diameter of the circle.
-    The Diameter is in pixels, so it won't change with zoom.
-
-    Also Fill and line data
-
-    """
-    def __init__(self,x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
-        draw_object.__init__(self,Foreground)
-
-        self.X = x
-        self.Y = y
-        self.Diameter = Diameter
-        # NOTE: the bounding box does not include the diameter of the dot, as that is in pixel coords.
-        # If this is  a problem, perhaps you should use a circle, instead!
-        self.BoundingBox = array(((x,y),(x,y)),Float)
-
-        self.LineColor = LineColor
-        self.LineStyle = LineStyle
-        self.LineWidth = LineWidth
-        self.FillColor = FillColor
-        self.FillStyle = FillStyle
-
-        self.SetPen(LineColor,LineStyle,LineWidth)
-        self.SetBrush(FillColor,FillStyle)
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        dc.SetPen(self.Pen)
-        dc.SetBrush(self.Brush)
-        radius = int(round(self.Diameter/2))
-        (X,Y) = WorldToPixel((self.X,self.Y))
-        dc.DrawEllipse((X - radius), (Y - radius), self.Diameter, self.Diameter)
-
-
-class Rectangle(draw_object):
-    def __init__(self,x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
-        draw_object.__init__(self,Foreground)
-
-        self.X = x
-        self.Y = y
-        self.Width = width
-        self.Height = height
-        self.BoundingBox = array(((x,y),(x+width,y+height)),Float)
-        self.LineColor = LineColor
-        self.LineStyle = LineStyle
-        self.LineWidth = LineWidth
-        self.FillColor = FillColor
-        self.FillStyle = FillStyle
-
-        self.SetPen(LineColor,LineStyle,LineWidth)
-        self.SetBrush(FillColor,FillStyle)
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        (X,Y) = WorldToPixel((self.X,self.Y))
-        (Width,Height) = ScaleFunction((self.Width,self.Height))
-
-        dc.SetPen(self.Pen)
-        dc.SetBrush(self.Brush)
-        dc.DrawRectangle(X,Y, Width,Height)
-
-class Ellipse(draw_object):
-    def __init__(self,x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
-        draw_object.__init__(self,Foreground)
-
-        self.X = x
-        self.Y = y
-        self.Width = width
-        self.Height = height
-        self.BoundingBox = array(((x,y),(x+width,y+height)),Float)
-        self.LineColor = LineColor
-        self.LineStyle = LineStyle
-        self.LineWidth = LineWidth
-        self.FillColor = FillColor
-        self.FillStyle = FillStyle
-
-        self.SetPen(LineColor,LineStyle,LineWidth)
-        self.SetBrush(FillColor,FillStyle)
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        (X,Y) = WorldToPixel((self.X,self.Y))
-        (Width,Height) = ScaleFunction((self.Width,self.Height))
-
-        dc.SetPen(self.Pen)
-        dc.SetBrush(self.Brush)
-        dc.DrawEllipse(X,Y, Width,Height)
-
-class Circle(draw_object):
-    def __init__(self,x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
-        draw_object.__init__(self,Foreground)
-
-        self.X = x
-        self.Y = y
-        self.Diameter = Diameter
-        self.BoundingBox = array(((x-Diameter/2,y-Diameter/2),(x+Diameter/2,y+Diameter/2)),Float)
-        self.LineColor = LineColor
-        self.LineStyle = LineStyle
-        self.LineWidth = LineWidth
-        self.FillColor = FillColor
-        self.FillStyle = FillStyle
-
-        self.SetPen(LineColor,LineStyle,LineWidth)
-        self.SetBrush(FillColor,FillStyle)
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        (X,Y) = WorldToPixel((self.X,self.Y))
-        (Diameter,dummy) = ScaleFunction((self.Diameter,self.Diameter))
-
-        dc.SetPen(self.Pen)
-        dc.SetBrush(self.Brush)
-        dc.DrawEllipse(X-Diameter/2,Y-Diameter/2, Diameter,Diameter)
-
-class Text(draw_object):
-    """
-
-    This class creates a text object, placed at the coordinates,
-    x,y. the "Position" argument is a two charactor string, indicating
-    where in relation to the coordinates the string should be oriented.
-
-    The first letter is:  t, c, or b, for top, center and bottom
-    The second letter is: l, c, or r, for left, center and right
-
-    I've provided arguments for Family, Style, and Weight of font, but
-    have not yet implimented them, so all text is:  wx.SWISS, wx.NORMAL, wx.NORMAL.
-    I'd love it if someone would impliment that!
-
-    The size is fixed, and does not scale with the drawing.
-
-    """
-    
-    def __init__(self,String,x,y,Size,ForeGround,BackGround,Family,Style,Weight,Underline,Position,Foreground = 0):
-        draw_object.__init__(self,Foreground)
-
-        self.String = String
-        self.Size = Size
-
-        self.ForeGround = ForeGround
-        if BackGround is None:
-            self.BackGround = None
-        else:
-            self.BackGround = BackGround
-        self.Family = Family
-        self.Style  = Style
-        self.Weight = Weight
-        self.Underline = Underline
-        self.Position = Position
-        # fixme: this should use the passed in parameters!
-        self.Font = wx.Font(Size, wx.MODERN, wx.NORMAL, wx.NORMAL, Underline, )
-
-        self.BoundingBox = array(((x,y),(x,y)),Float)
-
-        self.X = x
-        self.Y = y
-        self.x_shift = None
-        self.y_shift = None
-
-    def _Draw(self,dc,WorldToPixel,ScaleFunction):
-        (X,Y) = WorldToPixel((self.X,self.Y))
-        dc.SetFont(self.Font)
-        dc.SetTextForeground(self.ForeGround)
-        if self.BackGround:
-            dc.SetBackgroundMode(wx.SOLID)
-            dc.SetTextBackground(self.BackGround)
-        else:
-            dc.SetBackgroundMode(wx.TRANSPARENT)
-
-        # compute the shift, and adjust the coordinates, if neccesary
-        # This had to be put in here, becsuse there is no wx.DC during __init__
-        if self.x_shift is None or self.y_shift is None:
-            if self.Position == 'tl':
-                x_shift,y_shift = 0,0
-            else:
-                (w,h) = dc.GetTextExtent(self.String)
-                if self.Position[0] == 't':
-                    y_shift = 0
-                elif self.Position[0] == 'c':
-                    y_shift = h/2
-                elif self.Position[0] == 'b':
-                    y_shift = h
-                else:
-                    ##fixme: this should be a real derived exception
-                    raise "Invalid value for Text Object Position Attribute"
-                if self.Position[1] == 'l':
-                    x_shift = 0
-                elif self.Position[1] == 'c':
-                    x_shift = w/2
-                elif self.Position[1] == 'r':
-                    x_shift = w
-                else:
-                    ##fixme: this should be a real derived exception
-                    raise "Invalid value for Text Object Position Attribute"
-            self.x_shift = x_shift
-            self.y_shift = y_shift
-        dc.DrawText(self.String, X-self.x_shift, Y-self.y_shift)
-
-
-#---------------------------------------------------------------------------
-    
-class FloatCanvas(wx.Panel):
-    """
-    FloatCanvas.py
-
-    This is a high level window for drawing maps and anything else in an
-    arbitrary coordinate system.
-
-    The goal is to provide a convenient way to draw stuff on the screen
-    without having to deal with handling OnPaint events, converting to pixel
-    coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
-    also provides virtually unlimited zooming and scrolling
-
-    I am using it for two things:
-    1) general purpose drawing in floating point coordinates
-    2) displaying map data in Lat-long coordinates
-
-    If the projection is set to None, it will draw in general purpose
-    floating point coordinates. If the projection is set to 'FlatEarth', it
-    will draw a FlatEarth projection, centered on the part of the map that
-    you are viewing. You can also pass in your own projection function.
-
-    It is double buffered, so re-draws after the window is uncovered by something
-    else are very quick.
-
-    It relies on NumPy, which is needed for speed
-
-    Bugs and Limitations:
-        Lots: patches, fixes welcome
-
-    For Map drawing: It ignores the fact that the world is, in fact, a
-    sphere, so it will do strange things if you are looking at stuff near
-    the poles or the date line. so far I don't have a need to do that, so I
-    havn't bothered to add any checks for that yet.
-
-    Zooming:
-    I have set no zoom limits. What this means is that if you zoom in really 
-    far, you can get integer overflows, and get wierd results. It
-    doesn't seem to actually cause any problems other than wierd output, at
-    least when I have run it.
-
-    Speed:
-    I have done a couple of things to improve speed in this app. The one
-    thing I have done is used NumPy Arrays to store the coordinates of the
-    points of the objects. This allowed me to use array oriented functions
-    when doing transformations, and should provide some speed improvement
-    for objects with a lot of points (big polygons, polylines, pointsets).
-
-    The real slowdown comes when you have to draw a lot of objects, because
-    you have to call the wx.DC.DrawSomething call each time. This is plenty
-    fast for tens of objects, OK for hundreds of objects, but pretty darn
-    slow for thousands of objects.
-
-    The solution is to be able to pass some sort of object set to the DC
-    directly. I've used DC.DrawPointList(Points), and it helped a lot with
-    drawing lots of points. I havn't got a LineSet type object, so I havn't
-    used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList()
-    methods implimented, and then I'd also have a full set of Object sets
-    that could take advantage of them. I hope to get to it some day.
-
-    Copyright: Christopher Barker
-
-    License: Same as wxPython
-
-    Please let me know if you're using this!!!
-
-    Contact me at:
-
-    Chris.Barker@noaa.gov
-
-    """ 
-    
-    def __init__(self, parent, id = -1,
-                 size = wx.DefaultSize,
-                 ProjectionFun = None,
-                 BackgroundColor = "WHITE",
-                 Debug = 0,
-                 EnclosingFrame = None,
-                 UseToolbar = 1,
-                 UseBackground = 0,
-                 UseHitTest = 0):
-
-        wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
-
-        if ProjectionFun == 'FlatEarth':
-            self.ProjectionFun = self.FlatEarthProjection 
-        elif type(ProjectionFun) == types.FunctionType:
-            self.ProjectionFun = ProjectionFun 
-        elif ProjectionFun is None:
-            self.ProjectionFun = lambda x=None: array( (1,1), Float)
-        else:
-            raise('Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector')
-
-        self.UseBackground = UseBackground
-        self.UseHitTest = UseHitTest
-
-        self.NumBetweenBlits = 40
-
-        ## you can have a toolbar with buttons for zoom-in, zoom-out and
-        ## move.  If you don't use the toolbar, you should provide your
-        ## own way of navigating the canvas
-        if UseToolbar:
-            ## Create the vertical sizer for the toolbar and Panel
-            box = wx.BoxSizer(wx.VERTICAL)
-            box.Add(self.BuildToolbar(), 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
-            
-            self.DrawPanel = wx.Window(self,-1,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER)
-            box.Add(self.DrawPanel,1,wx.GROW)
-            
-            box.Fit(self)
-            self.SetAutoLayout(True)
-            self.SetSizer(box)
-        else:
-            self.DrawPanel = self
-
-        self.DrawPanel.BackgroundBrush = wx.Brush(BackgroundColor,wx.SOLID)
-
-        self.Debug = Debug
-
-        self.EnclosingFrame = EnclosingFrame
-        
-        wx.EVT_PAINT(self.DrawPanel, self.OnPaint)
-        wx.EVT_SIZE(self.DrawPanel, self.OnSize)
-        
-        wx.EVT_LEFT_DOWN(self.DrawPanel, self.LeftButtonEvent)
-        wx.EVT_LEFT_UP(self.DrawPanel, self.LeftButtonEvent)
-        wx.EVT_RIGHT_DOWN(self.DrawPanel, self.RightButtonEvent)
-        wx.EVT_MOTION(self.DrawPanel,    self.LeftButtonEvent)
-        
-        
-        self._DrawList = []
-        if self.UseBackground:
-            self._TopDrawList = []
-        self.BoundingBox = None
-        self.BoundingBoxDirty = 0
-        self.ViewPortCenter= array( (0,0), Float)
-        
-        self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
-        self.TransformVector = array( (1,-1), Float) # default Transformation
-        
-        self.Scale = 1
-
-        self.GUIMode = None
-        self.StartRBBox = None
-        self.PrevRBBox = None
-        self.StartMove = None
-        self.PrevMoveBox = None
-        # called just to make sure everything is initialized
-        if wx.Platform != "__WXMAC__":
-               self.OnSize(None)
-
-        
-    def BuildToolbar(self):
-        tb = wx.ToolBar(self,-1)
-        self.ToolBar = tb
-        
-        tb.SetToolBitmapSize((23,23))
-        
-        tb.AddTool(ID_ZOOM_IN_BUTTON, GetPlusBitmap(), isToggle=True,shortHelpString = "Zoom In")
-        wx.EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetMode)
-        
-        tb.AddTool(ID_ZOOM_OUT_BUTTON, GetMinusBitmap(), isToggle=True,shortHelpString = "Zoom Out")
-        wx.EVT_TOOL(self, ID_ZOOM_OUT_BUTTON, self.SetMode)
-        
-        tb.AddTool(ID_MOVE_MODE_BUTTON, GetHandBitmap(), isToggle=True,shortHelpString = "Move")
-        wx.EVT_TOOL(self, ID_MOVE_MODE_BUTTON, self.SetMode)
-        
-        tb.AddSeparator()
-        
-        tb.AddControl(wx.Button(tb, ID_ZOOM_TO_FIT_BUTTON, "Zoom To Fit",wx.DefaultPosition, wx.DefaultSize))
-        wx.EVT_BUTTON(self, ID_ZOOM_TO_FIT_BUTTON, self.ZoomToFit)
-
-        tb.Realize()
-        return tb
-
-    def SetMode(self,event):
-        for id in [ID_ZOOM_IN_BUTTON,ID_ZOOM_OUT_BUTTON,ID_MOVE_MODE_BUTTON]:
-            self.ToolBar.ToggleTool(id,0)
-        self.ToolBar.ToggleTool(event.GetId(),1)
-        if event.GetId() == ID_ZOOM_IN_BUTTON:
-            self.SetGUIMode("ZoomIn")
-        elif event.GetId() == ID_ZOOM_OUT_BUTTON:
-            self.SetGUIMode("ZoomOut")
-        elif event.GetId() == ID_MOVE_MODE_BUTTON:
-            self.SetGUIMode("Move")
-
-    
-    def SetGUIMode(self,Mode):
-        if Mode in ["ZoomIn","ZoomOut","Move",None]:
-            self.GUIMode = Mode
-        else:
-            raise "Not a valid Mode"
-            
-    def FlatEarthProjection(self,CenterPoint):
-        return array((cos(pi*CenterPoint[1]/180),1),Float)
-        
-    def LeftButtonEvent(self,event):
-        if self.EnclosingFrame:
-            if event.Moving:
-                position = self.PixelToWorld((event.GetX(),event.GetY()))
-                self.EnclosingFrame.SetStatusText("%8.3f, %8.3f"%tuple(position))
-        if self.GUIMode:
-            if self.GUIMode == "ZoomIn":
-                if event.LeftDown():
-                    self.StartRBBox = (event.GetX(),event.GetY())
-                    self.PrevRBBox = None
-                elif event.Dragging() and event.LeftIsDown() and self.StartRBBox:
-                    x0,y0 = self.StartRBBox
-                    x1,y1 = event.GetX(),event.GetY()
-                    w, h = abs(x1-x0),abs(y1-y0)
-                    w = max(w,int(h*self.AspectRatio))
-                    h = int(w/self.AspectRatio)
-                    x_c, y_c = (x0+x1)/2 , (y0+y1)/2
-                    dc = wx.ClientDC(self.DrawPanel)
-                    dc.BeginDrawing()
-                    dc.SetPen(wx.Pen('WHITE', 2,wx.SHORT_DASH))
-                    dc.SetBrush(wx.TRANSPARENT_BRUSH)
-                    dc.SetLogicalFunction(wx.XOR)
-                    if self.PrevRBBox:
-                        dc.DrawRectangle(*self.PrevRBBox)
-                    dc.DrawRectangle(x_c-w/2,y_c-h/2,w,h)
-                    self.PrevRBBox = (x_c-w/2,y_c-h/2,w,h)
-                    dc.EndDrawing()
-                    
-                elif event.LeftUp() and self.StartRBBox :
-                    self.PrevRBBox = None
-                    EndRBBox = (event.GetX(),event.GetY())
-                    StartRBBox = self.StartRBBox
-                    # if mouse has moved less that ten pixels, don't use the box.
-                    if abs(StartRBBox[0] - EndRBBox[0]) > 10 and abs(StartRBBox[1] - EndRBBox[1]) > 10:
-                        EndRBBox = self.PixelToWorld(EndRBBox)
-                        StartRBBox = self.PixelToWorld(StartRBBox)
-                        BB = array(((min(EndRBBox[0],StartRBBox[0]), min(EndRBBox[1],StartRBBox[1])),
-                                    (max(EndRBBox[0],StartRBBox[0]), max(EndRBBox[1],StartRBBox[1]))),Float)
-                        self.ZoomToBB(BB)
-                    else:
-                        Center = self.PixelToWorld(StartRBBox)
-                        self.Zoom(1.5,Center)
-                    self.StartRBBox = None
-                    
-            if self.GUIMode == "ZoomOut":
-                if event.LeftDown():
-                    Center = self.PixelToWorld((event.GetX(),event.GetY()))
-                    self.Zoom(1/1.5,Center)
-            elif self.GUIMode == "Move":
-                if event.LeftDown():
-                    self.StartMove = array((event.GetX(),event.GetY()))
-                    self.PrevMoveBox = None
-                elif event.Dragging() and event.LeftIsDown() and self.StartMove:
-                    x_1,y_1 = event.GetX(),event.GetY()
-                    w, h = self.PanelSize
-                    x_tl, y_tl = x_1 - self.StartMove[0], y_1 - self.StartMove[1]
-                    dc = wx.ClientDC(self.DrawPanel)
-                    dc.BeginDrawing()
-                    dc.SetPen(wx.Pen('WHITE', 1,))
-                    dc.SetBrush(wx.TRANSPARENT_BRUSH)
-                    dc.SetLogicalFunction(wx.XOR)
-                    if self.PrevMoveBox:
-                        dc.DrawRectangle(*self.PrevMoveBox)
-                    dc.DrawRectangle(x_tl,y_tl,w,h)
-                    self.PrevMoveBox = (x_tl,y_tl,w,h)
-                    dc.EndDrawing()
-                  
-                elif event.LeftUp() and self.StartMove:
-                    self.PrevMoveBox = None
-                    StartMove = self.StartMove
-                    EndMove = array((event.GetX(),event.GetY()))
-                    if sum((StartMove-EndMove)**2) > 16:
-                        self.Move(StartMove-EndMove,'Pixel')
-                    self.StartMove = None
-                    
-    def RightButtonEvent(self,event):
-        if self.GUIMode:
-            if self.GUIMode == "ZoomIn":
-                Center = self.PixelToWorld((event.GetX(),event.GetY()))
-                self.Zoom(1/1.5,Center)
-            elif self.GUIMode == "ZoomOut":
-                Center = self.PixelToWorld((event.GetX(),event.GetY()))
-                self.Zoom(1.5,Center)
-            else:
-                event.Skip()
-        event.Skip()
-                
-    def MakeNewBuffers(self):
-        # Make new offscreen bitmap:
-        self._Buffer = wx.EmptyBitmap(self.PanelSize[0],self.PanelSize[1])
-        if self.UseBackground:
-            self._BackBuffer = wx.EmptyBitmap(self.PanelSize[0],self.PanelSize[1])
-            self._BackgroundDirty = 1
-        else:
-            pass
-                
-    def OnSize(self,event):
-        self.PanelSize  = array(self.DrawPanel.GetClientSizeTuple(),Int32)
-        try:
-            self.AspectRatio = self.PanelSize[0]/self.PanelSize[1]
-        except ZeroDivisionError:
-            self.AspectRatio = 1.0
-        self.MakeNewBuffers()
-        self.Draw()
-        
-    def OnPaint(self, event):
-        #dc = wx.BufferedPaintDC(self.DrawPanel, self._Buffer)
-        dc = wx.PaintDC(self.DrawPanel)
-        dc.DrawBitmap(self._Buffer, 0,0)
-
-    def Draw(self):
-        """
-        The Draw method gets pretty complicated because of all the buffers
-
-        There is a main buffer set up to double buffer the screen, so
-        you can get quick re-draws when the window gets uncovered.
-
-        If self.UseBackground is set, and an object is set up with the
-        "ForeGround" flag, then it gets drawn to the screen after blitting
-        the background. This is done so that you can have a complicated
-        background, but have something changing on the foreground,
-        without having to wait for the background to get re-drawn. This
-        can be used to support simple animation, for instance.
-        
-        """
-        if self.Debug: start = clock()
-        ScreenDC =  wx.ClientDC(self.DrawPanel)
-        ViewPortWorld = ( self.PixelToWorld((0,0)), self.PixelToWorld(self.PanelSize) )
-        ViewPortBB = array( ( minimum.reduce(ViewPortWorld), maximum.reduce(ViewPortWorld) ) )
-        if self.UseBackground:
-            dc = wx.MemoryDC()
-            dc.SelectObject(self._BackBuffer)
-            dc.SetBackground(self.DrawPanel.BackgroundBrush)
-            if self._DrawList:
-                if  self._BackgroundDirty:
-                    dc.BeginDrawing()
-                    dc.Clear()
-                    i = 0
-                    for Object in self._DrawList:
-                        if self.BBCheck(Object.BoundingBox,ViewPortBB):
-                            #print "object is in Bounding Box"
-                            i+=1
-                            Object._Draw(dc,self.WorldToPixel,self.ScaleFunction)
-                            if i % self.NumBetweenBlits == 0:
-                                w,h = self.PanelSize
-                                ScreenDC.Blit(0, 0, w,h, dc, 0, 0)
-                    dc.EndDrawing()
-            else:
-                dc.Clear()
-            self._BackgroundDirty = 0
-            dc.SelectObject(self._Buffer)
-            dc.BeginDrawing()
-            ##Draw Background on Main Buffer:
-            dc.DrawBitmap(self._BackBuffer,0,0)
-            #Draw the OnTop stuff
-            i = 0
-            for Object in self._TopDrawList:
-                i+=1
-                Object._Draw(dc,self.WorldToPixel,self.ScaleFunction)
-                if i % self.NumBetweenBlits == 0:
-                    w, h = self.PanelSize
-                    ScreenDC.Blit(0, 0, w,h, dc, 0, 0)
-            dc.EndDrawing()
-        else: # not using a Background DC
-            dc = wx.MemoryDC()
-            dc.SelectObject(self._Buffer)
-            dc.SetBackground(self.DrawPanel.BackgroundBrush)
-            if self._DrawList:
-                dc.BeginDrawing()
-                dc.Clear()
-                i = 0
-                for Object in self._DrawList:
-                    if self.BBCheck(Object.BoundingBox,ViewPortBB):
-                        #print "object is in Bounding Box"
-                        i+=1
-                        Object._Draw(dc,self.WorldToPixel,self.ScaleFunction)
-                        if i % self.NumBetweenBlits == 0:
-                            w, h = self.PanelSize
-                            ScreenDC.Blit(0, 0, w, h, dc, 0, 0)
-                dc.EndDrawing()
-            else:
-                dc.Clear()
-        # now refresh the screen
-        #ScreenDC.DrawBitmap(self._Buffer,0,0) #NOTE: uisng DrawBitmap didn't work right on MSW
-        w, h = self.PanelSize
-        ScreenDC.Blit(0, 0, w, h, dc, 0, 0)
-
-        # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn
-        if self.PrevRBBox:
-            ScreenDC.SetPen(wx.Pen('WHITE', 2,wx.SHORT_DASH))
-            ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
-            ScreenDC.SetLogicalFunction(wx.XOR)
-            ScreenDC.DrawRectangle(*self.PrevRBBox)
-        elif self.PrevMoveBox:
-            ScreenDC.SetPen(wx.Pen('WHITE', 1,))
-            ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
-            ScreenDC.SetLogicalFunction(wx.XOR)
-            ScreenDC.DrawRectangle(*self.PrevMoveBox)
-        if self.Debug: print "Drawing took %f seconds of CPU time"%(clock()-start)
-
-    def BBCheck(self, BB1, BB2):
-        """
-
-        BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
-
-        """
-        if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and
-             (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ):
-            return True
-        else:
-            return False
-
-    def Move(self,shift,CoordType):
-        """
-        move the image in the window.
-
-        shift is an (x,y) tuple, specifying the amount to shift in each direction
-
-        It can be in any of three coordinates: Panel, Pixel, World,
-        specified by the CoordType parameter
-
-        Panel coordinates means you want to shift the image by some
-        fraction of the size of the displaed image
-
-        Pixel coordinates means you want to shift the image by some number of pixels
-
-        World coordinates meand you want to shift the image by an amount
-        in Floating point world coordinates
-
-        """
-        
-        shift = array(shift,Float)
-        if CoordType == 'Panel':# convert from panel coordinates
-            shift = shift * array((-1,1),Float) *self.PanelSize/self.TransformVector
-        elif CoordType == 'Pixel': # convert from pixel coordinates
-            shift = shift/self.TransformVector
-        elif CoordType == 'World': # No conversion
-            pass
-        else:
-            raise 'CoordType must be either "Panel", "Pixel", or "World"'
-            
-        self.ViewPortCenter = self.ViewPortCenter + shift 
-        self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
-        self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
-        self._BackgroundDirty = 1
-        self.Draw()
-        
-    def Zoom(self,factor,center = None):
-    
-        """
-        Zoom(factor, center) changes the amount of zoom of the image by factor.
-        If factor is greater than one, the image gets larger.
-        If factor is less than one, the image gets smaller.
-        
-        Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
-        If center is not given, the center will stay the same.
-        
-        """
-        self.Scale = self.Scale*factor
-        if center:
-            self.ViewPortCenter = array(center,Float)
-        self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
-        self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
-        self._BackgroundDirty = 1
-        self.Draw()
-        
-    def ZoomToFit(self,event):
-        self.ZoomToBB()
-        
-    def ZoomToBB(self,NewBB = None,DrawFlag = 1):
-
-        """
-
-        Zooms the image to the bounding box given, or to the bounding
-        box of all the objects on the canvas, if none is given.
-
-        """
-        
-        if NewBB:
-            BoundingBox = NewBB
-        else:
-            if self.BoundingBoxDirty:
-                self._ResetBoundingBox()
-            BoundingBox = self.BoundingBox
-        if BoundingBox:
-            self.ViewPortCenter = array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
-                                                                     (BoundingBox[0,1]+BoundingBox[1,1])/2 ),Float)
-            self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
-            # Compute the new Scale
-            BoundingBox = BoundingBox * self.MapProjectionVector
-            try:
-                self.Scale = min((self.PanelSize[0]  / (BoundingBox[1,0]-BoundingBox[0,0])),
-                                        (self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1])))*0.95
-            except ZeroDivisionError: # this will happen if the BB has zero width or height
-                try: #width
-                    self.Scale = (self.PanelSize[0]  / (BoundingBox[1,0]-BoundingBox[0,0]))*0.95
-                except ZeroDivisionError:
-                    try: # height
-                        self.Scale = (self.PanelSize[1]  / (BoundingBox[1,1]-BoundingBox[0,1]))*0.95
-                    except ZeroDivisionError: #zero size! (must be a single point)
-                        self.Scale = 1
-                        
-            self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
-            if DrawFlag:
-                self._BackgroundDirty = 1
-                self.Draw()
-                
-    def RemoveObjects(self,Objects):
-        for Object in Objects:
-            self.RemoveObject(Object,ResetBB = 0)
-        self.BoundingBoxDirty = 1
-        
-    def RemoveObject(self,Object,ResetBB = 1):
-        if Object.Foreground:
-            self._TopDrawList.remove(Object)
-        else:
-            self._DrawList.remove(Object)
-            self._BackgroundDirty = 1
-
-        if ResetBB:
-            self.BoundingBoxDirty = 1
-
-    def Clear(self, ResetBB = True):
-        self._DrawList = []
-        if self.UseBackground:
-            self._TopDrawList = []
-            self._BackgroundDirty = 1
-        if ResetBB:
-            self._ResetBoundingBox()
-
-    def _AddBoundingBox(self,NewBB):
-        if self.BoundingBox is None:
-            self.BoundingBox = NewBB
-            self.ZoomToBB(NewBB,DrawFlag = 0)
-        else:
-            self.BoundingBox = array(((min(self.BoundingBox[0,0],NewBB[0,0]),
-                                       min(self.BoundingBox[0,1],NewBB[0,1])),
-                                      (max(self.BoundingBox[1,0],NewBB[1,0]),
-                                       max(self.BoundingBox[1,1],NewBB[1,1]))),Float)
-    def _ResetBoundingBox(self):
-        # NOTE: could you remove an item without recomputing the entire bounding box?
-        self.BoundingBox = None
-        if self._DrawList:
-            self.BoundingBox = self._DrawList[0].BoundingBox
-        for Object in self._DrawList[1:]:
-            self._AddBoundingBox(Object.BoundingBox)
-        if self.UseBackground:
-            for Object in self._TopDrawList:
-                self._AddBoundingBox(Object.BoundingBox)
-        if self.BoundingBox is None:
-            self.ViewPortCenter= array( (0,0), Float)
-            self.TransformVector = array( (1,-1), Float)
-            self.MapProjectionVector = array( (1,1), Float)                    
-            self.Scale = 1
-        self.BoundingBoxDirty = 0
-
-    def PixelToWorld(self,Points):
-        """
-        Converts coordinates from Pixel coordinates to world coordinates.
-        
-        Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates.
-        
-        """
-        return  (((array(Points,Float) - (self.PanelSize/2))/self.TransformVector) + self.ViewPortCenter)
-        
-    def WorldToPixel(self,Coordinates):
-        """
-        This function will get passed to the drawing functions of the objects,
-        to transform from world to pixel coordinates.
-        Coordinates should be a NX2 array of (x,y) coordinates, or
-        a 2-tuple, or sequence of 2-tuples.
-        """
-        return  (((array(Coordinates,Float) - self.ViewPortCenter)*self.TransformVector)+(self.PanelSize/2)).astype('i')
-        
-    def ScaleFunction(self,Lengths):
-        """
-        This function will get passed to the drawing functions of the objects,
-        to Change a length from world to pixel coordinates.
-        
-        Lengths should be a NX2 array of (x,y) coordinates, or
-        a 2-tuple, or sequence of 2-tuples.
-        """
-        return  (array(Lengths,Float)*self.TransformVector).astype('i')
-        
-            
-    ## This is a set of methods that add objects to the Canvas. It kind
-    ## of seems like a lot of duplication, but I wanted to be able to
-    ## instantiate the draw objects separatley form adding them, but
-    ## also to be able to do add one oin one step. I'm open to better
-    ## ideas...
-    def AddRectangle(self,x,y,width,height,
-                                     LineColor = "Black",
-                                     LineStyle = "Solid",
-                                     LineWidth    = 1,
-                                     FillColor    = None,
-                                     FillStyle    = "Solid",
-                                     Foreground = 0):
-        Object = Rectangle(x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
-        self.AddObject(Object)
-        return Object
-        
-    def AddEllipse(self,x,y,width,height,
-                                     LineColor = "Black",
-                                     LineStyle = "Solid",
-                                     LineWidth    = 1,
-                                     FillColor    = None,
-                                     FillStyle    = "Solid",
-                                     Foreground = 0):
-        Object = Ellipse(x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
-        self.AddObject(Object)
-        return Object
-        
-    def AddCircle(self,x,y,Diameter,
-                                     LineColor = "Black",
-                                     LineStyle = "Solid",
-                                     LineWidth    = 1,
-                                     FillColor    = None,
-                                     FillStyle    = "Solid",
-                                     Foreground = 0):
-        Object = Circle(x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
-        self.AddObject(Object)
-        return Object
-        
-    def AddDot(self,x,y,Diameter,
-                                     LineColor = "Black",
-                                     LineStyle = "Solid",
-                                     LineWidth    = 1,
-                                     FillColor    = None,
-                                     FillStyle    = "Solid",
-                                     Foreground = 0):
-        Object = Dot(x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
-        self.AddObject(Object)
-        return Object
-        
-    def AddPolygon(self,Points,
-                               LineColor = "Black",
-                               LineStyle = "Solid",
-                               LineWidth    = 1,
-                               FillColor    = None,
-                               FillStyle    = "Solid",
-                                     Foreground = 0):
-    
-        Object = Polygon(Points,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
-        self.AddObject(Object)
-        return Object
-        
-    def AddLine(self,Points,
-                            LineColor = "Black",
-                            LineStyle = "Solid",
-                            LineWidth    = 1,
-                                     Foreground = 0):
-    
-        Object = Line(Points,LineColor,LineStyle,LineWidth,Foreground)
-        self.AddObject(Object)
-        return Object
-
-    def AddLineSet(self,Points,
-                   LineColors = "Black",
-                   LineStyles = "Solid",
-                   LineWidths = 1,
-                   Foreground = 0):
-    
-        Object = LineSet(Points,LineColors,LineStyles,LineWidths,Foreground)
-        self.AddObject(Object)
-        return Object
-
-    def AddPointSet(self,Points,
-                                    Color = "Black",
-                                    Diameter = 1,
-                                     Foreground = 0):
-        
-        Object = PointSet(Points,Color,Diameter,Foreground)
-        self.AddObject(Object)
-        return Object
-        
-    def AddText(self,String,x,y,
-                            Size = 20,
-                            ForeGround = 'Black',
-                            BackGround = None,
-                            Family = 'Swiss',
-                            Style = 'Normal',
-                            Weight = 'Normal',
-                            Underline = 0,
-                            Position = 'tl',
-                                     Foreground = 0):
-        Object = Text(String,x,y,Size,ForeGround,BackGround,Family,Style,Weight,Underline,Position,Foreground)
-        self.AddObject(Object)
-        return Object
-        
-    def AddObject(self,obj):
-        # put in a reference to the Canvas, so remove and other stuff can work
-        obj._Canvas = self
-        if  obj.Foreground and self.UseBackground:
-            self._TopDrawList.append(obj)
-        else:
-            self._DrawList.append(obj)
-            self._backgrounddirty = 1
-        self._AddBoundingBox(obj.BoundingBox)
-        return None
-        
-
-
-
-
-
-
diff --git a/wxPython/wx/lib/floatcanvas/FloatCanvas.py b/wxPython/wx/lib/floatcanvas/FloatCanvas.py
new file mode 100644 (file)
index 0000000..83865fb
--- /dev/null
@@ -0,0 +1,1962 @@
+
+try:
+    from Numeric import array,asarray,Float,cos,pi,sum,minimum,maximum,Int32,zeros
+except ImportError:
+    from numarray import array, asarray, Float, cos, pi, sum, minimum, maximum, Int32, zeros
+
+from time import clock, sleep
+
+import wx
+
+import types
+import os        
+
+import Resources
+
+## A global variable to hold the Pixels per inch that wxWindows thinks is in use
+## This is used for scaling fonts.
+## This can't be computed on module __init__, because a wx.App might not have iniitalized yet.
+global ScreenPPI 
+
+## a custom Exceptions:
+
+class FloatCanvasException(Exception):
+    pass
+
+## All the mouse events
+#EVT_FC_ENTER_WINDOW = wx.NewEventType()
+#EVT_FC_LEAVE_WINDOW = wx.NewEventType()
+EVT_FC_LEFT_DOWN = wx.NewEventType() 
+EVT_FC_LEFT_UP  = wx.NewEventType()
+EVT_FC_LEFT_DCLICK = wx.NewEventType() 
+EVT_FC_MIDDLE_DOWN = wx.NewEventType() 
+EVT_FC_MIDDLE_UP = wx.NewEventType() 
+EVT_FC_MIDDLE_DCLICK = wx.NewEventType() 
+EVT_FC_RIGHT_DOWN = wx.NewEventType() 
+EVT_FC_RIGHT_UP = wx.NewEventType() 
+EVT_FC_RIGHT_DCLICK = wx.NewEventType() 
+EVT_FC_MOTION = wx.NewEventType() 
+EVT_FC_MOUSEWHEEL = wx.NewEventType() 
+## these two are for the hit-test stuff, I never make them real Events
+EVT_FC_ENTER_OBJECT = wx.NewEventType()
+EVT_FC_LEAVE_OBJECT = wx.NewEventType()
+
+#def EVT_ENTER_WINDOW( window, function ):
+#    window.Connect( -1, -1, EVT_FC_ENTER_WINDOW, function ) 
+#def EVT_LEAVE_WINDOW( window, function ):
+#    window.Connect( -1, -1,EVT_FC_LEAVE_WINDOW , function ) 
+def EVT_LEFT_DOWN( window, function ):  
+    window.Connect( -1, -1,EVT_FC_LEFT_DOWN , function )
+def EVT_LEFT_UP( window, function ):
+    window.Connect( -1, -1,EVT_FC_LEFT_UP , function )
+def EVT_LEFT_DCLICK  ( window, function ):
+    window.Connect( -1, -1,EVT_FC_LEFT_DCLICK , function )
+def EVT_MIDDLE_DOWN  ( window, function ):
+    window.Connect( -1, -1,EVT_FC_MIDDLE_DOWN , function )
+def EVT_MIDDLE_UP  ( window, function ):
+    window.Connect( -1, -1,EVT_FC_MIDDLE_UP , function )
+def EVT_MIDDLE_DCLICK  ( window, function ):
+    window.Connect( -1, -1,EVT_FC_MIDDLE_DCLICK , function )
+def EVT_RIGHT_DOWN  ( window, function ):
+    window.Connect( -1, -1,EVT_FC_RIGHT_DOWN , function )
+def EVT_RIGHT_UP( window, function ):
+    window.Connect( -1, -1,EVT_FC_RIGHT_UP , function )
+def EVT_RIGHT_DCLICK( window, function ):
+    window.Connect( -1, -1,EVT_FC_RIGHT_DCLICK , function )
+def EVT_MOTION( window, function ):
+    window.Connect( -1, -1,EVT_FC_MOTION , function )
+def EVT_MOUSEWHEEL( window, function ):
+    window.Connect( -1, -1,EVT_FC_MOUSEWHEEL , function )
+
+class MouseEvent(wx.PyCommandEvent):
+
+    """
+
+    This event class takes a regular wxWindows mouse event as a parameter,
+    and wraps it so that there is access to all the original methods. This
+    is similar to subclassing, but you can't subclass a wxWindows event
+
+    The goal is to be able to it just like a regular mouse event.
+
+    It adds the method:
+
+    GetCoords() , which returns and (x,y) tuple in world coordinates.
+
+    Another differnce is that it is a CommandEvent, which propagates up
+    the window hierarchy until it is handled.
+
+    """
+
+    def __init__(self, EventType, NativeEvent, WinID, Coords = None):
+        wx.PyCommandEvent.__init__(self)
+
+        self.SetEventType( EventType )
+        self._NativeEvent = NativeEvent
+        self.Coords = Coords
+    
+    def SetCoords(self,Coords):
+        self.Coords = Coords
+        
+    def GetCoords(self):
+        return self.Coords
+
+    def __getattr__(self, name):
+        #return eval(self.NativeEvent.__getattr__(name) )
+        return getattr(self._NativeEvent, name)
+
+#### ColorGEnerator class is now obsolete. I'm using a python generator function instead.
+##class ColorGenerator:
+
+##    """
+
+##    An instance of this class generates a unique color each time
+##    GetNextColor() is called. Someday I will use a proper Python
+##    generator for this class.
+
+##    The point of this generator is for the hit-test bitmap, each object
+##    needs to be a unique color. Also, each system can be running a
+##    different number of colors, and it doesn't appear to be possible to
+##    have a wxMemDC with a different colordepth as the screen so this
+##    generates colors far enough apart that they can be distinguished on
+##    a 16bit screen. Anything less than 16bits won't work. It could, but
+##    I havn't written the code that way. You also wouldn't get many
+##    distict colors
+    
+##    """
+
+##    def __init__(self):
+##        import sys
+##        ## figure out the color depth of the screen
+##        ## for some bizare reason, thisdoesn't work on OS-X
+##        if sys.platform == 'darwin':
+##            depth = 24
+##        else:
+##            b = wx.EmptyBitmap(1,1)
+##            depth = b.GetDepth()
+##        self.r = 0
+##        self.g = 0
+##        self.b = 0
+##        if depth == 16:
+##            self.step = 8
+##        elif depth >= 24:
+##            self.step = 1
+##        else:
+##            raise FloatCanvasException("ColorGenerator does not work with depth = %s"%depth )
+
+##    def GetNextColor(self):
+##        step = self.step
+##        ##r,g,b = self.r,self.g,self.b
+##        self.r += step
+##        if self.r > 255:
+##            self.r = step
+##            self.g += step
+##            if self.g > 255:
+##                self.g = step
+##                self.b += step
+##                if self.b > 255:
+##                    ## fixme: this should be a derived exception
+##                    raise FloatCanvasException("Too many objects in colorgenerator for HitTest")
+##        return (self.r,self.g,self.b)
+
+##    def Reset(self):
+##        self.r = 0
+##        self.g = 0
+##        self.b = 0
+
+def cycleidxs(indexcount, maxvalue, step):
+    if indexcount == 0:
+        yield ()
+    else:
+        for idx in xrange(0, maxvalue, step):
+            for tail in cycleidxs(indexcount - 1, maxvalue, step):
+                yield (idx, ) + tail
+
+def colorGenerator():
+    import sys
+    if sys.platform == 'darwin':
+        depth = 24
+    else:
+        b = wx.EmptyBitmap(1,1)
+        depth = b.GetDepth()
+    if depth == 16:
+        step = 8
+    elif depth >= 24:
+        step = 1
+    else:
+        raise "ColorGenerator does not work with depth = %s" % depth
+    return cycleidxs(indexcount=3, maxvalue=256, step=step)
+
+
+#### I don't know if the Set objects are useful, beyond the pointset object
+#### The problem is that when zoomed in, the BB is checked to see whether to draw the object.
+#### A Set object can defeat this
+
+##class ObjectSetMixin:
+##    """
+##    A mix-in class for draw objects that are sets of objects
+
+##    It contains methods for setting lists of pens and brushes
+
+##    """
+##    def SetPens(self,LineColors,LineStyles,LineWidths):
+##        """
+##        This method used when an object could have a list of pens, rather than just one
+##        It is used for LineSet, and perhaps others in the future.
+
+##        fixme: this should be in a mixin
+
+##        fixme: this is really kludgy, there has got to be a better way!
+
+##        """
+
+##        length = 1
+##        if type(LineColors) == types.ListType:
+##            length = len(LineColors)
+##        else:
+##            LineColors = [LineColors]
+
+##        if type(LineStyles) == types.ListType:
+##            length = len(LineStyles)
+##        else:
+##            LineStyles = [LineStyles]
+
+##        if type(LineWidths) == types.ListType:
+##            length = len(LineWidths)
+##        else:
+##            LineWidths = [LineWidths]
+
+##        if length > 1:
+##            if len(LineColors) == 1:
+##                LineColors = LineColors*length
+##            if len(LineStyles) == 1:
+##                LineStyles = LineStyles*length
+##            if len(LineWidths) == 1:
+##                LineWidths = LineWidths*length
+
+##        self.Pens = []
+##        for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):
+##            if LineColor is None or LineStyle is None:
+##                self.Pens.append(wx.TRANSPARENT_PEN)
+##                # what's this for?> self.LineStyle = 'Transparent'
+##            if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
+##                Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
+##                self.Pens.append(Pen)
+##            else:
+##                self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])
+##        if length == 1:
+##            self.Pens = self.Pens[0]
+
+
+
+class DrawObject:
+    """
+    This is the base class for all the objects that can be drawn.
+
+    """
+
+    def __init__(self,InForeground  = False):
+        self.InForeground = InForeground
+
+        self._Canvas = None
+
+        self.HitColor = None
+        self.CallBackFuncs = {}
+
+        ## these are the defaults
+        self.HitAble = False
+        self.HitLine = True
+        self.HitFill = True
+        self.MinHitLineWidth = 3
+        self.HitLineWidth = 3 ## this gets re-set by the subclasses if necessary
+        
+    # I pre-define all these as class variables to provide an easier
+    # interface, and perhaps speed things up by caching all the Pens
+    # and Brushes, although that may not help, as I think wx now
+    # does that on it's own. Send me a note if you know!
+
+    BrushList = {
+            ( None,"Transparent")  : wx.TRANSPARENT_BRUSH,
+            ("Blue","Solid")       : wx.BLUE_BRUSH,
+            ("Green","Solid")      : wx.GREEN_BRUSH,
+            ("White","Solid")      : wx.WHITE_BRUSH,
+            ("Black","Solid")      : wx.BLACK_BRUSH,
+            ("Grey","Solid")       : wx.GREY_BRUSH,
+            ("MediumGrey","Solid") : wx.MEDIUM_GREY_BRUSH,
+            ("LightGrey","Solid")  : wx.LIGHT_GREY_BRUSH,
+            ("Cyan","Solid")       : wx.CYAN_BRUSH,
+            ("Red","Solid")        : wx.RED_BRUSH
+                    }
+    PenList = {
+            (None,"Transparent",1)   : wx.TRANSPARENT_PEN,
+            ("Green","Solid",1)      : wx.GREEN_PEN,
+            ("White","Solid",1)      : wx.WHITE_PEN,
+            ("Black","Solid",1)      : wx.BLACK_PEN,
+            ("Grey","Solid",1)       : wx.GREY_PEN,
+            ("MediumGrey","Solid",1) : wx.MEDIUM_GREY_PEN,
+            ("LightGrey","Solid",1)  : wx.LIGHT_GREY_PEN,
+            ("Cyan","Solid",1)       : wx.CYAN_PEN,
+            ("Red","Solid",1)        : wx.RED_PEN
+            }
+
+    FillStyleList = {
+            "Transparent"    : wx.TRANSPARENT,
+            "Solid"          : wx.SOLID,
+            "BiDiagonalHatch": wx.BDIAGONAL_HATCH,
+            "CrossDiagHatch" : wx.CROSSDIAG_HATCH,
+            "FDiagonal_Hatch": wx.FDIAGONAL_HATCH,
+            "CrossHatch"     : wx.CROSS_HATCH,
+            "HorizontalHatch": wx.HORIZONTAL_HATCH,
+            "VerticalHatch"  : wx.VERTICAL_HATCH
+            }
+
+    LineStyleList = {
+            "Solid"      : wx.SOLID,
+            "Transparent": wx.TRANSPARENT,
+            "Dot"        : wx.DOT,
+            "LongDash"   : wx.LONG_DASH,
+            "ShortDash"  : wx.SHORT_DASH,
+            "DotDash"    : wx.DOT_DASH,
+            }
+
+    def Bind(self, Event, CallBackFun):
+        self.CallBackFuncs[Event] = CallBackFun
+        self.HitAble = True
+        self._Canvas.UseHitTest = True
+        if not self._Canvas._HTdc:
+            self._Canvas.MakeNewHTdc()
+        if not self.HitColor:
+            if not self._Canvas.HitColorGenerator:
+                self._Canvas.HitColorGenerator = colorGenerator()
+                self._Canvas.HitColorGenerator.next() # first call to prevent the background color from being used.
+            self.HitColor = self._Canvas.HitColorGenerator.next()
+            self.SetHitPen(self.HitColor,self.HitLineWidth)
+            self.SetHitBrush(self.HitColor)
+        # put the object in the hit dict, indexed by it's color
+        if not self._Canvas.HitDict:
+            self._Canvas.MakeHitDict()
+        self._Canvas.HitDict[Event][self.HitColor] = (self) # put the object in the hit dict, indexed by it's color
+
+
+    def UnBindAll(self):
+        ## fixme: this only removes one from each list, there could be more.
+        if self._Canvas.HitDict:
+            for List in self._Canvas.HitDict.itervalues():
+                try:
+                   List.remove(self)
+                except ValueError:
+                    pass
+        self.HitAble = False
+
+    def SetBrush(self,FillColor,FillStyle):
+        if FillColor is None or FillStyle is None:
+            self.Brush = wx.TRANSPARENT_BRUSH
+            self.FillStyle = "Transparent"
+        else:
+            self.Brush = self.BrushList.setdefault( (FillColor,FillStyle),  wx.Brush(FillColor,self.FillStyleList[FillStyle] ) )
+
+    def SetPen(self,LineColor,LineStyle,LineWidth):
+        if (LineColor is None) or (LineStyle is None):
+            self.Pen = wx.TRANSPARENT_PEN
+            self.LineStyle = 'Transparent'
+        else:
+             self.Pen = self.PenList.setdefault( (LineColor,LineStyle,LineWidth),  wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle]) )
+
+    def SetHitBrush(self,HitColor):
+        if not self.HitFill:
+            self.HitBrush = wx.TRANSPARENT_BRUSH
+        else:
+            self.HitBrush = self.BrushList.setdefault( (HitColor,"solid"),  wx.Brush(HitColor,self.FillStyleList["Solid"] ) )
+
+    def SetHitPen(self,HitColor,LineWidth):
+        if not self.HitLine:
+            self.HitPen = wx.TRANSPARENT_PEN
+        else:
+             self.HitPen = self.PenList.setdefault( (HitColor, "solid", LineWidth),  wx.Pen(HitColor, LineWidth, self.LineStyleList["Solid"]) )
+
+    def PutInBackground(self):
+        if self._Canvas and self.InForeground:
+            self._Canvas._ForeDrawList.remove(self)
+            self._Canvas._DrawList.append(self)
+            self._Canvas._BackgroundDirty = True
+            self.InForeground = False
+
+    def PutInForeground(self):
+        if self._Canvas and (not self.InForeground):
+            self._Canvas._ForeDrawList.append(self)
+            self._Canvas._DrawList.remove(self)
+            self._Canvas._BackgroundDirty = True
+            self.InForeground = True
+
+class XYObjectMixin:
+    """
+
+    This is a mixin class that provides some methods suitable for use
+    with objects that have a single (x,y) coordinate pair.
+
+    """
+
+    def Move(self, Delta ):
+        """
+
+        Move(Delta): moves the object by delta, where delta is a
+        (dx,dy) pair. Ideally a Numpy array of shape (2,)
+
+        """
+        
+        Delta = asarray(Delta, Float)
+        self.XY += Delta
+        self.BoundingBox = self.BoundingBox + Delta
+        if self._Canvas:
+            self._Canvas.BoundingBoxDirty = True      
+
+class PointsObjectMixin:
+    """
+
+    This is a mixin class that provides some methods suitable for use
+    with objects that have a set of (x,y) coordinate pairs.
+
+    """
+
+## This is code for the XYMixin object, it needs to be adapeted and tested.
+##    def Move(self, Delta ):
+##        """
+
+##        Move(Delta): moves the object by delta, where delta is an (dx,
+##        dy) pair. Ideally a Numpy array or shape (2,)
+
+##        """
+        
+##        Delta = array(Delta, Float)
+##        self.XY += Delta
+##        self.BoundingBox = self.BoundingBox + Delta##array((self.XY, (self.XY + self.WH)), Float)
+##        if self._Canvas:
+##            self._Canvas.BoundingBoxDirty = True      
+
+    def SetPoints(self,Points):
+        self.Points = Points
+        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+        if self._Canvas:
+            self._Canvas.BoundingBoxDirty = True
+
+
+     
+class Polygon(DrawObject,PointsObjectMixin):
+
+    """
+
+    The Polygon class takes a list of 2-tuples, or a NX2 NumPy array of
+    point coordinates.  so that Points[N][0] is the x-coordinate of
+    point N and Points[N][1] is the y-coordinate or Points[N,0] is the
+    x-coordinate of point N and Points[N,1] is the y-coordinate for
+    arrays.
+
+    """
+    def __init__(self,
+                 Points,
+                 LineColor = "Black",
+                 LineStyle = "Solid",
+                 LineWidth    = 1,
+                 FillColor    = None,
+                 FillStyle    = "Solid",
+                 InForeground = False):
+        DrawObject.__init__(self,InForeground)
+        self.Points = array(Points,Float) # this DOES need to make a copy
+        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+        self.LineColor = LineColor
+        self.LineStyle = LineStyle
+        self.LineWidth = LineWidth
+        self.FillColor = FillColor
+        self.FillStyle = FillStyle
+
+        self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+        self.SetPen(LineColor,LineStyle,LineWidth)
+        self.SetBrush(FillColor,FillStyle)
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel = None, HTdc=None):
+        Points = WorldToPixel(self.Points)
+        dc.SetPen(self.Pen)
+        dc.SetBrush(self.Brush)
+        dc.DrawPolygon(Points)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.SetBrush(self.HitBrush)
+            HTdc.DrawPolygon(Points)
+            
+##class PolygonSet(DrawObject):
+##    """
+##    The PolygonSet class takes a Geometry.Polygon object.
+##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
+    
+##    it creates a set of line segments, from (x1,y1) to (x2,y2)
+    
+##    """
+    
+##    def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,InForeground = False):
+##        DrawObject.__init__(self, InForeground)
+
+##        ##fixme: there should be some error checking for everything being the right length.
+
+        
+##        self.Points = array(Points,Float)
+##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+##        self.LineColors = LineColors
+##        self.LineStyles = LineStyles
+##        self.LineWidths = LineWidths
+##        self.FillColors = FillColors
+##        self.FillStyles = FillStyles
+
+##        self.SetPens(LineColors,LineStyles,LineWidths)
+
+##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
+##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+##        Points = WorldToPixel(self.Points)
+##        Points.shape = (-1,4)
+##        dc.DrawLineList(Points,self.Pens)
+
+class Line(DrawObject,PointsObjectMixin):
+    """
+    The Line class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
+    so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
+    or  Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
+
+    It will draw a straight line if there are two points, and a polyline if there are more than two.
+
+    """
+    def __init__(self,Points,
+                 LineColor = "Black",
+                 LineStyle = "Solid",
+                 LineWidth    = 1,
+                 InForeground = False):
+        DrawObject.__init__(self, InForeground)
+
+
+        self.Points = array(Points,Float)
+        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+        self.LineColor = LineColor
+        self.LineStyle = LineStyle
+        self.LineWidth = LineWidth
+
+        self.SetPen(LineColor,LineStyle,LineWidth)
+
+        self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+            
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        Points = WorldToPixel(self.Points)
+        dc.SetPen(self.Pen)
+        dc.DrawLines(Points)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.DrawLines(Points)
+
+##class LineSet(DrawObject, ObjectSetMixin):
+##    """
+##    The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
+##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
+    
+##    it creates a set of line segments, from (x1,y1) to (x2,y2)
+    
+##    """
+    
+##    def __init__(self,Points,LineColors,LineStyles,LineWidths,InForeground = False):
+##        DrawObject.__init__(self, InForeground)
+
+##        NumLines = len(Points) / 2
+##        ##fixme: there should be some error checking for everything being the right length.
+
+        
+##        self.Points = array(Points,Float)
+##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+##        self.LineColors = LineColors
+##        self.LineStyles = LineStyles
+##        self.LineWidths = LineWidths
+
+##        self.SetPens(LineColors,LineStyles,LineWidths)
+
+##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
+##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+##        Points = WorldToPixel(self.Points)
+##        Points.shape = (-1,4)
+##        dc.DrawLineList(Points,self.Pens)
+
+class PointSet(DrawObject):
+    """
+    The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
+    so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
+    or  Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
+
+    Each point will be drawn the same color and Diameter. The Diameter is in screen points,
+    not world coordinates.
+
+    At this point, the hit-test code does not distingish between the
+    points, you will only know that one of the poins got hit, not which
+    one.
+
+    In the case of points, the HitLineWidth is used as diameter.
+
+    """
+    def __init__(self, Points, Color = "Black", Diameter =  1, InForeground = False):
+        DrawObject.__init__(self,InForeground)
+
+        self.Points = array(Points,Float)
+        self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
+        self.BoundingBox = array(((min(self.Points[:,0]),
+                                   min(self.Points[:,1])),
+                                  (max(self.Points[:,0]),
+                                   max(self.Points[:,1]))),Float)
+
+        self.Color = Color
+        self.Diameter = Diameter
+
+        self.HitLineWidth = self.MinHitLineWidth
+        self.SetPen(Color,"Solid",1)
+        self.SetBrush(Color,"Solid")
+
+    def SetPoints(self,Points):
+        self.Points = array(Points, Float)
+        self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
+        self.BoundingBox = array(((min(self.Points[:,0]),
+                                   min(self.Points[:,1]) ),
+                                  (max(self.Points[:,0]),
+                                   max(self.Points[:,1]) ) ) )
+        if self._Canvas:
+            self._Canvas.BoundingBoxDirty = True
+
+    def DrawD2(self, dc, Points):
+        # A Little optimization for a diameter2 - point
+        dc.DrawPointList(Points)
+        dc.DrawPointList(Points + (1,0))
+        dc.DrawPointList(Points + (0,1))
+        dc.DrawPointList(Points + (1,1))
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        dc.SetPen(self.Pen)
+        Points = WorldToPixel(self.Points)
+        if self.Diameter <= 1:
+            dc.DrawPointList(Points)
+        elif self.Diameter <= 2:
+            self.DrawD2(dc, Points)
+        else:
+            dc.SetBrush(self.Brush)
+            radius = int(round(self.Diameter/2))
+            for xy in Points:
+                dc.DrawEllipsePointSize( (xy - radius), (self.Diameter, self.Diameter) )
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            if self.Diameter <= 1:
+                HTdc.DrawPointList(Points)
+            elif self.Diameter <= 2:
+                self.DrawD2(HTdc, Points)
+            else:
+                HTdc.SetBrush(self.HitBrush)
+                radius = int(round(self.Diameter/2))
+                for xy in Points:
+                    HTdc.DrawEllipsePointSize(  (xy - radius), (self.Diameter, self.Diameter) )
+            
+#### Does anyone need this?                    
+##class Dot(DrawObject):
+##    """
+##    The Dot class takes an x.y coordinate pair, and the Diameter of the circle.
+##    The Diameter is in pixels, so it won't change with zoom.
+
+##    Also Fill and line data
+
+##    """
+##    def __init__(self,x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,InForeground = False):
+##        DrawObject.__init__(self,InForeground)
+
+##        self.X = x
+##        self.Y = y
+##        self.Diameter = Diameter
+##        # NOTE: the bounding box does not include the diameter of the dot, as that is in pixel coords.
+##        # If this is  a problem, perhaps you should use a circle, instead!
+##        self.BoundingBox = array(((x,y),(x,y)),Float)
+
+##        self.LineColor = LineColor
+##        self.LineStyle = LineStyle
+##        self.LineWidth = LineWidth
+##        self.FillColor = FillColor
+##        self.FillStyle = FillStyle
+
+##        self.SetPen(LineColor,LineStyle,LineWidth)
+##        self.SetBrush(FillColor,FillStyle)
+
+##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
+##        dc.SetPen(self.Pen)
+##        dc.SetBrush(self.Brush)
+##        radius = int(round(self.Diameter/2))
+##        (X,Y) = WorldToPixel((self.X,self.Y))
+##        dc.DrawEllipse((X - radius), (Y - radius), self.Diameter, self.Diameter)
+
+class RectEllipse(DrawObject, XYObjectMixin):
+    def __init__(self,x,y,width,height,
+                 LineColor = "Black",
+                 LineStyle = "Solid",
+                 LineWidth    = 1,
+                 FillColor    = None,
+                 FillStyle    = "Solid",
+                 InForeground = False):
+        
+        DrawObject.__init__(self,InForeground)
+
+        self.XY = array( (x, y), Float)
+        self.WH = array( (width, height), Float )
+        self.BoundingBox = array(((x,y), (self.XY + self.WH)), Float)
+        self.LineColor = LineColor
+        self.LineStyle = LineStyle
+        self.LineWidth = LineWidth
+        self.FillColor = FillColor
+        self.FillStyle = FillStyle
+
+        self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+        self.SetPen(LineColor,LineStyle,LineWidth)
+        self.SetBrush(FillColor,FillStyle)
+
+
+    def SetUpDraw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+        dc.SetPen(self.Pen)
+        dc.SetBrush(self.Brush)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.SetBrush(self.HitBrush)
+        return ( WorldToPixel(self.XY),
+                 ScaleWorldToPixel(self.WH) )
+
+    def SetXY(self, x, y):
+        self.XY = array( (x, y), Float)
+        self.BoundingBox = array((self.XY, (self.XY + self.WH) ), Float)
+        if self._Canvas:
+            self._Canvas.BoundingBoxDirty = True
+
+
+class Rectangle(RectEllipse):
+#    def __init__(*args, **kwargs):
+#        RectEllipse.__init__(*args, **kwargs)
+#        raise "an error"
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        ( XY, WH ) = self.SetUpDraw(dc,
+                                    WorldToPixel,
+                                    ScaleWorldToPixel,
+                                    HTdc)
+        dc.DrawRectanglePointSize(XY, WH)
+        if HTdc and self.HitAble:
+            HTdc.DrawRectanglePointSize(XY, WH)
+
+class Ellipse(RectEllipse):
+#    def __init__(*args, **kwargs):
+#        RectEllipse.__init__(*args, **kwargs)
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        ( XY, WH ) = self.SetUpDraw(dc,
+                                    WorldToPixel,
+                                    ScaleWorldToPixel,
+                                    HTdc)
+        dc.DrawEllipsePointSize(XY, WH)
+        if HTdc and self.HitAble:
+            HTdc.DrawEllipsePointSize(XY, WH)
+
+class Circle(Ellipse):
+    def __init__(self, x ,y, Diameter, **kwargs):
+        RectEllipse.__init__(self ,
+                             x-Diameter/2.,
+                             y-Diameter/2.,
+                             Diameter,
+                             Diameter,
+                             **kwargs)
+        
+class TextObjectMixin:
+    """
+
+    A mix in class that holds attributes and methods that are needed by
+    the Text objects
+
+    """
+    
+    ## I'm caching fonts, because on GTK, getting a new font can take a
+    ## while. However, it gets cleared after every full draw as hanging
+    ## on to a bunch of large fonts takes a massive amount of memory.
+
+    FontList = {}
+
+    def SetFont(self, Size, Family, Style, Weight, Underline, FaceName):
+        self.Font = self.FontList.setdefault( (Size,
+                                               Family,
+                                               Style,
+                                               Weight,
+                                               Underline,
+                                               FaceName),
+                                               wx.Font(Size,
+                                                       Family,
+                                                       Style, 
+                                                       Weight,
+                                                       Underline,
+                                                       FaceName) )
+        return self.Font
+
+    ## store the function that shift the coords for drawing text. The
+    ## "c" parameter is the correction for world coordinates, rather
+    ## than pixel coords as the y axis is reversed
+    ShiftFunDict = {'tl': lambda x, y, w, h, world=0: (x, y) ,
+                    'tc': lambda x, y, w, h, world=0: (x - w/2, y) , 
+                    'tr': lambda x, y, w, h, world=0: (x - w, y) , 
+                    'cl': lambda x, y, w, h, world=0: (x, y - h/2 + world*h) , 
+                    'cc': lambda x, y, w, h, world=0: (x - w/2, y - h/2 + world*h) , 
+                    'cr': lambda x, y, w, h, world=0: (x - w, y - h/2 + world*h) ,
+                    'bl': lambda x, y, w, h, world=0: (x, y - h + 2*world*h) ,
+                    'bc': lambda x, y, w, h, world=0: (x - w/2, y - h + 2*world*h) , 
+                    'br': lambda x, y, w, h, world=0: (x - w, y - h + 2*world*h)}
+
+class Text(DrawObject, TextObjectMixin):
+    """
+    This class creates a text object, placed at the coordinates,
+    x,y. the "Position" argument is a two charactor string, indicating
+    where in relation to the coordinates the string should be oriented.
+
+    The first letter is: t, c, or b, for top, center and bottom The
+    second letter is: l, c, or r, for left, center and right The
+    position refers to the position relative to the text itself. It
+    defaults to "tl" (top left).
+
+    Size is the size of the font in pixels, or in points for printing
+    (if it ever gets implimented). Those will be the same, If you assume
+    72 PPI.
+
+    Family:
+        Font family, a generic way of referring to fonts without
+        specifying actual facename. One of:
+            wx.DEFAULT:  Chooses a default font. 
+            wx.DECORATIVE: A decorative font. 
+            wx.ROMAN: A formal, serif font. 
+            wx.SCRIPT: A handwriting font. 
+            wx.SWISS: A sans-serif font. 
+            wx.MODERN: A fixed pitch font.
+        NOTE: these are only as good as the wxWindows defaults, which aren't so good.
+    Style:
+        One of wx.NORMAL, wx.SLANT and wx.ITALIC.
+    Weight:
+        One of wx.NORMAL, wx.LIGHT and wx.BOLD.
+    Underline:
+        The value can be True or False. At present this may have an an
+        effect on Windows only.
+
+    Alternatively, you can set the kw arg: Font, to a wx.Font, and the above will be ignored.
+    
+    The size is fixed, and does not scale with the drawing.
+
+    The hit-test is done on the entire text extent
+
+    """
+    
+    def __init__(self,String,x,y,
+                 Size =  12,
+                 Color = "Black",
+                 BackgroundColor = None,
+                 Family = wx.MODERN,
+                 Style = wx.NORMAL,
+                 Weight = wx.NORMAL,
+                 Underline = False,
+                 Position = 'tl',
+                 InForeground = False,
+                 Font = None):
+        
+        DrawObject.__init__(self,InForeground)
+
+        self.String = String
+        # Input size in in Pixels, compute points size from PPI info.
+        # fixme: for printing, we'll have to do something a little different
+        self.Size = int(round(72.0 * Size / ScreenPPI))
+
+        self.Color = Color
+        self.BackgroundColor = BackgroundColor
+
+        if not Font:
+            FaceName = ''
+        else:
+            FaceName           =  Font.GetFaceName()           
+            Family             =  Font.GetFamily()
+            Size               =  Font.GetPointSize()          
+            Style              =  Font.GetStyle()
+            Underlined         =  Font.GetUnderlined()         
+            Weight             =  Font.GetWeight()
+        self.SetFont(Size, Family, Style, Weight, Underline, FaceName)
+
+        self.BoundingBox = array(((x,y),(x,y)),Float)
+
+        self.XY = ( x,y )
+
+       # use a memDC --  ScreenDC doesn't work with 2.5.1 and GTK2
+        #dc = wx.MemoryDC()
+        #bitmap = wx.EmptyBitmap(1, 1)
+        #dc.SelectObject(bitmap)               
+        #dc.SetFont(self.Font)
+        #(self.TextWidth, self.TextHeight) = dc.GetTextExtent(self.String)
+        (self.TextWidth, self.TextHeight) = (None, None)
+        self.ShiftFun = self.ShiftFunDict[Position]
+
+    def SetXY(self, x, y):
+        self.XY = ( x,y )
+        self.BoundingBox = array((self.XY, self.XY),Float)
+        if self._Canvas:
+            self._Canvas.BoundingBoxDirty = True
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        XY = WorldToPixel(self.XY)
+        dc.SetFont(self.Font)
+        dc.SetTextForeground(self.Color)
+        if self.BackgroundColor:
+            dc.SetBackgroundMode(wx.SOLID)
+            dc.SetTextBackground(self.BackgroundColor)
+        else:
+            dc.SetBackgroundMode(wx.TRANSPARENT)
+        if self.TextWidth is None or self.TextHeight is None:
+            (self.TextWidth, self.TextHeight) = dc.GetTextExtent(self.String)
+        XY = self.ShiftFun(XY[0], XY[1], self.TextWidth, self.TextHeight)
+        dc.DrawTextPoint(self.String, XY)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.SetBrush(self.HitBrush)
+            HTdc.DrawRectanglePointSize(XY, (self.TextWidth, self.TextHeight) )
+
+class ScaledText(DrawObject, TextObjectMixin, XYObjectMixin):
+    """
+    This class creates a text object that is scaled when zoomed.  It is
+    placed at the coordinates, x,y. the "Position" argument is a two
+    charactor string, indicating where in relation to the coordinates
+    the string should be oriented.
+
+    The first letter is: t, c, or b, for top, center and bottom The
+    second letter is: l, c, or r, for left, center and right The
+    position refers to the position relative to the text itself. It
+    defaults to "tl" (top left).
+
+    Size is the size of the font in world coordinates.
+
+    Family:
+        Font family, a generic way of referring to fonts without
+        specifying actual facename. One of:
+            wx.DEFAULT:  Chooses a default font. 
+            wx.DECORATI: A decorative font. 
+            wx.ROMAN: A formal, serif font. 
+            wx.SCRIPT: A handwriting font. 
+            wx.SWISS: A sans-serif font. 
+            wx.MODERN: A fixed pitch font.
+        NOTE: these are only as good as the wxWindows defaults, which aren't so good.
+    Style:
+        One of wx.NORMAL, wx.SLANT and wx.ITALIC.
+    Weight:
+        One of wx.NORMAL, wx.LIGHT and wx.BOLD.
+    Underline:
+        The value can be True or False. At present this may have an an
+        effect on Windows only.
+
+    Alternatively, you can set the kw arg: Font, to a wx.Font, and the
+    above will be ignored. The size of the font you specify will be
+    ignored, but the rest of it's attributes will be preserved.
+    
+    The size will scale as the drawing is zoomed.
+
+    Bugs/Limitations:
+
+    As fonts are scaled, the do end up a little different, so you don't
+    get exactly the same picture as you scale up and doen, but it's
+    pretty darn close.
+    
+    On wxGTK1 on my Linux system, at least, using a font of over about
+    3000 pts. brings the system to a halt. It's the Font Server using
+    huge amounts of memory. My work around is to max the font size to
+    3000 points, so it won't scale past there. GTK2 uses smarter font
+    drawing, so that may not be an issue in future versions, so feel
+    free to test. Another smarter way to do it would be to set a global
+    zoom limit at that point.
+
+    The hit-test is done on the entire text extent. This could be made
+    optional, but I havn't gotten around to it.
+
+    """
+    
+    def __init__(self, String, x, y , Size,
+                 Color = "Black",
+                 BackgroundColor = None,
+                 Family = wx.MODERN,
+                 Style = wx.NORMAL,
+                 Weight = wx.NORMAL,
+                 Underline = False,
+                 Position = 'tl',
+                 Font = None,
+                 InForeground = False):
+        
+        DrawObject.__init__(self,InForeground)
+
+        self.String = String
+        self.XY = array( (x, y), Float)
+        self.Size = Size     
+        self.Color = Color
+        self.BackgroundColor = BackgroundColor
+        self.Family = Family   
+        self.Style = Style    
+        self.Weight = Weight   
+        self.Underline = Underline
+        if not Font:
+            self.FaceName = ''
+        else:
+            self.FaceName           =  Font.GetFaceName()           
+            self.Family             =  Font.GetFamily()    
+            self.Style              =  Font.GetStyle()     
+            self.Underlined         =  Font.GetUnderlined()         
+            self.Weight             =  Font.GetWeight()    
+
+        # Experimental max font size value on wxGTK2: this works OK on
+        # my system If it's any larger, there is a crash, with the
+        # message: The application 'FloatCanvasDemo.py' lost its
+        # connection to the display :0.0; most likely the X server was
+        # shut down or you killed/destroyed the application.
+        self.MaxSize = 2750
+        
+        self.ShiftFun = self.ShiftFunDict[Position]
+
+        ## Compute the BB
+        ## this isn't exact, as fonts don't scale exactly.
+        dc = wx.MemoryDC()
+        bitmap = wx.EmptyBitmap(1, 1)
+        dc.SelectObject(bitmap) #wxMac needs a Bitmap selected for GetTextExtent to work.
+        DrawingSize = 40 # pts This effectively determines the resolution that the BB is computed to.
+        ScaleFactor = float(Size) / DrawingSize
+        dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
+        (w,h) = dc.GetTextExtent(self.String)
+        w = w * ScaleFactor
+        h = h * ScaleFactor
+        x, y = self.ShiftFun(x, y, w, h, world = 1)
+        self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
+
+        # the new coords are set to the corner of the BB:
+        #self.X = self.BoundingBox[0,0]
+        #self.Y = self.BoundingBox[1,1]
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        (X,Y) = WorldToPixel( (self.XY) )
+
+        # compute the font size:
+        Size = abs( ScaleWorldToPixel( (self.Size, self.Size) )[1] ) # only need a y coordinate length
+        ## Check to see if the font size is large enough to blow up the X font server
+        ## If so, limit it. Would it be better just to not draw it?
+        ## note that this limit is dependent on how much memory you have, etc.
+        if Size > self.MaxSize:
+            Size = self.MaxSize
+        dc.SetFont(self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underline, self.FaceName))
+        dc.SetTextForeground(self.Color)
+        if self.BackgroundColor:
+            dc.SetBackgroundMode(wx.SOLID)
+            dc.SetTextBackground(self.BackgroundColor)
+        else:
+            dc.SetBackgroundMode(wx.TRANSPARENT)
+        (w,h) = dc.GetTextExtent(self.String)
+        # compute the shift, and adjust the coordinates, if neccesary
+        # This had to be put in here, because it changes with Zoom, as
+        # fonts don't scale exactly.
+        xy = self.ShiftFun(X, Y, w, h)
+
+        dc.DrawTextPoint(self.String, xy)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.SetBrush(self.HitBrush)
+            HTdc.DrawRectanglePointSize(xy, (w, h) )
+
+
+#---------------------------------------------------------------------------
+class FloatCanvas(wx.Panel):
+    """
+    FloatCanvas.py
+
+    This is a high level window for drawing maps and anything else in an
+    arbitrary coordinate system.
+
+    The goal is to provide a convenient way to draw stuff on the screen
+    without having to deal with handling OnPaint events, converting to pixel
+    coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
+    also provides virtually unlimited zooming and scrolling
+
+    I am using it for two things:
+    1) general purpose drawing in floating point coordinates
+    2) displaying map data in Lat-long coordinates
+
+    If the projection is set to None, it will draw in general purpose
+    floating point coordinates. If the projection is set to 'FlatEarth', it
+    will draw a FlatEarth projection, centered on the part of the map that
+    you are viewing. You can also pass in your own projection function.
+
+    It is double buffered, so re-draws after the window is uncovered by something
+    else are very quick.
+
+    It relies on NumPy, which is needed for speed (maybe, I havn't profiled it)
+
+    Bugs and Limitations:
+        Lots: patches, fixes welcome
+
+    For Map drawing: It ignores the fact that the world is, in fact, a
+    sphere, so it will do strange things if you are looking at stuff near
+    the poles or the date line. so far I don't have a need to do that, so I
+    havn't bothered to add any checks for that yet.
+
+    Zooming:
+    I have set no zoom limits. What this means is that if you zoom in really 
+    far, you can get integer overflows, and get wierd results. It
+    doesn't seem to actually cause any problems other than wierd output, at
+    least when I have run it.
+
+    Speed:
+    I have done a couple of things to improve speed in this app. The one
+    thing I have done is used NumPy Arrays to store the coordinates of the
+    points of the objects. This allowed me to use array oriented functions
+    when doing transformations, and should provide some speed improvement
+    for objects with a lot of points (big polygons, polylines, pointsets).
+
+    The real slowdown comes when you have to draw a lot of objects, because
+    you have to call the wx.DC.DrawSomething call each time. This is plenty
+    fast for tens of objects, OK for hundreds of objects, but pretty darn
+    slow for thousands of objects.
+
+    The solution is to be able to pass some sort of object set to the DC
+    directly. I've used DC.DrawPointList(Points), and it helped a lot with
+    drawing lots of points. I havn't got a LineSet type object, so I havn't
+    used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList()
+    methods implimented, and then I'd also have a full set of Object sets
+    that could take advantage of them. I hope to get to it some day.
+
+    Mouse Events:
+
+    At this point, there are a full set of custom mouse events. They are
+    just like the rebulsr mouse events, but include an extra attribute:
+    Event.GetCoords(), that returns the (x,y) position in world
+    coordinates, as a length-2 NumPy vector of Floats.
+    
+    Copyright: Christopher Barker
+
+    License: Same as the version of wxPython you are using it with
+
+    Please let me know if you're using this!!!
+
+    Contact me at:
+
+    Chris.Barker@noaa.gov
+
+    """ 
+    
+    def __init__(self, parent, id = -1,
+                 size = wx.DefaultSize,
+                 ProjectionFun = None,
+                 BackgroundColor = "WHITE",
+                 Debug = False):
+
+        wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
+        
+        global ScreenPPI ## A global variable to hold the Pixels per inch that wxWindows thinks is in use.
+        dc = wx.ScreenDC()
+        ScreenPPI = dc.GetPPI()[0] # Assume square pixels
+        del dc
+
+        self.HitColorGenerator = None
+        self.UseHitTest = None
+
+        self.NumBetweenBlits = 500
+
+        self.BackgroundBrush = wx.Brush(BackgroundColor,wx.SOLID)
+
+        self.Debug = Debug
+
+        wx.EVT_PAINT(self, self.OnPaint)
+        wx.EVT_SIZE(self, self.OnSize)
+        
+        wx.EVT_LEFT_DOWN(self, self.LeftDownEvent ) 
+        wx.EVT_LEFT_UP(self, self.LeftUpEvent ) 
+        wx.EVT_LEFT_DCLICK(self, self.LeftDoubleClickEvent ) 
+        wx.EVT_MIDDLE_DOWN(self, self.MiddleDownEvent ) 
+        wx.EVT_MIDDLE_UP(self, self.MiddleUpEvent ) 
+        wx.EVT_MIDDLE_DCLICK(self, self.MiddleDoubleClickEvent ) 
+        wx.EVT_RIGHT_DOWN(self, self.RightDownEvent)
+        wx.EVT_RIGHT_UP(self, self.RightUpEvent ) 
+        wx.EVT_RIGHT_DCLICK(self, self.RightDoubleCLickEvent ) 
+        wx.EVT_MOTION(self, self.MotionEvent ) 
+        wx.EVT_MOUSEWHEEL(self, self.WheelEvent ) 
+
+        ## CHB: I'm leaving these out for now.
+        #wx.EVT_ENTER_WINDOW(self, self. ) 
+        #wx.EVT_LEAVE_WINDOW(self, self. ) 
+
+        ## create the Hit Test Dicts:
+        self.HitDict = None
+
+
+        self._DrawList = []
+        self._ForeDrawList = []
+        self._ForegroundBuffer = None
+        self.BoundingBox = None
+        self.BoundingBoxDirty = False
+        self.ViewPortCenter= array( (0,0), Float)
+
+        self.SetProjectionFun(ProjectionFun)
+        
+        self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
+        self.TransformVector = array( (1,-1), Float) # default Transformation
+        
+        self.Scale = 1
+
+        self.GUIMode = None
+        self.StartRBBox = None
+        self.PrevRBBox = None
+        self.StartMove = None
+        self.PrevMoveXY = None
+        self.ObjectUnderMouse = None
+        
+        # called just to make sure everything is initialized
+        self.OnSize(None)
+
+        self.InHereNum = 0
+
+    def SetProjectionFun(self,ProjectionFun):
+        if ProjectionFun == 'FlatEarth':
+            self.ProjectionFun = self.FlatEarthProjection 
+        elif type(ProjectionFun) == types.FunctionType:
+            self.ProjectionFun = ProjectionFun 
+        elif ProjectionFun is None:
+            self.ProjectionFun = lambda x=None: array( (1,1), Float)
+        else:
+            raise FloatCanvasException('Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector')
+
+    def FlatEarthProjection(self,CenterPoint):
+        return array((cos(pi*CenterPoint[1]/180),1),Float)
+
+    def SetMode(self,Mode):
+        if Mode in ["ZoomIn","ZoomOut","Move","Mouse",None]:
+            self.GUIMode = Mode
+        else:
+            raise FloatCanvasException('"%s" is Not a valid Mode'%Mode)
+
+    def MakeHitDict(self):
+        ##fixme: Should this just be None if nothing has been bound? 
+        self.HitDict = {EVT_FC_LEFT_DOWN: {},
+                        EVT_FC_LEFT_UP: {},
+                        EVT_FC_LEFT_DCLICK: {},
+                        EVT_FC_MIDDLE_DOWN: {},
+                        EVT_FC_MIDDLE_UP: {},
+                        EVT_FC_MIDDLE_DCLICK: {},
+                        EVT_FC_RIGHT_DOWN: {},
+                        EVT_FC_RIGHT_UP: {},
+                        EVT_FC_RIGHT_DCLICK: {},
+                        EVT_FC_ENTER_OBJECT: {},
+                        EVT_FC_LEAVE_OBJECT: {},
+                        }        
+            
+    def RaiseMouseEvent(self, Event, EventType):
+        """
+        This is called in various other places to raise a Mouse Event
+        """
+        #print "in Raise Mouse Event", Event
+        pt = self.PixelToWorld( Event.GetPosition() )
+        evt = MouseEvent(EventType, Event, self.GetId(), pt)
+        self.GetEventHandler().ProcessEvent(evt)       
+
+    def HitTest(self, event, HitEvent):
+        if self.HitDict:
+            # check if there are any objects in the dict for this event
+            if self.HitDict[ HitEvent ]:
+                xy = event.GetPosition()
+                if self._ForegroundHTdc:
+                    hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
+                else:
+                    hitcolor = self._HTdc.GetPixelPoint( xy )
+                color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+                if color in self.HitDict[ HitEvent ]:
+                    Object = self.HitDict[ HitEvent ][color]
+                    ## Add the hit coords to the Object
+                    Object.HitCoords = self.PixelToWorld( xy )
+                    Object.CallBackFuncs[HitEvent](Object)
+                    return True
+            return False
+
+    def MouseOverTest(self, event):
+        ##fixme: Can this be cleaned up?
+        if self.HitDict:
+            xy = event.GetPosition()
+            if self._ForegroundHTdc:
+                hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
+            else:
+                hitcolor = self._HTdc.GetPixelPoint( xy )
+            color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+            OldObject = self.ObjectUnderMouse
+            ObjectCallbackCalled = False
+            if color in self.HitDict[ EVT_FC_ENTER_OBJECT ]:
+                Object = self.HitDict[ EVT_FC_ENTER_OBJECT][color]
+                if (OldObject is None):
+                    try:
+                        Object.CallBackFuncs[EVT_FC_ENTER_OBJECT](Object)
+                        ObjectCallbackCalled =  True
+                    except KeyError:
+                        pass # this means the enter event isn't bound for that object
+                elif OldObject == Object: # the mouse is still on the same object
+                    pass
+                    ## Is the mouse on a differnt object as it was...
+                elif not (Object == OldObject):
+                    # call the leave object callback
+                    try:
+                        OldObject.CallBackFuncs[EVT_FC_LEAVE_OBJECT](OldObject)
+                        ObjectCallbackCalled =  True
+                    except KeyError:
+                        pass # this means the leave event isn't bound for that object
+                    try:
+                        Object.CallBackFuncs[EVT_FC_ENTER_OBJECT](Object)
+                        ObjectCallbackCalled =  True
+                    except KeyError:
+                        pass # this means the enter event isn't bound for that object
+                    ## set the new object under mouse
+                self.ObjectUnderMouse = Object
+            elif color in self.HitDict[ EVT_FC_LEAVE_OBJECT ]:
+                Object = self.HitDict[ EVT_FC_LEAVE_OBJECT][color]
+                self.ObjectUnderMouse = Object
+            else:
+                # no objects under mouse bound to mouse-over events
+                self.ObjectUnderMouse = None
+                if OldObject:
+                    try:
+                        OldObject.CallBackFuncs[EVT_FC_LEAVE_OBJECT](OldObject)
+                        ObjectCallbackCalled =  True
+                    except KeyError:
+                        pass # this means the leave event isn't bound for that object
+            return ObjectCallbackCalled
+
+
+    ## fixme: There is a lot of repeated code here
+    ##        Is there a better way?            
+    def LeftDoubleClickEvent(self,event):
+        if self.GUIMode == "Mouse":
+            EventType = EVT_FC_LEFT_DCLICK
+            if not self.HitTest(event, EventType):
+                self.RaiseMouseEvent(event, EventType)
+
+
+    def MiddleDownEvent(self,event):
+        if self.GUIMode == "Mouse":
+            EventType = EVT_FC_MIDDLE_DOWN
+            if not self.HitTest(event, EventType):
+                self.RaiseMouseEvent(event, EventType)
+
+    def MiddleUpEvent(self,event):
+        if self.GUIMode == "Mouse":
+            EventType = EVT_FC_MIDDLE_UP
+            if not self.HitTest(event, EventType):
+                self.RaiseMouseEvent(event, EventType)
+
+    def MiddleDoubleClickEvent(self,event):
+        if self.GUIMode == "Mouse":
+            EventType = EVT_FC_MIDDLE_DCLICK
+            if not self.HitTest(event, EventType):
+                self.RaiseMouseEvent(event, EventType)
+
+    def RightUpEvent(self,event):
+        if self.GUIMode == "Mouse":
+            EventType = EVT_FC_RIGHT_UP
+            if not self.HitTest(event, EventType):
+                self.RaiseMouseEvent(event, EventType)
+
+    def RightDoubleCLickEvent(self,event):
+        if self.GUIMode == "Mouse":
+            EventType = EVT_FC_RIGHT_DCLICK
+            if not self.HitTest(event, EventType):
+                self.RaiseMouseEvent(event, EventType)
+
+    def WheelEvent(self,event):
+        if self.GUIMode == "Mouse":
+            self.RaiseMouseEvent(event, EVT_FC_MOUSEWHEEL)
+
+
+    def LeftDownEvent(self,event):
+        if self.GUIMode:
+            if self.GUIMode == "ZoomIn":
+                self.StartRBBox = array( event.GetPosition() )
+                self.PrevRBBox = None
+                self.CaptureMouse()
+            elif self.GUIMode == "ZoomOut":
+                Center = self.PixelToWorld( event.GetPosition() )
+                self.Zoom(1/1.5,Center)
+            elif self.GUIMode == "Move":
+                self.StartMove = array( event.GetPosition() )
+                self.PrevMoveXY = (0,0)
+            elif self.GUIMode == "Mouse":
+                ## check for a hit
+                if not self.HitTest(event, EVT_FC_LEFT_DOWN):
+                   self.RaiseMouseEvent(event,EVT_FC_LEFT_DOWN)
+        else: 
+            pass
+
+    def LeftUpEvent(self,event):
+       if self.HasCapture():
+            self.ReleaseMouse()
+        if self.GUIMode:
+            if self.GUIMode == "ZoomIn":
+                if event.LeftUp() and not self.StartRBBox is None:
+                    self.PrevRBBox = None
+                    EndRBBox = event.GetPosition()
+                    StartRBBox = self.StartRBBox
+                    # if mouse has moved less that ten pixels, don't use the box.
+                    if ( abs(StartRBBox[0] - EndRBBox[0]) > 10
+                         and abs(StartRBBox[1] - EndRBBox[1]) > 10 ):
+                        EndRBBox = self.PixelToWorld(EndRBBox)
+                        StartRBBox = self.PixelToWorld(StartRBBox)
+                        BB = array(((min(EndRBBox[0],StartRBBox[0]),
+                                     min(EndRBBox[1],StartRBBox[1])),
+                                    (max(EndRBBox[0],StartRBBox[0]),
+                                     max(EndRBBox[1],StartRBBox[1]))),Float)
+                        self.ZoomToBB(BB)
+                    else:
+                        Center = self.PixelToWorld(StartRBBox)
+                        self.Zoom(1.5,Center)
+                    self.StartRBBox = None
+            elif self.GUIMode == "Move":
+                if not self.StartMove is None:
+                    StartMove = self.StartMove
+                    EndMove = array((event.GetX(),event.GetY()))
+                    if sum((StartMove-EndMove)**2) > 16:
+                        self.Move(StartMove-EndMove,'Pixel')
+                    self.StartMove = None
+            elif self.GUIMode == "Mouse":
+                EventType = EVT_FC_LEFT_UP
+                if not self.HitTest(event, EventType):
+                   self.RaiseMouseEvent(event, EventType)
+        else:
+            pass
+
+    def MotionEvent(self,event):
+        if self.GUIMode:
+            if self.GUIMode == "ZoomIn":
+                if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
+                    xy0 = self.StartRBBox
+                    xy1 = array( event.GetPosition() )
+                    wh  = abs(xy1 - xy0)
+                    wh[0] = max(wh[0], int(wh[1]*self.AspectRatio))
+                    wh[1] = int(wh[0] / self.AspectRatio)
+                    xy_c = (xy0 + xy1) / 2
+                    dc = wx.ClientDC(self)
+                    dc.BeginDrawing()
+                    dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+                    dc.SetBrush(wx.TRANSPARENT_BRUSH)
+                    dc.SetLogicalFunction(wx.XOR)
+                    if self.PrevRBBox:
+                        dc.DrawRectanglePointSize(*self.PrevRBBox)
+                    self.PrevRBBox = ( xy_c - wh/2, wh )
+                    dc.DrawRectanglePointSize( *self.PrevRBBox )
+                    dc.EndDrawing()
+            elif self.GUIMode == "Move":
+                if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
+                    xy1 = array( event.GetPosition() )
+                    wh = self.PanelSize
+                    xy_tl = xy1 - self.StartMove
+                    dc = wx.ClientDC(self)
+                    dc.BeginDrawing()
+                    x1,y1 = self.PrevMoveXY
+                    x2,y2 = xy_tl
+                    w,h = self.PanelSize
+                    if x2 > x1 and y2 > y1:
+                        xa = xb = x1
+                        ya = yb = y1
+                        wa = w
+                        ha = y2 - y1
+                        wb = x2-  x1
+                        hb = h
+                    elif x2 > x1 and y2 <= y1:
+                        xa = x1
+                        ya = y1
+                        wa = x2 - x1
+                        ha = h
+                        xb = x1
+                        yb = y2 + h
+                        wb = w
+                        hb = y1 - y2
+                    elif x2 <= x1 and y2 > y1:
+                        xa = x1
+                        ya = y1
+                        wa = w
+                        ha = y2 - y1
+                        xb = x2 + w
+                        yb = y1
+                        wb = x1 - x2
+                        hb = h - y2 + y1 
+                    elif x2 <= x1 and y2 <= y1:
+                        xa = x2 + w
+                        ya = y1
+                        wa = x1 - x2
+                        ha = h
+                        xb = x1
+                        yb = y2 + h
+                        wb = w
+                        hb = y1 - y2
+                    
+                    dc.SetPen(wx.TRANSPARENT_PEN)
+                    dc.SetBrush(self.BackgroundBrush)
+                    dc.DrawRectangle(xa, ya, wa, ha)
+                    dc.DrawRectangle(xb, yb, wb, hb)
+                    self.PrevMoveXY = xy_tl
+                    if self._ForegroundBuffer:
+                        dc.DrawBitmapPoint(self._ForegroundBuffer,xy_tl)
+                    else:
+                        dc.DrawBitmapPoint(self._Buffer,xy_tl)
+                    dc.EndDrawing()
+            elif self.GUIMode == "Mouse":
+                ## Only do something if there are mouse over events bound
+                if self.HitDict and (self.HitDict[ EVT_FC_ENTER_OBJECT ] or self.HitDict[ EVT_FC_LEAVE_OBJECT ] ):
+                    if not self.MouseOverTest(event):
+                        self.RaiseMouseEvent(event,EVT_FC_MOTION)
+            else:
+                    pass
+            self.RaiseMouseEvent(event,EVT_FC_MOTION)
+        else:
+            pass
+
+    def RightDownEvent(self,event):
+        if self.GUIMode:
+            if self.GUIMode == "ZoomIn":
+                Center = self.PixelToWorld((event.GetX(),event.GetY()))
+                self.Zoom(1/1.5,Center)
+            elif self.GUIMode == "ZoomOut":
+                Center = self.PixelToWorld((event.GetX(),event.GetY()))
+                self.Zoom(1.5,Center)
+            elif self.GUIMode == "Mouse":
+                EventType = EVT_FC_RIGHT_DOWN
+                if not self.HitTest(event, EventType):
+                   self.RaiseMouseEvent(event, EventType)
+        else:
+            pass
+        
+    def MakeNewBuffers(self):
+        self._BackgroundDirty = True
+        # Make new offscreen bitmap:
+        self._Buffer = wx.EmptyBitmap(*self.PanelSize)
+        #dc = wx.MemoryDC()
+        #dc.SelectObject(self._Buffer)
+        #dc.Clear()
+        if self._ForeDrawList:
+            self._ForegroundBuffer = wx.EmptyBitmap(*self.PanelSize)
+        else:
+            self._ForegroundBuffer = None
+        if self.UseHitTest:
+            self.MakeNewHTdc()
+        else:
+            self._HTdc = None
+            self._ForegroundHTdc = None
+
+    def MakeNewHTdc(self):
+        ## Note: While it's considered a "bad idea" to keep a
+        ## MemoryDC around I'm doing it here because a wx.Bitmap
+        ## doesn't have a GetPixel method so a DC is needed to do
+        ## the hit-test. It didn't seem like a good idea to re-create
+        ## a wx.MemoryDC on every single mouse event, so I keep it
+        ## around instead
+        self._HTdc = wx.MemoryDC()
+        self._HTBitmap = wx.EmptyBitmap(*self.PanelSize) 
+        self._HTdc.SelectObject( self._HTBitmap )
+        self._HTdc.SetBackground(wx.BLACK_BRUSH)
+        if self._ForeDrawList:
+            self._ForegroundHTdc = wx.MemoryDC()
+            self._ForegroundHTBitmap = wx.EmptyBitmap(*self.PanelSize) 
+            self._ForegroundHTdc.SelectObject( self._ForegroundHTBitmap )
+            self._ForegroundHTdc.SetBackground(wx.BLACK_BRUSH)
+        else:
+           self._ForegroundHTdc = None 
+    
+    def OnSize(self,event):
+        self.PanelSize  = array(self.GetClientSizeTuple(),Int32)
+        self.HalfPanelSize = self.PanelSize / 2 # lrk: added for speed in WorldToPixel
+        if self.PanelSize[0] == 0 or self.PanelSize[1] == 0:
+            self.AspectRatio = 1.0
+        else:
+            self.AspectRatio = float(self.PanelSize[0]) / self.PanelSize[1]
+        self.MakeNewBuffers()
+        self.Draw()
+        
+    def OnPaint(self, event):
+        dc = wx.PaintDC(self)
+        if self._ForegroundBuffer:
+            dc.DrawBitmap(self._ForegroundBuffer,0,0)
+        else:
+            dc.DrawBitmap(self._Buffer,0,0)
+
+    def Draw(self, Force=False):
+        """
+        There is a main buffer set up to double buffer the screen, so
+        you can get quick re-draws when the window gets uncovered.
+
+        If there are any objects in self._ForeDrawList, then the
+        background gets drawn to a new buffer, and the foreground
+        objects get drawn on top of it. The final result if blitted to
+        the screen, and stored for future Paint events.  This is done so
+        that you can have a complicated background, but have something
+        changing on the foreground, without having to wait for the
+        background to get re-drawn. This can be used to support simple
+        animation, for instance.
+        
+        """
+        #print "In Draw"
+        if self.Debug: start = clock()
+        ScreenDC =  wx.ClientDC(self)
+        ViewPortWorld = ( self.PixelToWorld((0,0)),
+                          self.PixelToWorld(self.PanelSize) )
+        ViewPortBB = array( ( minimum.reduce(ViewPortWorld),
+                              maximum.reduce(ViewPortWorld) ) )
+        dc = wx.MemoryDC()
+        dc.SelectObject(self._Buffer)
+        if self._BackgroundDirty or Force:
+            #print "Background is Dirty"
+            dc.SetBackground(self.BackgroundBrush)
+            dc.Clear()
+            if self._HTdc:
+                self._HTdc.Clear()
+            self._DrawObjects(dc, self._DrawList, ScreenDC, ViewPortBB, self._HTdc)
+            self._BackgroundDirty = False
+
+        if self._ForeDrawList:
+            ## If an object was just added to the Foreground, there might not yet be a buffer
+            if self._ForegroundBuffer is None:
+                self._ForegroundBuffer = wx.EmptyBitmap(self.PanelSize[0],
+                                                        self.PanelSize[1])
+
+            dc = wx.MemoryDC() ## I got some strange errors (linewidths wrong) if I didn't make a new DC here
+            dc.SelectObject(self._ForegroundBuffer)
+            dc.DrawBitmap(self._Buffer,0,0)
+            if self._ForegroundHTdc is None:
+                self._ForegroundHTdc = wx.MemoryDC()
+                self._ForegroundHTdc.SelectObject( wx.EmptyBitmap(
+                                                   self.PanelSize[0],
+                                                   self.PanelSize[1]) )
+            if self._HTdc:
+                ## blit the background HT buffer to the foreground HT buffer
+                self._ForegroundHTdc.Blit(0, 0,
+                                          self.PanelSize[0], self.PanelSize[1],
+                                          self._HTdc, 0, 0)
+            self._DrawObjects(dc,
+                              self._ForeDrawList,
+                              ScreenDC,
+                              ViewPortBB,
+                              self._ForegroundHTdc)
+        ScreenDC.Blit(0, 0, self.PanelSize[0],self.PanelSize[1], dc, 0, 0)
+##        wx.GetApp().Yield(True)
+        # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn
+        # This seeems out of place, but it works.
+        if self.PrevRBBox:
+            ScreenDC.SetPen(wx.Pen('WHITE', 2,wx.SHORT_DASH))
+            ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
+            ScreenDC.SetLogicalFunction(wx.XOR)
+            ScreenDC.DrawRectanglePointSize(*self.PrevRBBox)
+        if self.Debug: print "Drawing took %f seconds of CPU time"%(clock()-start)
+
+        ## Clear the font cache
+        ## IF you don't do this, the X font server starts to take up Massive amounts of memory
+        ## This is mostly a problem with very large fonts, that you get with scaled text when zoomed in.
+        DrawObject.FontList = {}
+
+    def _ShouldRedraw(DrawList, ViewPortBB): # lrk: adapted code from BBCheck
+        # lrk: Returns the objects that should be redrawn
+
+        BB2 = ViewPortBB
+        redrawlist = []
+        for Object in DrawList:
+            BB1 = Object.BoundingBox
+            if (BB1[1,0] > BB2[0,0] and BB1[0,0] < BB2[1,0] and
+                 BB1[1,1] > BB2[0,1] and BB1[0,1] < BB2[1,1]):
+                redrawlist.append(Object)
+        return redrawlist       
+    _ShouldRedraw = staticmethod(_ShouldRedraw)
+
+
+##    def BBCheck(self, BB1, BB2):
+##        """
+
+##        BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
+
+##        """
+##        if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and
+##             (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ):
+##            return True
+##        else:
+##            return False
+
+    def Move(self,shift,CoordType):
+        """
+        move the image in the window.
+
+        shift is an (x,y) tuple, specifying the amount to shift in each direction
+
+        It can be in any of three coordinates: Panel, Pixel, World,
+        specified by the CoordType parameter
+
+        Panel coordinates means you want to shift the image by some
+        fraction of the size of the displaed image
+
+        Pixel coordinates means you want to shift the image by some number of pixels
+
+        World coordinates mean you want to shift the image by an amount
+        in Floating point world coordinates
+
+        """
+        
+        shift = array(shift,Float)
+        if CoordType == 'Panel':# convert from panel coordinates
+            shift = shift * array((-1,1),Float) *self.PanelSize/self.TransformVector
+        elif CoordType == 'Pixel': # convert from pixel coordinates
+            shift = shift/self.TransformVector
+        elif CoordType == 'World': # No conversion
+            pass
+        else:
+            raise FloatCanvasException('CoordType must be either "Panel", "Pixel", or "World"')
+            
+        self.ViewPortCenter = self.ViewPortCenter + shift 
+        self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+        self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
+        self._BackgroundDirty = True
+        self.Draw()
+
+    def Zoom(self,factor,center = None):
+    
+        """
+        Zoom(factor, center) changes the amount of zoom of the image by factor.
+        If factor is greater than one, the image gets larger.
+        If factor is less than one, the image gets smaller.
+        
+        Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
+        If center is not given, the center will stay the same.
+        
+        """
+        self.Scale = self.Scale*factor
+        if not center is None:
+            self.ViewPortCenter = array(center,Float)
+        self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+        self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
+        self._BackgroundDirty = True
+        self.Draw()
+        
+    def ZoomToBB(self, NewBB = None, DrawFlag = True):
+
+        """
+
+        Zooms the image to the bounding box given, or to the bounding
+        box of all the objects on the canvas, if none is given.
+
+        """
+        
+        if not  NewBB is None:
+            BoundingBox = NewBB
+        else:
+            if self.BoundingBoxDirty:
+                self._ResetBoundingBox()
+            BoundingBox = self.BoundingBox
+        if not BoundingBox is None:
+            self.ViewPortCenter = array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
+                                         (BoundingBox[0,1]+BoundingBox[1,1])/2 ),Float)
+            self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+            # Compute the new Scale
+            BoundingBox = BoundingBox * self.MapProjectionVector
+            try:
+                self.Scale = min(abs(self.PanelSize[0] / (BoundingBox[1,0]-BoundingBox[0,0])),
+                                 abs(self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1])) )*0.95
+            except ZeroDivisionError: # this will happen if the BB has zero width or height
+                try: #width == 0
+                    self.Scale = (self.PanelSize[0]  / (BoundingBox[1,0]-BoundingBox[0,0]))*0.95
+                except ZeroDivisionError:
+                    try: # height == 0
+                        self.Scale = (self.PanelSize[1]  / (BoundingBox[1,1]-BoundingBox[0,1]))*0.95
+                    except ZeroDivisionError: #zero size! (must be a single point)
+                        self.Scale = 1
+                     
+            self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
+            if DrawFlag:
+                self._BackgroundDirty = True
+                self.Draw()
+        else:
+            # Reset the shifting and scaling to defaults when there is no BB
+            self.ViewPortCenter= array( (0,0), Float)
+            self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
+            self.TransformVector = array( (1,-1), Float) # default Transformation
+            self.Scale = 1
+            
+    def RemoveObjects(self, Objects):
+        for Object in Objects:
+            self.RemoveObject(Object, ResetBB = False)
+        self.BoundingBoxDirty = True
+        
+    def RemoveObject(self, Object, ResetBB = True):
+        ##fixme: Using the list.remove method is kind of slow
+        if Object.InForeground:
+            self._ForeDrawList.remove(Object)
+        else:
+            self._DrawList.remove(Object)
+            self._BackgroundDirty = True
+        if ResetBB:
+            self.BoundingBoxDirty = True
+
+    def ClearAll(self, ResetBB = True):
+        self._DrawList = []
+        self._ForeDrawList = []
+        self._BackgroundDirty = True
+        self.HitColorGenerator = None
+        self.UseHitTest = False
+        if ResetBB:
+            self._ResetBoundingBox()
+        self.MakeNewBuffers()
+        self.HitDict = None
+
+##    No longer called
+##    def _AddBoundingBox(self,NewBB):
+##        if self.BoundingBox is None:
+##            self.BoundingBox = NewBB
+##            self.ZoomToBB(NewBB,DrawFlag = False)
+##        else:
+##            self.BoundingBox = array( ( (min(self.BoundingBox[0,0],NewBB[0,0]),
+##                                         min(self.BoundingBox[0,1],NewBB[0,1])),
+##                                        (max(self.BoundingBox[1,0],NewBB[1,0]),
+##                                         max(self.BoundingBox[1,1],NewBB[1,1]))),
+##                                      Float)
+
+    def _getboundingbox(bboxarray): # lrk: added this
+
+        upperleft = minimum.reduce(bboxarray[:,0])
+        lowerright = maximum.reduce(bboxarray[:,1])
+        return array((upperleft, lowerright), Float)
+
+    _getboundingbox = staticmethod(_getboundingbox)
+
+    def _ResetBoundingBox(self):
+        if self._DrawList or self._ForeDrawList:
+            bboxarray = zeros((len(self._DrawList)+len(self._ForeDrawList), 2, 2),Float) 
+            i = -1 # just in case _DrawList is empty
+            for (i, BB) in enumerate(self._DrawList):
+                bboxarray[i] = BB.BoundingBox
+            for (j, BB) in enumerate(self._ForeDrawList):
+                bboxarray[i+j+1] = BB.BoundingBox
+            self.BoundingBox = self._getboundingbox(bboxarray)
+        else:
+            self.BoundingBox = None
+            self.ViewPortCenter= array( (0,0), Float)
+            self.TransformVector = array( (1,-1), Float)
+            self.MapProjectionVector = array( (1,1), Float)                    
+            self.Scale = 1        
+        self.BoundingBoxDirty = False
+
+    def PixelToWorld(self,Points):
+        """
+        Converts coordinates from Pixel coordinates to world coordinates.
+        
+        Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates.
+        
+        """
+        return  (((asarray(Points,Float) - (self.PanelSize/2))/self.TransformVector) + self.ViewPortCenter)
+        
+    def WorldToPixel(self,Coordinates):
+        """
+        This function will get passed to the drawing functions of the objects,
+        to transform from world to pixel coordinates.
+        Coordinates should be a NX2 array of (x,y) coordinates, or
+        a 2-tuple, or sequence of 2-tuples.
+        """
+        #Note: this can be called by users code for various reasons, so asarray is needed.
+        return  (((asarray(Coordinates,Float) - self.ViewPortCenter)*self.TransformVector)+(self.HalfPanelSize)).astype('i')
+
+    def ScaleWorldToPixel(self,Lengths):
+        """
+        This function will get passed to the drawing functions of the objects,
+        to Change a length from world to pixel coordinates.
+        
+        Lengths should be a NX2 array of (x,y) coordinates, or
+        a 2-tuple, or sequence of 2-tuples.
+        """
+        return  ( (asarray(Lengths,Float)*self.TransformVector) ).astype('i')
+
+    def ScalePixelToWorld(self,Lengths):
+        """
+        This function computes a pair of x.y lengths,
+        to change then from pixel to world coordinates.
+        
+        Lengths should be a NX2 array of (x,y) coordinates, or
+        a 2-tuple, or sequence of 2-tuples.
+        """
+
+        return  (asarray(Lengths,Float) / self.TransformVector)
+    
+    def AddObject(self,obj):
+        # put in a reference to the Canvas, so remove and other stuff can work
+        obj._Canvas = self
+        if  obj.InForeground:
+            self._ForeDrawList.append(obj)
+            self.UseForeground = True
+        else:
+            self._DrawList.append(obj)
+            self._BackgroundDirty = True
+        self.BoundingBoxDirty = True
+        return True
+
+    def _DrawObjects(self, dc, DrawList, ScreenDC, ViewPortBB, HTdc = None):
+        """
+        This is a convenience function;
+        This function takes the list of objects and draws them to specified
+        device context.  
+        """
+        dc.SetBackground(self.BackgroundBrush)
+        dc.BeginDrawing()
+        #i = 0
+        PanelSize0, PanelSize1 = self.PanelSize # for speed
+        WorldToPixel = self.WorldToPixel # for speed
+        ScaleWorldToPixel = self.ScaleWorldToPixel # for speed
+        Blit = ScreenDC.Blit # for speed
+        NumBetweenBlits = self.NumBetweenBlits # for speed
+        for i, Object in enumerate(self._ShouldRedraw(DrawList, ViewPortBB)):
+            Object._Draw(dc, WorldToPixel, ScaleWorldToPixel, HTdc)
+            if i % NumBetweenBlits == 0:
+                Blit(0, 0, PanelSize0, PanelSize1, dc, 0, 0)
+        dc.EndDrawing()
+
+##    ## This is a way to automatically add a AddObject method for each
+##    ## object type This code has been replaced by Leo's code above, so
+##    ## that it happens at module init, rather than as needed. The
+##    ## primary advantage of this is that dir(FloatCanvas) will have
+##    ## them, and docstrings are preserved. Probably more useful
+##    ## exceptions if there is a problem, as well.
+##    def __getattr__(self, name):
+##        if name[:3] == "Add":
+##            func=globals()[name[3:]]
+##            def AddFun(*args, **kwargs):
+##                Object = func(*args, **kwargs)
+##                self.AddObject(Object)
+##                return Object
+##            ## add it to FloatCanvas' dict for future calls.
+##            self.__dict__[name] = AddFun
+##            return AddFun
+##        else:
+##            raise AttributeError("FloatCanvas has no attribute '%s'"%name)
+
+def _makeFloatCanvasAddMethods(): ## lrk's code for doing this in module __init__
+    classnames = ["Circle", "Ellipse", "Rectangle", "ScaledText", "Polygon",
+               "Line", "Text", "PointSet"]
+    for classname in classnames:
+        klass = globals()[classname]
+        def getaddshapemethod(klass=klass):
+            def addshape(self, *args, **kwargs):
+                Object = klass(*args, **kwargs)
+                self.AddObject(Object)
+                return Object
+            return addshape
+        addshapemethod = getaddshapemethod()
+        methodname = "Add" + classname
+        setattr(FloatCanvas, methodname, addshapemethod)
+        docstring = "Creates %s and adds its reference to the canvas.\n" % classname
+        docstring += "Argument protocol same as %s class" % classname
+        if klass.__doc__:
+            docstring += ", whose docstring is:\n%s" % klass.__doc__
+        FloatCanvas.__dict__[methodname].__doc__ = docstring
+
+_makeFloatCanvasAddMethods()    
+
+
diff --git a/wxPython/wx/lib/floatcanvas/NavCanvas.py b/wxPython/wx/lib/floatcanvas/NavCanvas.py
new file mode 100644 (file)
index 0000000..5d7ee0c
--- /dev/null
@@ -0,0 +1,125 @@
+"""
+A Panel that includes the FloatCanvas and Navigation controls
+
+"""
+
+#from Numeric import array,Float,cos,pi,sum,minimum,maximum,Int32
+
+#from time import clock, sleep
+
+import wx
+
+#import types
+#import os        
+
+import FloatCanvas, Resources
+
+
+ID_ZOOM_IN_BUTTON = wx.NewId()
+ID_ZOOM_OUT_BUTTON = wx.NewId()
+ID_ZOOM_TO_FIT_BUTTON = wx.NewId()
+ID_MOVE_MODE_BUTTON = wx.NewId()
+ID_POINTER_BUTTON = wx.NewId()
+
+
+#---------------------------------------------------------------------------
+    
+class NavCanvas(wx.Panel):
+    """
+    NavCanvas.py
+
+    This is a high level window that encloses the FloatCanvas in a panel
+    and adds a Navigation toolbar.
+
+    Copyright: wxWindows Software Foundation (Assigned by: Christopher Barker)
+
+    License: Same as the version of wxPython you are using it with
+
+    Please let me know if you're using this!!!
+
+    Contact me at:
+
+    Chris.Barker@noaa.gov
+
+    """ 
+    
+    def __init__(self, parent, id = -1,
+                 size = wx.DefaultSize,
+                 **kwargs): # The rest just get passed into FloatCanvas
+
+        wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
+
+        ## Create the vertical sizer for the toolbar and Panel
+        box = wx.BoxSizer(wx.VERTICAL)
+        box.Add(self.BuildToolbar(), 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
+        
+        self.Canvas = FloatCanvas.FloatCanvas( self, wx.NewId(),
+                                   size = wx.DefaultSize,
+                                   **kwargs)
+        box.Add(self.Canvas,1,wx.GROW)
+
+        box.Fit(self)
+        self.SetSizer(box)
+
+        # default to Mouse mode
+        self.ToolBar.ToggleTool(ID_POINTER_BUTTON,1)
+        self.Canvas.SetMode("Mouse")
+        
+        return None
+
+    def __getattr__(self, name):
+        """
+        Delegate all extra methods to the Canvas
+        """
+        attrib = getattr(self.Canvas, name)
+        ## add the attribute to this module's dict for future calls
+        self.__dict__[name] = attrib
+        return attrib
+
+    def BuildToolbar(self):
+        tb = wx.ToolBar(self,-1)
+        self.ToolBar = tb
+        
+        tb.SetToolBitmapSize((23,23))
+        
+        tb.AddTool(ID_POINTER_BUTTON, Resources.GetPointerBitmap(), isToggle=True, shortHelpString = "Pointer")
+        wx.EVT_TOOL(self, ID_POINTER_BUTTON, self.SetToolMode)
+
+        tb.AddTool(ID_ZOOM_IN_BUTTON, Resources.GetPlusBitmap(), isToggle=True, shortHelpString = "Zoom In")
+        wx.EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetToolMode)
+        
+        tb.AddTool(ID_ZOOM_OUT_BUTTON, Resources.GetMinusBitmap(), isToggle=True, shortHelpString = "Zoom Out")
+        wx.EVT_TOOL(self, ID_ZOOM_OUT_BUTTON, self.SetToolMode)
+        
+        tb.AddTool(ID_MOVE_MODE_BUTTON, Resources.GetHandBitmap(), isToggle=True, shortHelpString = "Move")
+        wx.EVT_TOOL(self, ID_MOVE_MODE_BUTTON, self.SetToolMode)
+        
+        tb.AddSeparator()
+        
+        tb.AddControl(wx.Button(tb, ID_ZOOM_TO_FIT_BUTTON, "Zoom To Fit",wx.DefaultPosition, wx.DefaultSize))
+        wx.EVT_BUTTON(self, ID_ZOOM_TO_FIT_BUTTON, self.ZoomToFit)
+
+        tb.Realize()
+        tb.SetSizeHints(tb.GetSize())
+        return tb
+
+    def SetToolMode(self,event):
+        for id in [ID_ZOOM_IN_BUTTON,
+                   ID_ZOOM_OUT_BUTTON,
+                   ID_MOVE_MODE_BUTTON,
+                   ID_POINTER_BUTTON]:
+            self.ToolBar.ToggleTool(id,0)
+        self.ToolBar.ToggleTool(event.GetId(),1)
+        if event.GetId() == ID_ZOOM_IN_BUTTON:
+            self.Canvas.SetMode("ZoomIn")
+        elif event.GetId() == ID_ZOOM_OUT_BUTTON:
+            self.Canvas.SetMode("ZoomOut")
+        elif event.GetId() == ID_MOVE_MODE_BUTTON:
+            self.Canvas.SetMode("Move")
+        elif event.GetId() == ID_POINTER_BUTTON:
+            self.Canvas.SetMode("Mouse")
+
+
+    def ZoomToFit(self,Event):
+        self.Canvas.ZoomToBB()
+
diff --git a/wxPython/wx/lib/floatcanvas/Resources.py b/wxPython/wx/lib/floatcanvas/Resources.py
new file mode 100644 (file)
index 0000000..de0a4f3
--- /dev/null
@@ -0,0 +1,66 @@
+"""
+Resources.py  Various resources needed by the FloatCanvas package
+
+Includes, icons, etc.
+
+"""
+
+
+### These are some functions for bitmaps of icons.
+import wx, cPickle, zlib
+
+def GetHandData():
+    return cPickle.loads(zlib.decompress(
+'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
+\x01\xc8S\xb6t\x06A(\x1f\x0b\xa0\xa9\x8c\x9e\x1e6\x19\xa0\xa8\x1e\x88\xd4C\
+\x97\xd1\x83\xe8\x80 \x9c2zh\xa6\xc1\x11X\n\xab\x8c\x02\x8a\x0cD!\x92\x12\
+\x98\x8c\x1e\x8a\x8b\xd1d\x14\xf4\x90%\x90LC\xf6\xbf\x1e\xba\xab\x91%\xd0\
+\xdc\x86C\x06\xd9m\xe8!\xaa\x87S\x86\x1a1\xa7\x07\x00v\x0f[\x17' ))
+
+def GetHandBitmap():
+    return wx.BitmapFromXPMData(GetHandData())
+
+#----------------------------------------------------------------------
+def GetPlusData():
+    return cPickle.loads(zlib.decompress(
+'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
+\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=l2`\r\
+\xe82HF\xe9a\xc8\xe8\xe9A\x9c@\x8a\x0c\x0e\xd3p\xbb\x00\x8f\xab\xe1>\xd5\xd3\
+\xc3\x15:P)l!\n\x91\xc2\x1a\xd6`)\xec\xb1\x00\x92\xc2\x11?\xb8e\x88\x8fSt\
+\x19=\x00\x82\x16[\xf7' ))
+
+def GetPlusBitmap():
+    return wx.BitmapFromXPMData(GetPlusData())
+
+#----------------------------------------------------------------------
+def GetMinusData():
+    return cPickle.loads(zlib.decompress(
+'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
+\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=\xa2e\
+\x10\x16@\x99\xc82zz\x10\'\x90"\x83\xc34r\xdc\x86\xf0\xa9\x9e\x1e\xae\xd0\
+\x81Ja\x0bQ\x88\x14\xd6\xb0\x06Ka\x8f\x05\x90\x14\x8e\xf8\xc1-C|\x9c\xa2\xcb\
+\xe8\x01\x00\xed\x0f[\x87' ))
+
+def GetMinusBitmap():
+    return wx.BitmapFromXPMData(GetMinusData())
+
+## NOTE: this was created using a newer version of img2py than the above
+import cStringIO
+def GetPointerData():
+    return zlib.decompress(
+'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\xa2 \xcc\xc1\
+\x06$W\x8a/\x9d\x06\xa4X\x8a\x9d<C8\x80\xa0\x86#\xa5\x03\xc8\xcf\xf4tq\x0c\
+\xa9\x98s\xf4\x92\xa3\x10\x83 \x0f\x8b\xc3\xea?\xa6\xf9\xb3\xae\xaf\x9b\xbcj\
+\xef\x9a3-\x13\xba\x99o\xb1\xf07l\xcfYg5\xdbd\xf3\xdf\x0c\x91\xb2\x8b\x1e\
+\x81+fM\xf3[u \xe8\xb7N\xcd{\xbf\xfdG\x97\xf0\xaa~}P\xf0\xdb\xd8\xe2\xa9\xdf\
+\xec]\x8c\r\xbb\xdb\xbcN9\x08Y\x1d0\\\xb6\xf7\x9f\xd0\xaam\xbe\x0b\xeb\xdb\
+\x97\xfc\xbd\xfc8\xe3\x94\xfd\xdb\x7fs\xa4\xa7\x17\xf0\x9d;\x03\xb4\x94\xc1\
+\xd3\xd5\xcfe\x9dSB\x13\x00\xcbEE&' )
+
+def GetPointerBitmap():
+    return wx.BitmapFromImage(GetPointerImage())
+
+def GetPointerImage():
+    stream = cStringIO.StringIO(GetPointerData())
+    return wx.ImageFromStream(stream)
+
diff --git a/wxPython/wx/lib/floatcanvas/__init__.py b/wxPython/wx/lib/floatcanvas/__init__.py
new file mode 100644 (file)
index 0000000..0467819
--- /dev/null
@@ -0,0 +1,95 @@
+"""
+This is the floatcanvas package. It provides two primary modules, and a
+support module.
+
+FloatCanvas.py contains the main FloatCanvas class, and it's supporting
+classes.  NavCanvas.py contains a wrapper for the FloatCanvas that
+provides the canvas and a toolbar with tools that allow you to navigate
+the canvas (zooming, panning, etc.)  Resources.py is a module that
+contains a few resources required by the FloatCanvas (icons, etc)
+
+The FloatCanvas is a high level window for drawing maps and anything
+else in an arbitrary coordinate system.
+
+The goal is to provide a convenient way to draw stuff on the screen
+without having to deal with handling OnPaint events, converting to pixel
+coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
+also provides virtually unlimited zooming and scrolling
+
+I am using it for two things:
+1) general purpose drawing in floating point coordinates
+2) displaying map data in Lat-long coordinates
+
+If the projection is set to None, it will draw in general purpose
+floating point coordinates. If the projection is set to 'FlatEarth', it
+will draw a FlatEarth projection, centered on the part of the map that
+you are viewing. You can also pass in your own projection function.
+
+It is double buffered, so re-draws after the window is uncovered by
+something else are very quick.
+
+It relies on NumPy, which is needed for speed (maybe, I haven't profiled
+it). It will also use numarray, if you don't have Numeric, but it is
+slower.
+
+Bugs and Limitations: Lots: patches, fixes welcome
+
+For Map drawing: It ignores the fact that the world is, in fact, a
+sphere, so it will do strange things if you are looking at stuff near
+the poles or the date line. so far I don't have a need to do that, so I
+havn't bothered to add any checks for that yet.
+
+Zooming: I have set no zoom limits. What this means is that if you zoom
+in really far, you can get integer overflows, and get weird results. It
+doesn't seem to actually cause any problems other than weird output, at
+least when I have run it.
+
+Speed: I have done a couple of things to improve speed in this app. The
+one thing I have done is used NumPy Arrays to store the coordinates of
+the points of the objects. This allowed me to use array oriented
+functions when doing transformations, and should provide some speed
+improvement for objects with a lot of points (big polygons, polylines,
+pointsets).
+
+The real slowdown comes when you have to draw a lot of objects, because
+you have to call the wx.DC.DrawSomething call each time. This is plenty
+fast for tens of objects, OK for hundreds of objects, but pretty darn
+slow for thousands of objects.
+
+If you are zoomed in, it checks the Bounding box of an object before
+drawing it. This makes it a great deal faster when there are a lot of
+objects and you are zoomed in so that only a few are shown.
+
+One solution is to be able to pass some sort of object set to the DC
+directly. I've used DC.DrawPointList(Points), and it helped a lot with
+drawing lots of points. However, when zoomed in, the Bounding boxes need
+to be checked, so I may some day write C++ code that does the loop and
+checks the BBs.
+
+Mouse Events:
+
+At this point, there are a full set of custom mouse events. They are
+just like the regular mouse events, but include an extra attribute:
+Event.GetCoords(), that returns the (x,y) position in world coordinates,
+as a length-2 NumPy vector of Floats.
+
+See the Demo for what it can do, and how to use it.
+
+Copyright: Christopher Barker
+
+License: Same as the version of wxPython you are using it with.
+
+Check for updates at:
+http://home.comcast.net/~chrishbarker/FloatCanvas/
+
+Please let me know if you're using this!!!
+
+Contact me at:
+
+Chris.Barker@noaa.gov
+
+"""
+
+__version__ = "0.8.3"
+
+