File: bboxdump.py

package info (click to toggle)
thuban 1.2.2-2
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 7,596 kB
  • ctags: 5,301
  • sloc: python: 30,411; ansic: 6,181; xml: 4,127; cpp: 1,595; makefile: 166; sh: 101
file content (246 lines) | stat: -rw-r--r-- 7,999 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# Copyright (C) 2004 by Intevation GmbH
# Authors:
# Frank Koormann <frank@intevation.de> (2004)
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

"""
Extend thuban with a bounding box dump.

Dumps the bounding boxes of all shapes of the selected layer.
An optional column can be specified to group the objects, 
in this case the bounding box is a union of the separate boxes.
"""

__version__ = '$Revision: 2817 $'
# $Source$
# $Id: bboxdump.py 2817 2008-01-27 00:01:32Z bernhard $

import os, sys
import string

import wx
from wx.lib.dialogs import ScrolledMessageDialog

from Thuban import _

from Thuban.UI import internal_from_wxstring
from Thuban.UI.common import ThubanBeginBusyCursor, ThubanEndBusyCursor
from Thuban.UI.command import registry, Command
from Thuban.UI.mainwindow import main_menu, _has_selected_shape_layer

import shapelib
import dbflib

# Widget IDs
ID_FILENAME = 4001
ID_ATTRIBUTES = 4002
ID_SELFN = 4003

