Moving the WMS previewer. You can enable it loading the wms_preview plugin in the ini file

This commit is contained in:
Adrià Mercader 2011-04-11 17:23:27 +01:00
parent 6ae23e4116
commit 76183b6f23
28 changed files with 1701 additions and 1 deletions

View File

@ -0,0 +1,7 @@
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

View File

@ -0,0 +1,47 @@
import urllib2
from pylons.i18n import _
import ckan.lib.helpers as h, json
from ckan.lib.base import BaseController, c, g, request, \
response, session, render, config, abort, redirect
from ckan.model import Package
class ViewController(BaseController):
def __before__(self, action, **env):
super(ViewController, self).__before__(action, **env)
# All calls to this controller must be with a sysadmin key
if not self.authorizer.is_sysadmin(c.user):
response_msg = _('Not authorized to see this page')
status = 401
abort(status, response_msg)
def wms_preview(self,id):
#check if package exists
c.pkg = Package.get(id)
if c.pkg is None:
abort(404, 'Package not found')
for res in c.pkg.resources:
if res.format == "WMS":
c.wms = res
break
if not c.wms:
abort(400, 'This package does not have a WMS')
return render('ckanext/spatial/wms_preview.html')
def proxy(self):
if not 'url' in request.params:
abort(400)
try:
server_response = urllib2.urlopen(request.params['url'])
headers = server_response.info()
if headers.get('Content-Type'):
response.content_type = headers.get('Content-Type')
return server_response.read()
except urllib2.HTTPError as e:
response.status_int = e.getcode()
return

5
ckanext/spatial/html.py Normal file
View File

@ -0,0 +1,5 @@
MAP_VIEW="""
<div class="mapview">
<a href="/package/%(name)s/map">View available WMS layers &raquo;</a>
</div>
"""

72
ckanext/spatial/plugin.py Normal file
View File

@ -0,0 +1,72 @@
import os
from logging import getLogger
from genshi.input import HTML
from genshi.filters import Transformer
import ckan.lib.helpers as h
from ckan.plugins import implements, SingletonPlugin
from ckan.plugins import IRoutes, IConfigurer
from ckan.plugins import IConfigurable, IGenshiStreamFilter
import html
log = getLogger(__name__)
class WMSPreview(SingletonPlugin):
implements(IGenshiStreamFilter)
implements(IRoutes, inherit=True)
implements(IConfigurer, inherit=True)
def filter(self, stream):
from pylons import request, tmpl_context as c
routes = request.environ.get('pylons.routes_dict')
if routes.get('controller') == 'package' and \
routes.get('action') == 'read' and c.pkg.id:
is_inspire = (c.pkg.extras.get('INSPIRE') == 'True')
# TODO: What about WFS, WCS...
is_wms = (c.pkg.extras.get('resource-type') == 'service')
if is_inspire and is_wms:
data = {'name': c.pkg.name}
stream = stream | Transformer('body//div[@class="resources subsection"]')\
.append(HTML(html.MAP_VIEW % data))
return stream
def before_map(self, map):
map.connect('map_view', '/package/:id/map',
controller='ckanext.spatial.controllers.view:ViewController',
action='wms_preview')
map.connect('proxy', '/proxy',
controller='ckanext.spatial.controllers.view:ViewController',
action='proxy')
map.connect('api_spatial_query', '/api/2/search/package/geo',
controller='ckanext.spatial.controllers.api:ApiController',
action='spatial_query')
return map
def update_config(self, config):
here = os.path.dirname(__file__)
rootdir = os.path.dirname(os.path.dirname(here))
template_dir = os.path.join(rootdir, 'templates')
public_dir = os.path.join(rootdir, 'public')
if config.get('extra_template_paths'):
config['extra_template_paths'] += ','+template_dir
else:
config['extra_template_paths'] = template_dir
if config.get('extra_public_paths'):
config['extra_public_paths'] += ','+public_dir
else:
config['extra_public_paths'] = public_dir

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
This is a custom build of the OpenLayers Javascript mapping library,
slimmed down to only the features we need.
The file ckan.cfg contains the build profile used to build OpenLayers.
In order to add more functionality, new classes must be added in the
build profile, and then run the build command from the OpenLayers
distribution:
1. svn co http://svn.openlayers.org/trunk/openlayers
2. Modify ckan.cfg
3. Go to build/ and execute::
python build.py {path-to-ckan.cfg} {output-file}
The theme used for the OpenLayers controls is the "dark" theme made available
by Development Seed under the BSD License:
https://github.com/developmentseed/openlayers_themes/blob/master/LICENSE.txt

