1 |
# Copyright (C) 2003, 2004 by Intevation GmbH |
2 |
# Authors: |
3 |
# Jan-Oliver Wagner <[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 |
Provide layers via OGC WMS. |
10 |
|
11 |
This extension is in a very experimental stage! |
12 |
It just demonstrates how to add a special |
13 |
layer into Thuban via an extension. |
14 |
Some things are not wired, so be prepared for Exceptions |
15 |
everywhere. |
16 |
|
17 |
You will need PyOGCLib 0.1.0, see |
18 |
http://pyogclib.sourceforge.net/ |
19 |
Set the PYTHONPATH to the PyOGCLib directory before |
20 |
starting Thuban. |
21 |
""" |
22 |
|
23 |
__version__ = "$Revision$" |
24 |
|
25 |
import os, sys |
26 |
import xml.dom.minidom |
27 |
import tempfile |
28 |
|
29 |
from wxPython.wx import * |
30 |
|
31 |
from ogclib.WMSClient import WMSClient |
32 |
|
33 |
from Thuban.Model.layer import BaseLayer |
34 |
from Thuban.Model.proj import Projection |
35 |
from Thuban.Model.extension import Extension |
36 |
from Thuban.Model.resource import get_system_proj_file, EPSG_PROJ_FILE, \ |
37 |
EPSG_DEPRECATED_PROJ_FILE |
38 |
from Thuban.UI.command import registry, Command |
39 |
import Thuban.UI.mainwindow |
40 |
from Thuban import _ |
41 |
import Thuban.UI.baserenderer |
42 |
|
43 |
|
44 |
def epsg_code_to_projection(epsg): |
45 |
"""Find the projection for the given epsg code. |
46 |
|
47 |
epsg -- EPSG code as string |
48 |
""" |
49 |
proj_file, warnings = get_system_proj_file(EPSG_PROJ_FILE) |
50 |
|
51 |
for proj in proj_file.GetProjections(): |
52 |
if proj.EPSGCode() == epsg: |
53 |
return proj |
54 |
proj_file, warnings = get_system_proj_file(EPSG_DEPRECATED_PROJ_FILE) |
55 |
for proj in proj_file.GetProjections(): |
56 |
if proj.EPSGCode() == epsg: |
57 |
return proj |
58 |
return None |
59 |
|
60 |
class WMSExtension(Extension): |
61 |
def TreeInfo(self): |
62 |
return (_("Extension: %s") % self.title, |
63 |
[ object.TreeInfo() for object in self.objects ]) |
64 |
|
65 |
class WMSLayer(BaseLayer, WMSClient): |
66 |
|
67 |
def __init__(self, title, url): |
68 |
"""Initializes the WMSLayer. |
69 |
|
70 |
title -- Title of this layer. |
71 |
url -- URL of the WMS-Server wich must contain '?' |
72 |
|
73 |
If an error occured, self.error_msg is a string describing |
74 |
the problem(s). Else, self.error_msg is None. |
75 |
""" |
76 |
BaseLayer.__init__(self, title, visible = True, projection = None) |
77 |
self.url = url |
78 |
self.bbox = None |
79 |
self.latlonbbox = None |
80 |
self.error_msg = None |
81 |
self.layer_name = None |
82 |
|
83 |
wms_response = self.getCapabilities(self.url, '1.0') |
84 |
self._capa_dom = xml.dom.minidom.parseString(wms_response) |
85 |
|
86 |
root = self._capa_dom.documentElement |
87 |
cap = root.getElementsByTagName('Capability')[0] |
88 |
layer = cap.getElementsByTagName('Layer')[0] |
89 |
|
90 |
# get projected bounding box and latlon bounding box |
91 |
for node in layer.childNodes: |
92 |
if node.nodeName == 'BoundingBox': |
93 |
minx = node.attributes.get('minx').nodeValue |
94 |
miny = node.attributes.get('miny').nodeValue |
95 |
maxx = node.attributes.get('maxx').nodeValue |
96 |
maxy = node.attributes.get('maxy').nodeValue |
97 |
self.bbox = (float(minx), float(miny), float(maxx), float(maxy)) |
98 |
bbox = layer.getElementsByTagName('LatLonBoundingBox')[0] |
99 |
self.layer_name = layer.getElementsByTagName('Name')[0].childNodes[0].data |
100 |
minx = bbox.attributes.get('minx').nodeValue |
101 |
miny = bbox.attributes.get('miny').nodeValue |
102 |
maxx = bbox.attributes.get('maxx').nodeValue |
103 |
maxy = bbox.attributes.get('maxy').nodeValue |
104 |
self.latlonbbox = (float(minx), float(miny), float(maxx), float(maxy)) |
105 |
|
106 |
# get projection |
107 |
srs = layer.getElementsByTagName('SRS')[0].childNodes[0].data |
108 |
if len(srs.split(':')) == 1: |
109 |
epsg_id = srs |
110 |
else: |
111 |
epsg_id = srs.split(':')[1] |
112 |
|
113 |
p = epsg_code_to_projection(epsg_id) |
114 |
self.SetProjection(p) |
115 |
|
116 |
if p is None: |
117 |
self.error_msg = _('EPSG projection code %s not found!\n'\ |
118 |
'Setting projection to "None".\n'\ |
119 |
'Please set an appropriate projection yourself.'\ |
120 |
% epsg_id) |
121 |
|
122 |
# get title |
123 |
title = layer.getElementsByTagName('Title')[0].childNodes[0].data |
124 |
self.SetTitle(title.encode('latin1', 'replace')) |
125 |
|
126 |
self._capa_dom.unlink() |
127 |
|
128 |
def LatLongBoundingBox(self): |
129 |
"""Return the layer's bounding box in lat-lon. |
130 |
""" |
131 |
return self.latlonbbox |
132 |
|
133 |
def BoundingBox(self): |
134 |
"""Return the layer's bounding box in the intrinsic coordinate system. |
135 |
""" |
136 |
return self.bbox |
137 |
|
138 |
def GetMapImg(self, width, height, bbox): |
139 |
bbox_dict = { 'minx': bbox[0], 'miny': bbox[1], |
140 |
'maxx': bbox[2], 'maxy': bbox[3] } |
141 |
epsg_id = int(self.GetProjection().EPSGCode()) |
142 |
wms_response = self.getMap(self.url, 'JPEG', width, height, |
143 |
epsg_id, bbox_dict, |
144 |
[self.layer_name], version = '1.0') |
145 |
return wms_response |
146 |
|
147 |
|
148 |
|
149 |
def render_wms_layer(renderer, layer): |
150 |
offx, offy = renderer.offset |
151 |
width, height = renderer.dc.GetSizeTuple() |
152 |
|
153 |
scale = renderer.scale |
154 |
xmin = (0 - offx) / scale |
155 |
ymin = (offy - height) / scale |
156 |
xmax = (width - offx) / scale |
157 |
ymax = (offy - 0) / scale |
158 |
|
159 |
img = layer.GetMapImg(width, height, (xmin, ymin, xmax, ymax)) |
160 |
renderer.draw_raster_data(img, "JPEG") |
161 |
|
162 |
return () |
163 |
|
164 |
Thuban.UI.baserenderer.add_renderer_extension(WMSLayer, render_wms_layer) |
165 |
|
166 |
|
167 |
class SelectWMSServer(wxDialog): |
168 |
|
169 |
ID_COMBOVALUE = 4003 |
170 |
|
171 |
def __init__(self, parent): |
172 |
wxDialog.__init__(self, parent, -1, _("Select WMS Server"), |
173 |
style = wxDEFAULT_DIALOG_STYLE |
174 |
| wxSYSTEM_MENU |
175 |
| wxRESIZE_BORDER) |
176 |
|
177 |
self.combo_value = wxComboBox(self, self.ID_COMBOVALUE, size=(500,-1)) |
178 |
self.combo_value.Append("") |
179 |
self.combo_value.Append('http://frida.intevation.org/cgi-bin/frida_wms?') |
180 |
#self.combo_value.Append('http://wms.jpl.nasa.gov/wms.cgi?') |
181 |
#self.combo_value.Append('http://eukrante.hq:9089/cgi-bin/wms_shg?') |
182 |
#self.combo_value.Append('http://131.220.106.112:8080/deegree0.7/wms?') |
183 |
#self.combo_value.Append('http://demo.cubewerx.com/demo/cubeserv/cubeserv.cgi?CONFIG=gita&') |
184 |
self.combo_value.SetSelection(0) |
185 |
|
186 |
button_ok = wxButton(self, wxID_OK, _("OK")) |
187 |
button_ok.SetDefault() |
188 |
button_close = wxButton(self, wxID_CANCEL, _("Close")) |
189 |
|
190 |
vbox = wxBoxSizer(wxVERTICAL) |
191 |
vbox.Add(self.combo_value, 1, wxEXPAND|wxALL|wxCB_SORT, 10) |
192 |
hbox = wxBoxSizer(wxHORIZONTAL) |
193 |
hbox.Add(button_ok, 0, wxALL, 10) |
194 |
hbox.Add(button_close, 0, wxALL, 10) |
195 |
vbox.Add(hbox, 0, 10) |
196 |
|
197 |
self.SetAutoLayout(True) |
198 |
self.SetSizer(vbox) |
199 |
vbox.Fit(self) |
200 |
vbox.SetSizeHints(self) |
201 |
self.Layout() |
202 |
|
203 |
EVT_BUTTON(self, wxID_OK, self.OnOK) |
204 |
EVT_BUTTON(self, wxID_CANCEL, self.OnCancel) |
205 |
|
206 |
def OnOK(self, event): |
207 |
self.url = self.combo_value.GetValue() |
208 |
self.EndModal(wxID_OK) |
209 |
|
210 |
def OnCancel(self, event): |
211 |
self.EndModal(wxID_CANCEL) |
212 |
|
213 |
def wms_dialog(context): |
214 |
"""Request URL from user and add WMS Layer. |
215 |
|
216 |
context -- The Thuban context. |
217 |
""" |
218 |
dialog = SelectWMSServer(context.mainwindow) |
219 |
|
220 |
if dialog.ShowModal() == wxID_OK: |
221 |
url = dialog.url |
222 |
else: |
223 |
url = None |
224 |
dialog.Destroy() |
225 |
|
226 |
if url is None: |
227 |
return |
228 |
|
229 |
wms_layer = WMSLayer('A WMS Layer', url) |
230 |
if wms_layer.error_msg is not None: |
231 |
context.mainwindow.RunMessageBox(_('WMS'), wms_layer.error_msg) |
232 |
|
233 |
map = context.mainwindow.canvas.Map() |
234 |
if map.projection is None: |
235 |
map.SetProjection(wms_layer.projection) |
236 |
has_layers = map.HasLayers() |
237 |
map.AddLayer(wms_layer) |
238 |
if not has_layers: |
239 |
# if we're adding a layer to an empty map, fit the |
240 |
# new map to the window |
241 |
context.mainwindow.canvas.FitMapToWindow() |
242 |
|
243 |
wxInitAllImageHandlers() |
244 |
wms_extension = WMSExtension('WMS') |
245 |
|
246 |
# register the new command |
247 |
registry.Add(Command('wms', _('Add WMS layer ...'), wms_dialog, |
248 |
helptext = _('Add a WMS Layer'))) |
249 |
|
250 |
# find the experimental menu (create it anew if not found) |
251 |
main_menu = Thuban.UI.mainwindow.main_menu |
252 |
experimental_menu = main_menu.find_menu('experimental') |
253 |
if experimental_menu is None: |
254 |
experimental_menu = main_menu.InsertMenu('experimental', _('Experimenta&l')) |
255 |
|
256 |
# finally add the new entry to the experimental menu |
257 |
experimental_menu.InsertItem('wms') |