1 |
# Copyright (c) 2003, 2004 by Intevation GmbH |
2 |
# Authors: |
3 |
# Jan-Oliver Wagner <[email protected]> |
4 |
# Martin Schulze <[email protected]> |
5 |
# |
6 |
# This program is free software; you can redistribute it and/or modify |
7 |
# it under the terms of the GNU General Public License as published by |
8 |
# the Free Software Foundation; either version 2 of the License, or |
9 |
# (at your option) any later version. |
10 |
# |
11 |
# This program is distributed in the hope that it will be useful, |
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
# GNU General Public License for more details. |
15 |
# |
16 |
# You should have received a copy of the GNU General Public License |
17 |
# along with this program; if not, write to the Free Software |
18 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
20 |
""" |
21 |
Graphic Layer via OGC WMS. |
22 |
|
23 |
class WMSLayer: |
24 |
__init__() |
25 |
|
26 |
LatLongBoundingBox() |
27 |
BoundingBox() |
28 |
|
29 |
getFormat(format) |
30 |
calcFormat(formats) |
31 |
|
32 |
GetMapImg(width, height, bbox) |
33 |
|
34 |
Requirements: |
35 |
- PyOGCLib <http://www.sourceforge.net/projects/pyogclib> |
36 |
|
37 |
Requires the ogclib installed regularily on the system or checked out |
38 |
next to the Thuban checkout. Or set the PYTHONPATH to the PyOGCLib |
39 |
directory before starting Thuban. |
40 |
|
41 |
""" |
42 |
|
43 |
__version__ = "$Revision$" |
44 |
# $Source$ |
45 |
# $Id$ |
46 |
|
47 |
|
48 |
from Thuban.Model.layer import BaseLayer |
49 |
from Thuban.Model.resource import get_system_proj_file, EPSG_PROJ_FILE, \ |
50 |
EPSG_DEPRECATED_PROJ_FILE |
51 |
from Thuban.UI.common import ThubanBeginBusyCursor, ThubanEndBusyCursor |
52 |
|
53 |
from capabilities import WMSCapabilities |
54 |
|
55 |
from ogclib.WMSClient import WMSClient |
56 |
|
57 |
|
58 |
def epsg_code_to_projection(epsg): |
59 |
"""Find the projection for the given epsg code. |
60 |
|
61 |
epsg -- EPSG code as string |
62 |
""" |
63 |
proj_file, warnings = get_system_proj_file(EPSG_PROJ_FILE) |
64 |
|
65 |
for proj in proj_file.GetProjections(): |
66 |
if proj.EPSGCode() == epsg: |
67 |
return proj |
68 |
proj_file, warnings = get_system_proj_file(EPSG_DEPRECATED_PROJ_FILE) |
69 |
for proj in proj_file.GetProjections(): |
70 |
if proj.EPSGCode() == epsg: |
71 |
return proj |
72 |
return None |
73 |
|
74 |
|
75 |
class WMSLayer(BaseLayer): |
76 |
""" |
77 |
WMS Layer |
78 |
|
79 |
This layer incorporates all methods from the Thuban BaseLayer and |
80 |
adds specific methods for operating with a WMS server. |
81 |
""" |
82 |
|
83 |
def __init__(self, title, url): |
84 |
"""Initializes the WMSLayer. |
85 |
|
86 |
title -- Title of this layer. |
87 |
url -- URL of the WMS-Server wich must contain '?' |
88 |
|
89 |
If an error occured, self.error_msg is a string describing |
90 |
the problem(s). Else, self.error_msg is None. |
91 |
""" |
92 |
BaseLayer.__init__(self, title, visible = True, projection = None) |
93 |
self.url = url |
94 |
self.bbox = None |
95 |
self.latlonbbox = None |
96 |
self.error_msg = None |
97 |
self.layer_name = None |
98 |
self.capabilities = None |
99 |
|
100 |
# Change the cursor to demonstrate that we're busy but working |
101 |
ThubanBeginBusyCursor() |
102 |
self.capabilities = WMSCapabilities(url) |
103 |
ThubanEndBusyCursor() |
104 |
|
105 |
# name of the top layer of the remote map |
106 |
foo = self.capabilities.getLayers() |
107 |
if len(foo) == 0: |
108 |
self.error_msg = _('No layers found in remote resource:\n'\ |
109 |
'%s') % url |
110 |
return |
111 |
top_layer = foo[0] |
112 |
self.layer_name = top_layer |
113 |
|
114 |
# first projection of the top layer |
115 |
foo = self.capabilities.getLayerSRS(top_layer) |
116 |
if len(foo) == 0: |
117 |
self.error_msg = _('No LatLonBoundingBox found for top layer %s')\ |
118 |
% top_layer |
119 |
return |
120 |
top_srs = foo[0] |
121 |
|
122 |
# BoundingBox of the top layer |
123 |
bbox = self.capabilities.getLayerBBox(top_layer, top_srs) |
124 |
if len(bbox) == 0: |
125 |
self.error_msg = _('No BoundingBox found for layer %s and EPSG:')\ |
126 |
% (top_layer, top_srs) |
127 |
return |
128 |
self.bbox = (float(bbox['minx']), |
129 |
float(bbox['miny']), |
130 |
float(bbox['maxx']), |
131 |
float(bbox['maxy'])) |
132 |
|
133 |
# LatLonBox of the top layer |
134 |
bbox = self.capabilities.getLayerLatLonBBox(top_layer) |
135 |
self.latlonbbox = (float(bbox['minx']), |
136 |
float(bbox['miny']), |
137 |
float(bbox['maxx']), |
138 |
float(bbox['maxy'])) |
139 |
|
140 |
# get projection |
141 |
p = epsg_code_to_projection(top_srs) |
142 |
self.SetProjection(p) |
143 |
|
144 |
if p is None: |
145 |
self.error_msg = _('EPSG projection code %s not found!\n'\ |
146 |
'Setting projection to "None".\n'\ |
147 |
'Please set an appropriate projection yourself.'\ |
148 |
% top_srs) |
149 |
|
150 |
# pre-determine the used format |
151 |
self.wmsformat, self.format = \ |
152 |
self.calcFormat(self.capabilities.getFormats()) |
153 |
if self.wmsformat is None: |
154 |
self.error_msg = \ |
155 |
_('No supported image format found in remote resource') |
156 |
return |
157 |
|
158 |
# get and set the title |
159 |
self.SetTitle(self.capabilities.getTitle().encode('latin1', 'replace')) |
160 |
|
161 |
|
162 |
def LatLongBoundingBox(self): |
163 |
""" |
164 |
Return the layer's bounding box in lat-lon |
165 |
""" |
166 |
return self.latlonbbox |
167 |
|
168 |
|
169 |
def BoundingBox(self): |
170 |
""" |
171 |
Return the layer's bounding box in the intrinsic coordinate system |
172 |
""" |
173 |
return self.bbox |
174 |
|
175 |
|
176 |
def getFormat(self, format): |
177 |
""" |
178 |
Return the image format for the render engine |
179 |
|
180 |
format -- format as returned by the WMS server |
181 |
|
182 |
If no mapping was found, None is returned. |
183 |
|
184 |
This routine uses a simple heuristic in order to find the |
185 |
broken down image format to be used with the internal render |
186 |
engine. |
187 |
|
188 |
An exception rule is implemented in order to not accept |
189 |
image/wbmp or WBMP which refers to WAP bitmap format and is |
190 |
not supported by the included render engine. |
191 |
""" |
192 |
fmap = {'png' : "PNG", |
193 |
'jpeg': "JPEG", |
194 |
'jpg' : "JPEG", |
195 |
'tif' : "TIFF", |
196 |
'gif' : "GIF", |
197 |
'wbmp': None, |
198 |
'bmp' : "BMP"} |
199 |
|
200 |
for f in fmap.keys(): |
201 |
if format.lower().find(f) > -1: |
202 |
return fmap[f] |
203 |
return None |
204 |
|
205 |
|
206 |
def calcFormat(self, formats): |
207 |
""" |
208 |
Calculate the preferred image format |
209 |
|
210 |
formats -- list of formates as returned by the WMS server |
211 |
|
212 |
The following priority is used: |
213 |
- PNG |
214 |
- JPEG |
215 |
- TIFF |
216 |
- GIF |
217 |
- BMP |
218 |
|
219 |
If no matching format was found, None, None will be returned. |
220 |
|
221 |
An exception rule is implemented in order to not accept |
222 |
image/wbmp or WBMP which refers to WAP bitmap format and is |
223 |
not supported by the included render engine. |
224 |
""" |
225 |
prio = ['jpeg', 'bmp'] |
226 |
for p in prio: |
227 |
for f in formats: |
228 |
if f.lower().find(p) > -1: |
229 |
if f.lower().find('wbmp') == -1: |
230 |
return f, self.getFormat(f) |
231 |
return None, None |
232 |
|
233 |
|
234 |
def GetMapImg(self, width, height, bbox): |
235 |
""" |
236 |
Retrieve a new map from the WMS server and return it |
237 |
|
238 |
width -- width in pixel of the desired image |
239 |
height -- height in pixel of the desired image |
240 |
bbox -- array of min(x,y) max(x,y) in the given SRS |
241 |
|
242 |
SRS and used image format will be retrieved from within the |
243 |
layer itself. |
244 |
""" |
245 |
bbox_dict = { 'minx': bbox[0], 'miny': bbox[1], |
246 |
'maxx': bbox[2], 'maxy': bbox[3] } |
247 |
|
248 |
# Change the cursor to demonstrate that we're busy but working |
249 |
ThubanBeginBusyCursor() |
250 |
|
251 |
wmsclient = WMSClient() |
252 |
|
253 |
epsg_id = int(self.GetProjection().EPSGCode()) |
254 |
|
255 |
wms_response = wmsclient.getMap(self.url, self.wmsformat, width, height, |
256 |
epsg_id, bbox_dict, |
257 |
[self.layer_name], version = self.capabilities.getVersion()) |
258 |
ThubanEndBusyCursor() |
259 |
return wms_response, self.format |