Merge branch 'feature-default-search-integration'

This commit is contained in:
amercader 2012-02-24 16:56:58 +00:00
commit 35ec44a038
15 changed files with 987 additions and 448 deletions

View File

@ -5,10 +5,15 @@ ckanext-spatial - Geo related plugins for CKAN
This extension contains plugins that add geospatial capabilities to CKAN.
The following plugins are currently available:
* Automatic geo-indexing and spatial API call (`spatial_query`).
* Spatial model for CKAN datasets and automatic geo-indexing (`spatial_metadata`)
* Spatial search integration and API call (`spatial_query`).
* Map widget integrated on the search form (`spatial_query_widget`).
* Map widget showing a dataset extent (`dataset_extent_map`).
* A Web Map Service (WMS) previewer (`wms_preview`).
All plugins except the WMS previewer require the `spatial_metadata` plugin.
Dependencies
============
@ -67,15 +72,26 @@ permissions) of the geometry_columns and spatial_ref_sys tables
Plugins are configured as follows in the CKAN ini file (Add only the ones you
are interested in)::
ckan.plugins = wms_preview spatial_query dataset_extent_map
ckan.plugins = spatial_metadata spatial_query spatial_query_widget dataset_extent_map wms_preview
If you are using the spatial search feature, you can define the projection
When enabling the spatial metadata, you can define the projection
in which extents are stored in the database with the following option. Use
the EPSG code as an integer (e.g 4326, 4258, 27700, etc). It defaults to
4326::
ckan.spatial.srid = 4326
If you want to define a default map extent for the different map widgets,
(e.g. if you are running a national instace of CKAN) you can do so adding
this configuration option::
ckan.spatial.default_map_extent=<minx>,<miny>,<maxx>,<maxy>
Coordinates must be in latitude/longitude, e.g.::
ckan.spatial.default_map_extent=-6.88,49.74,0.50,59.2
Tests
=====
@ -111,7 +127,8 @@ Spatial Query
=============
To enable the spatial query you need to add the `spatial_query` plugin to your
ini file (See 'Configuration').
ini file (See `Configuration`_). This plugin requires the `spatial_metadata`
plugin.
The extension adds the following call to the CKAN search API, which returns
datasets with an extent that intersects with the bounding box provided::
@ -126,6 +143,20 @@ forms:
- EPSG:4326
- 4326
As of CKAN 1.6, you can integrate your spatial query in the full CKAN
search, via the web interface (see the `Spatial Query Widget`_) or
via the `action API`__, e.g.::
POST http://localhost:5000/api/action/package_search
{
"q": "Pollution",
"extras": {
"ext_bbox": "-7.535093,49.208494,3.890688,57.372349"
}
}
__ http://docs.ckan.org/en/latest/apiv3.html
Geo-Indexing your datasets
--------------------------
@ -145,11 +176,24 @@ Every time a dataset is created, updated or deleted, the extension will synchron
the information stored in the extra with the geometry table.
Spatial Query Widget
====================
**Note**: this plugin requires CKAN 1.6 or higher.
To enable the search map widget you need to add the `spatial_query_widget` plugin to your
ini file (See `Configuration`_). You also need to load both the `spatial_metadata`
and the `spatial_query` plugins.
When the plugin is enabled, a map widget will be shown in the dataset search form,
where users can refine their searchs drawing an area of interest.
Dataset Map Widget
==================
To enable the dataset map you need to add the `dataset_map` plugin to your
ini file (See 'Configuration'). You need to load the `spatial_query` plugin also.
ini file (See `Configuration`_). You need to load the `spatial_metadata` plugin also.
When the plugin is enabled, if datasets contain a 'spatial' extra like the one
described in the previous section, a map will be shown on the dataset details page.
@ -159,7 +203,7 @@ WMS Previewer
=============
To enable the WMS previewer you need to add the `wms_preview` plugin to your
ini file (See 'Configuration').
ini file (See `Configuration`_).
Please note that this is an experimental plugin and may be unstable.
@ -173,8 +217,8 @@ layers, based on the GetCapabilities response.
Setting up PostGIS
==================
Configuration
-------------
PostGIS Configuration
---------------------
* Install PostGIS::

View File

