Creating your first Madrona project

These instructions will walk you through developing a basic implementation of Madrona. This includes installing the dependencies, setting up a sample app, testing that everything installed smoothly, then doing some simple customization. By the end you’ll have a basic Madrona application and a sense how to customize it for your needs.

Overview: example Madrona project

In this example, we’ll set up a new Madrona instance for the state of Oregon.

We will set up the following example feature types:

  • Areas of Interest (polygons)
  • Collections of features (folders)

as well as show you how to tweak certain aspects of the application such as:

  • Custom reports
  • Visual styling
  • Validation and manipulation of user-drawn geometries
  • Basemaps and KML datasets

Install Dependencies

You will need to install madrona’s dependencies and madrona itself. For detailed instructions, please follow the Installation guide.


If you are using the Madrona Virtual Machine, all of the necessary software is pre-installed.


Next we’ll create a new postgis-enabled database for this project and use django’s syncdb command to create the necessary tables. Assuming you installed postgis according to the installation instructions, this is as simple as:

sudo su postgres
createdb example -U postgres   # you may want to use a different database user depending on your postgres configuration


Before you begin, you’ll need to know the hostname or IP Address and port at which your server will be accessible.


In the examples below, you must replace <HOST_OR_IP_ADDRESS> and <PORT> with values appropriate for your networking setup.

Create new madrona project

In order to jumpstart the coding process, Madrona provides a script,, which auto-generates the boring parts of the codebase for you. First we’ll need to decide on some basic parameters of our project.

Our study region will be defined (roughly) by the following polygon in WKT format:

SRID=4326;POLYGON ((-125.0 41.8, -125.0 46.4, -116.4 46.4, -116.4 41.8, -125.0 41.8))

Choose a directory to store your project and run the script:

cd /usr/local/userapps \
    --project "Example" \
    --app example \
    --domain "<HOST_OR_IP_ADDRESS>:<PORT>" \
    --connection "dbname='example' user='postgres'" \
    --studyregion "SRID=4326;POLYGON ((-125.0 41.8, -125.0 46.4, -116.4 46.4, -116.4 41.8, -125.0 41.8))" \
    --aoi "My Areas of Interest"  \
    --folder "Collection of Features"  \
    --kml "Global Marine|"

Now enter the project directory where the new code lives:

cd exampleproject/exampleproject/

Finally, create a superuser account to manage the site:

python createsuperuser --username=madrona
# You'll be prompted to enter a password twice

Examining Features

First, we want to examine the Features models. Open example/ and find the definition for the AOI Feature:

class MyAreasOfInterest(PolygonFeature):
    description = models.TextField(null=True, blank=True)
    class Options:
        form = 'example.forms.MyAreasOfInterestForm'

This defines the MyAreasOfInterest polygon feature, adds a description attribute and defines the form used to edit it.


The python convention is to name your model classes using CapsCase. Whatever you do, don’t use underscores _ in the feature class name.

Next, open the file containing the forms called example/

from models import MyAreasOfInterest
class MyAreasOfInterestForm(SpatialFeatureForm):
    class Meta(SpatialFeatureForm.Meta):
        model = MyAreasOfInterest

Here we define the form or editing interface for our feature. Notice that the classes are inherited from the SpatialFeatureForm class. Right now, we are just accepting the defaults and linking it to our MyAreasOfInterest model.


Similarly, we could group features into collections. We can inherit from the base FeatureCollection and specify the mandatory valid_children Option to configure which feature types can be placed in a folder.

Again, from examples/

