Skip to content

Commit

Permalink
Allow for multiple bounding box display on the map on the records page
Browse files Browse the repository at this point in the history
This fixes issue #24.  This required a fundamental change to the
metadata object model and portal querying capabilities to support
multiple bounding boxes throughout the application.
  • Loading branch information
Homme Zwaagstra committed Apr 20, 2012
1 parent 4f0b19f commit d5f4a34
Show file tree
Hide file tree
Showing 16 changed files with 271 additions and 179 deletions.
22 changes: 14 additions & 8 deletions html/js/full/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,11 @@ function populate_areas() {
// if there's an existing area, select it
if (area) {
select.filter(':visible').change();
} else if (bbox) {
} else if (bboxes) {
var bbox = new OpenLayers.Bounds();
for (var i = 0; i < bboxes.length; i++) {
bbox.extend(bboxes[i]);
}
add_box(bbox);
map.zoomToExtent(bbox); // zoom to the box
} else {
Expand Down Expand Up @@ -522,7 +526,7 @@ function check_query() {
area.empty();
if (!criteria['terms'].length &&
!criteria['dates'].start && !criteria['dates'].end &&
!criteria['area'] && !criteria['bbox']) {
!criteria['area'] && !criteria['bbox'].length) {
term.append('<span><strong>everything</strong> in the catalogue.</span>');
return;
} else if (!criteria['terms'].length)
Expand Down Expand Up @@ -550,12 +554,14 @@ function check_query() {

if (criteria['area'])
area.append('<span> in <strong>'+criteria['area']+'</strong></span>');
else if (criteria['bbox']) {
var n = criteria['bbox'][3].toFixed(2);
var s = criteria['bbox'][1].toFixed(2);
var e = criteria['bbox'][2].toFixed(2);
var w = criteria['bbox'][0].toFixed(2);
area.append('<span> in <strong>'+n+'N '+s+'S '+e+'E '+w+'W</strong></span>');
else if (criteria['bbox'].length) {
for (var i = 0; i < criteria['bbox'].length; i++) {
var n = criteria['bbox'][i][3].toFixed(2);
var s = criteria['bbox'][i][1].toFixed(2);
var e = criteria['bbox'][i][2].toFixed(2);
var w = criteria['bbox'][i][0].toFixed(2);
area.append('<span> in <strong>'+n+'N '+s+'S '+e+'E '+w+'W</strong></span>');
}
}
},
complete: function(req, status) {
Expand Down
30 changes: 21 additions & 9 deletions python/medin/dws.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,16 @@ class SummaryResponse(BriefResponse):

def _processDocument(self, doc):

try:
extent = doc.Spatial[0].BoundingBox
bbox = [extent.LimitWest, extent.LimitSouth, extent.LimitEast, extent.LimitNorth]
except AttributeError, IndexError:
bbox = None
bboxes = []
for extent in doc.Spatial:
try:
extent = extent.BoundingBox
bboxes.append([extent.LimitWest, extent.LimitSouth, extent.LimitEast, extent.LimitNorth])
except (AttributeError, IndexError):
pass

ret = super(SummaryResponse, self)._processDocument(doc)
ret['bbox'] = bbox
ret['bbox'] = bboxes
ret['abstract'] = doc.Abstract

return ret
Expand Down Expand Up @@ -368,11 +370,21 @@ def prepareCaller(self, query, result_type, logger):

# add the spatial criteria
aid = query.getArea(cast=False)
boxes = []
if aid:
bbox = query.areas.getBBOX(aid)
boxes.append(query.areas.getBBOX(aid))
else:
bbox = query.getBBOX()
if bbox:
boxes.extend(query.getBoxes())

if boxes:
# get the total extent, as the DWS does not currently
# support multiple bounding boxes
bbox = [
min((box[0] for box in boxes)),
min((box[1] for box in boxes)),
max((box[2] for box in boxes)),
max((box[3] for box in boxes))]

(search.SpatialSearch.BoundingBox.LimitWest,
search.SpatialSearch.BoundingBox.LimitSouth,
search.SpatialSearch.BoundingBox.LimitEast,
Expand Down
60 changes: 31 additions & 29 deletions python/medin/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class Metadata(object):
topic_category = None # element 9
service_type = None # element 10
keywords = None # element 11
bbox = None # element 12
bboxes = None # element 12
extents = None # element 13
vertical_extent = None # element 14
reference_system = None # element 15
Expand Down Expand Up @@ -453,7 +453,7 @@ def parse(self):
m.topic_category = self.topicCategory() # element 9
m.service_type = self.serviceType() # element 10
m.keywords = self.keywords() # element 11
m.bbox = self.bbox() # element 12
m.bboxes = self.bboxes() # element 12
m.extents = self.extents() # element 13
m.vertical_extent = self.verticalExtent() # element 14
m.reference_system = self.referenceSystem() # element 15
Expand Down Expand Up @@ -647,26 +647,24 @@ def keywords(self):
return keywords

@_assignContext
def bbox(self):
def bboxes(self):
"""Element 12: Geographic Bounding Box"""

try:
node = self.xpath.xpathEval('//gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox')[0]
except IndexError:
return []
self.xpath.setContextNode(node)

ordinates = []

for direction, latlon in (('west', 'longitude'), ('south', 'latitude'), ('east', 'longitude'), ('north', 'latitude')):
try:

ordinate = self.xpath.xpathEval('./gmd:%sBound%s/gco:Decimal/text()' % (direction, latlon.capitalize()))[0].content.strip()
except IndexError:
return []
ordinates.append(float(ordinate))

return ordinates
boxes = []
for node in self.xpath.xpathEval('//gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox'):
self.xpath.setContextNode(node)

ordinates = []

for direction, latlon in (('west', 'longitude'), ('south', 'latitude'), ('east', 'longitude'), ('north', 'latitude')):
try:

ordinate = self.xpath.xpathEval('./gmd:%sBound%s/gco:Decimal/text()' % (direction, latlon.capitalize()))[0].content.strip()
except IndexError:
return []
ordinates.append(float(ordinate))
boxes.append(tuple(ordinates))
return boxes

@_assignContext
def extents(self):
Expand Down Expand Up @@ -1252,13 +1250,17 @@ def vocab2row(vocab, default=None):

writer.writerows(iter_element_values(11, 'Keywords', metadata.keywords))

row = metadata.bbox
row = metadata.bboxes
boxes = []
if row and not isinstance(row, Exception):
row = [['West', metadata.bbox[0]],
['South', metadata.bbox[1]],
['East', metadata.bbox[2]],
['North', metadata.bbox[3]]]
writer.writerows(iter_element_values(12, 'Geographic extent', row))
for box in row:
boxes.extend(
[['West', box[0]],
['South', box[1]],
['East', box[2]],
['North', box[3]]]
)
writer.writerows(iter_element_values(12, 'Geographic extent', boxes))

row = metadata.extents
if row and not isinstance(row, Exception):
Expand All @@ -1270,10 +1272,10 @@ def vocab2row(vocab, default=None):
row = metadata.reference_system
if row and not isinstance(row, Exception):
tmp = []
for key in ('identifier', 'source', 'url', 'name', 'scope'):
if not row[key]:
for key in ('identifier', 'name', 'type', 'scope'):
if not hasattr(row, key):
continue
tmp.append([key.capitalize(), row[key]])
tmp.append([key.capitalize(), getattr(row, key)])
row = tmp
writer.writerows(iter_element_values(15, 'Spatial reference system', row))

Expand Down
61 changes: 32 additions & 29 deletions python/medin/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def verify(self):
errors.append('The start date cannot be greater than the end date')

try:
self.getBBOX()
self.getBoxes()
except QueryError, e:
errors.append(str(e))

Expand Down Expand Up @@ -201,41 +201,44 @@ def asDate(self, key, cast, default, is_start):
if cast: return dt
return date

def getBBOX(self, cast=True, default=''):
def getBoxes(self, cast=True, default=''):
try:
bbox = self['bbox'][0]
bboxes = self['bbox']
except KeyError, AttributeError:
return default

if not cast:
return bbox

bbox = bbox.split(',', 3)
if not len(bbox) == 4:
if self.raise_errors:
raise QueryError('The bounding box must be in the format minx,miny,maxx,maxy')
return default
try:
bbox = [float(n) for n in bbox]
except ValueError:
if self.raise_errors:
raise QueryError('The bounding box must consist of numbers')
return default
return bboxes

boxes = []
for bbox in bboxes:
bbox = bbox.split(',', 3)
if not len(bbox) == 4:
if self.raise_errors:
raise QueryError('The bounding box must be in the format minx,miny,maxx,maxy')
return default
try:
bbox = [float(n) for n in bbox]
except ValueError:
if self.raise_errors:
raise QueryError('The bounding box must consist of numbers')
return default

if bbox[0] > bbox[2]:
if self.raise_errors:
raise QueryError('The bounding box east value is less than the west')
return default
if bbox[0] > bbox[2]:
if self.raise_errors:
raise QueryError('The bounding box east value is less than the west')
return default

if bbox[1] > bbox[3]:
if self.raise_errors:
raise QueryError('The bounding box north value is less than the south')
return default
if bbox[1] > bbox[3]:
if self.raise_errors:
raise QueryError('The bounding box north value is less than the south')
return default

return tuple(bbox)
boxes.append(tuple(bbox))
return boxes

def setBBOX(self, bbox):
self['bbox'] = ','.join((str(i) for i in box))
def setBoxes(self, bboxes):
self['bbox'] = [','.join((str(i) for i in box)) for box in bboxes]

def getSort(self, cast=True, default=''):
try:
Expand Down Expand Up @@ -376,8 +379,8 @@ def asDict(self, verify=True):
a['dates'] = dates

# add the area
bbox = self.getBBOX(default=False)
a['bbox'] = bbox
bboxes = self.getBoxes(default=False)
a['bbox'] = bboxes
a['area'] = self.getArea(default=None)

return a
Expand Down
59 changes: 33 additions & 26 deletions python/medin/spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,40 +198,47 @@ def tilecache (environ, start_response):

return wsgiHandler(environ, start_response, _tilecache_service)

def metadata_image(bbox, mapfile):
def metadata_image(bboxes, mapfile):
"""Create a metadata image"""

from json import dumps as tojson
import mapnik

minx, miny, maxx, maxy = bbox

# create the bounding box as a json string
width, height = (maxx - minx, maxy - miny)
min_dim = 0.0125 # minimum dimension for display as a rectangle (in degrees)
if width < min_dim or height < min_dim: # it should be a point
feature = { "type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [minx, miny]
},
"properties": {
"type": "point"
features = []

for bbox in bboxes:
minx, miny, maxx, maxy = bbox

# create the bounding box as a json string
width, height = (maxx - minx, maxy - miny)
min_dim = 0.0125 # minimum dimension for display as a rectangle (in degrees)
if width < min_dim or height < min_dim: # it should be a point
feature = { "type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [minx, miny]
},
"properties": {
"type": "point"
}
}
}
width, height = (9, 9)
else:
feature = { "type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[minx, miny], [maxx, miny], [maxx, maxy], [minx, maxy], [minx, miny]]]
},
"properties": {
"type": "bbox"
width, height = (9, 9)
else:
feature = { "type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[minx, miny], [maxx, miny], [maxx, maxy], [minx, maxy], [minx, miny]]]
},
"properties": {
"type": "bbox"
}
}
}
features.append(feature)

json = tojson(feature)
json = tojson({
"type": "FeatureCollection",
"features": features
})

# instantiate the map
m = mapnik.Map(250, 250)
Expand Down
Loading

0 comments on commit d5f4a34

Please sign in to comment.