View File

@ -0,0 +1,40 @@
[first]
OpenLayers/SingleFile.js
OpenLayers.js
OpenLayers/BaseTypes.js
OpenLayers/BaseTypes/Class.js
OpenLayers/Util.js
Rico/Corner.js
[last]
[include]
OpenLayers/Console.js
OpenLayers/Ajax.js
OpenLayers/Events.js
OpenLayers/Map.js
OpenLayers/Layer.js
OpenLayers/Layer/Grid.js
OpenLayers/Layer/HTTPRequest.js
OpenLayers/Layer/WMS.js
OpenLayers/Layer/WMS/Untiled.js
OpenLayers/Tile.js
OpenLayers/Tile/Image.js
OpenLayers/Control/Navigation.js
OpenLayers/Control/PanZoom.js
OpenLayers/Control/PanZoomBar.js
OpenLayers/Control/Scale.js
OpenLayers/Control/MousePosition.js
OpenLayers/Control/LayerSwitcher.js
OpenLayers/Format/XML.js
OpenLayers/Format/WMSCapabilities/v1_1_1.js
OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js
OpenLayers/Format/WMSCapabilities/v1_3_0.js
[exclude]
Firebug/firebug.js
Firebug/firebugx.js
OpenLayers/Lang/de.js
OpenLayers/Lang/en-CA.js
OpenLayers/Lang/fr.js
OpenLayers/Lang/cs-CZ.js

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

View File

@ -0,0 +1,17 @@
.olLayerGoogleCopyright {
right: 3px;
bottom: 2px;
left: auto;
}
.olLayerGoogleV3.olLayerGoogleCopyright {
bottom: 0px;
right: 0px !important;
}
.olLayerGooglePoweredBy {
left: 2px;
bottom: 2px;
}
.olLayerGoogleV3.olLayerGooglePoweredBy {
bottom: 0px !important;
}

View File

@ -0,0 +1,7 @@
.olControlZoomPanel div {
background-image: url(img/zoom-panel-NOALPHA.png);
}
.olControlPanPanel div {
background-image: url(img/pan-panel-NOALPHA.png);
}

View File