@ -1,20 +1,13 @@
from string import Template
from ckan.lib.base import request, config, abort
from ckan.controllers.api import ApiController as BaseApiController
from ckan.model import Session, Package
from ckanext.spatial.lib import get_srid
from ckanext.spatial.model import PackageExtent
from ckanext.spatial.lib import get_srid, validate_bbox, bbox_query
from geoalchemy import WKTSpatialElement, functions
class ApiController(BaseApiController):
db_srid = int(config.get('ckan.spatial.srid', '4326'))
bbox_template = Template('POLYGON (($minx $miny, $minx $maxy, $maxx $maxy, $maxx $miny, $minx $miny))')
def spatial_query(self):
error_400_msg = 'Please provide a suitable bbox parameter [minx,miny,maxx,maxy]'
@ -22,32 +15,20 @@ class ApiController(BaseApiController):
if not 'bbox' in request.params:
abort(400,error_400_msg)
bbox = request.params['bbox'].split(',')
if len(bbox) is not 4:
bbox = validate_bbox(request.params['bbox'])
if not bbox:
abort(400,error_400_msg)
try:
minx = float(bbox[0])
miny = float(bbox[1])
maxx = float(bbox[2])
maxy = float(bbox[3])
except ValueError,e:
abort(400,error_400_msg)
wkt = self.bbox_template.substitute(minx=minx,miny=miny,maxx=maxx,maxy=maxy)
srid = get_srid(request.params.get('crs')) if 'crs' in request.params else None
if srid and srid != self.db_srid:
# Input geometry needs to be transformed to the one used on the database
input_geometry = functions.transform(WKTSpatialElement(wkt,srid),self.db_srid)
else:
input_geometry = WKTSpatialElement(wkt,self.db_srid)
extents = Session.query(PackageExtent) \
.filter(PackageExtent.package_id==Package.id) \
.filter(PackageExtent.the_geom.intersects(input_geometry)) \
.filter(Package.state==u'active')
extents = bbox_query(bbox,srid)
format = request.params.get('format','')
return self._output_results(extents,format)
def _output_results(self,extents,format=None):
ids = [extent.package_id for extent in extents]

View File

@ -31,3 +31,39 @@ PACKAGE_MAP_EXTRA_FOOTER="""
"""
SPATIAL_SEARCH_FORM_EXTRA_HEADER="""
<link type="text/css" rel="stylesheet" media="all" href="/ckanext/spatial/css/spatial_search_form.css" />
"""
SPATIAL_SEARCH_FORM_EXTRA_FOOTER="""
<script type="text/javascript" src="/ckanext/spatial/js/openlayers/OpenLayers_dataset_map.js"></script>
<script type="text/javascript" src="/ckanext/spatial/js/spatial_search_form.js"></script>
<script type="text/javascript">
//<![CDATA[
$(document).ready(function(){
CKAN.SpatialSearchForm.bbox = '%(bbox)s';
CKAN.SpatialSearchForm.defaultExtent = '%(default_extent)s';
CKAN.SpatialSearchForm.setup();
})
//]]>
</script>
"""
SPATIAL_SEARCH_FORM="""
<input type="hidden" id="ext_bbox" name="ext_bbox" value="%(bbox)s" />
<input type="hidden" id="ext_prev_extent" name="ext_prev_extent" value="" />
<div id="spatial-search-show"><a href="#" class="more">Filter by location</a></div>
<div id="spatial-search-container">
<div id="spatial-search-map-container">
<div id="spatial-search-map"></div>
<div id="spatial-search-toolbar">
<input type="button" id="draw-box" value="Select an area" class="pretty-button"/>
<input type="button" id="clear-box" value="Clear" class="pretty-button"/>
<div class="helper">Click on the 'Select' button to draw an area of interest. Use the map controls or the mouse wheel to zoom. Drag to pan the map.</div>
</div>
</div>
<div id="spatial-search-map-attribution">Map data CC-BY-SA by <a href="http://openstreetmap.org">OpenStreetMap</a> | Tiles by <a href="http://www.mapquest.com">MapQuest</a></div>
</div>
"""

View File

