/[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 239 - (show annotations)
Wed Jul 24 17:15:54 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: 27198 byte(s)
(ZoomOutTool.MouseUp): Use the correct
x-coordinate in case of simple clicks

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, 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 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 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 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
721 shape_ids = layer.ShapesInRegion(box)
722 shape_ids.reverse()
723
724 if shapetype == SHAPETYPE_POLYGON:
725 for i in shape_ids:
726 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 for i in shape_ids:
737 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 for i in shape_ids:
747 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 layer, shape = self.find_shape_at(x, y, selected_layer = 0)
765 # 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 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