class BBoxDumpDialog(wx.Dialog):
    """Bounding Box Dump Dialog

    Specify a filename for the dump and optionally a layer's column 
    field to group objects.
    """

    def __init__(self, parent, title, layer = None):
        wx.Dialog.__init__(self, parent, -1, title,
                          style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)

        if layer is None:
            return wx.ID_CANCEL

        # Store the layer
        self.layer = layer

        # Filename selection elements
        self.filename = wx.TextCtrl(self, ID_FILENAME, "")
        self.button_selectfile = wx.Button(self, ID_SELFN, _('Select...'))
        wx.EVT_BUTTON(self, ID_SELFN, self.OnSelectFilename)

        # Column choice elements
        self.choice_column = wx.Choice(self, ID_ATTRIBUTES)
        self.choice_column.Append(_('Select...'), None)
        for col in self.layer.ShapeStore().Table().Columns():
                self.choice_column.Append(col.name, col)
        self.choice_column.SetSelection(0)

        # Dialog button elements
        self.button_dump = wx.Button(self, wx.ID_OK, _("OK"))
        wx.EVT_BUTTON(self, wx.ID_OK, self.OnDump)
        self.button_dump.SetDefault()
        # TODO: Disable the OK button until a filename is entered ...
        # self.button_dump.Enable(False)
        self.button_cancel = wx.Button(self, wx.ID_CANCEL, _("Cancel"))


        # Dialog Layout: three horizontal box sizers.
        topbox = wx.BoxSizer(wx.VERTICAL)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        topbox.Add(hbox, 0, wx.ALL|wx.EXPAND)
        hbox.Add(wx.StaticText(self, -1, _("File:")),
                 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
        hbox.Add(self.filename, 1, wx.ALL|wx.EXPAND, 4)
        hbox.Add(self.button_selectfile, 0, wx.ALL, 4)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        topbox.Add(hbox, 0, wx.ALL|wx.EXPAND)
        hbox.Add(wx.StaticText(self, -1, _("Group by:")),
                 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
        hbox.Add(self.choice_column, 1, wx.ALL|wx.EXPAND, 4)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        topbox.Add(hbox, 0, wx.ALL|wx.EXPAND)
        hbox.Add(self.button_dump, 0, wx.ALL|wx.ALIGN_CENTER,
                  10)
        hbox.Add(self.button_cancel, 0, wx.ALL|wx.ALIGN_CENTER,
                  10)

        # Finalize ...
        self.SetAutoLayout(True)
        self.SetSizer(topbox)
        topbox.Fit(self)
        topbox.SetSizeHints(self)

        # Store for later use
        self.parent = parent

    def OnDump(self, event):
        """Bounding Box Dump Dialog event handler OK button.

        Prepare the inputs from the dialog and call processing.
        """
        i = self.choice_column.GetSelection()
        column = self.choice_column.GetClientData(i)
        self.Close()

        ThubanBeginBusyCursor()
        try:
            bboxmessage = bboxdump(self.layer, column, self.filename.GetValue())
        finally:
            ThubanEndBusyCursor()

        if bboxmessage:
            dlg = ScrolledMessageDialog(
                                self.parent, bboxmessage,
                                _("Bounding Box Dump %s") % self.layer.Title())
            dlg.ShowModal()

    def OnSelectFilename(self, event):
        """Bounding Box Dump Dialog event handler File Selection.

        Opens a file dialog to specify a file to dump into.
        """
        dlg = wx.FileDialog(self, _("Dump Bounding Boxes To"), 
                       os.path.dirname(self.filename.GetValue()), 
                       os.path.basename(self.filename.GetValue()),
                       _("CSV Files (*.csv)|*.csv|") +
                       _("All Files (*.*)|*.*"),
                       wx.SAVE|wx.OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename.SetValue(internal_from_wxstring(dlg.GetPath()))
            dlg.Destroy()
        else:
            dlg.Destroy()


def bboxdump(layer, column, filename):
    """Bounding Box Dump Processing

    layer    - Layer of shapes to be dumped
    column   - optional column to group shapes (else None)
    filename - optional filename to dump into (else empty string, i.e. dump
           to message dialog)
    """
    # Preparation
    shapelist = {}
    bboxmessage = []
 
    dlg= wx.ProgressDialog(_("Bounding Box Dump"),
                          _("Collecting shapes ..."),
                          layer.ShapeStore().NumShapes(),
                          None)

    cnt = 0
    step =  int(layer.ShapeStore().NumShapes() / 100.0)
    if step == 0:
        step = 1

    # Collect shape ids to be dumped
    if column is None:
        # A simple dump of shapes bbox is required
        for s in layer.ShapeStore().AllShapes():
            i = s.ShapeID()
            shapelist[i] = (i,)
            if cnt % step == 0:
                dlg.Update(cnt)
            cnt = cnt + 1
    else:
        # group them by column ... 
        for s in layer.ShapeStore().AllShapes():
            i = s.ShapeID()
            row = layer.ShapeStore().Table().ReadRowAsDict(i)
            att = row[column.name]
            if not shapelist.has_key(att):
                shapelist[att] = []
            shapelist[att].append(i)
            if cnt % step == 0:
                dlg.Update(cnt)
            cnt = cnt + 1

    dlg.Destroy()
    dlg= wx.ProgressDialog(_("Bounding Box Dump"),
                          _("Dump bounding boxes of selected shapes ..."),
                          len(shapelist),
                          None)
    cnt = 0
    step = int(len(shapelist) / 100.0)
    if step == 0:
        step = 1

    # Dump them, sorted
    keys = shapelist.keys()
    keys.sort()
    for key in keys:
        bbox = layer.ShapesBoundingBox(shapelist[key])
        bboxmessage.append("%.3f,%.3f,%.3f,%.3f,%s\n" % (
                            bbox[0], bbox[1], bbox[2], bbox[3], key))
        if cnt % step == 0:
            dlg.Update(cnt)
        cnt = cnt + 1
    dlg.Destroy()

    # finally
    if filename != '':
        bboxfile = file(filename, 'w+')
        bboxfile.write(string.join(bboxmessage))
        bboxfile.close()
        return None
    else:
        return string.join(bboxmessage)

def LayerBBoxDump(context):
    """Menu Handler BBoxDump
    """
    layer = context.mainwindow.canvas.SelectedLayer()
    if layer is not None:
        dlg = BBoxDumpDialog(context.mainwindow, _("Bounding Box Dump"),
                             layer = layer)
        dlg.ShowModal()


# bboxdump executed as an extension to Thuban

# register the new command
registry.Add(Command('bboxdump', _('BBox Dump'), LayerBBoxDump,
                     helptext = _('Dump Bounding Boxes of Layer Objects'),
                     sensitive = _has_selected_shape_layer))

# find the extensions menu (create it anew if not found)
extensions_menu = main_menu.FindOrInsertMenu('extensions', _('E&xtensions'))

# finally add the new entry to the extensions menu
extensions_menu.InsertItem('bboxdump')