@ -1,6 +1,7 @@
import logging
from string import Template
from ckan.model import Session
from ckan.model import Session, Package
from ckan.lib.base import config
from ckanext.spatial.model import PackageExtent
@ -72,3 +73,46 @@ def save_package_extent(package_id, geometry = None, srid = None):
Session.add(package_extent)
log.debug('Created new extent for package %s' % package_id)
def validate_bbox(bbox_values):
if isinstance(bbox_values,basestring):
bbox_values = bbox_values.split(',')
if len(bbox_values) is not 4:
return None
try:
bbox = {}
bbox['minx'] = float(bbox_values[0])
bbox['miny'] = float(bbox_values[1])
bbox['maxx'] = float(bbox_values[2])
bbox['maxy'] = float(bbox_values[3])
except ValueError,e:
return None
return bbox
def bbox_query(bbox,srid=None):
db_srid = int(config.get('ckan.spatial.srid', '4326'))
bbox_template = Template('POLYGON (($minx $miny, $minx $maxy, $maxx $maxy, $maxx $miny, $minx $miny))')
wkt = bbox_template.substitute(minx=bbox['minx'],
miny=bbox['miny'],
maxx=bbox['maxx'],
maxy=bbox['maxy'])
if srid and srid != db_srid:
# Input geometry needs to be transformed to the one used on the database
input_geometry = functions.transform(WKTSpatialElement(wkt,srid),db_srid)
else:
input_geometry = WKTSpatialElement(wkt,db_srid)
extents = Session.query(PackageExtent) \
.filter(PackageExtent.package_id==Package.id) \
.filter(PackageExtent.the_geom.intersects(input_geometry)) \
.filter(Package.state==u'active')
return extents

View File

@ -1,13 +1,17 @@
import os
from logging import getLogger
from pylons import config
from pylons.i18n import _
from genshi.input import HTML
from genshi.filters import Transformer
import ckan.lib.helpers as h
from ckan.lib.search import SearchError
from ckan.lib.helpers import json
from ckan import model
from ckan.plugins import implements, SingletonPlugin
from ckan.plugins import IRoutes
from ckan.plugins import IConfigurable, IConfigurer
@ -19,15 +23,14 @@ from ckan.logic.action.update import package_error_summary
import html
from ckanext.spatial.lib import save_package_extent
from ckanext.spatial.lib import save_package_extent,validate_bbox, bbox_query
from ckanext.spatial.model import setup as setup_model
log = getLogger(__name__)
class SpatialQuery(SingletonPlugin):
class SpatialMetadata(SingletonPlugin):
implements(IRoutes, inherit=True)
implements(IPackageController, inherit=True)
implements(IConfigurable, inherit=True)
@ -36,12 +39,6 @@ class SpatialQuery(SingletonPlugin):
if not config.get('ckan.spatial.testing',False):
setup_model()
def before_map(self, map):
map.connect('api_spatial_query', '/api/2/search/{register:dataset|package}/geo',
controller='ckanext.spatial.controllers.api:ApiController',
action='spatial_query')
return map
def create(self, package):
self.check_spatial_extra(package)
@ -88,6 +85,67 @@ class SpatialQuery(SingletonPlugin):
def delete(self, package):
save_package_extent(package.id,None)
class SpatialQuery(SingletonPlugin):
implements(IRoutes, inherit=True)
implements(IPackageController, inherit=True)
def before_map(self, map):
map.connect('api_spatial_query', '/api/2/search/{register:dataset|package}/geo',
controller='ckanext.spatial.controllers.api:ApiController',
action='spatial_query')
return map
def before_search(self,search_params):
if 'extras' in search_params and 'ext_bbox' in search_params['extras'] \
and search_params['extras']['ext_bbox']:
bbox = validate_bbox(search_params['extras']['ext_bbox'])
if not bbox:
raise SearchError('Wrong bounding box provided')
extents = bbox_query(bbox)
if extents.count() == 0:
# We don't need to perform the search
search_params['abort_search'] = True
else:
# We'll perform the existing search but also filtering by the ids
# of datasets within the bbox
bbox_query_ids = [extent.package_id for extent in extents]
q = search_params.get('q','')
new_q = '%s AND ' % q if q else ''
new_q += '(%s)' % ' OR '.join(['id:%s' % id for id in bbox_query_ids])
search_params['q'] = new_q
return search_params
class SpatialQueryWidget(SingletonPlugin):
implements(IGenshiStreamFilter)
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') == 'search':
data = {
'bbox': request.params.get('ext_bbox',''),
'default_extent': config.get('ckan.spatial.default_map_extent','')
}
stream = stream | Transformer('body//div[@id="dataset-search-ext"]')\
.append(HTML(html.SPATIAL_SEARCH_FORM % data))
stream = stream | Transformer('head')\
.append(HTML(html.SPATIAL_SEARCH_FORM_EXTRA_HEADER % data))
stream = stream | Transformer('body')\
.append(HTML(html.SPATIAL_SEARCH_FORM_EXTRA_FOOTER % data))
return stream
class DatasetExtentMap(SingletonPlugin):

