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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 246 - (show annotations)
Mon Jul 29 13:38:04 2002 UTC (22 years, 7 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/view.py
File MIME type: text/x-python
File size: 27224 byte(s)
(MapCanvas.find_shape_at): Change the
selected_layer parameter to searched_layer which is the layer to
search in.
(MapCanvas.SelectShapeAt): New parameter layer to restrict the
search to that layer. Return the selected layer and shape.

1 # Copyright (c) 2001, 2002 by Intevation GmbH
2 # 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 EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW
19
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 from Thuban.Lib.connector import Publisher
33
34 from renderer import ScreenRenderer, PrinterRender
35
36 import labeldialog
37
38 from messages import SELECTED_SHAPE, VIEW_POSITION
39
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 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
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 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 = (cx, 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
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 sx, sy = self.start
172 x, y = self.current
173 width, height = self.view.GetSizeTuple()
174
175 bitmapdc = wx.wxMemoryDC()
176 bitmapdc.SelectObject(self.view.bitmap)
177
178 dc = self.view.drag_dc
179 dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
180
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 class MapCanvas(wxWindow, Publisher):
248
249 """A widget that displays a map and offers some interaction"""
250
251 def __init__(self, parent, winid, interactor):
252 wxWindow.__init__(self, parent, winid)
253 self.SetBackgroundColour(wxColour(255, 255, 255))
254
255 # the map displayed in this canvas. Set with SetMap()
256 self.map = None
257
258 # scale and offset describe the transformation from projected
259 # coordinates to window coordinates.
260 self.scale = 1.0
261 self.offset = (0, 0)
262
263 # whether the user is currently dragging the mouse, i.e. moving
264 # the mouse while pressing a mouse button
265 self.dragging = 0
266
267 # the currently active tool
268 self.tool = None
269
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 self.redraw_on_idle = 0
278
279 # The region to update when idle
280 self.update_region = wx.wxRegion()
281
282 # 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 # 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 # subscribe the WX events we're interested in
295 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 EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
300 wx.EVT_SIZE(self, self.OnSize)
301 wx.EVT_IDLE(self, self.OnIdle)
302
303 def __del__(self):
304 wxWindow.__del__(self)
305 Publisher.__del__(self)
306
307 def OnPaint(self, event):
308 dc = wxPaintDC(self)
309 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 # update the region that has to be redrawn
313 self.update_region.UnionRegion(self.GetUpdateRegion())
314 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
326 # clear the region
327 self.update_region = wx.wxRegion()
328
329 def do_redraw(self):
330 # This should only be called if we have a non-empty map.
331
332 # get the update region and reset it. We're not actually using
333 # it anymore, though.
334 update_box = self.update_region.GetBox()
335 self.update_region = wx.wxRegion()
336
337 # Get the window size.
338 width, height = self.GetSizeTuple()
339
340 # If self.bitmap's still there, reuse it. Otherwise redraw it
341 if self.bitmap is not None:
342 bitmap = self.bitmap
343 else:
344 bitmap = wx.wxEmptyBitmap(width, height)
345 dc = wx.wxMemoryDC()
346 dc.SelectObject(bitmap)
347 dc.BeginDrawing()
348
349 # clear the background
350 dc.SetBrush(wx.wxWHITE_BRUSH)
351 dc.SetPen(wx.wxTRANSPARENT_PEN)
352 dc.DrawRectangle(0, 0, width, height)
353
354 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
361 # draw the map into the bitmap
362 renderer = ScreenRenderer(dc, self.scale, self.offset)
363
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 selected_layer, selected_shape)
368
369 dc.EndDrawing()
370 dc.SelectObject(wx.wxNullBitmap)
371 self.bitmap = bitmap
372
373 # blit the bitmap to the screen
374 dc = wx.wxMemoryDC()
375 dc.SelectObject(bitmap)
376 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 self.map.Unsubscribe(channel, self.full_redraw)
393 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 self.map.Subscribe(channel, self.full_redraw)
399 self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)
400 self.FitMapToWindow()
401 # 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 self.full_redraw()
405
406 def Map(self):
407 return self.map
408
409 def redraw(self, *args):
410 self.Refresh(0)
411
412 def full_redraw(self, *args):
413 self.bitmap = None
414 self.redraw()
415
416 def projection_changed(self, *args):
417 self.FitMapToWindow()
418 self.full_redraw()
419
420 def set_view_transform(self, scale, offset):
421 self.scale = scale
422 self.offset = offset
423 self.full_redraw()
424
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 if llx == urx or lly == ury:
443 # zero with or zero height. Do Nothing
444 return
445 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 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 width, height = self.GetSizeTuple()
469 scale = self.scale * factor
470 offx, offy = self.offset
471 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 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 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 def OnLeftDown(self, event):
546 self.set_current_position(event)
547 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 self.set_current_position(event)
559 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 self.set_current_position(event)
567 if self.dragging:
568 self.tool.Hide(self.drag_dc)
569 self.tool.MouseMove(event)
570 self.tool.Show(self.drag_dc)
571
572 def OnLeaveWindow(self, event):
573 self.set_current_position(None)
574
575 def OnIdle(self, event):
576 if self.redraw_on_idle:
577 self.do_redraw()
578 self.redraw_on_idle = 0
579
580 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 def shape_selected(self, layer, shape):
590 """Redraw the map.
591
592 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
606 # remember the selection so we can compare when it changes again.
607 self.last_selected_layer = layer
608 self.last_selected_shape = shape
609
610 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 def find_shape_at(self, px, py, select_labels = 0, searched_layer = None):
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 searched_layer is given (or not None
643 which it defaults to), only search in that layer.
644 """
645 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 box = self.unprojected_rect_around_point(px, py)
655
656 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 for i in range(len(labels) - 1, -1, -1):
664 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
690 if searched_layer:
691 layers = [searched_layer]
692 else:
693 layers = self.map.Layers()
694
695 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
705 layer_proj = layer.projection
706 if layer_proj is not None:
707 inverse = layer_proj.Inverse
708 else:
709 inverse = None
710
711 shapetype = layer.ShapeType()
712
713 select_shape = -1
714
715 shape_ids = layer.ShapesInRegion(box)
716 shape_ids.reverse()
717
718 if shapetype == SHAPETYPE_POLYGON:
719 for i in shape_ids:
720 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 for i in shape_ids:
731 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 for i in shape_ids:
741 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 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 # 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 self.interactor.SelectLayerAndShape(layer, shape)
774 return result
775
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