/[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 176 - (hide annotations)
Wed May 15 13:38:49 2002 UTC (22 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 27202 byte(s)
remove some debug prints

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    
144     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     self.view.ZoomFactor(0.5, center = (cy, cy))
155     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    
188     class IdentifyTool(Tool):
189    
190     """The "Identify" Tool"""
191    
192     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    
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    
318     # 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     dc.Clear()
324     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    
387     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    
556     def OnLeftUp(self, event):
557     self.ReleaseMouse()
558 bh 122 self.set_current_position(event)
559 bh 6 if self.dragging:
560     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 43 def find_shape_at(self, px, py, select_labels = 0, selected_layer = 1):
633     """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     If the optional parameter selected_layer is true (default), only
643     search in the currently selected layer.
644     """
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    
659     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     if selected_layer:
691     layer = self.interactor.SelectedLayer()
692     if layer is not None:
693     layers = [layer]
694     else:
695     # no layer selected. Use an empty list to effectively
696     # ignore all layers.
697     layers = []
698     else:
699     layers = self.map.Layers()
700    
701 bh 6 for layer_index in range(len(layers) - 1, -1, -1):
702     layer = layers[layer_index]
703    
704     # search only in visible layers
705     if not layer.Visible():
706     continue
707    
708     filled = layer.fill is not None
709     stroked = layer.stroke is not None
710    
711     layer_proj = layer.projection
712     if layer_proj is not None:
713     inverse = layer_proj.Inverse
714     else:
715     inverse = None
716    
717     shapetype = layer.ShapeType()
718    
719     select_shape = -1
720 bh 159
721     shape_ids = layer.ShapesInRegion(box)
722     shape_ids.reverse()
723    
724 bh 6 if shapetype == SHAPETYPE_POLYGON:
725 bh 159 for i in shape_ids:
726 bh 6 result = point_in_polygon_shape(layer.shapefile.cobject(),
727     i,
728     filled, stroked,
729     map_proj, layer_proj,
730     scale, -scale, offx, offy,
731     px, py)
732     if result:
733     select_shape = i
734     break
735     elif shapetype == SHAPETYPE_ARC:
736 bh 159 for i in shape_ids:
737 bh 6 result = point_in_polygon_shape(layer.shapefile.cobject(),
738     i, 0, 1,
739     map_proj, layer_proj,
740     scale, -scale, offx, offy,
741     px, py)
742     if result < 0:
743     select_shape = i
744     break
745     elif shapetype == SHAPETYPE_POINT:
746 bh 159 for i in shape_ids:
747 bh 6 shape = layer.Shape(i)
748     x, y = shape.Points()[0]
749     if inverse:
750     x, y = inverse(x, y)
751     if forward:
752     x, y = forward(x, y)
753     x = x * scale + offx
754     y = -y * scale + offy
755     if hypot(px - x, py - y) < 5:
756     select_shape = i
757     break
758    
759     if select_shape >= 0:
760     return layer, select_shape
761     return None, None
762    
763     def SelectShapeAt(self, x, y):
764 bh 78 layer, shape = self.find_shape_at(x, y, selected_layer = 0)
765 bh 43 # If layer is None, then shape will also be None. We don't want
766     # to deselect the currently selected layer, so we simply select
767     # the already selected layer again.
768     if layer is None:
769     layer = self.interactor.SelectedLayer()
770 bh 6 self.interactor.SelectLayerAndShape(layer, shape)
771    
772     def LabelShapeAt(self, x, y):
773     ox = x; oy = y
774     label_layer = self.map.LabelLayer()
775     layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
776     if layer is None and shape_index is not None:
777     # a label was selected
778     label_layer.RemoveLabel(shape_index)
779     elif layer is not None:
780     text = labeldialog.run_label_dialog(self, layer.table, shape_index)
781     if text:
782     proj = self.map.projection
783     if proj is not None:
784     map_proj = proj
785     else:
786     map_proj = None
787     proj = layer.projection
788     if proj is not None:
789     layer_proj = proj
790     else:
791     layer_proj = None
792    
793     shapetype = layer.ShapeType()
794     if shapetype == SHAPETYPE_POLYGON:
795     x, y = shape_centroid(layer.shapefile.cobject(),
796     shape_index,
797     map_proj, layer_proj, 1, 1, 0, 0)
798     if map_proj is not None:
799     x, y = map_proj.Inverse(x, y)
800     else:
801     shape = layer.Shape(shape_index)
802     if shapetype == SHAPETYPE_POINT:
803     x, y = shape.Points()[0]
804     else:
805     # assume SHAPETYPE_ARC
806     points = shape.Points()
807     x, y = points[len(points) / 2]
808     if layer_proj is not None:
809     x, y = layer_proj.Inverse(x, y)
810     if shapetype == SHAPETYPE_POINT:
811     halign = ALIGN_LEFT
812     valign = ALIGN_CENTER
813     elif shapetype == SHAPETYPE_POLYGON:
814     halign = ALIGN_CENTER
815     valign = ALIGN_CENTER
816     elif shapetype == SHAPETYPE_ARC:
817     halign = ALIGN_LEFT
818     valign = ALIGN_CENTER
819     label_layer.AddLabel(x, y, text,
820     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