/[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 1933 - (show annotations)
Tue Nov 11 16:37:53 2003 UTC (21 years, 3 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/projdialog.py
File MIME type: text/x-python
File size: 36720 byte(s)
* Thuban/Model/resource.py (get_system_proj_file): Add a filename
parameter so that this function can be used for all proj files in
Resource/Projections
(DEFAULT_PROJ_FILE, EPSG_PROJ_FILE): New. Predefined filenames for
get_system_proj_file

* Thuban/UI/projdialog.py (ProjFrame.__init__): Instead of one
ProjFile self.__sysProjFile use a dictionary of system ProjFile
objects self._sys_proj_files
(ProjFrame.build_dialog): Adapt to the new changes in the
ProjectionList constructor. Add a check button to toggle whether
EPSG projections are shown
(ProjFrame._OnShowEPSG): New. Handler for the epsg check button
events.
(ProjFrame.load_user_proj, ProjFrame.load_system_proj): Only show
the busy cursor if the files have not yet been loaded by the
dialog.
(ProjFrame.load_system_proj): Add a parameter for the name of the
proj file. Maintain the dictionary of system projections
self._sys_proj_files

* Thuban/UI/projlist.py (ProjectionList): Merge the system_projs,
user_projs parameters into one parameter proj_files which is a
list of proj files.
(ProjectionList._subscribe_proj_files)
(ProjectionList._unsubscribe_proj_files): New. Move
subscription/unsubscription of projfile messages to separate
methods
(ProjectionList.Destroy): The unsubscribe is now done in
_unsubscribe_proj_files
(ProjectionList.update_projections): We now have a list of proj
file objects
(ProjectionList.SetProjFiles): New method to set a new list of
proj file objects

* test/test_proj.py (ProjFileReadTests.test_get_system_proj_file):
Specify explicitly which system proj file to load.

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