/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/view.py
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/Thuban/UI/view.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 261 - (hide annotations)
Thu Aug 15 17:44:33 2002 UTC (22 years, 6 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 27228 byte(s)
(MapCanvas.OnLeftUp): Only release the mouse
when we have actually captured it.

1 bh 78 # Copyright (c) 2001, 2002 by Intevation GmbH
2 bh 6 # Authors:
3     # Bernhard Herzog <[email protected]>
4     #
5     # This program is free software under the GPL (>=v2)
6     # Read the file COPYING coming with Thuban for details.
7    
8     """
9     Classes for display of a map and interaction with it
10     """
11    
12     __version__ = "$Revision$"
13    
14     from math import hypot
15    
16     from wxPython.wx import wxWindow,\
17     wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
18 bh 122 EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW
19 bh 6
20    
21     from wxPython import wx
22    
23     from wxproj import point_in_polygon_shape, shape_centroid
24    
25    
26     from Thuban.Model.messages import MAP_PROJECTION_CHANGED, \
27     LAYERS_CHANGED, LAYER_LEGEND_CHANGED, LAYER_VISIBILITY_CHANGED
28     from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \
29     SHAPETYPE_POINT
30     from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
31     ALIGN_LEFT, ALIGN_RIGHT
32 bh 122 from Thuban.Lib.connector import Publisher
33 bh 6
34     from renderer import ScreenRenderer, PrinterRender
35    
36     import labeldialog
37    
38 bh 122 from messages import SELECTED_SHAPE, VIEW_POSITION
39 bh 6
40    
41     #
42     # The tools
43     #
44    
45     class Tool:
46    
47     """
48     Base class for the interactive tools
49     """
50    
51     def __init__(self, view):
52     """Intitialize the tool. The view is the canvas displaying the map"""
53     self.view = view
54     self.start = self.current = None
55     self.dragging = 0
56     self.drawn = 0
57    
58     def Name(self):
59     """Return the tool's name"""
60     return ''
61    
62     def drag_start(self, x, y):
63     self.start = self.current = x, y
64     self.dragging = 1
65    
66     def drag_move(self, x, y):
67     self.current = x, y
68    
69     def drag_stop(self, x, y):
70     self.current = x, y
71     self.dragging = 0
72    
73     def Show(self, dc):
74     if not self.drawn:
75     self.draw(dc)
76     self.drawn = 1
77    
78     def Hide(self, dc):
79     if self.drawn:
80     self.draw(dc)
81     self.drawn = 0
82    
83     def draw(self, dc):
84     pass
85    
86     def MouseDown(self, event):
87     self.drag_start(event.m_x, event.m_y)
88    
89     def MouseMove(self, event):
90     if self.dragging:
91     self.drag_move(event.m_x, event.m_y)
92    
93     def MouseUp(self, event):
94     if self.dragging:
95     self.drag_move(event.m_x, event.m_y)
96    
97     def Cancel(self):
98     self.dragging = 0
99    
100    
101     class RectTool(Tool):
102    
103     """Base class for tools that draw rectangles while dragging"""
104    
105     def draw(self, dc):
106     sx, sy = self.start
107     cx, cy = self.current
108     dc.DrawRectangle(sx, sy, cx - sx, cy - sy)
109    
110     class ZoomInTool(RectTool):
111    
112     """The Zoom-In Tool"""
113    
114     def Name(self):
115     return "ZoomInTool"
116    
117     def proj_rect(self):
118     """return the rectangle given by start and current in projected
119     coordinates"""
120     sx, sy = self.start
121     cx, cy = self.current
122     left, top = self.view.win_to_proj(sx, sy)
123     right, bottom = self.view.win_to_proj(cx, cy)
124     return (min(left, right), min(top, bottom),
125     max(left, right), max(top, bottom))
126    
127     def MouseUp(self, event):
128     if self.dragging:
129     Tool.MouseUp(self, event)
130 bh 57 sx, sy = self.start
131     cx, cy = self.current
132     if sx == cx and sy == cy:
133     # Just a mouse click. Simply zoom in by a factor of two
134     self.view.ZoomFactor(2, center = (cx, cy))
135     else:
136     # A drag. Zoom in to the rectangle
137     self.view.FitRectToWindow(self.proj_rect())
138 bh 6
139    
140     class ZoomOutTool(RectTool):
141    
142     """The Zoom-Out Tool"""
143 bh 239
144 bh 6 def Name(self):
145     return "ZoomOutTool"
146    
147     def MouseUp(self, event):
148     if self.dragging:
149     Tool.MouseUp(self, event)
150     sx, sy = self.start
151     cx, cy = self.current
152 bh 57 if sx == cx and sy == cy:
153     # Just a mouse click. Simply zoom out by a factor of two
154 bh 239 self.view.ZoomFactor(0.5, center = (cx, cy))
155 bh 57 else:
156     # A drag. Zoom out to the rectangle
157     self.view.ZoomOutToRect((min(sx, cx), min(sy, cy),
158     max(sx, cx), max(sy, cy)))
159 bh 6
160    
161     class PanTool(Tool):
162    
163     """The Pan Tool"""
164    
165     def Name(self):
166     return "PanTool"
167    
168     def MouseMove(self, event):
169     if self.dragging:
170     Tool.MouseMove(self, event)
171 bh 159 sx, sy = self.start
172 bh 6 x, y = self.current
173     width, height = self.view.GetSizeTuple()
174 bh 159
175     bitmapdc = wx.wxMemoryDC()
176     bitmapdc.SelectObject(self.view.bitmap)
177    
178 bh 6 dc = self.view.drag_dc
179 bh 159 dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
180 bh 6
181     def MouseUp(self, event):
182     if self.dragging:
183     Tool.MouseUp(self, event)
184     sx, sy = self.start
185     cx, cy = self.current
186     self.view.Translate(cx - sx, cy - sy)
187 bh 246
188 bh 6 class IdentifyTool(Tool):
189    
190     """The "Identify" Tool"""
191 bh 246
192 bh 6 def Name(self):
193     return "IdentifyTool"
194    
195     def MouseUp(self, event):
196     self.view.SelectShapeAt(event.m_x, event.m_y)
197    
198    
199     class LabelTool(Tool):
200    
201     """The "Label" Tool"""
202    
203     def Name(self):
204     return "LabelTool"
205    
206     def MouseUp(self, event):
207     self.view.LabelShapeAt(event.m_x, event.m_y)
208    
209    
210    
211    
212     class MapPrintout(wx.wxPrintout):
213    
214     """
215     wxPrintout class for printing Thuban maps
216     """
217    
218     def __init__(self, map):
219     wx.wxPrintout.__init__(self)
220     self.map = map
221    
222     def GetPageInfo(self):
223     return (1, 1, 1, 1)
224    
225     def HasPage(self, pagenum):
226     return pagenum == 1
227    
228     def OnPrintPage(self, pagenum):
229     if pagenum == 1:
230     self.draw_on_dc(self.GetDC())
231    
232     def draw_on_dc(self, dc):
233     width, height = self.GetPageSizePixels()
234     llx, lly, urx, ury = self.map.ProjectedBoundingBox()
235     scalex = width / (urx - llx)
236     scaley = height / (ury - lly)
237     scale = min(scalex, scaley)
238     offx = 0.5 * (width - (urx + llx) * scale)
239     offy = 0.5 * (height + (ury + lly) * scale)
240    
241     resx, resy = self.GetPPIPrinter()
242     renderer = PrinterRender(dc, scale, (offx, offy), resolution = resx)
243     renderer.RenderMap(self.map)
244     return wx.true
245    
246 bh 246
247 bh 122 class MapCanvas(wxWindow, Publisher):
248 bh 6
249     """A widget that displays a map and offers some interaction"""
250    
251 bh 23 def __init__(self, parent, winid, interactor):
252 bh 6 wxWindow.__init__(self, parent, winid)
253     self.SetBackgroundColour(wxColour(255, 255, 255))
254 bh 125
255     # the map displayed in this canvas. Set with SetMap()
256 bh 6 self.map = None
257 bh 125
258     # scale and offset describe the transformation from projected
259     # coordinates to window coordinates.
260 bh 6 self.scale = 1.0
261     self.offset = (0, 0)
262 bh 125
263     # whether the user is currently dragging the mouse, i.e. moving
264     # the mouse while pressing a mouse button
265 bh 6 self.dragging = 0
266 bh 125
267     # the currently active tool
268 bh 6 self.tool = None
269 bh 125
270     # The current mouse position of the last OnMotion event or None
271     # if the mouse is outside the window.
272     self.current_position = None
273    
274     # If true, OnIdle will call do_redraw to do the actual
275     # redrawing. Set by OnPaint to avoid some unnecessary redraws.
276     # To force a redraw call full_redraw().
277 bh 6 self.redraw_on_idle = 0
278 bh 125
279 bh 145 # The region to update when idle
280     self.update_region = wx.wxRegion()
281    
282 bh 125 # the bitmap serving as backing store
283     self.bitmap = None
284    
285     # the interactor
286     self.interactor = interactor
287     self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)
288    
289 bh 174 # keep track of which layers/shapes are selected to make sure we
290     # only redraw when necessary
291     self.last_selected_layer = None
292     self.last_selected_shape = None
293    
294 bh 125 # subscribe the WX events we're interested in
295 bh 6 EVT_PAINT(self, self.OnPaint)
296     EVT_LEFT_DOWN(self, self.OnLeftDown)
297     EVT_LEFT_UP(self, self.OnLeftUp)
298     EVT_MOTION(self, self.OnMotion)
299 bh 122 EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
300 bh 125 wx.EVT_SIZE(self, self.OnSize)
301 bh 6 wx.EVT_IDLE(self, self.OnIdle)
302    
303 bh 122 def __del__(self):
304     wxWindow.__del__(self)
305     Publisher.__del__(self)
306    
307 bh 6 def OnPaint(self, event):
308     dc = wxPaintDC(self)
309 bh 57 if self.map is not None and self.map.HasLayers():
310     # We have a non-empty map. Redraw it in idle time
311     self.redraw_on_idle = 1
312 bh 145 # update the region that has to be redrawn
313     self.update_region.UnionRegion(self.GetUpdateRegion())
314 bh 57 else:
315     # If we've got no map or if the map is empty, simply clear
316     # the screen.
317 bh 246
318 bh 57 # XXX it's probably possible to get rid of this. The
319     # background color of the window is already white and the
320     # only thing we may have to do is to call self.Refresh()
321     # with a true argument in the right places.
322     dc.BeginDrawing()
323 bh 246 dc.Clear()
324 bh 57 dc.EndDrawing()
325 bh 6
326 bh 145 # clear the region
327     self.update_region = wx.wxRegion()
328    
329 bh 6 def do_redraw(self):
330 bh 145 # This should only be called if we have a non-empty map.
331 bh 125
332 bh 149 # get the update region and reset it. We're not actually using
333     # it anymore, though.
334 bh 145 update_box = self.update_region.GetBox()
335     self.update_region = wx.wxRegion()
336    
337     # Get the window size.
338 bh 6 width, height = self.GetSizeTuple()
339    
340 bh 125 # If self.bitmap's still there, reuse it. Otherwise redraw it
341     if self.bitmap is not None:
342     bitmap = self.bitmap
343 bh 6 else:
344 bh 125 bitmap = wx.wxEmptyBitmap(width, height)
345     dc = wx.wxMemoryDC()
346     dc.SelectObject(bitmap)
347     dc.BeginDrawing()
348 bh 57
349 bh 125 # clear the background
350     dc.SetBrush(wx.wxWHITE_BRUSH)
351     dc.SetPen(wx.wxTRANSPARENT_PEN)
352     dc.DrawRectangle(0, 0, width, height)
353 bh 6
354 bh 125 if 1: #self.interactor.selected_map is self.map:
355     selected_layer = self.interactor.selected_layer
356     selected_shape = self.interactor.selected_shape
357     else:
358     selected_layer = None
359     selected_shape = None
360 bh 57
361 bh 125 # draw the map into the bitmap
362     renderer = ScreenRenderer(dc, self.scale, self.offset)
363 bh 149
364     # Pass the entire bitmap as update_region to the renderer.
365     # We're redrawing the whole bitmap, after all.
366     renderer.RenderMap(self.map, (0, 0, width, height),
367 bh 145 selected_layer, selected_shape)
368 bh 125
369     dc.EndDrawing()
370     dc.SelectObject(wx.wxNullBitmap)
371     self.bitmap = bitmap
372    
373 bh 57 # blit the bitmap to the screen
374 bh 125 dc = wx.wxMemoryDC()
375     dc.SelectObject(bitmap)
376 bh 6 clientdc = wxClientDC(self)
377     clientdc.BeginDrawing()
378     clientdc.Blit(0, 0, width, height, dc, 0, 0)
379     clientdc.EndDrawing()
380    
381     def Print(self):
382     printer = wx.wxPrinter()
383     printout = MapPrintout(self.map)
384     printer.Print(self, printout, wx.true)
385     printout.Destroy()
386 bh 246
387 bh 6 def SetMap(self, map):
388     redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,
389     LAYER_VISIBILITY_CHANGED)
390     if self.map is not None:
391     for channel in redraw_channels:
392 bh 125 self.map.Unsubscribe(channel, self.full_redraw)
393 bh 6 self.map.Unsubscribe(MAP_PROJECTION_CHANGED,
394     self.projection_changed)
395     self.map = map
396     if self.map is not None:
397     for channel in redraw_channels:
398 bh 125 self.map.Subscribe(channel, self.full_redraw)
399 bh 6 self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)
400     self.FitMapToWindow()
401 bh 57 # force a redraw. If map is not empty, it's already been called
402     # by FitMapToWindow but if map is empty it hasn't been called
403     # yet so we have to explicitly call it.
404 bh 125 self.full_redraw()
405 bh 6
406     def Map(self):
407     return self.map
408    
409     def redraw(self, *args):
410     self.Refresh(0)
411    
412 bh 125 def full_redraw(self, *args):
413     self.bitmap = None
414     self.redraw()
415    
416 bh 6 def projection_changed(self, *args):
417     self.FitMapToWindow()
418 bh 125 self.full_redraw()
419 bh 6
420     def set_view_transform(self, scale, offset):
421     self.scale = scale
422     self.offset = offset
423 bh 125 self.full_redraw()
424 bh 6
425     def proj_to_win(self, x, y):
426     """\
427     Return the point in window coords given by projected coordinates x y
428     """
429     offx, offy = self.offset
430     return (self.scale * x + offx, -self.scale * y + offy)
431    
432     def win_to_proj(self, x, y):
433     """\
434     Return the point in projected coordinates given by window coords x y
435     """
436     offx, offy = self.offset
437     return ((x - offx) / self.scale, (offy - y) / self.scale)
438    
439     def FitRectToWindow(self, rect):
440     width, height = self.GetSizeTuple()
441     llx, lly, urx, ury = rect
442 bh 45 if llx == urx or lly == ury:
443     # zero with or zero height. Do Nothing
444     return
445 bh 6 scalex = width / (urx - llx)
446     scaley = height / (ury - lly)
447     scale = min(scalex, scaley)
448     offx = 0.5 * (width - (urx + llx) * scale)
449     offy = 0.5 * (height + (ury + lly) * scale)
450     self.set_view_transform(scale, (offx, offy))
451    
452     def FitMapToWindow(self):
453     """\
454     Set the scale and offset so that the map is centered in the
455     window
456     """
457     bbox = self.map.ProjectedBoundingBox()
458     if bbox is not None:
459     self.FitRectToWindow(bbox)
460    
461 bh 57 def ZoomFactor(self, factor, center = None):
462     """Multiply the zoom by factor and center on center.
463    
464     The optional parameter center is a point in window coordinates
465     that should be centered. If it is omitted, it defaults to the
466     center of the window
467     """
468 bh 6 width, height = self.GetSizeTuple()
469     scale = self.scale * factor
470     offx, offy = self.offset
471 bh 57 if center is not None:
472     cx, cy = center
473     else:
474     cx = width / 2
475     cy = height / 2
476     offset = (factor * (offx - cx) + width / 2,
477     factor * (offy - cy) + height / 2)
478 bh 6 self.set_view_transform(scale, offset)
479    
480     def ZoomOutToRect(self, rect):
481     # rect is given in window coordinates
482    
483     # determine the bbox of the displayed region in projected
484     # coordinates
485     width, height = self.GetSizeTuple()
486     llx, lly = self.win_to_proj(0, height - 1)
487     urx, ury = self.win_to_proj(width - 1, 0)
488    
489     sx, sy, ex, ey = rect
490     scalex = (ex - sx) / (urx - llx)
491     scaley = (ey - sy) / (ury - lly)
492     scale = min(scalex, scaley)
493    
494     offx = 0.5 * ((ex + sx) - (urx + llx) * scale)
495     offy = 0.5 * ((ey + sy) + (ury + lly) * scale)
496     self.set_view_transform(scale, (offx, offy))
497    
498     def Translate(self, dx, dy):
499     offx, offy = self.offset
500     self.set_view_transform(self.scale, (offx + dx, offy + dy))
501    
502     def ZoomInTool(self):
503     self.tool = ZoomInTool(self)
504    
505     def ZoomOutTool(self):
506     self.tool = ZoomOutTool(self)
507    
508     def PanTool(self):
509     self.tool = PanTool(self)
510    
511     def IdentifyTool(self):
512     self.tool = IdentifyTool(self)
513    
514     def LabelTool(self):
515     self.tool = LabelTool(self)
516    
517     def CurrentTool(self):
518     return self.tool and self.tool.Name() or None
519    
520 bh 122 def CurrentPosition(self):
521     """Return current position of the mouse in projected coordinates.
522    
523     The result is a 2-tuple of floats with the coordinates. If the
524     mouse is not in the window, the result is None.
525     """
526     if self.current_position is not None:
527     x, y = self.current_position
528     return self.win_to_proj(x, y)
529     else:
530     return None
531    
532     def set_current_position(self, event):
533     """Set the current position from event
534    
535     Should be called by all events that contain mouse positions
536     especially EVT_MOTION. The event paramete may be None to
537     indicate the the pointer left the window.
538     """
539     if event is not None:
540     self.current_position = (event.m_x, event.m_y)
541     else:
542     self.current_position = None
543     self.issue(VIEW_POSITION)
544    
545 bh 6 def OnLeftDown(self, event):
546 bh 122 self.set_current_position(event)
547 bh 6 if self.tool is not None:
548     self.drag_dc = wxClientDC(self)
549     self.drag_dc.SetLogicalFunction(wxINVERT)
550     self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
551     self.CaptureMouse()
552     self.tool.MouseDown(event)
553     self.tool.Show(self.drag_dc)
554     self.dragging = 1
555 bh 246
556 bh 6 def OnLeftUp(self, event):
557 bh 122 self.set_current_position(event)
558 bh 6 if self.dragging:
559 bh 261 self.ReleaseMouse()
560 bh 6 self.tool.Hide(self.drag_dc)
561     self.tool.MouseUp(event)
562     self.drag_dc = None
563     self.dragging = 0
564    
565     def OnMotion(self, event):
566 bh 122 self.set_current_position(event)
567 bh 6 if self.dragging:
568     self.tool.Hide(self.drag_dc)
569     self.tool.MouseMove(event)
570     self.tool.Show(self.drag_dc)
571    
572 bh 122 def OnLeaveWindow(self, event):
573     self.set_current_position(None)
574    
575 bh 6 def OnIdle(self, event):
576     if self.redraw_on_idle:
577     self.do_redraw()
578     self.redraw_on_idle = 0
579    
580 bh 125 def OnSize(self, event):
581     # the window's size has changed. We have to get a new bitmap. If
582     # we want to be clever we could try to get by without throwing
583     # everything away. E.g. when the window gets smaller, we could
584     # either keep the bitmap or create the new one from the old one.
585     # Even when the window becomes larger some parts of the bitmap
586     # could be reused.
587     self.full_redraw()
588    
589 bh 6 def shape_selected(self, layer, shape):
590 bh 174 """Redraw the map.
591 bh 6
592 bh 174 Receiver for the SELECTED_SHAPE messages. Try to redraw only
593     when necessary.
594     """
595     # A redraw is necessary when the display has to change, which
596     # means that either the status changes from having no selection
597     # to having a selection shape or vice versa, or when the fact
598     # whether there is a selection at all doesn't change, when the
599     # shape which is selected has changed (which means that layer or
600     # shapeid changes).
601     if ((shape is not None or self.last_selected_shape is not None)
602     and (shape != self.last_selected_shape
603     or layer != self.last_selected_layer)):
604     self.full_redraw()
605 bh 176
606     # remember the selection so we can compare when it changes again.
607 bh 174 self.last_selected_layer = layer
608     self.last_selected_shape = shape
609    
610 bh 159 def unprojected_rect_around_point(self, x, y):
611     """return a rect a few pixels around (x, y) in unprojected corrdinates
612    
613     The return value is a tuple (minx, miny, maxx, maxy) suitable a
614     parameter to a layer's ShapesInRegion method.
615     """
616     map_proj = self.map.projection
617     if map_proj is not None:
618     inverse = map_proj.Inverse
619     else:
620     inverse = None
621    
622     xs = []
623     ys = []
624     for dx, dy in ((-1, -1), (1, -1), (1, 1), (-1, 1)):
625     px, py = self.win_to_proj(x + dx, y + dy)
626     if inverse:
627     px, py = inverse(px, py)
628     xs.append(px)
629     ys.append(py)
630     return (min(xs), min(ys), max(xs), max(ys))
631    
632 bh 246 def find_shape_at(self, px, py, select_labels = 0, searched_layer = None):
633 bh 43 """Determine the shape at point px, py in window coords
634    
635     Return the shape and the corresponding layer as a tuple (layer,
636     shape).
637    
638     If the optional parameter select_labels is true (default false)
639     search through the labels. If a label is found return it's index
640     as the shape and None as the layer.
641    
642 bh 246 If the optional parameter searched_layer is given (or not None
643     which it defaults to), only search in that layer.
644 bh 43 """
645 bh 6 map_proj = self.map.projection
646     if map_proj is not None:
647     forward = map_proj.Forward
648     else:
649     forward = None
650    
651     scale = self.scale
652     offx, offy = self.offset
653    
654 bh 159 box = self.unprojected_rect_around_point(px, py)
655    
656 bh 6 if select_labels:
657     labels = self.map.LabelLayer().Labels()
658 bh 246
659 bh 6 if labels:
660     dc = wxClientDC(self)
661     font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
662     dc.SetFont(font)
663 bh 60 for i in range(len(labels) - 1, -1, -1):
664 bh 6 label = labels[i]
665     x = label.x
666     y = label.y
667     text = label.text
668     if forward:
669     x, y = forward(x, y)
670     x = x * scale + offx
671     y = -y * scale + offy
672     width, height = dc.GetTextExtent(text)
673     if label.halign == ALIGN_LEFT:
674     # nothing to be done
675     pass
676     elif label.halign == ALIGN_RIGHT:
677     x = x - width
678     elif label.halign == ALIGN_CENTER:
679     x = x - width/2
680     if label.valign == ALIGN_TOP:
681     # nothing to be done
682     pass
683     elif label.valign == ALIGN_BOTTOM:
684     y = y - height
685     elif label.valign == ALIGN_CENTER:
686     y = y - height/2
687     if x <= px < x + width and y <= py <= y + height:
688     return None, i
689 bh 43
690 bh 246 if searched_layer:
691     layers = [searched_layer]
692 bh 43 else:
693     layers = self.map.Layers()
694    
695 bh 6 for layer_index in range(len(layers) - 1, -1, -1):
696     layer = layers[layer_index]
697    
698     # search only in visible layers
699     if not layer.Visible():
700     continue
701    
702     filled = layer.fill is not None
703     stroked = layer.stroke is not None
704 bh 246
705 bh 6 layer_proj = layer.projection
706     if layer_proj is not None:
707     inverse = layer_proj.Inverse
708     else:
709     inverse = None
710 bh 246
711 bh 6 shapetype = layer.ShapeType()
712    
713     select_shape = -1
714 bh 159
715     shape_ids = layer.ShapesInRegion(box)
716     shape_ids.reverse()
717    
718 bh 6 if shapetype == SHAPETYPE_POLYGON:
719 bh 159 for i in shape_ids:
720 bh 6 result = point_in_polygon_shape(layer.shapefile.cobject(),
721     i,
722     filled, stroked,
723     map_proj, layer_proj,
724     scale, -scale, offx, offy,
725     px, py)
726     if result:
727     select_shape = i
728     break
729     elif shapetype == SHAPETYPE_ARC:
730 bh 159 for i in shape_ids:
731 bh 6 result = point_in_polygon_shape(layer.shapefile.cobject(),
732     i, 0, 1,
733     map_proj, layer_proj,
734     scale, -scale, offx, offy,
735     px, py)
736     if result < 0:
737     select_shape = i
738     break
739     elif shapetype == SHAPETYPE_POINT:
740 bh 159 for i in shape_ids:
741 bh 6 shape = layer.Shape(i)
742     x, y = shape.Points()[0]
743     if inverse:
744     x, y = inverse(x, y)
745     if forward:
746     x, y = forward(x, y)
747     x = x * scale + offx
748     y = -y * scale + offy
749     if hypot(px - x, py - y) < 5:
750     select_shape = i
751     break
752    
753     if select_shape >= 0:
754     return layer, select_shape
755     return None, None
756    
757 bh 246 def SelectShapeAt(self, x, y, layer = None):
758     """\
759     Select and return the shape and its layer at window position (x, y)
760    
761     If layer is given, only search in that layer. If no layer is
762     given, search through all layers.
763    
764     Return a tuple (layer, shapeid). If no shape is found, return
765     (None, None).
766     """
767     layer, shape = result = self.find_shape_at(x, y, searched_layer=layer)
768 bh 43 # If layer is None, then shape will also be None. We don't want
769     # to deselect the currently selected layer, so we simply select
770     # the already selected layer again.
771     if layer is None:
772     layer = self.interactor.SelectedLayer()
773 bh 6 self.interactor.SelectLayerAndShape(layer, shape)
774 bh 246 return result
775 bh 6
776     def LabelShapeAt(self, x, y):
777     ox = x; oy = y
778     label_layer = self.map.LabelLayer()
779     layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
780     if layer is None and shape_index is not None:
781     # a label was selected
782     label_layer.RemoveLabel(shape_index)
783     elif layer is not None:
784     text = labeldialog.run_label_dialog(self, layer.table, shape_index)
785     if text:
786     proj = self.map.projection
787     if proj is not None:
788     map_proj = proj
789     else:
790     map_proj = None
791     proj = layer.projection
792     if proj is not None:
793     layer_proj = proj
794     else:
795     layer_proj = None
796    
797     shapetype = layer.ShapeType()
798     if shapetype == SHAPETYPE_POLYGON:
799     x, y = shape_centroid(layer.shapefile.cobject(),
800     shape_index,
801     map_proj, layer_proj, 1, 1, 0, 0)
802     if map_proj is not None:
803     x, y = map_proj.Inverse(x, y)
804     else:
805     shape = layer.Shape(shape_index)
806     if shapetype == SHAPETYPE_POINT:
807     x, y = shape.Points()[0]
808     else:
809     # assume SHAPETYPE_ARC
810     points = shape.Points()
811     x, y = points[len(points) / 2]
812     if layer_proj is not None:
813     x, y = layer_proj.Inverse(x, y)
814     if shapetype == SHAPETYPE_POINT:
815     halign = ALIGN_LEFT
816     valign = ALIGN_CENTER
817     elif shapetype == SHAPETYPE_POLYGON:
818     halign = ALIGN_CENTER
819     valign = ALIGN_CENTER
820     elif shapetype == SHAPETYPE_ARC:
821     halign = ALIGN_LEFT
822     valign = ALIGN_CENTER
823     label_layer.AddLabel(x, y, text,
824     halign = halign, valign = valign)

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26