View File

@ -0,0 +1,71 @@
#spatial-search-show{
margin-top: 10px;
}
#spatial-search-show a.more:after {
content: ' »';
font-size: 150%;
position: relative;
bottom: -1px;
}
#spatial-search-show a.less:before {
content: '« ';
font-size: 150%;
position: relative;
bottom: -1px;
}
#spatial-search-container {
display: none;
}
#spatial-search-map-container {
float:left;
margin-top: 10px;
margin-bottom: 0px;
width: 100%;
}
#spatial-search-map {
float:left;
width: 300px;
height: 200px;
}
#spatial-search-toolbar{
float:left;
margin-left: 20px;
width: 250px;
}
#clear-box {
margin-left: 5px;
}
#spatial-search-toolbar div{
margin: 5px 0;
}
#spatial-search-toolbar .helper{
font-style: italic;
font-size: small;
}
#spatial-search-map-attribution{
font-size: x-small;
}
/* OpenLayers overrides */
.olControlZoomPanel {
top: 10px !important;
}
.olControlZoomPanel div {
background-image: url(/ckanext/spatial/js/openlayers/img/zoom-panel.png) !important;
}

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,7 @@ OpenLayers/Layer/Vector.js
OpenLayers/Control/Navigation.js
OpenLayers/Control/PanZoom.js
OpenLayers/Control/PanZoomBar.js
OpenLayers/Control/ZoomPanel.js
OpenLayers/Control/Scale.js
OpenLayers/Control/MousePosition.js
OpenLayers/Control/LayerSwitcher.js

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

View File