@ -0,0 +1,423 @@
div.olMap {
z-index: 0;
padding: 0px!important;
margin: 0px!important;
cursor: default;
}
div.olMapViewport {
text-align: left;
}
div.olLayerDiv {
-moz-user-select: none;
-khtml-user-select: none;
}
.olLayerGoogleCopyright {
left: 2px;
bottom: 2px;
}
.olLayerGoogleV3.olLayerGoogleCopyright {
right: auto !important;
}
.olLayerGooglePoweredBy {
left: 2px;
bottom: 15px;
}
.olLayerGoogleV3.olLayerGooglePoweredBy {
bottom: 15px !important;
}
.olControlAttribution {
font-size: smaller;
right: 3px;
bottom: 4.5em;
position: absolute;
display: block;
}
.olControlScale {
right: 3px;
bottom: 3em;
display: block;
position: absolute;
font-size: smaller;
}
.olControlScaleLine {
display: block;
position: absolute;
left: 10px;
bottom: 15px;
font-size: xx-small;
}
.olControlScaleLineBottom {
border: solid 2px black;
border-bottom: none;
margin-top:-2px;
text-align: center;
}
.olControlScaleLineTop {
border: solid 2px black;
border-top: none;
text-align: center;
}
.olControlPermalink {
right: 3px;
bottom: 1.5em;
display: block;
position: absolute;
font-size: smaller;
}
div.olControlMousePosition {
bottom: 0em;
right: 3px;
display: block;
position: absolute;
font-family: Arial;
font-size: smaller;
}
.olControlOverviewMapContainer {
position: absolute;
bottom: 0px;
right: 0px;
}
.olControlOverviewMapElement {
padding: 10px 18px 10px 10px;
background-color: #00008B;
-moz-border-radius: 1em 0 0 0;
}
.olControlOverviewMapMinimizeButton {
right: 0px;
bottom: 80px;
cursor: pointer;
}
.olControlOverviewMapMaximizeButton {
right: 0px;
bottom: 80px;
cursor: pointer;
}
.olControlOverviewMapExtentRectangle {
overflow: hidden;
background-image: url("img/blank.gif");
cursor: move;
border: 2px dotted red;
}
.olControlOverviewMapRectReplacement {
overflow: hidden;
cursor: move;
background-image: url("img/overview_replacement.gif");
background-repeat: no-repeat;
background-position: center;
}
.olLayerGeoRSSDescription {
float:left;
width:100%;
overflow:auto;
font-size:1.0em;
}
.olLayerGeoRSSClose {
float:right;
color:gray;
font-size:1.2em;
margin-right:6px;
font-family:sans-serif;
}
.olLayerGeoRSSTitle {
float:left;font-size:1.2em;
}
.olPopupContent {
padding:5px;
overflow: auto;
}
.olControlNavToolbar {
width:0px;
height:0px;
}
.olControlNavToolbar div {
display:block;
width: 28px;
height: 28px;
top: 300px;
left: 6px;
position: relative;
}
.olControlNavigationHistory {
background-image: url("img/navigation_history.png");
background-repeat: no-repeat;
width: 24px;
height: 24px;
}
.olControlNavigationHistoryPreviousItemActive {
background-position: 0px 0px;
}
.olControlNavigationHistoryPreviousItemInactive {
background-position: 0px -24px;
}
.olControlNavigationHistoryNextItemActive {
background-position: -24px 0px;
}
.olControlNavigationHistoryNextItemInactive {
background-position: -24px -24px;
}
.olControlNavToolbar .olControlNavigationItemActive {
background-image: url("img/panning-hand-on.png");
background-repeat: no-repeat;
}
.olControlNavToolbar .olControlNavigationItemInactive {
background-image: url("img/panning-hand-off.png");
background-repeat: no-repeat;
}
.olControlNavToolbar .olControlZoomBoxItemActive {
background-image: url("img/drag-rectangle-on.png");
background-color: orange;
background-repeat: no-repeat;
}
.olControlNavToolbar .olControlZoomBoxItemInactive {
background-image: url("img/drag-rectangle-off.png");
background-repeat: no-repeat;
}
.olControlEditingToolbar {
top: 0px;
right: 0px;
height: 30px;
width: 200px;
}
.olControlEditingToolbar div {
background-image: url("img/editing_tool_bar.png");
background-repeat: no-repeat;
float:right;
width: 24px;
height: 24px;
margin: 5px;
}
.olControlEditingToolbar .olControlNavigationItemActive {
background-position: -103px -23px;
}
.olControlEditingToolbar .olControlNavigationItemInactive {
background-position: -103px -0px;
}
.olControlEditingToolbar .olControlDrawFeaturePointItemActive {
background-position: -77px -23px;
}
.olControlEditingToolbar .olControlDrawFeaturePointItemInactive {
background-position: -77px -0px;
}
.olControlEditingToolbar .olControlDrawFeaturePathItemInactive {
background-position: -51px 0px;
}
.olControlEditingToolbar .olControlDrawFeaturePathItemActive {
background-position: -51px -23px;
}
.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive {
background-position: -26px 0px;
}
.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive {
background-position: -26px -23px ;
}
div.olControlSaveFeaturesItemActive {
background-image: url(img/save_features_on.png);
background-repeat: no-repeat;
background-position: 0px 1px;
}
div.olControlSaveFeaturesItemInactive {
background-image: url(img/save_features_off.png);
background-repeat: no-repeat;
background-position: 0px 1px;
}
.olHandlerBoxZoomBox {
border: 2px solid red;
position: absolute;
background-color: white;
opacity: 0.50;
font-size: 1px;
filter: alpha(opacity=50);
}
.olHandlerBoxSelectFeature {
border: 2px solid blue;
position: absolute;
background-color: white;
opacity: 0.50;
font-size: 1px;
filter: alpha(opacity=50);
}
.olControlPanPanel {
top: 10px;
left: 5px;
}
.olControlPanPanel div {
background-image: url(img/pan-panel.png);
height: 18px;
width: 18px;
cursor: pointer;
position: absolute;
}
.olControlPanPanel .olControlPanNorthItemInactive {
top: 0px;
left: 9px;
background-position: 0px 0px;
}
.olControlPanPanel .olControlPanSouthItemInactive {
top: 36px;
left: 9px;
background-position: 18px 0px;
}
.olControlPanPanel .olControlPanWestItemInactive {
position: absolute;
top: 18px;
left: 0px;
background-position: 0px 18px;
}
.olControlPanPanel .olControlPanEastItemInactive {
top: 18px;
left: 18px;
background-position: 18px 18px;
}
.olControlZoomPanel {
top: 71px;
left: 14px;
}
.olControlZoomPanel div {
background-image: url(img/zoom-panel.png);
position: absolute;
height: 18px;
width: 18px;
cursor: pointer;
}
.olControlZoomPanel .olControlZoomInItemInactive {
top: 0px;
left: 0px;
background-position: 0px 0px;
}
.olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
top: 18px;
left: 0px;
background-position: 0px -18px;
}
.olControlZoomPanel .olControlZoomOutItemInactive {
top: 36px;
left: 0px;
background-position: 0px 18px;
}
/*
* When a potential text is bigger than the image it move the image
* with some headers (closes #3154)
*/
.olControlPanZoomBar div {
font-size: 1px;
}
.olPopupCloseBox {
background: url("img/close.gif") no-repeat;
cursor: pointer;
}
.olFramedCloudPopupContent {
padding: 5px;
overflow: auto;
}
.olControlNoSelect {
-moz-user-select: none;
-khtml-user-select: none;
}
.olImageLoadError {
background-color: pink;
opacity: 0.5;
filter: alpha(opacity=50); /* IE */
}
/**
* Cursor styles
*/
.olCursorWait {
cursor: wait;
}
.olDragDown {
cursor: move;
}
.olDrawBox {
cursor: crosshair;
}
.olControlDragFeatureOver {
cursor: move;
}
.olControlDragFeatureActive.olControlDragFeatureOver.olDragDown {
cursor: -moz-grabbing;
}
/**
* Layer switcher
*/
.olControlLayerSwitcher {
position: absolute;
top: 25px;
right: 0px;
width: 20em;
font-family: sans-serif;
font-weight: bold;
margin-top: 3px;
margin-left: 3px;
margin-bottom: 3px;
font-size: smaller;
color: white;
background-color: transparent;
}
.olControlLayerSwitcher .layersDiv {
padding-top: 5px;
padding-left: 10px;
padding-bottom: 5px;
padding-right: 75px;
background-color: darkblue;
width: 100%;
height: 100%;
}
.olControlLayerSwitcher .layersDiv .baseLbl,
.olControlLayerSwitcher .layersDiv .dataLbl {
margin-top: 3px;
margin-left: 3px;
margin-bottom: 3px;
}
.olControlLayerSwitcher .layersDiv .baseLayersDiv,
.olControlLayerSwitcher .layersDiv .dataLayersDiv {
padding-left: 10px;
}
.olControlLayerSwitcher .maximizeDiv,
.olControlLayerSwitcher .minimizeDiv {
top: 5px;
right: 0px;
cursor: pointer;
}
.olBingAttribution {
color: #DDD;
}
.olBingAttribution.road {
color: #333;
}

