跳转至

为 IDL 8 图形窗口自定义鼠标处理程序

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/customizing-mouse-handler-for-idl-8-graphics-windows

15307 为本文评分:

未评分

为 IDL 8 图形窗口自定义鼠标处理程序

匿名用户 2014年5月22日 星期四

这篇博客文章提供了一个示例程序,以解答我们在过去几周技术支持中收到的一些问题。示例代码包含两个例程("ex_closest_xy_widget" 和 "closest_wid_mouse_down"),并在本文底部展示。

该示例程序解决的主要问题是:

  1. 给定一个包含不同 X 和 Y 值的数据集,如果给你另一组 X0 和 Y0 坐标,你如何确定哪个数据点最接近这些坐标?
  2. 如何使用 IDL 8 Graphics 创建一个快速的交互式 GUI 来显示问题 (1) 的信息?
  3. 如何调整 LEGEND 对象,使其只显示一个符号?

问题 (1) 的一个解决方案是计算数据集中每个数据点的 X 与 X0 以及 Y 与 Y0 之间的差值。然后,计算这些差值的幅值并找到最小值。由于 IDL 是向量化的,这可以用几行代码完成:

d = sqrt((x-(x0))^2+(y-(y0))^2) imin = where(d eq min(d))

IDL 8 Graphics(又称新图形,或称函数图形)是交互式的,你可以自定义图形窗口的事件处理程序。因此,你可以使用 IDL 8 Graphics 生成相当不错的 GUI 应用程序,而无需从头开始构建部件。因此,问题 (2) 的解决方案是:先用原始数据生成一个散点图,然后自定义 MOUSE_DOWN_HANDLER,允许用户在图中选择“目标”坐标,接着你就可以使用问题 (1) 的解决方案来找到最接近的数据点并显示该信息。

最后,如果你尝试使用 LEGEND 函数为带有符号的绘图创建图例,你会发现默认情况下它会显示一个符号三次。如果你想将图例更改为只显示符号一次,可以通过将 SAMPLE_WIDTH 属性设置为 0 来实现(三个符号仍然生成,但彼此重叠在一起)。然而,这可能导致符号出现在图例边框之外。要解决此问题,你可以调整 HORIZONTAL_SPACING 关键字。下面展示了一段演示如何实现此操作的代码示例:

lgnd = legend(target=[p,p0, p1], $ sample_width=0, $ horizontal_spacing=.1, $ /auto_text_color)

示例例程 "closest_wid_mouse_down" 和 "ex_closest_xy_widget" 如下所示。我要感谢我的同事 Jim Uba,他对下面的代码贡献很大,并找出了前面讨论的 LEGEND 技巧。运行此代码后,在窗口中左键单击,它应该会显示一个类似于下图的绘图:

function closest_wid_mouse_down, owin, x0, y0, button, keymods, clicks compile_opt idl2

if (button eq 1 ) then begin ;only perform on left mouse click

;get the state variable from the plot window
state = owin.uvalue

;get the reference to the random data point
p = state['p']

;the x and y points returned by the mouse
;handler are in device coordinates by default
;use the convertcoord method to convert it
;to data coordinates
data_coord = p.convertcoord(x0,y0,/device, /to_data)
x0 = data_coord[0]
y0 = data_coord[1]

state['x0']=x0
state['y0']=y0

;get the reference to the "closest point" scatter plot
p1 = state['p1']
;get the reference to the "selected point" scatter plot
p0 = state['p0']

;get the data
x = state['x']
y = state['y']

; calcuate the distances from the data
; points to the target point
d = sqrt((x-(x0))^2+(y-(y0))^2)

; find the array index of the xy point
; with minimum distance to the target point.
imin = where(d eq min(d))

;change the data of the "selected point" scatter plot
;to the point selected by the user. set hide=0 to make
;sure data displays correctly
p0.setdata, [x0,x0],[y0,y0]
p0.hide=0

;change the data of the "selected point" scatter plot
;to the point selected by the user
p1.setdata, [x[imin],x[imin]], [y[imin],y[imin]]
p1.hide=0

;print the selected point
print, "selected point:"
print, "x: ", x0
print, "y: ", y0

;print the closest point
print, "closest data point:"
print, "x: ", x[imin]
print, "y: ", y[imin]

endif

return, 1 end

pro ex_closest_xy_widget

; create random xy data points x = randomu(seed, 100) y = randomu(seed, 100)

;inital points to get the distance x0 = 0 y0 = 0

;generate scatterplot of random xy points p = scatterplot(x, y, $ sym_color='black', symbol='o', $ sym_size=1, sym_thick=3, $ aspect_ratio=1, $ name='random points', uvalue='p')

;generate scatterplot of the selected point ;this plot isn't very useful at this time because ;the user hasn't selected a point. use hide keyword ;to hide it for now p0 = scatterplot([x0,x0], [y0,y0], $ sym_color='red', symbol='+', $ sym_size=2, sym_thick=3, $ /current, /overplot, $ name='target point', /hide,uvalue='p0')

;generate scatterplot of the selected point ;this plot isn't very useful at this time because ;the user hasn't selected a point. use p1 = scatterplot( [x[0],x[0]], [y[0],y[0]], $ sym_color='green', symbol='+', $ sym_size=2, sym_thick=3, $ /current, /overplot, $ name='closest point',/hide,uvalue='p2')

;generate a state variable to store information needed for the ;handler. note: using a hash is a really convenient way to do ;this because they are easy to generate and use, and the data is ;stored as heap memory state=hash() state['p'] = p state['x'] = x state['y'] = y state['x0'] = x0 state['y0'] = y0 state['p0'] = p0 state['p1'] = p1

;assign the state variable as the uvalue of the ;scatterplot window p.window.uvalue = state

;set the mouse_down_handler of the ;window to "closet_wid_mouse_down" p.window.mouse_down_handler="closest_wid_mouse_down"

;generate a legend. by default this will generate 3 ;symbols. if you make the sample_width=0, and adjust ;the horizontal_spacing, you can make the legend on lgnd = legend(target=[p,p0, p1], $ sample_width=0, $ horizontal_spacing=.1, $ /auto_text_color)

;position the plot and legend in the window p.position = [.54,.45] lgnd.position=[.84,.99]

end

它不仅仅是分析,它是一个转换器! 弥合 GIS 与遥感之间的鸿沟