13 |
layer into Thuban via an extension. |
layer into Thuban via an extension. |
14 |
Some things are not wired, so be prepared for Exceptions |
Some things are not wired, so be prepared for Exceptions |
15 |
everywhere. |
everywhere. |
|
|
|
|
You will need PyOGCLib 0.1.0, see |
|
|
http://pyogclib.sourceforge.net/ |
|
|
Set the PYTHONPATH to the PyOGCLib directory before |
|
|
starting Thuban. |
|
16 |
""" |
""" |
17 |
|
|
18 |
__version__ = "$Revision$" |
__version__ = "$Revision$" |
19 |
|
# $Source$ |
20 |
|
# $Id$ |
21 |
|
|
22 |
import os, sys |
import os, sys |
23 |
import xml.dom.minidom |
import xml.dom.minidom |
25 |
|
|
26 |
from wxPython.wx import * |
from wxPython.wx import * |
27 |
|
|
|
from ogclib.WMSClient import WMSClient |
|
|
|
|
|
from Thuban.Model.layer import BaseLayer |
|
28 |
from Thuban.Model.proj import Projection |
from Thuban.Model.proj import Projection |
29 |
from Thuban.Model.extension import Extension |
from Thuban.Model.extension import Extension |
|
from Thuban.Model.resource import get_system_proj_file, EPSG_PROJ_FILE, \ |
|
|
EPSG_DEPRECATED_PROJ_FILE |
|
30 |
from Thuban.UI.command import registry, Command |
from Thuban.UI.command import registry, Command |
31 |
import Thuban.UI.mainwindow |
from Thuban.UI.mainwindow import main_menu, layer_properties_dialogs |
|
from Thuban.UI.common import ThubanBeginBusyCursor, ThubanEndBusyCursor |
|
32 |
from Thuban import _ |
from Thuban import _ |
33 |
import Thuban.UI.baserenderer |
import Thuban.UI.baserenderer |
34 |
|
from Thuban.UI.extensionregistry import ExtensionDesc, ext_registry |
35 |
|
|
36 |
from capabilities import WMSCapabilities |
from layer import WMSLayer |
37 |
|
|
38 |
def epsg_code_to_projection(epsg): |
ext_registry.add(ExtensionDesc( |
39 |
"""Find the projection for the given epsg code. |
name = 'WMS', |
40 |
|
version = '0.1.0', |
41 |
epsg -- EPSG code as string |
authors= [ 'Jan-Oliver Wagner' ], |
42 |
""" |
copyright = '2003, 2004 Intevation GmbH', |
43 |
proj_file, warnings = get_system_proj_file(EPSG_PROJ_FILE) |
desc = _("Provide layers via OGC WMS."))) |
44 |
|
|
|
for proj in proj_file.GetProjections(): |
|
|
if proj.EPSGCode() == epsg: |
|
|
return proj |
|
|
proj_file, warnings = get_system_proj_file(EPSG_DEPRECATED_PROJ_FILE) |
|
|
for proj in proj_file.GetProjections(): |
|
|
if proj.EPSGCode() == epsg: |
|
|
return proj |
|
|
return None |
|
45 |
|
|
46 |
class WMSExtension(Extension): |
class WMSExtension(Extension): |
47 |
def TreeInfo(self): |
def TreeInfo(self): |
48 |
return (_("Extension: %s") % self.title, |
return (_("Extension: %s") % self.title, |
49 |
[ object.TreeInfo() for object in self.objects ]) |
[ object.TreeInfo() for object in self.objects ]) |
50 |
|
|
|
class WMSLayer(BaseLayer): |
|
|
|
|
|
def __init__(self, title, url): |
|
|
"""Initializes the WMSLayer. |
|
|
|
|
|
title -- Title of this layer. |
|
|
url -- URL of the WMS-Server wich must contain '?' |
|
|
|
|
|
If an error occured, self.error_msg is a string describing |
|
|
the problem(s). Else, self.error_msg is None. |
|
|
""" |
|
|
BaseLayer.__init__(self, title, visible = True, projection = None) |
|
|
self.url = url |
|
|
self.bbox = None |
|
|
self.latlonbbox = None |
|
|
self.error_msg = None |
|
|
self.layer_name = None |
|
|
self.capabilities = None |
|
|
|
|
|
# Change the cursor to demonstrate that we're busy but working |
|
|
ThubanBeginBusyCursor() |
|
|
self.capabilities = WMSCapabilities(url) |
|
|
ThubanEndBusyCursor() |
|
|
|
|
|
# name of the top layer of the remote map |
|
|
foo = self.capabilities.getLayers() |
|
|
if len(foo) == 0: |
|
|
self.error_msg = _('No layers found in remote resource:\n'\ |
|
|
'%s') % url |
|
|
return |
|
|
top_layer = foo[0] |
|
|
self.layer_name = top_layer |
|
|
|
|
|
# first projection of the top layer |
|
|
foo = self.capabilities.getLayerSRS(top_layer) |
|
|
if len(foo) == 0: |
|
|
self.error_msg = _('No LatLonBoundingBox found for top layer %s')\ |
|
|
% top_layer |
|
|
return |
|
|
top_srs = foo[0] |
|
|
|
|
|
# BoundingBox of the top layer |
|
|
bbox = self.capabilities.getLayerBBox(top_layer, top_srs) |
|
|
if len(bbox) == 0: |
|
|
self.error_msg = _('No BoundingBox found for layer %s and EPSG:')\ |
|
|
% (top_layer, top_srs) |
|
|
return |
|
|
self.bbox = (float(bbox['minx']), |
|
|
float(bbox['miny']), |
|
|
float(bbox['maxx']), |
|
|
float(bbox['maxy'])) |
|
|
|
|
|
# LatLonBox of the top layer |
|
|
bbox = self.capabilities.getLayerLatLonBBox(top_layer) |
|
|
self.latlonbbox = (float(bbox['minx']), |
|
|
float(bbox['miny']), |
|
|
float(bbox['maxx']), |
|
|
float(bbox['maxy'])) |
|
|
|
|
|
# get projection |
|
|
p = epsg_code_to_projection(top_srs) |
|
|
self.SetProjection(p) |
|
|
|
|
|
if p is None: |
|
|
self.error_msg = _('EPSG projection code %s not found!\n'\ |
|
|
'Setting projection to "None".\n'\ |
|
|
'Please set an appropriate projection yourself.'\ |
|
|
% top_srs) |
|
|
|
|
|
# pre-determine the used format |
|
|
self.wmsformat, self.format = \ |
|
|
self.calcFormat(self.capabilities.getFormats()) |
|
|
if self.wmsformat is None: |
|
|
self.error_msg = \ |
|
|
_('No supported image format found in remote resource') |
|
|
return |
|
|
|
|
|
# get and set the title |
|
|
self.SetTitle(self.capabilities.getTitle().encode('latin1', 'replace')) |
|
|
|
|
|
|
|
|
def LatLongBoundingBox(self): |
|
|
"""Return the layer's bounding box in lat-lon. |
|
|
""" |
|
|
return self.latlonbbox |
|
|
|
|
|
def BoundingBox(self): |
|
|
"""Return the layer's bounding box in the intrinsic coordinate system. |
|
|
""" |
|
|
return self.bbox |
|
|
|
|
|
|
|
|
def getFormat(self, format): |
|
|
""" |
|
|
Return the image format for the render engine |
|
|
|
|
|
format -- format as returned by the WMS server |
|
|
|
|
|
If no mapping was found, None is returned |
|
|
|
|
|
An exception rule is implemented in order to not accept |
|
|
image/wbmp or WBMP which refers to WAP bitmap format and is |
|
|
not supported by the included render engine. |
|
|
""" |
|
|
fmap = {'png' : "PNG", |
|
|
'jpeg': "JPEG", |
|
|
'jpg' : "JPEG", |
|
|
'tif' : "TIFF", |
|
|
'gif' : "GIF", |
|
|
'wbmp': None, |
|
|
'bmp' : "BMP"} |
|
|
|
|
|
for f in fmap.keys(): |
|
|
if format.lower().find(f) > -1: |
|
|
return fmap[f] |
|
|
return None |
|
|
|
|
|
|
|
|
def calcFormat(self, formats): |
|
|
""" |
|
|
Calculate the preferred image format |
|
|
|
|
|
formats -- list of formates as returned by the WMS server |
|
|
|
|
|
The following priority is used: |
|
|
- PNG |
|
|
- JPEG |
|
|
- TIFF |
|
|
- GIF |
|
|
- BMP |
|
|
|
|
|
If no matching format was found, None, None will be returned. |
|
|
|
|
|
An exception rule is implemented in order to not accept |
|
|
image/wbmp or WBMP which refers to WAP bitmap format and is |
|
|
not supported by the included render engine. |
|
|
""" |
|
|
prio = ['png', 'jpeg', 'jpg', 'tif', 'gif', 'bmp'] |
|
|
for p in prio: |
|
|
for f in formats: |
|
|
if f.lower().find(p) > -1: |
|
|
if f.lower().find('wbmp') == -1: |
|
|
return f, self.getFormat(f) |
|
|
return None, None |
|
|
|
|
|
|
|
|
def GetMapImg(self, width, height, bbox): |
|
|
bbox_dict = { 'minx': bbox[0], 'miny': bbox[1], |
|
|
'maxx': bbox[2], 'maxy': bbox[3] } |
|
|
|
|
|
# Change the cursor to demonstrate that we're busy but working |
|
|
ThubanBeginBusyCursor() |
|
|
|
|
|
wmsclient = WMSClient() |
|
|
|
|
|
epsg_id = int(self.GetProjection().EPSGCode()) |
|
|
|
|
|
wms_response = wmsclient.getMap(self.url, self.wmsformat, width, height, |
|
|
epsg_id, bbox_dict, |
|
|
[self.layer_name], version = self.capabilities.getVersion()) |
|
|
ThubanEndBusyCursor() |
|
|
return wms_response, self.format |
|
|
|
|
51 |
|
|
52 |
def render_wms_layer(renderer, layer): |
def render_wms_layer(renderer, layer): |
53 |
offx, offy = renderer.offset |
offx, offy = renderer.offset |
65 |
return () |
return () |
66 |
|
|
67 |
Thuban.UI.baserenderer.add_renderer_extension(WMSLayer, render_wms_layer) |
Thuban.UI.baserenderer.add_renderer_extension(WMSLayer, render_wms_layer) |
68 |
|
from properties import wmsProperties |
69 |
|
layer_properties_dialogs.add(WMSLayer, wmsProperties) |
70 |
|
|
71 |
|
|
72 |
class SelectWMSServer(wxDialog): |
class SelectWMSServer(wxDialog): |
124 |
|
|
125 |
if dialog.ShowModal() == wxID_OK: |
if dialog.ShowModal() == wxID_OK: |
126 |
url = dialog.url |
url = dialog.url |
127 |
|
if len(url) == 0: |
128 |
|
url = None |
129 |
else: |
else: |
130 |
url = None |
url = None |
131 |
dialog.Destroy() |
dialog.Destroy() |
155 |
helptext = _('Add a WMS Layer'))) |
helptext = _('Add a WMS Layer'))) |
156 |
|
|
157 |
# find the experimental menu (create it anew if not found) |
# find the experimental menu (create it anew if not found) |
158 |
main_menu = Thuban.UI.mainwindow.main_menu |
experimental_menu = main_menu.FindOrInsertMenu('experimental', |
159 |
experimental_menu = main_menu.find_menu('experimental') |
_('Experimenta&l')) |
|
if experimental_menu is None: |
|
|
experimental_menu = main_menu.InsertMenu('experimental', _('Experimenta&l')) |
|
160 |
|
|
161 |
# finally add the new entry to the experimental menu |
# finally add the new entry to the experimental menu |
162 |
experimental_menu.InsertItem('wms') |
experimental_menu.InsertItem('wms') |