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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2051 - (show annotations)
Wed Jan 21 17:09:15 2004 UTC (21 years, 1 month ago) by frank
Original Path: trunk/thuban/Thuban/UI/projdialog.py
File MIME type: text/x-python
File size: 37454 byte(s)
Make Thuban remember path selections (at least for one application run).

* Thuban/UI/application.py (Application.OnInit): Initialize path as a
	attribute of application object. Path is a dictionary of
	strings, currently with the items "data" and "projection".
	(Application.SetPath): New, stores path for the specified item.
	(Application.Path): New, return path for the specified item.

* Thuban/UI/mainwindow.py
	(MainWindow.OpenSession, MainWindow.SaveSessionAs,
	MainWindow.AddLayer, MainWindow.AddRasterLayer,
	MainWindow.TableOpen): Access "data" path information of the
	application.

* Thuban/UI/projdialog.py (ProjFrame._OnImport, ProjFrame._OnExport):
	Access "projection" path information of the application.

1 # Copyright (c) 2003 by Intevation GmbH
2 # Authors:
3 # Jonathan Coles <[email protected]>
4 # Frank Koormann <[email protected]>
5 # Jan-Oliver Wagner <[email protected]>
6 #
7 # This program is free software under the GPL (>=v2)
8 # Read the file COPYING coming with Thuban for details.
9
10 """Projection dialog"""
11
12 __version__ = "$Revision$"
13 # $Source$
14 # $Id$
15
16 import os
17 from wxPython.wx import *
18
19 from Thuban import _
20
21 from Thuban.Model.proj import Projection, ProjFile
22
23 from Thuban.Model.resource import get_user_proj_file, get_system_proj_file, \
24 read_proj_file, write_proj_file, \
25 DEFAULT_PROJ_FILE, EPSG_PROJ_FILE, \
26 EPSG_DEPRECATED_PROJ_FILE
27 from Thuban.UI.dialogs import NonModalNonParentDialog
28
29 from common import ThubanBeginBusyCursor, ThubanEndBusyCursor
30 from sizers import NotebookLikeSizer
31 from projlist import PROJ_SELECTION_CHANGED, ProjectionList
32 from common import ThubanBeginBusyCursor, ThubanEndBusyCursor
33
34
35
36 ID_PROJ_PROJCHOICE = 4002
37 ID_PROJ_ADDTOLIST = 4003
38 ID_PROJ_NEW = 4004
39 ID_PROJ_REVERT = 4006
40 ID_PROJ_AVAIL = 4009
41 ID_PROJ_SAVE = 4010
42 ID_PROJ_IMPORT = 4011
43 ID_PROJ_EXPORT = 4012
44 ID_PROJ_REMOVE = 4013
45 ID_PROJ_PROJNAME = 4014
46
47 CLIENT_PROJ = 0
48 CLIENT_PROJFILE = 1
49
50 class ProjFrame(NonModalNonParentDialog):
51
52 def __init__(self, parent, name, title, receiver):
53 """Initialize the projection dialog.
54
55 receiver -- An object that implements the following methods:
56 SetProjection(projection)
57 GetProjection()
58 """
59 NonModalNonParentDialog.__init__(self, parent, name, title)
60
61 self.projection_panel_defs = [
62 ("tmerc", _("Transverse Mercator"), TMPanel),
63 ("utm", _("Universal Transverse Mercator"), UTMPanel),
64 ("lcc", _("Lambert Conic Conformal"), LCCPanel),
65 ("latlong", _("Geographic"), GeoPanel),
66 ("longlat", _("Geographic"), GeoPanel)]#longlat is an alias of proj
67 self.receiver = receiver
68 self.haveTried = False
69 self.curProjPanel = None
70 self.__usrProjFile = None
71 self._sys_proj_files = {}
72
73 self.build_dialog()
74 self.Layout()
75
76 self.originalProjection = self.receiver.GetProjection()
77
78 self.projection_list.SelectProjection(self.originalProjection)
79 self.projection_list.SetFocus()
80
81 def build_dialog(self):
82 """Build the dialog's widgets and set the event handlers"""
83 self.topBox = top_box = wxBoxSizer(wxVERTICAL)
84
85 main_box = wxBoxSizer(wxHORIZONTAL)
86 top_box.Add(main_box, 1, wxALL|wxEXPAND)
87
88 #
89 # The projection list and associated controls
90 #
91 vbox = wxBoxSizer(wxVERTICAL)
92 main_box.Add(vbox, 4, wxALL|wxEXPAND)
93
94 #label = wxStaticText(self, -1, _("Available Projections:"))
95 #vbox.Add(label, 0, wxLEFT|wxRIGHT|wxTOP, 4)
96
97 hbox = wxBoxSizer(wxHORIZONTAL)
98 vbox.Add(hbox, 1, wxALL|wxEXPAND)
99 proj_files = [self.load_user_proj(),
100 self.load_system_proj(DEFAULT_PROJ_FILE)]
101 self.projection_list = ProjectionList(self, proj_files,
102 self.receiver.GetProjection())
103 hbox.Add(self.projection_list, 1, wxALL|wxEXPAND|wxADJUST_MINSIZE, 4)
104 self.projection_list.Subscribe(PROJ_SELECTION_CHANGED,
105 self.proj_selection_changed)
106
107 # Projection List specific actions (Import/Export/Remove)
108 buttons = wxBoxSizer(wxVERTICAL)
109 hbox.Add(buttons, 0, wxALL)
110 self.button_import = wxButton(self, ID_PROJ_IMPORT, _("Import..."))
111 EVT_BUTTON(self, ID_PROJ_IMPORT, self._OnImport)
112 buttons.Add(self.button_import, 1, wxALL|wxEXPAND, 4)
113 self.button_export = wxButton(self, ID_PROJ_EXPORT, _("Export..."))
114 EVT_BUTTON(self, ID_PROJ_EXPORT, self._OnExport)
115 buttons.Add(self.button_export, 1, wxALL|wxEXPAND, 4)
116 buttons.Add(20, 20, 0, wxEXPAND, 0)
117 self.button_remove = wxButton(self, ID_PROJ_REMOVE, _("Remove"))
118 EVT_BUTTON(self, ID_PROJ_REMOVE, self._OnRemove)
119 buttons.Add(self.button_remove, 1, wxALL|wxEXPAND, 4)
120
121 buttons.Add(20, 20, 0, wxEXPAND, 0)
122 label = wxStaticText(self, -1, _("Show EPSG:"))
123 buttons.Add(label, 0, wxLEFT|wxRIGHT|wxTOP, 4)
124 self.check_epsg = wxCheckBox(self, -1, _("Normal"))
125 EVT_CHECKBOX(self, self.check_epsg.GetId(), self._OnShowEPSG)
126 buttons.Add(self.check_epsg, 1, wxALL|wxEXPAND, 4)
127 self.check_epsg_depr = wxCheckBox(self, -1, _("Deprecated"))
128 EVT_CHECKBOX(self, self.check_epsg_depr.GetId(), self._OnShowEPSG)
129 buttons.Add(self.check_epsg_depr, 1, wxALL|wxEXPAND, 4)
130
131 # The file path
132 self.projfilepath = wxStaticText(self, -1, "")
133 vbox.Add(self.projfilepath, 0, wxALL|wxEXPAND)
134
135 #
136 # The projection editor part
137 #
138 self.edit_box = wxStaticBox(self, -1, _("Edit"))
139 sizer_edit = wxStaticBoxSizer(self.edit_box, wxHORIZONTAL)
140 main_box.Add(sizer_edit, 5, wxALL|wxEXPAND)
141
142 # Projection Specific Entries (Name/Projection)
143 self.sizer_projctrls = wxBoxSizer(wxVERTICAL)
144 sizer_edit.Add(self.sizer_projctrls, 1, wxALL|wxEXPAND)
145
146 hbox = wxBoxSizer(wxHORIZONTAL)
147 self.sizer_projctrls.Add(hbox, 0, wxALL|wxEXPAND)
148 label = wxStaticText(self, -1, _("Name:"))
149 hbox.Add(label, 0, wxALL|wxALIGN_CENTER_VERTICAL, 4)
150 self.projname = wxTextCtrl(self, ID_PROJ_PROJNAME, "")
151 EVT_TEXT(self, ID_PROJ_PROJNAME, self._OnProjName)
152 hbox.Add(self.projname, 1, wxALL|wxEXPAND, 4)
153
154 hbox = wxBoxSizer(wxHORIZONTAL)
155 self.sizer_projctrls.Add(hbox, 0, wxALL|wxEXPAND)
156 label = wxStaticText(self, -1, _("Projection:"))
157 hbox.Add(label, 0, wxALL|wxALIGN_CENTER_VERTICAL, 4)
158 self.projchoice = wxChoice(self, ID_PROJ_PROJCHOICE)
159 self.projchoice.SetSelection(0)
160 EVT_CHOICE(self, ID_PROJ_PROJCHOICE, self._OnProjChoice)
161 hbox.Add(self.projchoice, 1, wxALL|wxEXPAND, 4)
162 # Fill the projection choice list.
163 self.nbsizer = NotebookLikeSizer()
164 self.sizer_projctrls.Add(self.nbsizer, 1,
165 wxALL|wxEXPAND|wxADJUST_MINSIZE, 3)
166 self.projection_panels = []
167 self.projchoice.Append(_("<Unknown>"), "")
168 for proj_type, name, cls in self.projection_panel_defs:
169 self.projchoice.Append(name, proj_type)
170 panel = cls(self, self.receiver)
171 panel.Hide()
172 panel.projection_index = len(self.projection_panels)
173 panel.projection_type = proj_type
174 self.projection_panels.append(panel)
175 self.nbsizer.Add(panel)
176 self.unknown_projection_panel = UnknownProjPanel(self, self.receiver)
177 self.unknown_projection_panel.Hide()
178 self.nbsizer.Add(self.unknown_projection_panel)
179
180 # Projection Specific actions (New/Save/Add)
181 buttons = wxBoxSizer(wxVERTICAL)
182 sizer_edit.Add(buttons, 0, wxALL)
183 self.button_new = wxButton(self, ID_PROJ_NEW, _("New"))
184 EVT_BUTTON(self, ID_PROJ_NEW, self._OnNew)
185 buttons.Add(self.button_new, 0, wxEXPAND|wxALL, 4)
186 self.button_add = wxButton(self, ID_PROJ_ADDTOLIST, _("Add to List"))
187 EVT_BUTTON(self, ID_PROJ_ADDTOLIST, self._OnAddToList)
188 buttons.Add(self.button_add, 0, wxEXPAND|wxALL, 4)
189 buttons.Add(20, 20, 0, wxEXPAND, 0)
190 self.button_save = wxButton(self, ID_PROJ_SAVE,_("Update"))
191 EVT_BUTTON(self, ID_PROJ_SAVE, self._OnSave)
192 buttons.Add(self.button_save, 0, wxEXPAND|wxALL|wxALIGN_BOTTOM, 4)
193
194 #
195 # Main Action buttons (Try/Revert/OK/Close)
196 #
197 buttons = wxBoxSizer(wxHORIZONTAL)
198 top_box.Add(buttons, 0, wxALL|wxALIGN_RIGHT, 10)
199 self.button_try = wxButton(self, wxID_APPLY, _("Try"))
200 EVT_BUTTON(self, wxID_APPLY, self.OnApply)
201 buttons.Add(self.button_try, 0, wxRIGHT, 10)
202 self.button_revert = wxButton(self, ID_PROJ_REVERT, _("Revert"))
203 EVT_BUTTON(self, ID_PROJ_REVERT, self._OnRevert)
204 buttons.Add(self.button_revert, 0, wxRIGHT, 10)
205 self.button_ok = wxButton(self, wxID_OK, _("OK"))
206 EVT_BUTTON(self, wxID_OK, self.OnOK)
207 self.button_ok.SetDefault()
208 buttons.Add(self.button_ok, 0, wxRIGHT, 10)
209 self.button_close = wxButton(self, wxID_CANCEL, _("Close"))
210 EVT_BUTTON(self, wxID_CANCEL, self.OnCancel)
211 buttons.Add(self.button_close, 0, wxRIGHT, 10)
212
213
214 #
215 # Automatic Layout
216 #
217 self.SetAutoLayout(1)
218 self.SetSizer(top_box)
219 top_box.Fit(self)
220 top_box.SetSizeHints(self)
221
222 def OnClose(self, event):
223 self.projection_list.Unsubscribe(PROJ_SELECTION_CHANGED,
224 self.proj_selection_changed)
225 # Destroy the projection list explicitly so that it properly
226 # unsubscribes everything. It would be cleaner if the projection
227 # could do this by itself but wx doesn't always send destroy
228 # events for non-top-level widgets
229 self.projection_list.Destroy()
230 NonModalNonParentDialog.OnClose(self, event)
231
232 def OnApply(self, event):
233 self.__SetProjection()
234 self.haveTried = True
235
236 def OnOK(self, event):
237 self.__SetProjection()
238 self.Close()
239
240 def OnCancel(self, event):
241 """Cancel just closes the dialog, but we call it cancel so we
242 can overload the functionality of wxDialog.
243 """
244 self.Close()
245
246 def _OnRevert(self, event):
247 if self.haveTried:
248 self.receiver.SetProjection(self.originalProjection)
249 self.haveTried = False
250
251 def _OnNew(self, event):
252
253 self.projection_list.ClearSelection()
254 self.projname.Clear()
255
256 # supply a projection panel if there wasn't one
257 if self.curProjPanel is None:
258 self.projchoice.SetSelection(0)
259 self.__DoOnProjChoice()
260
261 if self.curProjPanel is not None:
262 self.curProjPanel.Clear()
263
264 def _OnSave(self, event):
265
266 sel = self.projection_list.selected_projections()
267 assert len(sel) == 1, "button shouldn't be enabled"
268
269 proj, projfile = sel[0]
270
271 assert proj is not None and projfile is not None
272
273 newproj = self.__GetProjection()
274
275 if newproj is not None:
276 # FIXME: we should only allow this for the user proj file.
277 projfile.Replace(proj, newproj)
278 self.write_proj_file(projfile)
279 self.projection_list.SelectProjection(newproj)
280
281 def _OnAddToList(self, event):
282
283 proj = self.__GetProjection()
284 if proj is not None:
285 self.__usrProjFile.Add(proj)
286 self.write_proj_file(self.__usrProjFile)
287 self.projection_list.SelectProjection(proj)
288
289 def show_warnings(self, title, filename, warnings):
290 """Show the warnings (a list of strings) in a dialog
291
292 If the list is empty no dialog will be shown.
293 """
294 if warnings:
295 text = (_('Warnings when reading "%s":\n\n%s')
296 % (filename, "\n\n".join(warnings)))
297 self.parent.RunMessageBox(title, text)
298
299 def _OnImport(self, event):
300 """Handler for the 'Import' button
301
302 Ask the user for a filename, read the projections from that file
303 add them to the user ProjFile object and write the user file
304 back to disk.
305 """
306 dlg = wxFileDialog(self, _("Import"),
307 self.parent.application.Path("projection"), style = wxOPEN)
308
309 if dlg.ShowModal() == wxID_OK:
310 path = dlg.GetPath()
311
312 ThubanBeginBusyCursor()
313 try:
314 try:
315 projFile, warnings = read_proj_file(path)
316 except IOError, (errno, errstr):
317 self.__ShowError(dlg.GetPath(), errstr)
318 else:
319 self.show_warnings(_("Warnings"), path, warnings)
320 for proj in projFile.GetProjections():
321 self.__usrProjFile.Add(proj)
322 self.write_proj_file(self.__usrProjFile)
323 self.parent.application.SetPath("projection", path)
324 finally:
325 ThubanEndBusyCursor()
326 dlg.Destroy()
327
328 def _OnExport(self, event):
329 """Handler for the 'Export' button.
330
331 Ask the user for a filename and write the selected projections
332 to that file.
333 """
334 sel = self.projection_list.selected_projections()
335 assert len(sel) != 0, "button should be disabled"
336
337 dlg = wxFileDialog(self, _("Export"),
338 self.parent.application.Path("projection"),
339 style=wxSAVE|wxOVERWRITE_PROMPT)
340
341 if dlg.ShowModal() == wxID_OK:
342 proj_file = ProjFile(dlg.GetPath())
343 for proj, pf in sel:
344 if proj is not None:
345 proj_file.Add(proj)
346 self.write_proj_file(proj_file)
347 self.parent.application.SetPath("projection", dlg.GetPath())
348
349 dlg.Destroy()
350
351 def _OnRemove(self, event):
352 """Handler for the 'Remove' button
353
354 Remove any selected projection that came from the user's
355 ProjFile. If the user ProjFile was modified write it back to
356 disk.
357 """
358 sel = self.projection_list.selected_projections()
359 assert len(sel) != 0, "button should be disabled!"
360
361 modified = False
362 for proj, pf in sel:
363 if proj is not None and pf is self.__usrProjFile:
364 pf.Remove(proj)
365 modified = True
366
367 if modified:
368 self.write_proj_file(self.__usrProjFile)
369
370 def _OnShowEPSG(self, event):
371 """Handler for the EVT_CHECKBOX events from the EPSG check button
372
373 If the button is checked add the EPSG_PROJ_FILE to the list of
374 projfiles shown by the projection list. Otherwise remove it
375 """
376 proj_files = [self.load_user_proj(),
377 self.load_system_proj(DEFAULT_PROJ_FILE)]
378 if self.check_epsg.IsChecked():
379 proj_files.append(self.load_system_proj(EPSG_PROJ_FILE))
380 if self.check_epsg_depr.IsChecked():
381 proj_files.append(self.load_system_proj(EPSG_DEPRECATED_PROJ_FILE))
382 self.projection_list.SetProjFiles(proj_files)
383
384 def _OnProjName(self, event):
385 self.__VerifyButtons()
386
387 def __ShowError(self, filename, errstr):
388 wxMessageDialog(self,
389 _("The following error occured:\n") +
390 filename + "\n" + errstr,
391 _("Error"), wxOK | wxICON_ERROR).ShowModal()
392
393 def __VerifyButtons(self):
394 """Update button sensitivity"""
395
396 num_sel = self.projection_list.GetSelectedItemCount()
397
398 self.button_import.Enable(True)
399 self.button_export.Enable(True)
400 self.button_save.Enable(True)
401 self.button_remove.Enable(True)
402
403 self.edit_box.Enable(True)
404
405 for ctrl in [self.button_import,
406 self.button_export,
407 self.button_remove,
408 self.button_save,
409 self.button_add,
410 self.projchoice,
411 self.projname,
412 self.edit_box]:
413 ctrl.Enable(True)
414
415 if self.curProjPanel is not None:
416 self.curProjPanel.Enable(True)
417
418 if num_sel == 0:
419 self.button_import.Enable(True)
420 self.button_export.Enable(False)
421 self.button_remove.Enable(False)
422 self.button_save.Enable(False)
423
424 elif num_sel == 1:
425
426 selection = self.projection_list.selected_projections()
427 proj, projFile = selection[0]
428
429 self.button_save.Enable(len(self.projname.GetValue()) > 0)
430 self.button_add.Enable(len(self.projname.GetValue()) > 0)
431
432 if proj is None:
433 # <None> is selected
434 for ctrl in [self.button_export,
435 self.button_remove,
436 self.button_save,
437 self.button_add,
438 self.projchoice,
439 self.projname]:
440 ctrl.Enable(False)
441
442 if self.curProjPanel is not None:
443 self.curProjPanel.Enable(False)
444
445 elif proj is self.originalProjection:
446 self.button_remove.Enable(False)
447
448 if projFile is None:
449 self.button_save.Enable(False)
450
451 else:
452 self.edit_box.Enable(False)
453
454 def proj_selection_changed(self, projs):
455 """Subscribed to the projection_list's PROJ_SELECTION_CHANGED message
456
457 Update the dialog to reflect the new selection.
458 """
459 if len(projs) == 0:
460 self.projfilepath.SetLabel(_("No Projections selected"))
461 elif len(projs) == 1:
462 proj, projfile = projs[0]
463 if proj is None:
464 # user selected <None>
465 self.projname.Clear()
466 self.projfilepath.SetLabel("")
467 else:
468 if projfile is not None:
469 filename = os.path.basename(projfile.GetFilename())
470 self.projfilepath.SetLabel(_("Source of Projection: %s")
471 % filename)
472 else:
473 # only None if the currently used projection is selected
474 self.projfilepath.SetLabel("")
475
476 self.projname.SetValue(proj.Label())
477
478 myProjType = proj.GetParameter("proj")
479 i = 0
480 for projType, name, cls in self.projection_panel_defs:
481 if myProjType == projType:
482 self.projchoice.Enable(True)
483 self.projchoice.SetSelection(i + 1)
484 self.__DoOnProjChoice()
485
486 #
487 # self.curProjPanel should not be null
488 # after a call to __DoOnProjChoice
489 #
490 assert self.curProjPanel is not None
491
492 self.curProjPanel.SetProjection(proj)
493 break
494 i += 1
495 else:
496 self.projchoice.Select(0)
497 self.projchoice.Disable()
498 self._show_proj_panel(UnknownProjPanel)
499 assert self.curProjPanel is not None
500 self.curProjPanel.SetProjection(proj)
501 else:
502 self.projfilepath.SetLabel(_("Multiple Projections selected"))
503
504 self.__VerifyButtons()
505
506 def _OnProjChoice(self, event):
507 self.__DoOnProjChoice()
508
509 def __DoOnProjChoice(self):
510 """Create and layout a projection panel based on the selected
511 projection type.
512
513 This is necessary to have in seperate method since calls to
514 wxChoice.SetSelection() do not trigger an event.
515
516 At the end of this method self.curProjPanel will not be None
517 if there was a item selected.
518 """
519 choice = self.projchoice
520
521 sel = choice.GetSelection()
522 if sel != -1:
523 proj_type = choice.GetClientData(sel)
524 for t, name, cls in self.projection_panel_defs:
525 if t == proj_type:
526 self._show_proj_panel(cls)
527 break
528 # FIXME: what to do if sel == -1?
529
530 def _show_proj_panel(self, panel_class):
531 """Show the panel as the projection panel"""
532 if panel_class is UnknownProjPanel:
533 self.edit_box.Disable()
534 self.nbsizer.Activate(self.unknown_projection_panel)
535 self.curProjPanel = self.unknown_projection_panel
536 else:
537 self.edit_box.Enable(True)
538 self.unknown_projection_panel.Hide()
539 for panel in self.projection_panels:
540 if panel.__class__ is panel_class:
541 self.nbsizer.Activate(panel)
542 self.curProjPanel = panel
543
544 def __SetProjection(self):
545 """Set the receiver's projection."""
546
547 #
548 # save the original projection only once in case
549 # we try to apply several different projections
550 #
551 self.receiver.SetProjection(self.__GetProjection())
552
553 def __GetProjection(self):
554 """Return a suitable Projection object based on the current
555 state of the dialog box selections.
556
557 Could be None.
558 """
559
560 assert self.projection_list.GetSelectedItemCount() < 2, \
561 "button should be disabled"
562
563 sel = self.projection_list.selected_projections()
564 if len(sel) == 1:
565 if sel[0][0] is None:
566 # <None> is selected
567 return None
568
569 # self.curProjPanel should always contain the most relevant data
570 # for a projection
571 if self.curProjPanel is not None:
572 return Projection(self.curProjPanel.GetParameters(),
573 self.projname.GetValue())
574
575 return None
576
577 def load_user_proj(self):
578 """Return the user's ProjFile
579
580 If the file has not yet been loaded by the dialog, load it first
581 with get_user_proj_file and cache it in self.__usrProjFile.
582
583 Show a busy cursor while loading the file.
584
585 If the file is not available, leave a note to the console.
586 """
587 if self.__usrProjFile is None:
588 ThubanBeginBusyCursor()
589 try:
590 projfile, warnings = get_user_proj_file()
591 if warnings:
592 sys.stderr.write("".join(warnings))
593 self.__usrProjFile = projfile
594 finally:
595 ThubanEndBusyCursor()
596 return self.__usrProjFile
597
598 def load_system_proj(self, name):
599 """Load the system ProjFile with the given name.
600
601 If the file has not been loaded yet, load it first with
602 get_system_proj_file and put it into the cache. The name is
603 simply forwarded to get_system_proj_file.
604
605 Show a busy cursor while loading the file.
606 """
607 if name not in self._sys_proj_files:
608 ThubanBeginBusyCursor()
609 try:
610 projfile, warnings = get_system_proj_file(name)
611 self.show_warnings(_("Warnings"), projfile.GetFilename(),
612 warnings)
613 self._sys_proj_files[name] = projfile
614 finally:
615 ThubanEndBusyCursor()
616 return self._sys_proj_files[name]
617
618 def write_proj_file(self, proj_file):
619 """Write the ProjFile object proj_file back to its file
620
621 Show a busy cursor while writing and if an error occurs show a
622 dialog with the error message.
623 """
624 try:
625 ThubanBeginBusyCursor()
626 try:
627 write_proj_file(proj_file)
628 finally:
629 ThubanEndBusyCursor()
630 except IOError, (errno, errstr):
631 self.__ShowError(proj_file.GetFilename(), errstr)
632
633
634
635 class ProjPanel(wxPanel):
636 """Base class for all projection panels."""
637
638 def __init__(self, parent):
639 wxPanel.__init__(self, parent, -1)
640
641 self.__ellps = wxChoice(self, -1)
642 self.ellpsData = [("", _("<Unknown>")),
643 ("airy" , _("Airy")),
644 ("bessel", _("Bessel 1841")),
645 ("clrk66", _("Clarke 1866")),
646 ("clrk80", _("Clarke 1880")),
647 ("GRS80" , _("GRS 1980 (IUGG, 1980)")),
648 ("intl" , _("International 1909 (Hayford)")),
649 ("WGS84" , _("WGS 84"))]
650
651 for tag, name in self.ellpsData:
652 self.__ellps.Append(name, tag)
653
654 self.__ellps.SetSelection(0)
655
656 def _DoLayout(self, childPanel = None):
657
658 panelSizer = wxBoxSizer(wxVERTICAL)
659
660 sizer = wxBoxSizer(wxHORIZONTAL)
661 sizer.Add(wxStaticText(self, -1, _("Ellipsoid:")), 0,
662 wxALL|wxALIGN_CENTER_VERTICAL, 4)
663 sizer.Add(self.__ellps, 1, wxALL|wxALIGN_CENTER_VERTICAL, 4)
664 panelSizer.Add(sizer, 0, wxALL|wxEXPAND, 4)
665
666 if childPanel is not None:
667 panelSizer.Add(childPanel, 0, wxEXPAND, 0)
668
669 self.SetAutoLayout(1)
670 self.SetSizer(panelSizer)
671 panelSizer.Fit(self)
672 panelSizer.SetSizeHints(self)
673 self.Layout()
674
675 def SetProjection(self, proj):
676 if proj is not None:
677 param = proj.GetParameter("ellps")
678 i = 0
679 for tag, name in self.ellpsData:
680 if param == tag:
681 self.__ellps.SetSelection(i)
682 return # returning early!
683 i += 1
684
685 #
686 # if proj is none, or the parameter couldn't be found...
687 #
688 self.__ellps.SetSelection(0)
689
690 def GetParameters(self):
691 ellps = self.__ellps.GetSelection()
692 if ellps > 0:
693 return ["ellps=" + self.__ellps.GetClientData(ellps)]
694 return []
695
696
697 ID_TMPANEL_LAT = 4001
698 ID_TMPANEL_LONG = 4002
699 ID_TMPANEL_FASLE_EAST = 4003
700 ID_TMPANEL_FALSE_NORTH = 4004
701 ID_TMPANEL_SCALE = 4005
702
703 class UnknownProjPanel(ProjPanel):
704
705 """Panel for unknown projection types"""
706
707 def __init__(self, parent, receiver):
708 ProjPanel.__init__(self, parent)
709
710 self.__text = _("Thuban does not know the parameters\n"
711 "for the current projection and cannot\n"
712 "display a configuration panel.\n\n"
713 "The unidentified set of parameters is:\n\n")
714
715 self.__textbox = wxTextCtrl(self, -1, self.__text, size=(100,200),
716 style=wxTE_READONLY|wxTE_MULTILINE|wxTE_LINEWRAP)
717 self._DoLayout()
718
719 def _DoLayout(self):
720 sizer = wxBoxSizer(wxVERTICAL)
721
722 sizer.Add(self.__textbox, 0, wxALL|wxEXPAND, 4)
723
724 ProjPanel._DoLayout(self, sizer)
725
726 def GetProjName(self):
727 return "Unknown"
728
729 def SetProjection(self, proj):
730 """Append the available parameters to the info text."""
731 text = self.__text
732 for param in proj.GetAllParameters():
733 text = text + '%s\n' % param
734 self.__textbox.SetValue(text)
735
736 def GetParameters(self):
737 return None
738
739 class TMPanel(ProjPanel):
740 """Projection panel for Transverse Mercator."""
741
742 def __init__(self, parent, receiver):
743 ProjPanel.__init__(self, parent)
744
745 self.__latitude = wxTextCtrl(self, ID_TMPANEL_LAT)
746 self.__longitude = wxTextCtrl(self, ID_TMPANEL_LONG)
747 self.__falseEast = wxTextCtrl(self, ID_TMPANEL_FASLE_EAST)
748 self.__falseNorth = wxTextCtrl(self, ID_TMPANEL_FALSE_NORTH)
749 self.__scale = wxTextCtrl(self, ID_TMPANEL_SCALE)
750
751 self._DoLayout()
752
753 def _DoLayout(self):
754
755 sizer = wxFlexGridSizer(4, 2, 0, 0)
756 sizer.Add(wxStaticText(self, -1, _("Latitude:")), 0, wxALL, 4)
757 sizer.Add(self.__latitude, 0, wxALL, 4)
758 sizer.Add(wxStaticText(self, -1, _("Longitude:")), 0, wxALL, 4)
759 sizer.Add(self.__longitude, 0, wxALL, 4)
760 sizer.Add(wxStaticText(self, -1, _("False Easting:")), 0, wxALL, 4)
761 sizer.Add(self.__falseEast, 0, wxALL, 4)
762 sizer.Add(wxStaticText(self, -1, _("False Northing:")), 0, wxALL, 4)
763 sizer.Add(self.__falseNorth, 0, wxALL, 4)
764 sizer.Add(wxStaticText(self, -1, _("Scale Factor:")), 0, wxALL, 4)
765 sizer.Add(self.__scale, 0, wxALL, 4)
766
767 ProjPanel._DoLayout(self, sizer)
768
769 def GetProjName(self):
770 return _("Transverse Mercator")
771
772 def SetProjection(self, proj):
773 ProjPanel.SetProjection(self, proj)
774
775 self.__latitude.SetValue(proj.GetParameter("lat_0"))
776 self.__longitude.SetValue(proj.GetParameter("lon_0"))
777 self.__falseEast.SetValue(proj.GetParameter("x_0"))
778 self.__falseNorth.SetValue(proj.GetParameter("y_0"))
779 self.__scale.SetValue(proj.GetParameter("k"))
780
781 ProjPanel.SetProjection(self, proj)
782
783 def GetParameters(self):
784 params = ["proj=tmerc",
785 "lat_0=" + self.__latitude.GetValue(),
786 "lon_0=" + self.__longitude.GetValue(),
787 "x_0=" + self.__falseEast.GetValue(),
788 "y_0=" + self.__falseNorth.GetValue(),
789 "k=" + self.__scale.GetValue()]
790 params.extend(ProjPanel.GetParameters(self))
791 return params
792
793 def Clear(self):
794 self.__latitude.Clear()
795 self.__longitude.Clear()
796 self.__falseEast.Clear()
797 self.__falseNorth.Clear()
798 self.__scale.Clear()
799
800 ProjPanel.Clear(self)
801
802 ID_UTMPANEL_ZONE = 4001
803 ID_UTMPANEL_SOUTH = 4002
804 ID_UTMPANEL_PROP = 4003
805
806 class UTMPanel(ProjPanel):
807 """Projection Panel for Universal Transverse Mercator."""
808
809 def __init__(self, parent, receiver):
810 ProjPanel.__init__(self, parent)
811
812 self.receiver = receiver
813
814 self.__zone = wxSpinCtrl(self, ID_UTMPANEL_ZONE, "1", min=1, max=60)
815 self.__propButton = wxButton(self, ID_UTMPANEL_PROP, _("Propose"))
816 self.__south = wxCheckBox(self, ID_UTMPANEL_SOUTH,
817 _("Southern Hemisphere"))
818
819 self._DoLayout()
820
821 EVT_BUTTON(self, ID_UTMPANEL_PROP, self._OnPropose)
822
823 def _DoLayout(self):
824
825 sizer = wxBoxSizer(wxVERTICAL)
826 psizer = wxBoxSizer(wxHORIZONTAL)
827 psizer.Add(wxStaticText(self, -1, _("Zone:")), 0, wxALL, 4)
828 psizer.Add(self.__zone, 0, wxALL, 4)
829 psizer.Add(self.__propButton, 0, wxALL, 4)
830 sizer.Add(psizer, 0, wxALL, 4)
831 sizer.Add(self.__south, 0, wxALL, 4)
832
833 ProjPanel._DoLayout(self, sizer)
834
835 def GetProjName(self):
836 return _("Universal Transverse Mercator")
837
838 def SetProjection(self, proj):
839 self.__zone.SetValue(int(proj.GetParameter("zone")))
840 self.__south.SetValue(proj.GetParameter("south") != "")
841 ProjPanel.SetProjection(self, proj)
842
843 def GetParameters(self):
844 params = ["proj=utm", "zone=" + str(self.__zone.GetValue())]
845 if self.__south.IsChecked():
846 params.append("south")
847
848 params.extend(ProjPanel.GetParameters(self))
849 return params
850
851 def Clear(self):
852 self.__zone.SetValue(1)
853 self.__south.SetValue(False)
854 ProjPanel.Clear(self)
855
856 def _OnPropose(self, event):
857 """Call the propose dialog.
858 If the receiver (e.g. the current map) has no bounding box,
859 inform the user accordingly.
860 """
861 bb = self.receiver.BoundingBox()
862 if bb is None:
863 dlg = wxMessageDialog(self,
864 _("Can not propose: No bounding box found."),
865 _("Projection: Propose UTM Zone"),
866 wxOK | wxICON_INFORMATION)
867 dlg.CenterOnParent()
868 result = dlg.ShowModal()
869 dlg.Destroy()
870 return
871
872 dlg = UTMProposeZoneDialog(self, self.receiver.BoundingBox())
873 if dlg.ShowModal() == wxID_OK:
874 self.__zone.SetValue(dlg.GetProposedZone())
875
876 class LCCPanel(ProjPanel):
877 """Projection Panel for Lambert Conic Conformal."""
878
879 def __init__(self, parent, receiver):
880 ProjPanel.__init__(self, parent)
881
882 self.__fspLatitude = wxTextCtrl(self, -1)
883 self.__sspLatitude = wxTextCtrl(self, -1)
884 self.__meridian = wxTextCtrl(self, -1)
885 self.__originLat = wxTextCtrl(self, -1)
886 self.__falseEast = wxTextCtrl(self, -1)
887 self.__falseNorth = wxTextCtrl(self, -1)
888
889 self._DoLayout()
890
891 def _DoLayout(self):
892
893 sizer = wxFlexGridSizer(6, 2, 0, 0)
894 sizer.Add(wxStaticText(self, -1,
895 _("Latitude of first standard parallel:")))
896 sizer.Add(self.__fspLatitude, 0, wxALL, 4)
897 sizer.Add(wxStaticText(self, -1,
898 _("Latitude of second standard parallel:")))
899 sizer.Add(self.__sspLatitude, 0, wxALL, 4)
900 sizer.Add(wxStaticText(self, -1, _("Central Meridian:")))
901 sizer.Add(self.__meridian, 0, wxALL, 4)
902 sizer.Add(wxStaticText(self, -1, _("Latitude of origin:")))
903 sizer.Add(self.__originLat, 0, wxALL, 4)
904 sizer.Add(wxStaticText(self, -1, _("False Easting:")))
905 sizer.Add(self.__falseEast, 0, wxALL, 4)
906 sizer.Add(wxStaticText(self, -1, _("False Northing:")))
907 sizer.Add(self.__falseNorth, 0, wxALL, 4)
908
909 ProjPanel._DoLayout(self, sizer)
910
911 def GetProjName(self):
912 return _("Lambert Conic Conformal")
913
914 def SetProjection(self, proj):
915 self.__fspLatitude.SetValue(proj.GetParameter("lat_1"))
916 self.__sspLatitude.SetValue(proj.GetParameter("lat_2"))
917 self.__originLat.SetValue(proj.GetParameter("lat_0"))
918 self.__meridian.SetValue(proj.GetParameter("lon_0"))
919 self.__falseEast.SetValue(proj.GetParameter("x_0"))
920 self.__falseNorth.SetValue(proj.GetParameter("y_0"))
921
922 ProjPanel.SetProjection(self, proj)
923
924 def GetParameters(self):
925 params = ["proj=lcc",
926 "lat_1=" + self.__fspLatitude.GetValue(),
927 "lat_2=" + self.__sspLatitude.GetValue(),
928 "lat_0=" + self.__originLat.GetValue(),
929 "lon_0=" + self.__meridian.GetValue(),
930 "x_0=" + self.__falseEast.GetValue(),
931 "y_0=" + self.__falseNorth.GetValue()]
932
933 params.extend(ProjPanel.GetParameters(self))
934 return params
935
936 def Clear(self):
937 self.__fspLatitude.Clear()
938 self.__sspLatitude.Clear()
939 self.__originLat.Clear()
940 self.__meridian.Clear()
941 self.__falseEast.Clear()
942 self.__falseNorth.Clear()
943
944 ProjPanel.Clear(self)
945
946 class GeoPanel(ProjPanel):
947 """Projection Panel for a Geographic Projection."""
948
949 def __init__(self, parent, receiver):
950 ProjPanel.__init__(self, parent)
951
952 self.__choices = [(_("Degrees"), "0.017453"),
953 (_("Radians"), "1")]
954
955 self.__scale = wxChoice(self, -1)
956 for choice, value in self.__choices:
957 self.__scale.Append(choice, value)
958
959 self._DoLayout()
960
961 def GetProjName(self):
962 return _("Geographic")
963
964 def SetProjection(self, proj):
965 value = proj.GetParameter("to_meter")
966 for i in range(len(self.__choices)):
967 choice, data = self.__choices[i]
968 if value == data:
969 self.__scale.SetSelection(i)
970 ProjPanel.SetProjection(self, proj)
971
972 def GetParameters(self):
973 params = ["proj=latlong",
974 "to_meter=%s" % self.__scale.GetClientData(
975 self.__scale.GetSelection())]
976
977 params.extend(ProjPanel.GetParameters(self))
978 return params
979
980 def Clear(self):
981 ProjPanel.Clear(self)
982
983 def _DoLayout(self):
984 sizer = wxBoxSizer(wxHORIZONTAL)
985
986 sizer.Add(wxStaticText(self, -1, _("Source Data is in: ")),
987 0, wxALL|wxALIGN_CENTER_VERTICAL, 4)
988 sizer.Add(self.__scale, 1, wxEXPAND|wxALL, 4)
989
990 self.__scale.SetSelection(0)
991
992 ProjPanel._DoLayout(self, sizer)
993
994
995 ID_UTM_PROPOSE_ZONE_DIALOG_TAKE = 4001
996 ID_UTM_PROPOSE_ZONE_DIALOG_CANCEL = 4002
997 class UTMProposeZoneDialog(wxDialog):
998
999 """Propose a sensible Zone considering the current map extent."""
1000
1001 def __init__(self, parent, (x, y, x2, y2)):
1002 wxDialog.__init__(self, parent, -1, _("Projection: Propose UTM Zone"),
1003 wxDefaultPosition, wxSize(200, 100))
1004 self.parent = parent
1005 x = x + 180
1006 x2 = x2 + 180
1007 center = (x2 - x) / 2 + x
1008 self.proposedZone = int(center / 6 + 1)
1009 self.dialogLayout()
1010
1011 def dialogLayout(self):
1012 topBox = wxBoxSizer(wxVERTICAL)
1013
1014 textBox = wxBoxSizer(wxVERTICAL)
1015 textBox.Add(wxStaticText(self, -1, _("The current map extent center "
1016 "lies in UTM Zone")),
1017 0, wxALIGN_CENTER|wxALL, 4)
1018 textBox.Add(wxStaticText(self, -1, str(self.proposedZone)),
1019 0, wxALIGN_CENTER|wxALL, 4)
1020
1021 topBox.Add(textBox, 1, wxEXPAND|wxALL, 4)
1022
1023 buttonBox = wxBoxSizer(wxHORIZONTAL)
1024 buttonBox.Add(wxButton(self, ID_UTM_PROPOSE_ZONE_DIALOG_TAKE,
1025 _("Take")), 0, wxALL, 4)
1026 buttonBox.Add(wxButton(self, ID_UTM_PROPOSE_ZONE_DIALOG_CANCEL,
1027 _("Cancel")), 0, wxALL, 4)
1028 topBox.Add(buttonBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 10)
1029 EVT_BUTTON(self, ID_UTM_PROPOSE_ZONE_DIALOG_TAKE, self.OnTake)
1030 EVT_BUTTON(self, ID_UTM_PROPOSE_ZONE_DIALOG_CANCEL, self.OnCancel)
1031
1032 self.SetAutoLayout(True)
1033 self.SetSizer(topBox)
1034 topBox.Fit(self)
1035 topBox.SetSizeHints(self)
1036
1037 def OnTake(self, event):
1038 self.EndModal(wxID_OK)
1039
1040 def OnCancel(self, event):
1041 self.EndModal(wxID_CANCEL)
1042
1043 def GetProposedZone(self):
1044 return self.proposedZone

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26