View File

@ -0,0 +1,158 @@
var CKAN = CKAN || {};
CKAN.WMSPreview = function($){
// Private
var defaultVersion = "1.3.0";
var preferredFormat = "image/png";
var proxy = "/proxy?url=";
var getURL = function(server,version){
if (server.indexOf("?") === -1)
server += "?"
var url = server +
"SERVICE=WMS" +
"&REQUEST=GetCapabilities" +
"&VERSION=" + defaultVersion
return (proxy) ? proxy + escape(url) : url;
}
var getFormat = function(formats){
for(var i = 0; i < formats.length; i++){
if (formats[i] == preferredFormat){
return formats[i];
}
}
return formats[0];
}
// Public
return {
map: null,
setup: function(server){
var url = getURL(server);
var self = this;
$.get(url,function(data){
// Most WMS Servers will return the version they prefer,
// regardless of which one you requested, so better check
// for the actual version returned.
var version = $(data).find("WMS_Capabilities").attr("version"); // 1.3.0
if (!version)
version = $(data).find("WMS_MS_Capabilities").attr("version"); // 1.1.1
var format = new OpenLayers.Format.WMSCapabilities({"version":version});
var capabilities = format.read(data);
if (capabilities.capability){
var layers = capabilities.capability.layers;
var olLayers = [];
var maxExtent = false;
var maxScale = false;
var minScale = false;
for (var count = 0; count < layers.length; count++){
layer = layers[count];
// Extend the maps's maxExtent to include this layer extent
layerMaxExtent = new OpenLayers.Bounds(layer.llbbox[0],layer.llbbox[1],layer.llbbox[2],layer.llbbox[3]);
if (!maxExtent){
maxExtent = layerMaxExtent;
} else {
maxExtent.extend(layerMaxExtent);
}
if (layer.maxScale && (layer.maxScale > maxScale || maxScale === false)) maxScale = layer.maxScale;
if (layer.minScale && (layer.minScale < minScale || minScale === false)) minScale = layer.minScale;
olLayers.push(new OpenLayers.Layer.WMS(
layer.title,
server,
{"layers": layer.name,
"format": getFormat(layer.formats),
"transparent":true
},
{"buffer":0,
"maxExtent": layerMaxExtent,
"maxScale": (layer.maxScale) ? layer.maxScale : null,
"minScale": (layer.minScale) ? layer.minScale : null,
"isBaseLayer": false,
"visibility": (count == 0)
}) //Tiled?
);
}
var dummyLayer = new OpenLayers.Layer("Dummy",{
"maxExtent": maxExtent,
"displayInLayerSwitcher":false,
"isBaseLayer":true,
"visibility":false,
"minScale": (minScale) ? minScale : null,
"maxScale": (maxScale) ? maxScale : null
});
olLayers.push(dummyLayer);
// Setup some sizes
var w = $("#container").width() * 0.50;
if (w > 1024) w = 1024;
$("#content").width($("#container").width());
$("#map").width(w);
$("#map").height(500);
// Create a new map
self.map = new OpenLayers.Map("map" ,
{
"projection": new OpenLayers.Projection("EPSG:4326"),
"maxResolution":"auto",
"controls":[
new OpenLayers.Control.PanZoomBar(),
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.CustomMousePosition({
"displayClass":"olControlMousePosition",
"numDigits":4
}),
new OpenLayers.Control.LayerSwitcher({
"div": document.getElementById("layers"),
"roundedCorner":false
})
],
"theme":"/ckanext/spatial/js/openlayers/theme/default/style.css"
});
self.map.maxExtent = maxExtent;
self.map.addLayers(olLayers);
self.map.zoomTo(1);
} else {
$("#main").prepend(
$("<div></div>").attr("class","flash-banner-box").append(
$("<div></div>").attr("class","flash-banner error").html(
"Error parsing the WMS capabilities document"
)
)
);
}
})
}
}
}(jQuery)
OpenLayers.ImgPath = "/ckanext/spatial/js/openlayers/img/";
OpenLayers.Lang.en.overlays = "Available Layers:";
OpenLayers.Control.CustomMousePosition = OpenLayers.Class(OpenLayers.Control.MousePosition,{
formatOutput: function(lonLat) {
var newHtml = OpenLayers.Control.MousePosition.prototype.formatOutput.apply(this, [lonLat]);
newHtml = "1:" + parseInt(this.map.getScale()) + " | WGS84 " + newHtml;
return newHtml;
},
CLASS_NAME: "OpenLayers.Control.CustomMousePosition"
})

