Skip to content

GNIP WM1: Loading layers to a map without GetCapabilities requests

mbertrand edited this page Jan 25, 2012 · 2 revisions

GNIP WM1 – Load GeoNode layers onto a map without GetCapabilities requests

Overview

When loading layers on a map, GeoNode should store/retrieve all necessary information in its own Layer model rather than query GeoServer’s WMS GetCapabilities document. When adding GeoNode layers to a map, use Geonetwork’s data search functionality rather than GeoExt’s WMSGetCapabilitiesStore to list available layers.

Proposed by

Matt Bertrand

Assigned to Release

TBD.

State

Implemented in WorldMap; needs to be modified/integrated with more universal ‘Lazy Load’ functionality in gxp code.

Motivation

Relying on WMS GetCapabilities to add or load layers in a map causes performance problems as the number of layers stored in a WMS server increases. Requests for the GetCapabilities document may time out, preventing the map’s layers from being loaded, or may cause ‘Script not responding’ warning messages to display in the browser. Bypassing the need to query the GetCapabilities document would improve performance and scalability.

Proposal

Store all required information in GeoNode’s Layer model

Much of the information required for displaying layers on a map is already stored in GeoNode’s Layer model. The following additional fields should be added to the model:

srs = models.CharField(_('SRS'), max_length=24, blank=True, null=True, default="EPSG:4326")

bbox  = models.TextField(_('bbox'), blank=True, null=True)
llbbox = models.TextField(_('llbbox'), blank=True, null=True)

Alternatively, the pre-existing ‘geographic_bounding_box’ field could be used instead of ‘srs’ and ‘llbox’, though in that case it should probably be removed from the layer’s edit form in GeoNode.

These fields can be populated with calls to GeoServer’s REST API via gsconfig.py:

self.srs = gs_resource.projection

self.bbox = str([float(gs_resource.native_bbox[0]),float(gs_resource.native_bbox[2]),float(gs_resource.native_bbox[1]),float(gs_resource.native_bbox[3])])

self.llbbox = str([float(gs_resource.latlon_bbox[0]),float(gs_resource.latlon_bbox[2]),float(gs_resource.latlon_bbox[1]),float(gs_resource.latlon_bbox[3])])

if self.geographic_bounding_box is '' or self.geographic_bounding_box is None:self.set_bbox(gs_resource.native_bbox, srs=self.srs)

Retrieve all necessary data from the Layer model

GeoNode currently uses a MapLayer.layer_config function for layers that are already saved as part of a map. This function should be modified to retrieve additional fields from the Layer model:

gnLayer = Layer.objects.filter(typename=self.name)
if gnLayer.count() == 1:
   if gnLayer[0].srs: cfg['srs'] = gnLayer[0].srs
   if gnLayer[0].bbox: cfg['bbox'] = simplejson.loads(gnLayer[0].bbox)
   if gnLayer[0].llbbox: cfg['llbbox'] = simplejson.loads(gnLayer[0].llbbox)

A similar layer_config function should be added to the Layer model and called when adding a new GeoNode layer to a map:

def layer_config(self, user):
    cfg = dict()
    cfg['name'] = self.typename
    cfg['title'] =self.title
    cfg['transparent'] = True
    if self.topic_category:
        cfg['group'] = self.topic_category.title
    else:
        cfg['group'] = 'General'
    cfg['url'] = settings.GEOSERVER_BASE_URL + "wms"
    cfg['srs'] = self.srs
    cfg['bbox'] = simplejson.loads(self.bbox)
    cfg['llbbox'] = simplejson.loads(self.llbbox)
    cfg['queryable'] = (self.storeType == 'dataStore'),
    cfg['disabled'] = user and not user.has_perm('maps.view_layer', obj=self)
    cfg['visibility'] = True
    cfg['abstract'] = self.abstract
    cfg['styles'] = ''
    return cfg

Client-side javascript for retrieving layer data from GeoNode

In WorldMap, a new gxp javascript object type was created, ‘GeoNodeSource’, that extends WMSSource. This object calculates the ‘maxExtent’ property from the ‘llbbox’ attribute returned by the Layer or MapLayer layer_config function, and adds ‘tileSize’ and ‘tileOrigin’ properties based on the global extent of the EPSG:900913 projection. One weakness of this approach is that it assumes that WorldMap/GeoNode will always use the 900913 projection for its maps. The code for GeoNodeSource is here:

https://github.com/cga-harvard/gxp/blob/master/src/script/plugins/GeoNodeSource.js

Tim Schaub has been working on an alternative approach, using a ‘Lazy Load’ function that will check to see if all required layer information is already present, and only retrieve the GetCapabilities document if there is anything missing. This lazy load capability could be used for remote WMS layers in addition to local GeoNode layers, and should probably replace/supercede the GeoNodeSource object.

Adding GeoNode layers to a map

Currently, GeoNode uses GeoExt’s WMSGetCapabilitiesStore, which requires a GetCapabilities request, to display a list of available layers to add to a map. This could be replaced with the existing data search interface, which queries Geonetwork for available layers and allows filtering by map extent and keywords:

Layer Search

Some slight modifications are necessary to the interface to add selected layers to the map without reloading the page. A new function in GeoExplorer.js is used to add passed layers to the map via an ajax call to return all the required parameters from Layer.layer_config():

https://github.com/cga-harvard/geonode-client/blob/develop/app/static/script/app/GeoExplorer.js#L1530