class CollectionOfFeatures(FeatureCollection):
    description = models.TextField(null=True, blank=True)
    class Options:
        form = 'example.forms.CollectionOfFeaturesForm'
        valid_children = (

Here, we’ve specified that CollectionOfFeatures can contain our AOI feature type as well as other collections (nested). This is all that is necessary to begin managing features and organizing them into Collections. With a few lines of code, we’ve defined the features and aspects of our application’s behavior. One of our goals is to make the customization and configuration of madrona as easy as possible - and its almost entirely driven by the model configurations you see above. There are few other things you need to do in order to get a fully functional application.

Trying it out

Now that we’ve examined the models and forms, we can run the development server to confirm everything is running: runserver<PORT>

And visit your web server at http://<HOST_OR_IP_ADDRESS>:<PORT> and explore.


Customizing the Madrona project

While the above shows the basic layout of a simple madrona app, we’ll now dive in and begin customizing it.

Verbose name

The first obvious step is to change how our feature name appears on screen. We can supply custom text using the verbose_name option:

class MyAreasOfInterest(PolygonFeature):
    description = models.TextField(null=True, blank=True)
    class Options:
        form = 'example.forms.MyAreasOfInterestForm'
        verbose_name = 'Areas that interest me'

For other options, see the Feature options docs.

Generating custom reports

The default template for the AOI feature just prints out some basic details. In order to customize it, open templates/myareasofinterest/show.html. There you will see a django html template responsible for creating the attributes page.

Each feature get’s passed to the template as the variable instance. Any attributes, properties and methods that exist on the feature model instances can be accessed via template variables. For example, to get the polygon area, you could use the following template substitution:


You could also define a custom property on your AOI model...:

class MyAreasOfInterest(PolygonFeature):
    description = models.TextField(null=True, blank=True)
    class Options:
        form = 'example.forms.MyAreasOfInterestForm'
        verbose_name = 'Areas that interest me'

    def acres(self):
        area_meters = self.geometry_final.area
        conversion = 0.000247105381
        area_acres = area_meters * conversion
        return area_acres

... and use the following in your template:

<p>Acreage is {{instance.acres}}</p>

The reporting can get as detailed and complex as your needs require and can leverage GeoDjango geometry operations as well as any spatial analysis supported by Python.

About page

Open the templates/news/about.html page. The default landing page is just a placeholder; here you can put any html description of your project or high-level documentation that you want the user to see when they first view the site:

{% load appname %}
<h1> About {% appname %}</h1>
<p> This app exists as an example to highlight some of the functionality
of the Madrona framework. </p>

Customizing KML styling

Every feature class has a default kml and kml_style properties which defines the KML representation of that feature. If you want to customize the look and behavior of your map features, you can override the kml property in your feature model. In example/

class MyAreasOfInterest(PolygonFeature):
    description = models.TextField(null=True, blank=True)
    class Options:
        form = 'example.forms.MyAreasOfInterestForm'
        verbose_name = 'Areas that interest me'

    def kml(self):
        return """
        <Placemark id="%s">
                <Data name="name"><value>%s</value></Data>
                <Data name="dsc"><value>%s</value></Data>
                <Data name="acres"><value>%s</value></Data>
        """ % (self.uid,
  , self.date_modified, self.acres

    def kml_style(self):
        return """
        <Style id="%s-default">
        """ % self.model_uid()

The above will give us KML placemarks with a different popup balloon (showing Name, Description and Acres) and change polygon styling to green fill.

Ultimately, whatever you can do with KML , you can do with your feature’s KML representation. However, there are some important guidelines to follow; For more information, see the kmlapp documentation.

Group Collaboration

Madrona provides a robust mechanism for sharing features between users. By default, all features you create under a single account are accessible by that user alone. But users can be made members of groups and can choose to share features with group members who can then view them, copy them, share them back with revisions, etc. This allows for truely collaborative multi-user workflows while maintaining privacy of data.

The first step is to use Django’s admin site to create users and groups.

  1. Navigate to http://<HOST_OR_IP_ADDRESS>:<PORT>/admin/auth/ and click + Add next to Groups. Give the group a name, “My Group”, and add the “madrona” user to it, and click Save:
  1. Of course you, the “madrona” user, are the only member of this group at the moment! Go back to http://<HOST_OR_IP_ADDRESS>:<PORT>/admin/auth/ and click + Add next to Users and follow the instructions on-screen to create another user and add them to the My Group group.

  2. Finally, at the command line prompt, enable sharing for “My Group” by this command:

    python enable_sharing --all

Back in the application, you should now be able to share features with other users through the Edit > Share menu item and view shapes that others have shared in the Shared with Me tab.

Handling geometries through manipulators

The manipulators app provides ways to validate user-drawn geometries and make sure they conform to rules that you define. For example, you may want to limit user-drawn shapes to be within the study region. For this case, there is a built in manipulator called ClipToStudyRegion which will, as the name suggests, clip a user-drawn shape to the coundary of the study region:

class MyAreasOfInterest(PolygonFeature):
    description = models.TextField(null=True, blank=True)
    class Options:
        form = 'example.forms.MyAreasOfInterestForm'
    verbose_name = 'Areas that interest me'
    manipulators = [ 'madrona.manipulators.manipulators.ClipToStudyRegion' ]

You can also choose from several other built-in manipulators, define custom manipulators or make them optional. For more information, see the manipulators documentation.

Managing basemaps and KML datasets

Base data layers are managed using a single KML file called the public layers list. If you defined KML layers when setting up your initial app, the layers list will be available at http://<HOST_OR_IP_ADDRESS>:<PORT>/layers/public/. Download that file, save as public.kml, and open for editing. You’ll see that it is a standard KML file with `NetworkLink`s to the base data layers. We can modify it by adding another KML NetworkLink

<NetworkLink id="global-marine">
    <name>Global Marine</name>

Once we’ve modified the public kml, browse to admin interface at http://<HOST_OR_IP_ADDRESS>:<PORT>/admin/layers/publiclayerlist/add/ and use it to upload the new KML file. After refreshing your browser cache, you should see the new KML avaible in the layers panel.

For more information, see the layers documentation.

Next Steps

See the documentation in the following sections to customize Madrona as needed:

The setup this guide has walked through only specifies how to run the django development server. To setup a public facing website using Apache, consult the Deployment notes.