View File

@ -0,0 +1,23 @@
#map{
float: left;
display: inline;
}
#layers{
float: left;
display: inline;
margin-left: 20px;
}
/* OpenLayers */
.dataLbl{
font-weight: bold;
margin-bottom: 20px;
}
.dataLayersDiv input{
margin: 0 10px 10px 0;
}
.dataLayersDiv span{
margin: 0;
}

View File

@ -26,6 +26,6 @@ setup(
"""
[ckan.plugins]
# Add plugins here, eg
# myplugin=ckanext.spatial:PluginClass
wms_preview=ckanext.spatial.plugin:WMSPreview
""",
)

View File

@ -0,0 +1,47 @@
<html xmlns:py="http://genshi.edgewall.org/"
xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
<py:def function="page_title">${c.pkg.title or c.pkg.name} - WMS preview</py:def>
<py:def function="optional_head">
<link type="text/css" rel="stylesheet" media="all" href="/ckanext/spatial/wms_preview.css" />
<script type="text/javascript" src="/ckanext/spatial/js/openlayers/OpenLayers_ckan.js"></script>
<script type="text/javascript" src="/ckanext/spatial/js/wms_preview.js"></script>
</py:def>
<div py:match="content">
<div class="map-view-content">
<h2 class="head">
${c.pkg.title}
</h2>
<!-- Source URL -->
<div class="url" py:if="c.pkg.url">
<p>
Source: <a href="${c.wms.url}" target="_blank">${c.wms.url}</a>
</p>
</div>
<p>
<a
href="${url(controller='package', action='read', id=c.pkg.name)}"
title="Package Details">
Package details</a>
</p>
<div class="package subsection">
<h3>WMS preview</h3>
<script type="text/javascript">
//<![CDATA[
$(document).ready(function(){
CKAN.WMSPreview.setup("${c.wms.url}");
})
//]]>
</script>
<div id="map"></div>
<div id="layers"></div>
</div>
</div>
</div>
<xi:include href="../../layout.html" />
</html>