@ -0,0 +1,224 @@
var CKAN = CKAN || {};
CKAN.SpatialSearchForm = function($){
// Projections
var proj4326 = new OpenLayers.Projection("EPSG:4326");
var proj900913 = new OpenLayers.Projection("EPSG:900913");
var getGeomType = function(feature){
return feature.geometry.CLASS_NAME.split(".").pop().toLowerCase()
}
var getStyle = function(geom_type){
var styles = CKAN.DatasetMap.styles;
var style = (styles[geom_type]) ? styles[geom_type] : styles["default"];
return new OpenLayers.StyleMap(OpenLayers.Util.applyDefaults(
style, OpenLayers.Feature.Vector.style["default"]))
}
var getParameterByName = function (name) {
var match = RegExp('[?&]' + name + '=([^&]*)')
.exec(window.location.search);
return match ?
decodeURIComponent(match[1].replace(/\+/g, ' '))
: null;
}
var getBoundsFromBbox = function(bbox){
var coords = bbox.split(",");
var bounds = new OpenLayers.Bounds(coords[0],coords[1],coords[2],coords[3]).transform(proj4326,proj900913);
return bounds;
}
// Public
return {
map: null,
mapInitialized: false,
bbox: null,
defaultExtent: null,
styles: {
"default":{
"fillColor":"#FCF6CF",
"strokeColor":"#B52",
"strokeWidth":2,
"fillOpacity":0.4,
}
},
setup: function(){
$("#spatial-search-show").click(this.toggleDiv);
// There is a bbox available from a previous search
if (CKAN.SpatialSearchForm.bbox){
this.toggleDiv();
}
},
toggleDiv: function(){
$("#spatial-search-show>a").toggleClass("more less");
$("#spatial-search-container").toggle();
if (!CKAN.SpatialSearchForm.mapInitialized){
CKAN.SpatialSearchForm.mapSetup()
}
},
mapSetup: function(){
var mapquestTiles = [
"http://otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg",
"http://otile2.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg",
"http://otile3.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg",
"http://otile4.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg"];
var layers = [
//new OpenLayers.Layer.OSM()
new OpenLayers.Layer.OSM("MapQuest-OSM Tiles", mapquestTiles)
]
// Create a new map
this.map = new OpenLayers.Map("spatial-search-map" ,
{
"projection": proj900913,
"displayProjection": proj4326,
"units": "m",
"numZoomLevels": 18,
"maxResolution": 156543.0339,
"maxExtent": new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508.34),
"controls": [
new OpenLayers.Control.ZoomPanel(),
new OpenLayers.Control.Navigation()
],
"theme":"/ckanext/spatial/js/openlayers/theme/default/style.css"
});
var query = new OpenLayers.Control.BoxQuery();
this.map.addControl(query);
this.map.addLayers(layers);
var vector_layer = new OpenLayers.Layer.Vector("Bounding Box",
{
"projection": proj4326,
"styleMap": new OpenLayers.StyleMap(this.styles["default"])
}
);
// Setup buttons events
$("#draw-box").click(function(){
if (!query.active){
query.activate();
$("#draw-box").addClass("depressed");
} else {
$("#draw-box").removeClass("depressed");
query.deactivate();
}
});
$("#clear-box").click(function(){
if (query.active){
$("#draw-box").removeClass("depressed");
query.deactivate();
}
vector_layer.destroyFeatures();
$("#ext_bbox").val('');
});
var bounds;
// Check if there's a bbox from a previous search or a default
// extent defined
if (this.bbox) {
var bboxBounds = getBoundsFromBbox(this.bbox);
var feature = new OpenLayers.Feature.Vector(
bboxBounds.toGeometry()
);
vector_layer.addFeatures([feature]);
bounds = bboxBounds;
}
var previousExtent = getParameterByName("ext_prev_extent");
if (previousExtent && this.bbox){
bounds = getBoundsFromBbox(previousExtent);
} else if (this.defaultExtent) {
bounds = getBoundsFromBbox(this.defaultExtent);
} else {
bounds = this.map.maxExtent;
}
this.map.zoomToExtent(bounds,true);
this.map.addLayer(vector_layer);
this.map.events.register("moveend",this,function(e){
$("#ext_prev_extent").val(e.object.getExtent().transform(proj900913,proj4326).toBBOX());
});
CKAN.SpatialSearchForm.mapInitialized = true;
this.map.events.triggerEvent("moveend");
}
}
}(jQuery)
// Custom control to handle clicks on the map
OpenLayers.Control.BoxQuery = OpenLayers.Class(OpenLayers.Control, {
type: OpenLayers.Control.TYPE_TOOL,
boxLayer: null,
draw: function() {
this.boxLayer = this.map.getLayersByName("Bounding Box")[0];
this.handler = new OpenLayers.Handler.Box( this,
{done: this.done}
);
},
done: function(position){
this.boxLayer = this.map.getLayersByName("Bounding Box")[0];
// We need a bounding box
if (position instanceof OpenLayers.Bounds) {
var bounds;
var minXY = this.map.getLonLatFromPixel(
new OpenLayers.Pixel(position.left, position.bottom));
var maxXY = this.map.getLonLatFromPixel(
new OpenLayers.Pixel(position.right, position.top));
bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
maxXY.lon, maxXY.lat);
} else {
return false;
}
this.boxLayer.destroyFeatures();
// Add new query extent
this.boxLayer.addFeatures([
new OpenLayers.Feature.Vector(bounds.toGeometry())
]);
// Transform bounds to wgs84
bounds.transform(this.map.getProjectionObject(),
new OpenLayers.Projection("EPSG:4326"));
// Store the coordinates in the hidden bbox field, so they are sent
// when user submits the form
$("#ext_bbox").val(bounds.toBBOX());
$("#draw-box").removeClass("depressed");
this.deactivate();
}
});
OpenLayers.ImgPath = "/ckanext/spatial/js/openlayers/img/";

View File

