Workspace stuff
CUSTOM_LEGENDS = 'custom_legends'
MAP_LOCATION = 'map_location'
MAP_BASE_LAYER = 'map_base_layer' # The selected base layer
CRUMBS_HOMEPAGE = {'name': 'home', 'title': 'hoofdpagina', 'url': '/'}
logger = logging.getLogger(__name__)
Default apps screen, make your own template.
def homepage(request,
template='lizard_map/app_screen.html',
crumbs_prepend=None,
application_screen_slug=None):
Optionally, if application_screen_slug is None, try to fetch GET parameter 'screen' from url.
if crumbs_prepend is not None:
crumbs = list(crumbs_prepend)
else:
crumbs = [CRUMBS_HOMEPAGE]
if application_screen_slug is None:
application_screen_slug = request.GET.get('screen', None)
return render_to_response(
template,
{'javascript_hover_handler': 'popup_hover_handler',
'javascript_click_handler': 'popup_click_handler',
'crumbs': crumbs,
'application_screen_slug': application_screen_slug},
context_instance=RequestContext(request))
Render page with one workspace.
def workspace(request,
workspace_id,
javascript_click_handler='popup_click_handler',
javascript_hover_handler='popup_hover_handler',
template='lizard_map/workspace.html'):
workspaces in dictionary, because of ... ?
workspace = get_object_or_404(Workspace, pk=workspace_id)
return render_to_response(
template,
{'workspaces': {'user': [workspace]},
'javascript_click_handler': javascript_click_handler,
'javascript_hover_handler': javascript_hover_handler,
},
context_instance=RequestContext(request))
@never_cache
reorder workspace items. returns workspace_id
def workspace_item_reorder(request,
workspace_id=None,
template='lizard_map/tag_workspace.html'):
reorders workspace_item[] in new order. expects all workspace_items from workspace
TODO: check permissions
if workspace_id is None:
workspace_id = request.GET['workspace_id']
workspace = get_object_or_404(Workspace, pk=workspace_id)
workspace_items = [
get_object_or_404(WorkspaceItem, pk=workspace_item_id) for
workspace_item_id in request.POST.getlist('workspace-items[]')]
for i, workspace_item in enumerate(workspace_items):
workspace_item.workspace = workspace
workspace_item.index = i * 10
workspace_item.save()
return HttpResponse(json.dumps(workspace.id))
TODO: put item_add and item_edit in 1 function
@never_cache
add new workspace item to workspace. returns rendered workspace
def workspace_item_add(request,
workspace_id=None,
is_temp_workspace=False,
template='lizard_map/tag_workspace.html'):
if workspace_id is None:
workspace_id = request.POST['workspace_id'] workspace = get_object_or_404(Workspace, pk=workspace_id) name = request.POST['name'] adapter_class = request.POST['adapter_class'] adapter_layer_json = request.POST['adapter_layer_json']if is_temp_workspace:
workspace.workspace_items.all().delete() if workspace.workspace_items.count() > 0: max_index = workspace.workspace_items.aggregate(
Max('index'))['index__max'] else: max_index = 10workspace.workspace_items.create(adapter_class=adapter_class,
index=max_index + 10, adapter_layer_json=adapter_layer_json, name=name) return HttpResponse(json.dumps(workspace.id))
@never_cache def workspace_item_empty(request, workspace_id, is_temp_workspace=False, template='lizard_map/tag_workspace.html'): Clear workspace items for given workspace.
workspace = get_object_or_404(Workspace, pk=workspace_id)
workspace.workspace_items.all().delete()
return HttpResponse("")
@never_cache
edits a workspace_item
def workspace_item_edit(request, workspace_item_id=None, visible=None):
if workspace_item_id is None:
workspace_item_id = request.POST['workspace_item_id']
workspace_item = get_object_or_404(WorkspaceItem, pk=workspace_item_id)
if visible is None:
if request.POST['visible']:
visible = request.POST['visible']
if visible:
lookup = {'true': True, 'false': False}
workspace_item.visible = lookup[visible]
workspace_item.save()
return HttpResponse(json.dumps(workspace_item.workspace.id))
@never_cache
Returns extent for the workspace in json.
def workspace_item_extent(request, workspace_item_id=None):
Transform to correct client-side projection, then return coordinates.
workspace_item_id = request.GET['workspace_item_id']
workspace_item = get_object_or_404(WorkspaceItem, pk=workspace_item_id)
extent = workspace_item.adapter.extent()
map_settings = MapSettings()
extent_converted = map_settings.convert_google_extent_map_srs(
extent['east'], extent['north'],
extent['west'], extent['south'])
return HttpResponse(json.dumps(extent_converted))
@never_cache
Edits snippet_group properties using post.
def snippet_group_graph_edit(request, snippet_group_id):
post = request.POST
title = post.get('title', None)
x_label = post.get('x_label', None)
y_label = post.get('y_label', None)
y_min = post.get('y_min', None)
y_max = post.get('y_max', None)
boundary_value = post.get('boundary_value', None)
percentile_value = post.get('percentile_value', None)
aggregation_period = post.get('aggregation_period', None)
restrict_to_month = post.get('restrict_to_month', None)
if restrict_to_month is not None:
try:
restrict_to_month = int(restrict_to_month)
assert restrict_to_month > 0
assert restrict_to_month < 13
except ValueError:
restrict_to_month = None
snippet_group = WorkspaceCollageSnippetGroup.objects.get(
pk=snippet_group_id)
if title is not None:
Empty string is also good! it will force default title.
snippet_group.layout_title = title
if x_label is not None:
snippet_group.layout_x_label = x_label
if y_label is not None:
snippet_group.layout_y_label = y_label
snippet_group.restrict_to_month = restrict_to_month
if aggregation_period is not None:
snippet_group.aggregation_period = int(aggregation_period)
try:
snippet_group.layout_y_min = float(y_min)
except (ValueError, TypeError):
snippet_group.layout_y_min = None
try:
snippet_group.layout_y_max = float(y_max)
except (ValueError, TypeError):
snippet_group.layout_y_max = None
try:
snippet_group.boundary_value = float(boundary_value)
except (ValueError, TypeError):
snippet_group.boundary_value = None
try:
snippet_group.percentile_value = float(percentile_value)
except (ValueError, TypeError):
snippet_group.percentile_value = None
snippet_group.save()
return HttpResponse('')
@never_cache
Draws a single image for the snippet_group. There MUST be at
def snippet_group_image(request, snippet_group_id, legend=True):
least 1 snippet in the group."""
snippet_group = WorkspaceCollageSnippetGroup.objects.get(
pk=snippet_group_id) snippets = snippet_group.snippets.all() identifiers = [snippet.identifier for snippet in snippets]
for identifier in identifiers: if not 'layout' in identifier:
identifier['layout'] = {} identifier['layout'][ 'aggregation_period'] = snippet_group.aggregation_period
if legend: for identifier in identifiers: identifier['layout']['legend'] = True
width = request.GET.get('width') height = request.GET.get('height') if width: width = int(width) else:
width = None if height: height = int(height) else:
height = None
using_workspace_item = snippets[0].workspace_item start_date, end_date = current_start_end_dates(request) layout_extra = snippet_group.layout() # Basic extra's, x-min, title, ...
layout_extra.update(slider_layout_extra(request))
return using_workspace_item.adapter.image(identifiers,
start_date, end_date, width, height, layout_extra=layout_extra)
@never_cache def workspace_item_delete(request, object_id=None): delete workspace item from workspace
returns workspace_id
if workspace_item_id is not provided, it tries to get the variable
workspace_item_id from the request.POST
if object_id is None:
object_id = request.POST['object_id'] workspace_item = get_object_or_404(WorkspaceItem, pk=object_id) workspace_id = workspace_item.workspace.id workspace_item.delete()return HttpResponse(json.dumps(workspace_id))
@never_cache def session_workspace_edit_item(request,
workspace_item_id=None, workspace_category='user'): edits workspace item, the function automatically finds best
appropriate workspace
if workspace_item_id is None, a new workspace_item will be created
using workspace_item_add TODO if workspace_item_id is filled in,
apply edits and save
workspace_id = request.session['workspaces'][workspace_category][0]
is_temp_workspace = workspace_category == 'temp'
if workspace_item_id is None:
return workspace_item_add(request, workspace_id, is_temp_workspace=is_temp_workspace)
return
@never_cache def session_workspace_extent(request, workspace_category='user'): Returns extent for the workspace in json.
if 'workspaces' in request.session: workspace_id = request.session['workspaces'][workspace_category][0] workspace = get_object_or_404(Workspace, pk=workspace_id) return HttpResponse(json.dumps(workspace.extent())) else: return HttpResponse(json.dumps(''))
def popup_json(found, popup_id=None, hide_add_snippet=False, request=None): Return html with info on list of 'found' objects.
Optionally give pagenumber (starts at 0). If omitted, just join
everything.
found: list of dictionaries {'distance': ..., 'timeserie': ...,
'workspace_item': ..., 'identifier': ...}.
Note: identifier must be a dict. {'id': the_real_id}.
Result format (used by the javascript popup function):
result = {'id': popup_id,
'x': x_found,
'y': y_found,
'html': result_html,
'big': big_popup,
}
html = {} x_found = None y_found = None
display_groups = {} display_group_order = [] for display_object in found:
workspace_item = display_object['workspace_item'] if workspace_item.id not in display_groups: display_groups[workspace_item.id] = [] display_groups[workspace_item.id].append(display_object) if workspace_item.id not in display_group_order: display_group_order.append(workspace_item.id)if len(display_groups) > 1: big_popup = True else: big_popup = False
non_user_workspace_item_ids = [] if request is not None: workspace_manager = WorkspaceManager(request) workspace_manager.load_workspaces() workspace_ids = dict([(ws.id, None) for ws in
workspace_manager.workspaces['user']])Add workspace_items of items not in workspace.
non_user_workspace_item_ids = [] for f in found: ws_item = f['workspace_item'] if ws_item.workspace.id not in workspace_ids: non_user_workspace_item_ids.append(ws_item.id)
for workspace_item_id, display_group in display_groups.items():
workspace_item = display_group[0]['workspace_item']
if (hide_add_snippet or (workspace_item_id in non_user_workspace_item_ids)):
add_snippet = False else: add_snippet = True
identifiers = [display_object['identifier'] for display_object in display_group]
html_per_workspace_item = workspace_item.adapter.html( identifiers=identifiers, layout_options={'add_snippet': add_snippet, 'legend': True}, )
if 'google_coords' in display_object: x_found, y_found = display_object['google_coords'] html[workspace_item.id] = html_per_workspace_item
result_html = [html[index] for index in display_group_order][:3]
if popup_id is None: popup_id = 'popup-id' result = {'id': popup_id, 'x': x_found, 'y': y_found, 'html': result_html, 'big': big_popup, } return HttpResponse(json.dumps(result))
def popup_collage_json(collage, popup_id, request=None):
display collage by adding display_groups together
TODO: see popup_json
html = [] snippet_groups = collage.snippet_groups.all() if len(snippet_groups) > 1:
big_popup = True else: big_popup = False google_x, google_y = None, Nonefor snippet_group in snippet_groups: snippets = snippet_group.snippets.all() if snippets:
# Pick workspace_item of first snippet to build html workspace_item = snippets[0].workspace_item html_per_workspace_item = workspace_item.adapter.html( snippet_group=snippet_group, layout_options={'legend': True}) html.append(html_per_workspace_item)
if 'google_coords' in snippets[0].location: google_x, google_y = snippets[0].location['google_coords']
result = {'id': popup_id, 'x': google_x, 'y': google_y, 'html': html, 'big': big_popup, } return HttpResponse(json.dumps(result))
def collage(request, collage_id, editable=False, template='lizard_map/collage.html'): Render page with one collage
show_table = request.GET.get('show_table', False)
collage = get_object_or_404(WorkspaceCollage, pk=collage_id)
return render_to_response(
template,
{'collage': collage,
'editable': editable,
'request': request,
'show_table': show_table},
context_instance=RequestContext(request))
@never_cache
finds session user workspace and add snippet to (only) corresponding
def session_collage_snippet_add(request,
workspace_item_id=None,
workspace_item_location_identifier=None,
workspace_item_location_shortname=None,
workspace_item_location_name=None,
workspace_collage_id=None,
workspace_category='user'):
collage
if workspace_item_id is None:
workspace_item_id = request.POST.get('workspace_item_id')
if workspace_item_location_identifier is None:
workspace_item_location_identifier = request.POST.get(
'workspace_item_location_identifier')
if workspace_item_location_shortname is None:
workspace_item_location_shortname = request.POST.get(
'workspace_item_location_shortname')
if workspace_item_location_name is None:
workspace_item_location_name = request.POST.get(
'workspace_item_location_name')
workspace_id = request.session['workspaces'][workspace_category][0]
workspace = Workspace.objects.get(pk=workspace_id)
workspace_item = WorkspaceItem.objects.get(pk=workspace_item_id)
if len(workspace.collages.all()) == 0:
workspace.collages.create()
collage = workspace.collages.all()[0]
Shorten name to 80 characters
workspace_item_location_shortname = short_string(
workspace_item_location_shortname, 80)
workspace_item_location_name = short_string(
workspace_item_location_name, 80)
create snippet using collage function: also finds/makes snippet group
snippet, _ = collage.get_or_create_snippet(
workspace_item=workspace_item,
identifier_json=workspace_item_location_identifier,
shortname=workspace_item_location_shortname,
name=workspace_item_location_name)
return HttpResponse(json.dumps(workspace_id))
removes snippet
def session_collage_snippet_delete(request,
object_id=None):
TODO: check permission of collage, workspace owners
if object_id is None:
object_id = request.POST.get('object_id')
snippet = get_object_or_404(WorkspaceCollageSnippet, pk=object_id)
snippet_group = snippet.snippet_group
snippet.delete()
return HttpResponse()
@never_cache
get snippet/fews location by snippet_id and return data
def snippet_popup(request, snippet_id=None):
if snippet_id is None:
snippet_id = request.GET.get('snippet_id')
snippet = get_object_or_404(WorkspaceCollageSnippet, pk=snippet_id)
popup_id = 'popup-snippet-%s' % snippet_id
return popup_json([snippet.location, ],
popup_id=popup_id,
request=request,
hide_add_snippet=True)
@never_cache
Render page with one collage in popup format
def collage_popup(request,
collage_id=None,
template='lizard_map/collage.html'):
collage_id in GET parameter.
if collage_id is None:
collage_id = request.GET.get('collage_id')
collage = get_object_or_404(WorkspaceCollage, pk=collage_id)
popup_id = 'popup-collage'
Only one collage popup allowed, also check jquery.workspace.js
return popup_collage_json(
collage,
popup_id=popup_id,
request=request)
@never_cache
Clear collage for given collage_id.
def collage_empty(request):
collage_id = request.POST.get('collage_id')
collage = get_object_or_404(WorkspaceCollage, pk=collage_id)
collage.delete()
return HttpResponse("")
@never_cache
Shows image corresponding to workspace item and location identifier(s)
def workspace_item_image(request, workspace_item_id):
identifier_list
identifier_json_list = request.GET.getlist('identifier')
identifier_list = [json.loads(identifier_json) for identifier_json in
identifier_json_list]
width = request.GET.get('width')
height = request.GET.get('height')
if width:
width = int(width)
else:
We want None, not u''.
width = None
if height:
height = int(height)
else:
We want None, not u''.
height = None
workspace_item = get_object_or_404(WorkspaceItem, pk=workspace_item_id)
start_date, end_date = current_start_end_dates(request)
add animation slider position
layout_extra = slider_layout_extra(request)
return workspace_item.adapter.image(identifier_list, start_date, end_date,
width, height,
layout_extra=layout_extra)
Edits snippet layout properties.
def snippet_edit(request, snippet_id):
Updates a session legend.
def legend_edit(request):
POST parameters: name min_value (optional) max_value (optional) steps (optional) min_color (optional): format ... max_color (optional) too_low_color (optional) too_high_color (optional)
request['session']['custom_legends'][
Get new legend from post parameters.
options = ['min_value', 'max_value', 'steps',
'min_color', 'max_color', 'too_low_color',
'too_high_color']
name = request.POST['name']
new_legend = {}
for option in options:
value = request.POST.get(option, None)
if value:
new_legend[option] = value
Update session data with new obtained legend.
custom_legends = request.session.get(CUSTOM_LEGENDS, {})
custom_legends[name] = new_legend
request.session[CUSTOM_LEGENDS] = custom_legends
return HttpResponse('')
Map stuff
Return PNG as WMS service.
def wms(request, workspace_id):
workspace = get_object_or_404(Workspace, pk=workspace_id)
width = int(request.GET.get('WIDTH')) height = int(request.GET.get('HEIGHT')) layers = request.GET.get('LAYERS') layers = [layer.strip() for layer in layers.split(',')] bbox = request.GET.get('BBOX') bbox = tuple([float(i.strip()) for i in bbox.split(',')]) srs = request.GET.get('SRS')
mapnik_map = mapnik.Map(width, height)
mapnik_map.srs = coordinates.srs_to_mapnik_projection[srs] mapnik_map.background = mapnik.Color('transparent')
workspace_items = workspace.workspace_items.filter(visible=True).reverse() for workspace_item in workspace_items:
logger.debug("Drawing layer for %s..." % workspace_item) layers, styles = workspace_item.adapter.layer(layer_ids=layers, request=request) layers.reverse() # first item should be drawn on top (=last) for layer in layers: mapnik_map.layers.append(layer) for name in styles: mapnik_map.append_style(name, styles[name])mapnik_map.zoom_to_box(layer.envelope())
logger.debug("Zooming to box...") mapnik_map.zoom_to_box(mapnik.Envelope(*bbox))
img = mapnik.Image(width, height) logger.debug("Rendering map...") mapnik.render(mapnik_map, img) http_user_agent = request.META.get('HTTP_USER_AGENT', '')
logger.debug("Converting image to rgba...") rgba_image = Image.fromstring('RGBA', (width, height), img.tostring()) buf = StringIO.StringIO() if 'MSIE 6.0' in http_user_agent: imgPIL = rgba_image.convert('P') imgPIL.save(buf, 'png', transparency=0) else: rgba_image.save(buf, 'png') buf.seek(0) response = HttpResponse(buf.read()) response['Content-type'] = 'image/png' return response
def search_name(request): Search for objects near GET x,y,radius then return
name.
Optional GET parameter srs, if omitted, assume google.
Optional GET parameter user_workspace_id: a workspace that is
currently shown.
workspace_manager = WorkspaceManager(request) workspace_collections = workspace_manager.load_or_create()
x = float(request.GET.get('x')) y = float(request.GET.get('y'))
radius = float(request.GET.get('radius')) srs = request.GET.get('srs') google_x, google_y = coordinates.srs_to_google(srs, x, y)
user_workspace_id = request.GET.get('user_workspace_id', None) if user_workspace_id is not None:
workspace_manager.add_other(user_workspace_id) workspace_collections = workspace_manager.workspacesfound = [] for workspace_collection in workspace_collections.values(): for workspace in workspace_collection:
for workspace_item in workspace.workspace_items.filter( visible=True): search_results = workspace_item.adapter.search( google_x, google_y, radius=radius) found += search_results if found:
found
is a list of dicts {'distance': ..., 'timeserie': ...}.found.sort(key=lambda item: item['distance']) result = {} result['name'] = found[0]['name']
x, y = coordinates.google_to_srs(google_x, google_y, srs)
result['x'] = x
result['y'] = y
For the x/y we use the original x/y value to position the popup to
the lower right of the cursor to prevent click propagation problems.
result['x'] = x + (radius / 10) result['y'] = y - (radius / 10) return HttpResponse(json.dumps(result)) else: return popup_json([])
def search_coordinates(request): searches for objects near GET x,y,radius returns json_popup of
results.
Optional GET parameter srs, if omitted, assume google.
Optional GET parameter user_workspace_id: a workspace that is
currently shown.
workspace_manager = WorkspaceManager(request) workspace_collections = workspace_manager.load_or_create()
x = float(request.GET.get('x')) y = float(request.GET.get('y'))
radius = float(request.GET.get('radius')) radius_search = radius if 'HTTP_USER_AGENT' in request.META:
analyzed_user_agent = analyze_http_user_agent( request.META['HTTP_USER_AGENT'])It's more difficult to point with your finger than with the mouse.
if analyzed_user_agent['device'] == 'iPad': radius_search = radius_search * 3 srs = request.GET.get('srs') google_x, google_y = coordinates.srs_to_google(srs, x, y)
user_workspace_id = request.GET.get('user_workspace_id', None) if user_workspace_id is not None: workspace_manager.add_other(user_workspace_id) workspace_collections = workspace_manager.workspaces
found = [] for workspace_collection in workspace_collections.values(): for workspace in workspace_collection: for workspace_item in workspace.workspace_items.filter(
visible=True): search_results = workspace_item.adapter.search( google_x, google_y, radius=radius_search) found += search_results if found:
found
is a list of dicts {'distance': ..., 'timeserie': ...}.found.sort(key=lambda item: item['distance']) return popup_json(found, request=request) else: return popup_json([])
Export
def export_snippet_group_csv(request, snippet_group_id):
Creates a table with each location as column. Each row is a datetime.
snippet_group = WorkspaceCollageSnippetGroup.objects.get(
pk=snippet_group_id) start_date, end_date = current_start_end_dates(request) table = snippet_group.values_table(start_date, end_date)response = HttpResponse(mimetype='text/csv') response['Content-Disposition'] = 'attachment; filename=export.csv' writer = csv.writer(response) for row in table: writer.writerow(row) return response
def export_identifier_csv(request, workspace_item_id=None, identifier_json=None):
Uses adapter.values to get values. Then return these values in csv format.
if workspace_item_id is None:
workspace_item_id = request.GET.get('workspace_item_id') if identifier_json is None: identifier_json = request.GET.get('identifier_json') workspace_item = WorkspaceItem.objects.get(pk=workspace_item_id) identifier = parse_identifier_json(identifier_json) start_date, end_date = current_start_end_dates(request) adapter = workspace_item.adapter values = adapter.values(identifier, start_date, end_date) filename = '%s.csv' % ( adapter.location(**identifier).get('name', 'export'))
response = HttpResponse(mimetype='text/csv') response['Content-Disposition'] = 'attachment; filename="%s"' % filename writer = csv.writer(response) writer.writerow(['Datum + tijdstip', 'Waarde', 'Eenheid']) for row in values: writer.writerow([row['datetime'], row['value'], row['unit']]) return response
def export_snippet_group_statistics_csv(request, snippet_group_id=None):
Exports snippet_group statistics as csv.
if snippet_group_id is None:
snippet_group_id = request.GET.get('snippet_group_id') snippet_group = WorkspaceCollageSnippetGroup.objects.get( pk=snippet_group_id) start_date, end_date = current_start_end_dates(request) statistics = snippet_group.statistics(start_date, end_date)response = HttpResponse(mimetype='text/csv') response['Content-Disposition'] = ('attachment; '
'filename=export_statistics.csv') writer = csv.writer(response) colnames = ['min', 'max', 'avg'] colnamesdisplay = ['min', 'max', 'avg'] if snippet_group.boundary_value is not None: colnames.append('count_lt') colnames.append('count_gte') colnamesdisplay.append( '# < %s' % snippet_group.boundary_value) colnamesdisplay.append( '# >= %s' % snippet_group.boundary_value) if snippet_group.percentile_value is not None: colnames.append('percentile') colnamesdisplay.append( 'percentile %f' % snippet_group.percentile_value) writer.writerow(colnamesdisplay) for row in statistics: writer.writerow([row[colname] for colname in colnames]) return response
Map locations are stored in the session with key MAP_SESSION. It
contains a dictionary with fields x, y and zoom.
def map_location_save(request):
Save map layout in session.
- extent as strings (POST top, left, right, bottom).
- selected base layer name.
top = request.POST['top'] left = request.POST['left'] right = request.POST['right'] bottom = request.POST['bottom'] base_layer_name = request.POST['base_layer_name'] request.session[MAP_LOCATION] = {
'top': top, 'left': left, 'right': right, 'bottom': bottom} request.session[MAP_BASE_LAYER] = base_layer_name return HttpResponse("")
def map_location_load_default(request):
Return start_extent