Ejemplo n.º 1
0
def create_tableau(factory, location):
    """ Create a single tableau for a given location using a given factory.

        This function does the actual work of generating an individual tableau
        based upon the factory definition.  It can be called directly to
        generate a desired tableau, or it can be called indirectly by the
        daemon (using the private 'create_tableau' API call) to generate
        tableaux en masse.

        Upon completion, we return None if the tableau-generation attempt was
        successful.  Otherwise, we return a suitable error message explaining
        why the tableau could not be generated.
    """
    # Get the location's outline.

    try:
        outline = Outline.objects.get(location=location)
    except Outline.DoesNotExist:
        # We can't create a tableau for a location without an outline ->
        # tell the user the bad news.
        return "Location has no outline."

    # Delete the existing tableau with this name, if any.

    Tableau.objects.filter(location=location,
                           name=factory.tableau_name).delete()

    # Create the new Tableau for this location.

    tableau = Tableau()
    tableau.location      = location
    tableau.name          = factory.tableau_name
    tableau.meta_data     = factory.tableau_meta_data
    tableau.bounds_render = factory.tableau_bounds_render
    tableau.save()

    # Create a single view for the tableau, covering the location's entire
    # extent.

    min_long,min_lat,max_long,max_lat = outline.outline.extent

    # Add a margin around the edges so that the location is easier to see.

    width = abs(max_long - min_long)
    height = abs(max_lat - min_lat)

    buffer = 0.02 * (width + height) / 2

    min_long = min_long - buffer
    max_long = max_long + buffer
    min_lat  = min_lat  - buffer
    max_lat  = max_lat  + buffer

    # Ensure the calculated view bounds are allowable.

    if min_lat < MIN_ALLOWED_LATITUDE:
        min_lat = MIN_ALLOWED_LATITUDE
    elif min_lat > MAX_ALLOWED_LATITUDE:
        min_lat = MAX_ALLOWED_LATITUDE

    if max_lat < MIN_ALLOWED_LATITUDE:
        max_lat = MIN_ALLOWED_LATITUDE
    elif max_lat > MAX_ALLOWED_LATITUDE:
        max_lat = MAX_ALLOWED_LATITUDE

    if min_long < MIN_ALLOWED_LONGITUDE:
        min_long = MIN_ALLOWED_LONGITUDE
    elif min_long > MAX_ALLOWED_LONGITUDE:
        min_long = MAX_ALLOWED_LONGITUDE

    if max_long < MIN_ALLOWED_LONGITUDE:
        max_long = MIN_ALLOWED_LONGITUDE
    elif max_long > MAX_ALLOWED_LONGITUDE:
        max_long = MAX_ALLOWED_LONGITUDE

    # Create the view.

    view = TableauView()
    view.tableau       = tableau
    view.ordinal_rank  = 1
    view.name          = location.display_name
    view.min_lat       = "%0.4f" % min_lat
    view.max_lat       = "%0.4f" % max_lat
    view.min_long      = "%0.4f" % min_long
    view.max_long      = "%0.4f" % max_long
    view.meta_data     = factory.view_meta_data
    view.bounds_render = factory.view_bounds_render
    view.save()

    # Add the factory's filter(s) to the tableau's view.

    for factory_filter in factory.tableaufactoryviewfilter_set.all():
        filter = TableauViewFilter()
        filter.tableauView    = view
        filter.type           = factory_filter.filter_type
        filter.value          = factory_filter.filter_value
        filter.meta_data      = factory_filter.filter_meta_data
        filter.draw_bubbles   = factory_filter.filter_draw_bubbles
        filter.polygon_render = factory_filter.filter_polygon_render
        filter.bubble_render  = factory_filter.filter_bubble_render
        filter.save()

    # Calculate the aspect ratio for the tableau's view.

    proj = GlobalMercator()

    min_x,min_y = proj.LatLonToMeters(min_lat, min_long)
    max_x,max_y = proj.LatLonToMeters(max_lat, max_long)

    viewWidth  = max_x - min_x
    viewHeight = max_y - min_y

    if viewWidth == 0: viewWidth = 1.0
    if viewHeight == 0: viewHeight = 1.0

    aspectRatio = viewWidth / viewHeight

    # Create a single layout for the tableau.  The size of the layout is
    # based on the view's aspect ratio.

    layout_width = LAYOUT_MAX_WIDTH
    layout_height = layout_width / aspectRatio

    if layout_height > LAYOUT_MAX_HEIGHT:
        layout_height = LAYOUT_MAX_HEIGHT
        layout_width  = layout_height * aspectRatio

    layout = TableauLayout()
    layout.tableau = tableau
    layout.width   = layout_width
    layout.height  = layout_height
    layout.save()

    # Create a single TableauLayoutViewPosition record, positioning the view
    # within the layout.

    position = TableauLayoutViewPosition()
    position.tableauLayout = layout
    position.tableauView   = view
    position.min_x         = 0
    position.min_y         = 0
    position.max_x         = layout_width
    position.max_y         = layout_height
    position.save()

    # Finally, tell the caller that the tableau was successfully generated.

    return None