@ -23,6 +23,7 @@ class SpatialTestBase:
geojson_examples = {
'point':'{"type":"Point","coordinates":[100.0,0.0]}',
'point_2':'{"type":"Point","coordinates":[20,10]}',
'line':'{"type":"LineString","coordinates":[[100.0,0.0],[101.0,1.0]]}',
'polygon':'{"type":"Polygon","coordinates":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]]}',
'polygon_holes':'{"type":"Polygon","coordinates":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]],[[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]]]}',

View File

@ -1,13 +1,14 @@
import logging
import json
from pprint import pprint
from nose.tools import assert_equal, assert_raises
from ckan.logic.action.create import package_create
from ckan.logic.action.delete import package_delete
from ckan import model
from ckan.model import Package, Session
import ckan.lib.search as search
from ckan.tests import CreateTestData, setup_test_search_index
from ckan.tests import CreateTestData, setup_test_search_index,WsgiAppCase
from ckan.tests.functional.api.base import ApiTestCase
from ckan.tests import TestController as ControllerTestCase
from ckanext.spatial.tests import SpatialTestBase
@ -72,3 +73,52 @@ class TestSpatialApi(ApiTestCase,SpatialTestBase,ControllerTestCase):
assert res_dict['count'] == 0
assert res_dict['results'] == []
class TestActionPackageSearch(SpatialTestBase,WsgiAppCase):
@classmethod
def setup_class(self):
super(TestActionPackageSearch,self).setup_class()
setup_test_search_index()
self.package_fixture_data_1 = {
'name' : u'test-spatial-dataset-search-point-1',
'title': 'Some Title 1',
'extras': [{'key':'spatial','value':self.geojson_examples['point']}]
}
self.package_fixture_data_2 = {
'name' : u'test-spatial-dataset-search-point-2',
'title': 'Some Title 2',
'extras': [{'key':'spatial','value':self.geojson_examples['point_2']}]
}
CreateTestData.create()
@classmethod
def teardown_class(self):
model.repo.rebuild_db()
def test_1_basic(self):
context = {'model':model,'session':Session,'user':'tester','extras_as_string':True}
package_dict_1 = package_create(context,self.package_fixture_data_1)
del context['package']
package_dict_2 = package_create(context,self.package_fixture_data_2)
postparams = '%s=1' % json.dumps({
'q': 'test',
'facet.field': ('groups', 'tags', 'res_format', 'license'),
'rows': 20,
'start': 0,
'extras': {
'ext_bbox': '%s,%s,%s,%s' % (10,10,40,40)
}
})
res = self.app.post('/api/action/package_search', params=postparams)
res = json.loads(res.body)
result = res['result']
# Only one dataset returned
assert_equal(res['success'], True)
assert_equal(result['count'], 1)
assert_equal(result['results'][0]['name'], 'test-spatial-dataset-search-point-2')

View File

@ -0,0 +1,22 @@
import logging
from pylons import config
from ckan.lib.helpers import url_for
from ckan.tests.functional.base import FunctionalTestCase
from ckanext.spatial.tests import SpatialTestBase
log = logging.getLogger(__name__)
class TestSpatialQueryWidget(FunctionalTestCase,SpatialTestBase):
def test_widget_shown(self):
# Load the dataset search page and check if the libraries have been loaded
offset = url_for(controller='package', action='search')
res = self.app.get(offset)
assert '<div id="spatial-search-container">' in res, res
assert '<script type="text/javascript" src="/ckanext/spatial/js/spatial_search_form.js"></script>' in res
assert config.get('ckan.spatial.default_extent') in res

View File

@ -26,9 +26,11 @@ setup(
"""
[ckan.plugins]
# Add plugins here, eg
wms_preview=ckanext.spatial.nongeos_plugin:WMSPreview
spatial_metadata=ckanext.spatial.plugin:SpatialMetadata
spatial_query=ckanext.spatial.plugin:SpatialQuery
spatial_query_widget=ckanext.spatial.plugin:SpatialQueryWidget
dataset_extent_map=ckanext.spatial.plugin:DatasetExtentMap
wms_preview=ckanext.spatial.nongeos_plugin:WMSPreview
[paste.paster_command]
spatial=ckanext.spatial.commands.spatial:Spatial
""",

View File

@ -15,8 +15,9 @@ port = 5000
use = config:../ckan/test-core.ini
# Here we hard-code the database and a flag to make default tests
# run fast.
ckan.plugins = spatial_query dataset_extent_map wms_preview
ckan.plugins = spatial_metadata spatial_query spatial_query_widget dataset_extent_map wms_preview synchronous_search
ckan.spatial.srid = 4326
ckan.spatial.default_map_extent=-6.88,49.74,0.50,59.2
ckan.spatial.testing = true
# NB: other test configuration should go in test-core.ini, which is
# what the postgres tests use.