The madrona.features app works along with other Madrona apps to create a system that is best described as a content management system for user-designed spatial features. This system can be configured quickly to support a range of management options such as marine protected areas, hydrokinetic generators, wind turbines, or undersea cables. With one model definition it is possible to define the data schema for storing these features, as well as what behaviors and actions they support within the Madrona interface. A high degree of customization can be achieved with a Madrona project with this declarative format, without having to customize the view or client-side javascript components.
Feature Classes can be configured to:
- Represent various management scenarios as Point, LineString or Polygon data
- Collect attributes from users using forms generated from the model definition (or customized forms)
- Pre-process and validate user-defined geometries with Manipulators
- Enable sharing of features among users
- Add custom downloads, like Shapefile or Excel files
- Support custom editing actions
In addition, FeatureCollection Classes can be created that are collections of Feature Class instances with their own attributes. These can be used to represent simple Folders to help users organize their designs, or represent management concepts such as Marine Protected Area Networks.
Lets walk through the basics of creating a simple Feature Class. The basic process involves:
- Defining a subclass of PointFeature, LineStringFeature, PolygonFeature, including the attributes to be stored with it.
- Creating an Options inner-class, and using it to specify a form to use when creating or editing this Feature Class.
- Creating kml and kml_style properties that define its KML representation
- Specifying links to downloads or services related to the Feature Class.
- Specifying any optional parameters on the Options inner-class
- Creating a template to use when displaying this Feature Class’ attributes
Look at this example:
from madrona.features import register
from madrona.features.models import PolygonFeature
from madrona.features.forms import FeatureForm
@register
class Mpa(PolygonFeature):
ext = models.CharField(max_length="12")
class Options:
verbose_name = 'Marine Protected Area'
form = 'myproject.forms.MpaForm'
links = (
alternate('Shapefile',
'mlpa.views.shapefile',
select='single',
type='application/shapefile'),
alternate('KMZ (Google Earth)',
'mlpa.views.kml_export',
select='single multiple',
type='application/vnd.google-earth.kmz',
generic=True),
related('MPA Spreadsheet',
'mlpa.views.spreadsheet',
select='single',
type='application/excel'),
edit('Delete w/Grids',
'mlpa.views.delete_w_grids',
confirm="Are you sure you want to delete with grids?",
select="single multiple",
args=[MpaArray],
kwargs={'keyword_argument': True}),
edit_form('Tags',
'mlpa.views.tag',
select='single multiple',
generic=True,
models=(MpaArray, MlpaMpa)),
)
class MpaForm(FeatureForm):
class Meta:
model = Mpa
Must be a subclass of one of the Feature subclasses (PointFeature, PolygonFeature, LineStringFeature)
Note
Keep the model name to under 30 characters in length. When creating a Feature model, django will automatically add permissions with a verbose name (e.g. “Can share Your Model Name”) which must be < 50 chars. Keeping the model name to around 30 chars or less will prevent SQL errors when creating the model.
Note
When creating a model for your unit tests, define the model outside of your TestCase class. Otherwise the feature class gets destroyed when the test finishes but it is never ‘unregistered’ which can lead to very difficult SQL/ORM debugging problems later in the tests.
All Feature Classes must have an Options inner-class that contains a property specifying the FeatureForm subclass that can be used to edit it. All other properties on the Options inner-class are optional.
The show template is used to render sidebar content for a feature within the Madrona interface, and can also be used to render a printable and bookmarkable page for it. This template can be placed in any template directory by default under {{slug}}/show.html. Subdirectories are used to allow for overriding templates as mentioned in the django documentation. The default path to the show template can be changed using an optional show_template parameter to the Options inner-class.
Templates will be rendered with the following context:
- instance - the feature class instance being being displayed
You can add to this list using the show_context Option property.
There are three primary visual representations of Features: KML, the KMLTree and static maps. While the base Feature classes define reasonable default styling, it is likely that you’ll need to customize the look and feel for your implementation.
You can use css to style the representation of the Features in the KMLTree, specifically the small icon to the left of the Feature name. There are two mechanisms to do this.
First you can specify an icon_url in the Options. This can be an absolute http URL or relative to the MEDIA_ROOT directory.
If you need more control over styling icons (such as specifying multiple styles or using scrolling sprites) you can use raw css by overriding the Feature.css() classmethod. For example:
@classmethod
def css(klass):
return """
li.KmlDocument > .icon {
background: url('%(media)s/sprites/kml.png?1302821411') no-repeat -566px 0px ! important;
}
li.%(uid)s > .icon {
background: url('%(media)s/sprites/kml.png?1302821411') no-repeat 0px 0px ! important;
}
""" % { 'uid': klass.model_uid(), 'media': settings.MEDIA_URL }
Some default copying behavior is provided with the built-in feature.copy method. Unless you want to reimplement/change all that logic (and maybe your application requires it) you can call the Super() function and just override the necessary bits:
@register
class Folder(FeatureCollection):
def copy(self, user):
copy = super(Folder, self).copy(user)
copy.name = copy.name.replace(' (copy)', '-Copy')
copy.save()
return copy
You must specify a list of required manipulators; if no manipulators are required simply pass an empty list []. Optional manipulators can be specified as well:
@register
class TestMpa(PolygonFeature):
...
class Options:
manipulators = [ 'madrona.manipulators.tests.TestManipulator' ]
optional_manipulators = [ 'madrona.manipulators.manipulators.ClipToGraticuleManipulator' ]
Model used for representing user-generated features
Attribute | Description |
---|---|
user | Creator |
name | Name of the object |
date_created | When it was created |
date_modified | When it was last updated. |
Add feature to specified FeatureCollection
Returns a copy of this feature, setting the user to the specified owner. Copies many-to-many relations
Specifies the CSS for representing features in kmltree, specifically the icon Works one of two ways: 1. Use the icon_url Option and this default css() classmethod 2. Override the css() classmethod for more complex cases
Returns model class Options object
For caching. This string represents a hash of all attributes that may influence reporting results. i.e. if this property changes, reports for the feature get rerun.
Is this feauture viewable by the specified user? Either needs to own it or have it shared with them. returns : Viewable(boolean), HttpResponse
A safety valve for kmlapp... If one feature’s .kml property fails, it won’t bring down the entire request. This property is never to be overridden!
class method providing the uid for the model class.
Remove feature from FeatureCollection
Share this feature with the specified group/groups. Owner must be a member of the group/groups. Group must have ‘can_share’ permissions else an Exception is raised
Unique identifier for this feature.
Abstract Model used for representing user-generated geometry features. Inherits from Feature and adds geometry-related methods/properties common to all geometry types.
Attribute Description user Creator name Name of the object date_created When it was created date_modified When it was last updated. manipulators List of manipulators to be applied when geom is saved.
This method contains all the logic to determine which manipulators get applied to a feature
Basic KML representation of the feature geometry
Fully-styled KML placemark representation of the feature. The Feature’s kml property MUST
- return a string containing a valid KML placemark element
- the placemark must have id= [the feature’s uid]
- if it references any style URLs, the corresponding Style element(s) must be provided by the feature’s .kml_style property
Must return a string with one or more KML Style elements whose id’s may be referenced by relative URL from within the feature’s .kml string In any given KML document, each unique kml_style string will get included so don’t worry if you have 10 million features with “blah-default” style... only one will appear in the final document and all the placemarks can refer to it. BEST TO TREAT THIS LIKE A CLASS METHOD - no instance specific vars.
Mapnik style object containing rules for symbolizing features in staticmap
Model used for representing user-generated point features. Inherits from SpatialFeature.
Attribute Description user Creator name Name of the object date_created When it was created date_modified When it was last updated. manipulators List of manipulators to be applied when geom is saved. geometry_original Original geometry as input by the user. geometry_final Geometry after manipulators are applied.
Model used for representing user-generated linestring features. Inherits from SpatialFeature.
Attribute Description user Creator name Name of the object date_created When it was created date_modified When it was last updated. manipulators List of manipulators to be applied when geom is saved. geometry_original Original geometry as input by the user. geometry_final Geometry after manipulators are applied.
Model used for representing user-generated polygon features. Inherits from SpatialFeature.
Attribute Description user Creator name Name of the object date_created When it was created date_modified When it was last updated. manipulators List of manipulators to be applied when geom is saved. geometry_original Original geometry as input by the user. geometry_final Geometry after manipulators are applied.
KML geometry representation of the centroid of the polygon
Subclasses of FeatureCollection have a one-to-many relationship with one or more Feature Classes. One could create a Marine Protected Area Network class that can only contain MPAs, or a Folder class that can contain any combination of FeatureClasses or even other Folders and FeatureCollections.
One important note about the sharing behavior of FeatureCollections - If a user shares a collection, all the features/collections contained within it are implicitly shared.
class MPANetwork(FeatureCollection):
class Options:
valid_children = ('mlpa.models.Mpa', )
A Folder/Collection of Features
Adds a specified Feature to the Collection
Returns a copy of this feature collection, setting the user to the specified owner. Recursively copies all children.
Delete all features in the set
Returns a list of Features belonging to the Collection Optionally recurse into all child containers or limit/filter for a list of feature classes
Removes a specified Feature from the Collection
Specifies a ModelForm that will be used to create and edit features of this class. The form must be a subclass of madrona.features.forms.FeatureForm, and the path to the form must be provided as a string. Otherwise you’ll cause circular reference issues.
Provide your feature class with a human readable name to be used within the interface. For example, this name determines the name used in the “Create” menu. If not specified, the CamelCase model name will be used. Even though it is optional, this property is obviously highly recommended.
Provide the path to an icon image to represent the feature, both in the menu and the tree view. Path can be an absolute http URL or relative to the MEDIA_ROOT directory.
By default, will look for the template at {{modelname}}/show.html when rendering shape attributes. For example, the template for a model named MpaArray would be mpaarray/show.html. You can specify a different template location with this option.
Use this option to specify a custom template to be shown when creating or editing a feature. By default, looks for a template under features/form.html.
Specify a base context to use for rendering templates when creating and editing features.
Specify a base context to use when rendering feature attributes.
Enabled by default, set to False to disable copy functionality. Calls the copy method of Feature, which can be overriden by subclasses to customize this functionality.
Defaults to clipping features to the study region. Set to [] to disable.
Optional list of manipulators that users can choose to apply to any digitized features.
Specify links associated a Feature Class that point to related downloads, export tools, and editing actions that can be performed.
Specify if the PNG link will be created for this Feature. Default is False.
Links allow developers to extend the functionality of features by specifying downloads, actions, or related pages that should be made available through the interface. There are 4 types of Links:
- alternate links specify alternative representations of features that should be made available through the Export menu.
- related links specify related downloads or pages that are also made available in the Export menu but in a downloads section.
- edit links specify items that should appear in the Edit menu and modify a selected feature, create a copy, or some other non-idempotent action.
There are several links that are provided by default specific for every feature:
- self links return html attributes
- edit links return html form for editing
- create links return html form for creating new feature
And several generic links that are, by default, applied to all feature types:
- Copy link creates a copy of the feature (see “Implementing A Custom Copy Method” for more)
- Delete links removes the feature permanantly
- Share links get forms for group-based sharing of features
- KML links return KML representation of the feature
- KMZ links return zipped KML (ie KMZ) representation of the feature
- GeoJSON links returns a GeoJSON FeatureCollection of the specified feature(s).
- PNG links return a PNG image of the feature (see staticmap).
Note that GeoJSON and PNG links can be turned off by specifying:
class Options:
export_png = False
export_geojson = False
Here’s an example of custom links in use:
@register
class RenewableEnergySite(Feature):
type = models.CharField(max_length=1, choices=TYPE_CHOICES)
class Options:
verbose_name = 'Renewable Energy Site'
form = 'madrona.features.tests.RenewableEnergySiteForm'
links = (
alternate('Export KML',
'madrona.features.tests.kml',
select='multiple single'
),
related('Viewshed Map',
'madrona.features.tests.viewshed_map',
select='single',
type='image/png'
),
edit('Delete w/related cables',
'madrona.features.tests.delete_w_cables',
select='single multiple',
confirm="""
Are you sure you want to delete this site and associated
undersea cables?
This action cannot be undone.
"""
)
)
Views that are wired up to features using links must accept a second argument named instance or instances depending on whether they can work on single or multiple selected features.
The features app will handle requests
Generic views will handle cases where a user is not authorized to view or edit a feature, requests related to features that cannot be found, and improperly configured views.
Returns True/False depending on whether user can view the link.
Confirmation message to show the user before POSTing to rel=edit link
Set to false for editing links that create a copy of the original. This will allow users who do not own the instance(s) but can view them perform the action.
Extra keyword arguments to pass to the view.
Whether this view can be applied to multiple feature classes.
Allows you to specify groups (a list of group names) that should have access to the link. Default is None; i.e. All users have link access regardless of group membership
For rel=edit links, identifies whether a form should be requested or that url should just be POST’ed to.
List of feature classes that a this view can be applied to, if it is generic.
Type of link - alternate, related, edit, or edit_form.
Determines whether this link accepts requests with single or multiple instances of a feature class. Valid values are “single”, “multiple”, “single multiple”, and “multiple single”.
Part of this link’s path.
Human-readable title for the link to be shown in the user interface.
MIME type of this link, useful for alternate links. May in the future be used to automatically assign an icon in the dropdown Export menu.
View function handling requests to this link.