Lizard-map models
=================


Workspaces
----------

All map-related work uses workspaces and workspace items.

    >>> from lizard_map.models import Workspace
    >>> from lizard_map.models import WorkspaceItem

A workspace can contain workspace items:

    >>> workspace1 = Workspace()
    >>> workspace1.save()
    >>> workspace1
    <Workspace: ...>
    >>> workspace1.workspace_items.all()
    []
    >>> workspace_item1 = workspace1.workspace_items.create()
    >>> workspace_item2 = workspace1.workspace_items.create()
    >>> workspace1.workspace_items.all()
    [<WorkspaceItem: (1) name= ws=... >, <WorkspaceItem: (2) name= ws=... >]

A workspace item can point to a method that returns a layer:

    >>> workspace_item1.has_adapter()
    False
    >>> workspace_item1.adapter_class = 'todo'
    >>> workspace_item1.has_adapter()
    True

That string that identifies a method is looked up as a so-called entry point:

    >>> import pkg_resources
    >>> list(pkg_resources.iter_entry_points(group='lizard_map.adapter_class'))
    [EntryPoint.parse(...)]
    >>> workspace_item1.adapter
    Traceback (most recent call last):
    ...
    AdapterClassNotFoundError: Entry point for 'todo' not found
    >>> workspace_item1.adapter_class = 'adapter_shapefile'
    >>> workspace_item1.adapter
    <... object at ...>

The layer method probably needs arguments. You can store them as a json string:

    >>> workspace_item1.adapter_layer_json
    ''
    >>> workspace_item1.adapter_layer_arguments
    {}
    >>> workspace_item1.adapter_layer_json = '{"bla": "yes"}'
    >>> workspace_item1.adapter_layer_arguments
    {'bla': 'yes'}

The method (looked up as a setuptools entry point) is called with the
parameters (from json):

    >>> workspace_item1.adapter_layer_json =  ''
    >>> workspace_item1.adapter.layer()
    ([<mapnik._mapnik.Layer object at ...>], {...})

A workspace item always has a name:

    >>> workspace_item1.name
    ''

Collages in workspaces
----------------------
Collages contain user selected locations from workspace items,
called snippets.

To get a list of all collages, make collage and add snippet:

    >>> workspace1.collages.all()
    [...]
    >>> collage = workspace1.collages.create(name='user collage')
    >>> collage
    <...>
    >>> workspace_item = workspace1.workspace_items.create()
    >>> snippet = collage.snippets.create(workspace_item=workspace_item, identifier='abcd')
    >>> snippet
    <...>

Attached geometry
-----------------

There are several items that need geometry.  And it isn't always handy or
possible to add the geometry fields to the original item's model.  So django's
generic relation mechanism is used to hook geometry to any and all.

    >>> from django.contrib.contenttypes.models import ContentType
    >>> from lizard_map.models import AttachedPoint

We'll abuse a workspace to tie a point geometry to it:

    >>> from django.contrib.gis.geos import Point
    >>> workspace1_geometry = AttachedPoint(
    ...     content_object=workspace1,
    ...     point=Point(1, 1))
    >>> workspace1_geometry.save()

Workspace doesn't have any reverse relation defined, so we have to query it by hand:

    >>> workspace_type = ContentType.objects.get_for_model(workspace1)
    >>> AttachedPoint.objects.filter(content_type__pk=workspace_type.id,
    ...                              object_id=workspace1.id)
    [<AttachedPoint: (1.0, 1.0)>]

You *can* define a relation back, though:
http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#reverse-generic-relations

We'll add a second workspace plus geometry:

    >>> workspace2 = Workspace()
    >>> workspace2.save()
    >>> workspace2_geometry = AttachedPoint(
    ...     content_object=workspace2,
    ...     point=Point(2, 2))
    >>> workspace2_geometry.save()

Important for the mapnik integration: getting the sql query to grab all items
of a certain type with attached points:

    >>> AttachedPoint.objects.all()
    [<AttachedPoint: (1.0, 1.0)>, <AttachedPoint: (2.0, 2.0)>]
    >>> AttachedPoint.objects.filter(content_type__pk=workspace_type.id)
    [<AttachedPoint: (1.0, 1.0)>, <AttachedPoint: (2.0, 2.0)>]
    >>> print AttachedPoint.objects.filter(content_type__pk=workspace_type.id).query
    SELECT ...
    >>> #.values
    >>> #.values_list
